Commit f20e7cfc authored by Lukas Burgey's avatar Lukas Burgey

Add polymorphic deployments

parent 549b7bef
......@@ -5,6 +5,7 @@ from django.contrib.auth.admin import UserAdmin
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin
from . import models
from .models import deployments
from .auth.v1.models import OIDCConfig
from .auth.v1.models.vo import VO, Group, Entitlement, EntitlementNameSpace
......@@ -54,8 +55,26 @@ admin.site.register(models.User, ClientAdmin)
admin.site.register(models.Site)
admin.site.register(models.Service)
admin.site.register(models.SSHPublicKey)
admin.site.register(models.CredentialState)
admin.site.register(models.Deployment)
admin.site.register(models.DeploymentState)
@admin.register(deployments.VODeployment)
class VODeploymentAdmin(PolymorphicChildModelAdmin):
show_in_index = True
@admin.register(deployments.ServiceDeployment)
class ServiceDeploymentAdmin(PolymorphicChildModelAdmin):
show_in_index = True
@admin.register(deployments.Deployment)
class DeploymentAdmin(PolymorphicChildModelAdmin):
base_model = Entitlement # Explicitly set here!
show_in_index = True # makes child model admin visible in main admin site
child_models = (deployments.VODeployment, deployments.ServiceDeployment)
admin.site.register([
deployments.DeploymentState,
deployments.CredentialState,
])
......@@ -40,7 +40,6 @@ class EntitlementSerializer(serializers.ModelSerializer):
fields = VO_FIELDS + (
'name_space',
'group_authority',
'role',
)
......
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('backend', '0025_auto_20181121_1149'),
]
operations = [
migrations.DeleteModel('Deployment'),
migrations.DeleteModel('DeploymentState'),
migrations.DeleteModel('CredentialState'),
]
# Generated by Django 2.1.3 on 2018-11-26 12:14
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django_mysql.models
import feudal.backend.models.deployments
class Migration(migrations.Migration):
dependencies = [
('contenttypes', '0002_remove_content_type_name'),
('backend', '0026_delete_deployment'),
]
operations = [
migrations.CreateModel(
name='CredentialState',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('state_target', models.CharField(choices=[('deployed', 'Deployed'), ('not_deployed', 'Not Deployed')], default='not_deployed', max_length=50)),
('state', models.CharField(choices=[('deployment_pending', 'VODeployment Pending'), ('removal_pending', 'Removal Pending'), ('deployed', 'Deployed'), ('not_deployed', 'Not Deployed'), ('questionnaire', 'Questionnaire'), ('failed', 'Failed'), ('rejected', 'Rejected')], default='not_deployed', max_length=50)),
('_credential_deleted', models.BooleanField(default=False)),
('credential', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='credential_states', to='backend.SSHPublicKey')),
],
),
migrations.CreateModel(
name='Deployment',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('state_target', models.CharField(choices=[('deployed', 'Deployed'), ('not_deployed', 'Not Deployed')], default='not_deployed', max_length=50)),
('is_active', models.BooleanField(default=True)),
],
options={
'abstract': False,
'base_manager_name': 'objects',
},
),
migrations.CreateModel(
name='DeploymentState',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('state', models.CharField(choices=[('deployment_pending', 'VODeployment Pending'), ('removal_pending', 'Removal Pending'), ('deployed', 'Deployed'), ('not_deployed', 'Not Deployed'), ('questionnaire', 'Questionnaire'), ('failed', 'Failed'), ('rejected', 'Rejected')], default='not_deployed', max_length=50)),
('message', models.TextField(default='', max_length=300)),
('questionnaire', django_mysql.models.JSONField(blank=True, default=feudal.backend.models.deployments.questionnaire_default, null=True)),
('credentials', django_mysql.models.JSONField(blank=True, default=feudal.backend.models.deployments.credential_default, null=True)),
],
),
migrations.CreateModel(
name='ServiceDeployment',
fields=[
('deployment_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='backend.Deployment')),
('service', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='service_deployments', to='backend.Service')),
],
options={
'abstract': False,
'base_manager_name': 'objects',
},
bases=('backend.deployment',),
),
migrations.CreateModel(
name='VODeployment',
fields=[
('deployment_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='backend.Deployment')),
('vo', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vo_deployments', to='backend.VO')),
],
options={
'abstract': False,
'base_manager_name': 'objects',
},
bases=('backend.deployment',),
),
migrations.AddField(
model_name='deploymentstate',
name='parent',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='state_items', to='backend.Deployment'),
),
migrations.AddField(
model_name='deploymentstate',
name='service',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='state_items', to='backend.Service'),
),
migrations.AddField(
model_name='deploymentstate',
name='site',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='state_items', to='backend.Site'),
),
migrations.AddField(
model_name='deploymentstate',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='state_items', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='deployment',
name='polymorphic_ctype',
field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_backend.deployment_set+', to='contenttypes.ContentType'),
),
migrations.AddField(
model_name='deployment',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='deployments', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='credentialstate',
name='target',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='credential_states', to='backend.DeploymentState'),
),
]
This diff is collapsed.
This diff is collapsed.
# stupid python circular imports
# pylint: disable=wrong-import-position
from rest_framework import serializers
from rest_polymorphic.serializers import PolymorphicSerializer
from feudal.backend.auth.v1.models.serializers.vo import VOSerializer
from ... import models
from ...models import Site, Service
from ..users import User, SSHPublicKey
from ..deployments import CredentialState, DeploymentState, Deployment, VODeployment, ServiceDeployment
# export
from .clients import RabbitMQInstanceSerializer
class SSHPublicKeySerializer(serializers.ModelSerializer):
class Meta:
model = models.SSHPublicKey
model = SSHPublicKey
fields = [
'id',
'name',
......@@ -19,7 +24,7 @@ class SSHPublicKeySerializer(serializers.ModelSerializer):
class SSHPublicKeyRefSerializer(serializers.ModelSerializer):
class Meta:
model = models.SSHPublicKey
model = SSHPublicKey
fields = [
'id',
'name',
......@@ -28,7 +33,7 @@ class SSHPublicKeyRefSerializer(serializers.ModelSerializer):
class CredentialSerializer(serializers.ModelSerializer):
class Meta:
model = models.SSHPublicKey
model = SSHPublicKey
fields = [
'id',
'name',
......@@ -36,11 +41,18 @@ class CredentialSerializer(serializers.ModelSerializer):
]
CREDENTIAL_SERIALIZER = serializers.DictField(
child=serializers.ListField(
child=CredentialSerializer()
)
)
class CredentialStateSerializer(serializers.ModelSerializer):
credential = SSHPublicKeyRefSerializer()
class Meta:
model = models.CredentialState
model = CredentialState
fields = [
'state',
'state_target',
......@@ -49,6 +61,128 @@ class CredentialStateSerializer(serializers.ModelSerializer):
]
# "exports"
from .webpage import DeploymentSerializer
from .clients import RabbitMQInstanceSerializer
class SiteSerializer(serializers.ModelSerializer):
class Meta:
model = Site
fields = ['id', 'name']
class ServiceSerializer(serializers.ModelSerializer):
site = SiteSerializer()
vos = VOSerializer(many=True)
class Meta:
model = Service
fields = [
'id',
'name',
'site',
'vos',
'description',
]
class DeploymentStateSerializer(serializers.ModelSerializer):
service = ServiceSerializer()
site = SiteSerializer()
questionnaire = serializers.JSONField()
credentials = serializers.JSONField()
vo = VOSerializer()
credential_states = CredentialStateSerializer(many=True)
class Meta:
model = DeploymentState
fields = [
'state',
'id',
'site',
'questionnaire',
'credentials',
'service',
'vo',
'message',
'credential_states',
'is_credential_pending',
'is_pending',
]
DEPLOYMENT_FIELDS = (
'id',
'state_target',
'id',
'state_items',
'credentials',
)
class AbstractDeploymentSerializer(serializers.ModelSerializer):
credentials = CredentialSerializer
state_items = DeploymentStateSerializer(many=True)
class Meta:
model = Deployment
fields = DEPLOYMENT_FIELDS
class VODeploymentSerializer(serializers.ModelSerializer):
class Meta:
model = VODeployment
fields = DEPLOYMENT_FIELDS + (
'vo',
'services',
)
class ServiceDeploymentSerializer(serializers.ModelSerializer):
class Meta:
model = ServiceDeployment
fields = DEPLOYMENT_FIELDS + (
'service',
)
class DeploymentSerializer(PolymorphicSerializer):
model_serializer_mapping = {
Deployment: AbstractDeploymentSerializer,
VODeployment: VODeploymentSerializer,
ServiceDeployment: ServiceDeploymentSerializer,
}
# contains properties which change less often
class UserSerializer(serializers.ModelSerializer):
vos = VOSerializer(many=True)
ssh_keys = SSHPublicKeySerializer(many=True)
class Meta:
model = User
fields = [
'profile_name',
'vos',
'id',
'ssh_keys',
'userinfo',
]
class UserStateSerializer(serializers.ModelSerializer):
vos = VOSerializer(many=True)
ssh_keys = SSHPublicKeySerializer(many=True)
deployments = DeploymentSerializer(many=True)
services = ServiceSerializer(many=True)
class Meta:
model = User
fields = [
'deployments',
'profile_name',
'vos',
'id',
'ssh_keys',
'userinfo',
'services',
]
......@@ -7,14 +7,6 @@ from rest_framework import serializers
from feudal.backend.auth.v1.models.serializers.vo import VOSerializer
from ... import models
from ..serializers import CredentialSerializer
CredentialSerializer = serializers.DictField(
child=serializers.ListField(
child=CredentialSerializer()
)
)
class ServiceSerializer(serializers.ModelSerializer):
......@@ -38,22 +30,6 @@ class UserSerializer(serializers.ModelSerializer):
]
class DeploymentSerializer(serializers.ModelSerializer):
user = UserSerializer()
vo = VOSerializer()
credentials = CredentialSerializer
class Meta:
model = models.Deployment
fields = [
'state_target',
'id',
'vo',
'user',
'credentials',
]
class RabbitMQInstanceSerializer(serializers.ModelSerializer):
class Meta:
model = models.RabbitMQInstance
......
# we don't need to deserialize, so we do not implement the abstract methods
# pylint: disable=abstract-method
from rest_framework import serializers
from feudal.backend.auth.v1.models.serializers.vo import VOSerializer
from ... import models
from .. import serializers as backend_serializers
class SiteSerializer(serializers.ModelSerializer):
class Meta:
model = models.Site
fields = ['id', 'name']
class ServiceSerializer(serializers.ModelSerializer):
site = SiteSerializer()
vos = VOSerializer(many=True)
class Meta:
model = models.Service
fields = [
'id',
'name',
'site',
'vos',
'description',
]
class DeploymentStateSerializer(serializers.ModelSerializer):
service = ServiceSerializer()
site = SiteSerializer()
questionnaire = serializers.JSONField()
credentials = serializers.JSONField()
vo = VOSerializer()
credential_states = backend_serializers.CredentialStateSerializer(many=True)
class Meta:
model = models.DeploymentState
fields = [
'state',
'id',
'site',
'questionnaire',
'credentials',
'service',
'vo',
'message',
'credential_states',
'is_credential_pending',
'is_pending',
]
class DeploymentSerializer(serializers.ModelSerializer):
services = ServiceSerializer(many=True)
state_items = DeploymentStateSerializer(many=True)
class Meta:
model = models.Deployment
fields = [
'state_target',
'services',
'vo',
'id',
'state_items',
]
# contains properties which change less often
class UserSerializer(serializers.ModelSerializer):
vos = VOSerializer(many=True)
ssh_keys = backend_serializers.SSHPublicKeySerializer(many=True)
class Meta:
model = models.User
fields = [
'profile_name',
'vos',
'id',
'ssh_keys',
'userinfo',
]
class UserStateSerializer(serializers.ModelSerializer):
vos = VOSerializer(many=True)
ssh_keys = backend_serializers.SSHPublicKeySerializer(many=True)
deployments = DeploymentSerializer(many=True)
services = ServiceSerializer(many=True)
class Meta:
model = models.User
fields = [
'deployments',
'profile_name',
'vos',
'id',
'ssh_keys',
'userinfo',
'services',
]
......@@ -389,15 +389,19 @@ class User(AbstractUser):
# check if the user has deactivated deployments for this exact vo
# if yes: reactivate the deployments
for dep in self.deployments.filter(vo=vo):
# TODO this does nothing for ServiceDeployments
for dep in self.deployments.filter(vodeployment__vo=vo):
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)
# TODO this does nothing for ServiceDeployments
# check if the user has deployments which need member ship of this vo
# if yes remove them
for dep in self.deployments.filter(vo=vo):
for dep in self.deployments.filter(vodeployment__vo=vo):
LOGGER.debug('user_changed_vo_removed: need to deactivate deployment %s', dep)
......
......@@ -8,8 +8,11 @@ from rest_framework.response import Response
from feudal.backend.auth.v1.models.vo import VO, Group, Entitlement
from ..models.serializers.clients import RabbitMQInstanceSerializer, DeploymentSerializer
from .. import models
from ..models import Site, Service
from ..models.brokers import RabbitMQInstance
from ..models.deployments import DeploymentState
from ..models.serializers import RabbitMQInstanceSerializer, DeploymentSerializer
LOGGER = logging.getLogger(__name__)
......@@ -69,7 +72,7 @@ class ResponseView(views.APIView):
return response_view_error(err)
return Response({})
except models.DeploymentState.DoesNotExist:
except DeploymentState.DoesNotExist:
LOGGER.error('[ResponseView] No matching DStateItem')
return response_view_error('no matching DeploymentState')
......@@ -93,7 +96,7 @@ class ConfigurationView(views.APIView):
description = group_service.get('description', None)
service = models.Service.get_service(
service = Service.get_service(
name,
site,
description=description,
......@@ -120,7 +123,7 @@ class ConfigurationView(views.APIView):
description = entitlement_service.get('description', '')
service = models.Service.get_service(
service = Service.get_service(
name,
site,
description=description,
......@@ -138,7 +141,7 @@ class ConfigurationView(views.APIView):
client_site = None
try:
client_site = request.user.site
except models.Site.DoesNotExist:
except Site.DoesNotExist:
raise ImproperlyConfigured("client has no site")
group_to_services = request.data.get('group_to_services', None)
......@@ -152,7 +155,7 @@ class ConfigurationView(views.APIView):
# TODO deactivate vanished services
# initialize the broker, just in case
broker = models.RabbitMQInstance.load()
broker = RabbitMQInstance.load()
broker.initialize()
response = {
......@@ -173,7 +176,7 @@ class DeregisterView(views.APIView):
client_site = None
try:
client_site = request.user.site
except models.Site.DoesNotExist:
except Site.DoesNotExist:
raise ImproperlyConfigured('client has no site')
# deregister the client / its services / its site
......
......@@ -2,16 +2,13 @@
import logging
from django.contrib.auth import authenticate
from django.contrib.auth.models import Group
from django.shortcuts import get_object_or_404
from rest_framework import status
from rest_framework import views
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from .. import models
from ..models import serializers as model_serializers
from ..models.serializers import webpage as serializers
from ..models import serializers
LOGGER = logging.getLogger(__name__)
......@@ -68,7 +65,7 @@ class ProvisioningView(views.APIView):
if user is None:
return _error_response("Unable to authenticate user")
LOGGER.debug("USER-RESTAPI: authenticated user %s using access token", user)
......
......@@ -11,8 +11,7 @@ from rest_framework.response import Response
from feudal.backend.auth.v1.models.vo import VO
from .. import models
from ..models import serializers as model_serializers
from ..models.serializers import webpage as serializers
from ..models import serializers
LOGGER = logging.getLogger(__name__)
......@@ -77,7 +76,7 @@ class SSHPublicKeyView(views.APIView):
request.user.user_changed_key_added(key)
return Response(
model_serializers.SSHPublicKeySerializer(
serializers.SSHPublicKeySerializer(
key,
).data
)
......@@ -95,7 +94,7 @@ class SSHPublicKeyView(views.APIView):
request.user.user_changed_key_added(key)
return Response(
model_serializers.SSHPublicKeySerializer(
serializers.SSHPublicKeySerializer(
key,
).data
)
......@@ -110,7 +109,7 @@ class DeploymentView(views.APIView):
'type' not in request.data or
'vo' not in request.data
):
LOGGER.error("Deployment api got malformed request %s: request misses fields (should have: 'type', and 'vo')", request.data)
LOGGER.error("VODeployment api got malformed request %s: request misses fields (should have: 'type', and 'vo')", request.data)
return _api_error_response('malformed request')
request_type = request.data['type']
......@@ -118,7 +117,7 @@ class DeploymentView(views.APIView):
deployment = None
if 'vo' in request.data:
try:
deployment = models.Deployment.get_deployment(
deployment = VODeployment.get_deployment(
request.user,
VO.objects.get(id=request.data['vo']),
)
......
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