Commit d84890f9 authored by Lukas Burgey's avatar Lukas Burgey

Implement simultaneous VO and Service Deployments

Makes Deployment and DeploymentState many to many.
parent 5197f6c6
# Generated by Django 2.1.3 on 2018-11-28 13:00
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('backend', '0027_auto_20181126_1314'),
]
operations = [
migrations.RemoveField(
model_name='deploymentstate',
name='parent',
),
migrations.AddField(
model_name='deploymentstate',
name='deployments',
field=models.ManyToManyField(related_name='states', to='backend.Deployment'),
),
migrations.AlterField(
model_name='deploymentstate',
name='service',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='states', to='backend.Service'),
),
migrations.AlterField(
model_name='deploymentstate',
name='site',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='states', to='backend.Site'),
),
migrations.AlterField(
model_name='deploymentstate',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='states', to=settings.AUTH_USER_MODEL),
),
]
......@@ -43,15 +43,19 @@ class Site(models.Model):
# ignores orphaned deployment states
dep_states = [
state
for state in self.state_items.all()
for state in self.states.all()
if (
state.is_pending or state.is_credential_pending
) and state.parent is not None
) and state.deployments.exists()
]
# make the deployments unique here
for item in dep_states:
deployments[item.parent.id] = item.parent
for state in dep_states:
# we filter for deployments of the same state_target here
# if we have multiple deployments per deployment state this causes only the
# deployments with the same state_target to be pending
for deployment in state.deployments.filter(state_target=state.state_target):
deployments[deployment.id] = deployment
return deployments.values()
......
This diff is collapsed.
......@@ -72,28 +72,29 @@ class DeploymentStateSerializer(serializers.ModelSerializer):
class Meta:
model = DeploymentState
fields = [
'state',
'id',
'site',
'questionnaire',
'credentials',
'service',
'message',
'credential_states',
'credentials',
'id',
'is_credential_pending',
'is_pending',
'message',
'questionnaire',
'service',
'site',
'state',
'state_target',
]
DEPLOYMENT_FIELDS = (
'state_target',
'id',
'state_items',
'states',
)
class AbstractDeploymentSerializer(serializers.ModelSerializer):
state_items = DeploymentStateSerializer(many=True)
states = DeploymentStateSerializer(many=True)
class Meta:
model = Deployment
......@@ -101,7 +102,7 @@ class AbstractDeploymentSerializer(serializers.ModelSerializer):
class VODeploymentSerializer(serializers.ModelSerializer):
state_items = DeploymentStateSerializer(many=True)
states = DeploymentStateSerializer(many=True)
vo = VOSerializer()
services = ServiceSerializer(many=True)
......@@ -114,7 +115,7 @@ class VODeploymentSerializer(serializers.ModelSerializer):
class ServiceDeploymentSerializer(serializers.ModelSerializer):
state_items = DeploymentStateSerializer(many=True)
states = DeploymentStateSerializer(many=True)
service = ServiceSerializer()
class Meta:
model = ServiceDeployment
......
......@@ -17,7 +17,7 @@ class DeploymentTest(TestCase):
def deployment_run(self, deployment, service_count):
self.assertIsNotNone(deployment)
self.assertEqual(len(deployment.services), service_count)
self.assertEqual(deployment.state_items.count(), service_count)
self.assertEqual(deployment.states.count(), service_count)
self.assertEqual(deployment.state_target, models.NOT_DEPLOYED)
if service_count > 0:
......@@ -28,15 +28,15 @@ class DeploymentTest(TestCase):
if service_count > 0:
self.assertEqual(deployment.state, models.DEPLOYMENT_PENDING)
self.assertFalse(deployment.target_reached)
for item in deployment.state_items.all():
for item in deployment.states.all():
self.assertEqual(item.state, models.DEPLOYMENT_PENDING)
else:
self.assertEqual(deployment.state, models.DEPLOYED)
self.assertTrue(deployment.target_reached)
# execute deployment
LOGGER.debug('deployment_run: %s state items', deployment.state_items.count())
for item in deployment.state_items.all():
LOGGER.debug('deployment_run: %s state items', deployment.states.count())
for item in deployment.states.all():
item.client_response({'state': models.DEPLOYED})
self.assertEqual(item.state, models.DEPLOYED)
......@@ -49,14 +49,14 @@ class DeploymentTest(TestCase):
if service_count > 0:
self.assertEqual(deployment.state, models.REMOVAL_PENDING)
self.assertFalse(deployment.target_reached)
for item in deployment.state_items.all():
for item in deployment.states.all():
self.assertEqual(item.state, models.REMOVAL_PENDING)
else:
self.assertEqual(deployment.state, models.NOT_DEPLOYED)
self.assertTrue(deployment.target_reached)
# execute removals
for item in deployment.state_items.all():
for item in deployment.states.all():
item.client_response({'state': models.NOT_DEPLOYED})
self.assertEqual(item.state, models.NOT_DEPLOYED)
......@@ -69,13 +69,13 @@ class DeploymentTest(TestCase):
self.assertEqual(deployment.state, models.NOT_DEPLOYED)
self.assertEqual(deployment.state_target, models.NOT_DEPLOYED)
self.assertEqual(len(deployment.services), service_count)
self.assertEqual(deployment.state_items.count(), service_count)
self.assertEqual(deployment.states.count(), service_count)
# start deployment
deployment.user_deploy()
if service_count == 0:
self.assertFalse(deployment.state_items.exists())
self.assertFalse(deployment.states.exists())
self.assertTrue(deployment.target_reached)
# add service
......@@ -86,14 +86,14 @@ class DeploymentTest(TestCase):
)
deployment.service_added(delayed_service)
for item in deployment.state_items.all():
for item in deployment.states.all():
self.assertEqual(item.state, models.DEPLOYMENT_PENDING)
self.assertEqual(deployment.state_items.count(), service_count + 1)
self.assertEqual(deployment.states.count(), service_count + 1)
self.assertEqual(deployment.state, models.DEPLOYMENT_PENDING)
# execute deployment
for item in deployment.state_items.all():
for item in deployment.states.all():
item.client_response({'state': models.DEPLOYED})
# the deployment was not done for new_service
......@@ -105,7 +105,7 @@ class DeploymentTest(TestCase):
self.assertEqual(deployment.state, models.REMOVAL_PENDING)
# execute removals
for item in deployment.state_items.all():
for item in deployment.states.all():
self.assertEqual(item.state, models.REMOVAL_PENDING)
item.client_response({'state': models.NOT_DEPLOYED})
......
......@@ -238,7 +238,7 @@ class User(AbstractUser):
# find which sites need to be notified
for dep in self.deployments.all():
for state_item in dep.state_items.all():
for state_item in dep.states.all():
if state_item.state != 'not_deployed':
sites.append(state_item.site)
......@@ -265,8 +265,6 @@ class User(AbstractUser):
# check if user needs to be in this entitlement
if not self.vos.filter(name=rem_ent_name, idp=self.idp).exists():
LOGGER.info(self.msg('New: %s'), ent)
self.vos.add(ent)
self.user_changed_vo_added(ent)
......@@ -290,8 +288,6 @@ class User(AbstractUser):
# check if user needs to be in this group
if not self.vos.filter(name=group_name, idp=self.idp).exists():
LOGGER.info(self.msg('New: %s'), group)
self.user_changed_vo_added(group)
self.vos.add(group)
......@@ -370,22 +366,22 @@ class User(AbstractUser):
LOGGER.info('user_changed')
def user_changed_key_added(self, key):
LOGGER.debug('user_changed_key_added: %s %s', self, key)
LOGGER.debug(self.msg('Added: {}'.format(key)))
for dep in self.deployments.all():
dep.user_credential_added(key)
def user_remove_key(self, key):
LOGGER.debug(self.msg('Remove: {}'.format(key)))
if key.delete_key():
return
LOGGER.debug('user_changed_key_removed: %s %s', self, key)
for dep in self.deployments.all():
dep.user_credential_removed(key)
def user_changed_vo_added(self, vo):
LOGGER.debug('user_changed_vo_added: %s %s', self, vo)
LOGGER.debug(self.msg('Added: {}'.format(vo)))
# check if the user has deactivated deployments for this exact vo
# if yes: reactivate the deployments
......@@ -395,7 +391,7 @@ class User(AbstractUser):
LOGGER.debug('user_changed_vo_added: need to activate deployment %s', dep)
def user_changed_vo_removed(self, vo):
LOGGER.debug('user_changed_vo_removed: %s %s', self, vo)
LOGGER.debug(self.msg('Removed: {}'.format(vo)))
# TODO this does nothing for ServiceDeployments
......
......@@ -35,10 +35,10 @@ class ResponseView(views.APIView):
def post(self, request):
client_site = request.user.site
output = request.data.get('output', {})
state_id = request.data.get('id', None)
deployment_id = request.data.get('id', None)
_service = request.data.get('service', {})
if state_id is None:
if deployment_id is None:
err = 'no state id'
LOGGER.error('Error parsing response from %s: %s', request.user, err)
return response_view_error(err)
......@@ -60,12 +60,13 @@ class ResponseView(views.APIView):
# find the corresponding DeploymentState for this response
try:
state_item = client_site.state_items.get(
parent__id=int(state_id),
deployment_state = client_site.states.get(
# TODO this query might not work
deployments__id=int(deployment_id),
site=client_site,
service=service,
)
err = state_item.client_response(output)
err = deployment_state.client_response(output)
if err is not None:
LOGGER.error('[ResponseView] Error parsing response from %s: %s', request.user, err)
return response_view_error(err)
......
......@@ -47,6 +47,7 @@ class ProvisioningView(views.APIView):
issuer_uri = request.data.get('iss', '')
key_name = request.data.get('key_name', '')
service_name = request.data.get('s', '')
state_target = request.data.get('state_target', deployments.DEPLOYED)
user = authenticate(
request,
......@@ -91,7 +92,12 @@ class ProvisioningView(views.APIView):
deployment = deployments.get_deployment(user, service=service)
if deployment is not None:
deployment.user_deploy()
if state_target == deployments.DEPLOYED:
deployment.user_deploy()
elif state_target == deployments.NOT_DEPLOYED:
deployment.user_remove()
else:
return _error_response(request, 'Invalid state_target "{}"'.format(state_target))
return Response(
serializers.DeploymentSerializer(deployment).data,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment