Commit 0fb63401 authored by Lukas Burgey's avatar Lukas Burgey
Browse files

Refactor the models

parent fbf2c756
......@@ -30,6 +30,28 @@ STATE_CHOICES = (
('rejected', 'Rejected'),
)
def exchanges_default():
return []
def user_info_default():
return {}
def questionnaire_default():
return {}
def credential_default():
return {}
# takes a list of states
# return '<state>' if all states are equal to '<state>'
# else it returns 'mixed'
def analyze_states(states):
_state = ''
for state in states:
if _state == '':
_state = state
elif _state != state:
return 'mixed'
return _state
# singleton for simple configs
# https://steelkiwi.com/blog/practical-application-singleton-design-pattern/
......@@ -54,22 +76,6 @@ class SingletonModel(models.Model):
obj.set_cache()
return cache.get(cls.__name__)
def exchanges_default():
return []
# takes a list of states
# return '<state>' if all states are equal to '<state>'
# else it returns 'mixed'
def analyze_states(states):
_state = ''
for state in states:
if _state == '':
_state = state
elif _state != state:
return 'mixed'
return _state
# clients are registerred at rabbitmq, when they are assigned to a site
# (because we only then know what services they provide)
......@@ -172,7 +178,7 @@ class RabbitMQInstance(SingletonModel):
channel = self._connection.channel()
channel.confirm_delivery()
return channel
except ConnectionClosed as exception:
except ConnectionClosed:
# reinitialize the connection
self._init_connection()
return self._channel
......@@ -219,14 +225,6 @@ class RabbitMQInstance(SingletonModel):
)
def user_info_default():
return {}
def questionnaire_default():
return {}
def credential_default():
return {}
class User(AbstractUser):
TYPE_CHOICES = (
('apiclient', 'API-Client'),
......@@ -275,10 +273,10 @@ class User(AbstractUser):
def profile_name(self):
if 'email' in self.userinfo:
return self.userinfo['email']
elif 'name':
elif 'name' in self.userinfo:
return self.userinfo['name']
else:
return self.id
return self.id
@property
def deployment_states(self):
......@@ -302,22 +300,21 @@ class User(AbstractUser):
@classmethod
def get_user(cls, userinfo, idp):
if 'sub' not in userinfo:
raise Exception('get_user needs a userinfo which contains the users subject')
raise ValueError('get_user needs a userinfo which contains the users subject')
query = cls.objects.filter(
sub=userinfo['sub'],
idp=idp,
)
if not query.exists():
return cls.construct_from_userinfo(userinfo, idp)
if len(query) > 1:
return Exception('Two user instances with same subject from the same idp')
try:
user = cls.objects.get(
sub=userinfo['sub'],
idp=idp,
)
user.update_userinfo(userinfo)
user.save()
return user
except cls.DoesNotExist:
return cls.construct_from_userinfo(userinfo, idp)
user = query.first()
user.update_userinfo(userinfo)
user.save()
return user
@classmethod
def construct_from_userinfo(cls, userinfo, idp):
......@@ -372,7 +369,7 @@ class User(AbstractUser):
elif self.user_type == 'apiclient':
try:
return 'APICLIENT {}@{}'.format(self.username, self.site)
except:
except Site.DoesNotExist:
return 'APICLIENT {}'.format(self.username)
else:
......@@ -431,38 +428,32 @@ class User(AbstractUser):
raise Exception('Missing attribute in userinfo: sub')
groups = userinfo.get('groups', [])
# FIXME probably inefficient
self.groups.clear()
for group_name in groups:
query = Group.objects.filter(name=group_name)
if not query.exists():
LOGGER.info("Adding group %s", group_name)
try:
group = Group.objects.get(name=group_name)
self.groups.add(group)
except Group.DoesNotExist:
LOGGER.info('Adding group %s', group_name)
group = Group(name=group_name)
group.save()
self.groups.add(group)
else:
for group in query.all():
self.groups.add(group)
# include the ssh key from unity
unity_key = userinfo.get('ssh_key', '')
unity_key_value = userinfo.get('ssh_key', '')
unity_key_name = 'unity_key'
query = self._ssh_keys.filter(name=unity_key_name)
if query.exists():
key = query.first()
if key.key != unity_key:
try:
key = self._ssh_keys.get(name=unity_key_name)
if key.key != unity_key_value:
key.delete_key()
key = SSHPublicKey(
name=unity_key_name,
key=unity_key,
user=self,
)
key.save()
else:
raise SSHPublicKey.DoesNotExist()
except SSHPublicKey.DoesNotExist:
key = SSHPublicKey(
name=unity_key_name,
key=unity_key,
key=unity_key_value,
user=self,
)
key.save()
......@@ -534,7 +525,7 @@ class Service(models.Model):
for user in User.objects.filter(
deployments__group=group,
).distinct():
LOGGER.debug(user.msg("New service for group. Adding to deployment"))
LOGGER.debug(user.msg('New service for group. Adding to deployment'))
# all group deployments have the same keys
# TODO check that assumption
......@@ -543,8 +534,6 @@ class Service(models.Model):
deployment.first().service_added(self)
class SSHPublicKey(models.Model):
name = models.CharField(
max_length=150,
......@@ -611,7 +600,7 @@ class SSHPublicKey(models.Model):
def __str__(self):
if self.deleted:
return "DELETED: {}".format(self.name)
return 'DELETED: {}'.format(self.name)
return self.name
def msg(self, msg):
......@@ -679,38 +668,39 @@ class Deployment(models.Model):
# if it does not exist it is created
@classmethod
def get_deployment(cls, user, service=None, group=None):
query = None
if service is not None:
query = cls.objects.filter(
user=user,
service=service,
)
elif group is not None:
query = cls.objects.filter(
user=user,
group=group,
)
else:
raise Exception("need either a group or service for the deployment")
if query.exists():
return query.first()
try:
if service is not None:
return cls.objects.get(
user=user,
service=service,
)
deployment = None
if service is not None:
deployment = cls(
user=user,
service=service,
)
elif group is not None:
deployment = cls(
user=user,
group=group,
)
elif group is not None:
return cls.objects.get(
user=user,
group=group,
)
else:
raise ValueError('Unable to create Deployment without service and group')
except cls.DoesNotExist:
deployment = None
if service is not None:
deployment = cls(
user=user,
service=service,
)
elif group is not None:
deployment = cls(
user=user,
group=group,
)
if not group.services.exists():
LOGGER.info(deployment.msg('No services for group'))
deployment.save()
LOGGER.debug(deployment.msg('created'))
return deployment
deployment.save()
LOGGER.debug(deployment.msg('created'))
return deployment
# deploy credentials which were deployed prior to deactivation
def activate(self):
......@@ -752,7 +742,7 @@ class Deployment(models.Model):
def service_added(self, service):
# a new service for this group was added and we may have to deploy some keys
LOGGER.debug(self.msg("Adding service {}".format(service)))
LOGGER.debug(self.msg('Adding service {}'.format(service)))
for state in self.states.all():
state.service_added(service)
......@@ -850,18 +840,15 @@ class DeploymentState(models.Model):
@classmethod
def get_state(cls, deployment, key):
# check if a state does already exist
query = cls.objects.filter(
deployment=deployment,
key=key,
)
state = None
if query.exists():
if len(query) == 1:
state = query.first()
else:
raise Exception("more than one state for a key")
else:
# create new state if not
try:
state = cls.objects.get(
deployment=deployment,
key=key,
)
return state
except cls.DoesNotExist:
state = cls(
deployment=deployment,
key=key,
......@@ -869,6 +856,13 @@ class DeploymentState(models.Model):
state.save()
LOGGER.debug(state.msg('created'))
except cls.MultipleObjectsReturned:
LOGGER.error(
deployment.msg('to many DeploymentState objects for key {}'.format(key.name))
)
raise
# generate state items
if deployment.service is not None:
for site in deployment.service.site.all():
......@@ -893,7 +887,7 @@ class DeploymentState(models.Model):
return state
def service_added(self, service):
LOGGER.debug(self.msg("Adding service {}".format(service)))
LOGGER.debug(self.msg('Adding service {}'.format(service)))
for site in service.site.all():
# create new DeploymentStateItems
item = DeploymentStateItem.get_state_item(
......@@ -935,7 +929,7 @@ class DeploymentState(models.Model):
msg,
)
else:
LOGGER.error("Deployment as neither a group or a service")
LOGGER.error('Deployment as neither a group or a service')
# update the state of the remote webpage
def publish_to_user(self):
......@@ -959,12 +953,12 @@ class DeploymentState(models.Model):
def __str__(self):
if self.service is not None:
return "{}:{}#{}".format(
return '{}:{}#{}'.format(
self.service,
self.key.name,
self.id,
)
return "{}:{}#{}".format(
return '{}:{}#{}'.format(
self.group,
self.key.name,
self.id,
......@@ -1029,16 +1023,12 @@ class DeploymentStateItem(models.Model):
@classmethod
def get_state_item(cls, parent=None, site=None, service=None):
query = parent.state_items.filter(
site=site,
service=service,
)
if query.exists():
if len(query) == 1:
return query.first()
else:
raise Exception("ambiguous DeploymentStateItem")
else:
try:
return parent.state_items.get(
site=site,
service=service,
)
except cls.DoesNotExist:
item = cls(
parent=parent,
site=site,
......@@ -1049,7 +1039,6 @@ class DeploymentStateItem(models.Model):
return item
# STATE transitions
# user: deployment requested
def user_deploy(self):
......@@ -1146,14 +1135,14 @@ class DeploymentStateItem(models.Model):
def __str__(self):
if self.group is not None:
return "{}:{}@{}:{}#{}".format(
return '{}:{}@{}:{}#{}'.format(
self.group,
self.service,
self.site,
self.key.name,
self.id,
)
return "{}:@{}:{}#{}".format(
return '{}:@{}:{}#{}'.format(
self.service,
self.site,
self.key.name,
......
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