Example #1
0
    def get_port(self):
        """
        Get application port for a given release. If pulling from private registry
        then use default port or read from ENV var, otherwise attempt to pull from
        the docker image
        """
        try:
            envs = self.config.values
            creds = self.get_registry_auth()

            if self.build.type == "buildpack":
                self.app.log('buildpack type detected. Defaulting to $PORT 5000')
                return 5000

            # application has registry auth - $PORT is required
            if (creds is not None) or (settings.REGISTRY_LOCATION != 'on-cluster'):
                if envs.get('PORT', None) is None:
                    if not self.app.appsettings_set.latest().routable:
                        return None
                    raise DryccException(
                        'PORT needs to be set in the application config '
                        'when using a private registry'
                    )

                # User provided PORT
                return int(envs.get('PORT'))

            # If the user provides PORT
            return int(envs.get('PORT', 5000))

        except Exception as e:
            raise DryccException(str(e)) from e
Example #2
0
    def path(self, request, *args, **kwargs):
        new_path = request.data.get('path')
        if new_path is None:
            raise DryccException("path is a required field")
        obj = self.get_object()
        container_types = [
            _ for _ in new_path.keys()
            if _ not in obj.app.types or _ not in obj.app.structure.keys()
        ]
        if container_types:
            raise DryccException(
                "process type {} is not included in profile".format(
                    ','.join(container_types)))

        if set(new_path.items()).issubset(set(obj.path.items())):
            raise DryccException("mount path not changed")

        other_volumes = self.get_app().volume_set.exclude(name=obj.name)
        type_paths = {}  # {'type1':[path1,path2], tyep2:[path3,path4]}
        for _ in other_volumes:
            for k, v in _.path.items():
                if k not in type_paths:
                    type_paths[k] = [v]
                else:
                    type_paths[k].append(k)
        repeat_path = [
            v for k, v in new_path.items() if v in type_paths.get(k, [])
        ]  # noqa
        if repeat_path:
            raise DryccException("path {} is used by another volume".format(
                ','.join(repeat_path)))
        path = obj.path
        pre_path = deepcopy(path)
        # merge mount path
        # remove path keys if a null value is provided
        for key, value in new_path.items():
            if value is None:
                # error if unsetting non-existing key
                if key not in path:
                    raise UnprocessableEntity(
                        '{} does not exist under {}'.format(key,
                                                            "volume"))  # noqa
                path.pop(key)
            else:
                path[key] = value
        obj.path = path  # after merge path
        obj.save()
        self.deploy(obj, pre_path)
        serializer = self.get_serializer(obj, many=False)
        return Response(serializer.data)
Example #3
0
    def create(self, user, *args, **kwargs):
        latest_release = self.app.release_set.filter(failed=False).latest()
        latest_version = self.app.release_set.latest().version
        try:
            new_release = latest_release.new(user,
                                             build=self,
                                             config=latest_release.config,
                                             source_version=self.version)
            self.app.deploy(new_release)
            return new_release
        except Exception as e:
            # check if the exception is during create or publish
            if ('new_release' not in locals()
                    and self.app.release_set.latest().version
                    == latest_version + 1):
                new_release = self.app.release_set.latest()
            if 'new_release' in locals():
                new_release.failed = True
                new_release.summary = "{} deployed {} which failed".format(
                    self.owner,
                    str(self.uuid)[:7])  # noqa
                # Get the exception that has occured
                new_release.exception = "error: {}".format(str(e))
                new_release.save()
            else:
                self.delete()

            raise DryccException(str(e)) from e
Example #4
0
    def set_tags(self, previous_config):
        """verify the tags exist on any nodes as labels"""
        if not self.tags:
            if settings.DRYCC_DEFAULT_CONFIG_TAGS:
                try:
                    tags = json.loads(settings.DRYCC_DEFAULT_CONFIG_TAGS)
                    self.tags = tags
                except json.JSONDecodeError as e:
                    logger.exception(e)
                    return
            else:
                return

        # Get all nodes with label selectors
        nodes = self._scheduler.node.get(labels=self.tags).json()
        if nodes['items']:
            return

        labels = ['{}={}'.format(key, value) for key, value in self.tags.items()]
        message = 'No nodes matched the provided labels: {}'.format(', '.join(labels))

        # Find out if there are any other tags around
        old_tags = getattr(previous_config, 'tags')
        if old_tags:
            old = ['{}={}'.format(key, value) for key, value in old_tags.items()]
            new = set(labels) - set(old)
            if new:
                message += ' - Addition of {} is the cause'.format(', '.join(new))

        raise DryccException(message)
Example #5
0
 def deploy(self, volume, pre_mount_path):
     app = self.get_app()
     latest_release = app.release_set.filter(failed=False).latest()
     latest_version = app.release_set.latest().version
     try:
         summary = "{user} changed volume mount for {volume}".\
             format(user=self.request.user, volume=volume.name)
         self.release = latest_release.new(self.request.user,
                                           config=latest_release.config,
                                           build=latest_release.build,
                                           summary=summary)
         # It's possible to mount volume before a build
         if self.release.build is not None:
             app.deploy(self.release)
     except Exception as e:
         if (not hasattr(self, 'release') and
                 app.release_set.latest().version == latest_version + 1):
             self.release = app.release_set.latest()
         if hasattr(self, 'release'):
             self.release.failed = True
             self.release.summary = "{} deploy with a volume that failed".\
                 format(self.request.user)  # noqa
             # Get the exception that has occured
             self.release.exception = "error: {}".format(str(e))
             self.release.save()
         volume.path = pre_mount_path
         volume.save()
         if isinstance(e, AlreadyExists):
             raise
         raise DryccException(str(e)) from e
Example #6
0
 def delete(self, *args, **kwargs):
     if self.binding == "Ready":
         raise DryccException("the plan is still binding")
     # Deatch ServiceInstance, updates k8s
     self.detach(*args, **kwargs)
     # Delete from DB
     return super(Resource, self).delete(*args, **kwargs)
Example #7
0
 def post_save(self, config):
     release = config.app.release_set.filter(failed=False).latest()
     latest_version = config.app.release_set.latest().version
     try:
         self.release = release.new(self.request.user,
                                    config=config,
                                    build=release.build)
         # It's possible to set config values before a build
         if self.release.build is not None:
             config.app.deploy(self.release)
     except Exception as e:
         if (not hasattr(self, 'release')
                 and config.app.release_set.latest().version
                 == latest_version + 1):
             self.release = config.app.release_set.latest()
         if hasattr(self, 'release'):
             self.release.failed = True
             self.release.summary = "{} deployed a config that failed".format(
                 self.request.user)  # noqa
             # Get the exception that has occured
             self.release.exception = "error: {}".format(str(e))
             self.release.save()
         else:
             config.delete()
         if isinstance(e, AlreadyExists):
             raise
         raise DryccException(str(e)) from e
Example #8
0
 def delete(self, *args, **kwargs):
     if self.path:
         raise DryccException("the volume is not unmounted")
     # Deatch volume, updates k8s
     self.detach()
     # Delete from DB
     return super(Volume, self).delete(*args, **kwargs)
Example #9
0
    def save(self, *args, **kwargs):
        previous_settings = None
        try:
            previous_settings = self.app.appsettings_set.latest()
        except AppSettings.DoesNotExist:
            pass

        try:
            self._update_routable(previous_settings)
            self._update_allowlist(previous_settings)
            self._update_autoscale(previous_settings)
            self._update_label(previous_settings)
        except (UnprocessableEntity, NotFound):
            raise
        except Exception as e:
            self.delete()
            raise DryccException(str(e)) from e

        if not self.summary and previous_settings:
            self.delete()
            raise AlreadyExists("{} changed nothing".format(self.owner))

        summary = ' '.join(self.summary)
        try:
            return super(AppSettings, self).save(**kwargs)
        finally:
            self.app.refresh()
        self.app.log('summary of app setting changes: {}'.format(summary),
                     logging.DEBUG)
Example #10
0
 def update(self, instance, validated_data):
     if instance.plan.split(':')[0] != validated_data.get('plan', '').split(':')[0]:  # noqa
         raise DryccException("the resource cann't changed")
     instance.plan = validated_data.get('plan')
     instance.options.update(validated_data.get('options', {}))
     instance.attach_update()
     instance.save()
     return instance
Example #11
0
 def run(self, request, **kwargs):
     app = self.get_object()
     if not request.data.get('command'):
         raise DryccException("command is a required field")
     volumes = request.data.get('volumes', None)
     if volumes:
         serializers.VolumeSerializer().validate_path(volumes)
     rc, output = app.run(self.request.user, request.data['command'],
                          volumes)
     return Response({'exit_code': rc, 'output': str(output)})
Example #12
0
 def unblock(self, request, **kwargs):
     try:
         models.blocklist.Blocklist.objects.filter(
             id=kwargs['id'],
             type=models.blocklist.Blocklist.get_type(
                 kwargs["type"])).delete()
         return HttpResponse(status=204)
     except ValueError as e:
         logger.info(e)
         raise DryccException("Unsupported block type: %s" % kwargs["type"])
Example #13
0
    def set_registry(self):
        # lower case all registry options for consistency
        self.registry = {key.lower(): value for key, value in self.registry.copy().items()}

        # PORT must be set if private registry is being used
        if self.registry and self.values.get('PORT', None) is None:
            # only thing that can get past post_save in the views
            raise DryccException(
                'PORT needs to be set in the config '
                'when using a private registry')
Example #14
0
 def unbind(self, *args, **kwargs):
     if not self.binding:
         raise DryccException("the resource is not binding")
     try:
         # We raise an exception when a resource doesn't exist
         self._scheduler.svcat.get_binding(self.app.id, self.name)
         self._scheduler.svcat.delete_binding(self.app.id, self.name)
         self.binding = None
         self.data = {}
         self.save()
     except KubeException as e:
         raise ServiceUnavailable("Could not unbind resource {} for application {}".format(self.name, self.app_id)) from e  # noqa
Example #15
0
    def attach(self, request, *args, **kwargs):
        try:
            if "domain" not in kwargs and not request.data.get('domain'):
                raise DryccException("domain is a required field")
            elif request.data.get('domain'):
                kwargs['domain'] = request.data['domain']

            self.get_object().attach(*args, **kwargs)
        except Http404:
            raise

        return Response(status=status.HTTP_201_CREATED)
Example #16
0
 def bind(self, *args, **kwargs):
     if self.status != "Ready":
         raise DryccException("the resource is not ready")
     if self.binding == "Ready":
         raise DryccException("the resource is binding")
     self.binding = "Binding"
     self.save()
     try:
         self._scheduler.svcat.get_binding(self.app.id, self.name)
         err = "Resource {} is binding".format(self.name)
         self.log(err, logging.INFO)
         raise AlreadyExists(err)
     except KubeException as e:
         logger.info(e)
         try:
             self._scheduler.svcat.create_binding(self.app.id, self.name,
                                                  **kwargs)
         except KubeException as e:
             msg = 'There was a problem binding the resource ' \
                   '{} for {}'.format(self.name, self.app_id)
             raise ServiceUnavailable(msg) from e
Example #17
0
 def block(self, request, **kwargs):
     try:
         blocklist, _ = models.blocklist.Blocklist.objects.get_or_create(
             id=kwargs['id'],
             type=models.blocklist.Blocklist.get_type(kwargs["type"]),
             defaults={"remark": request.data.get("remark")})
         for app in blocklist.related_apps:
             scale_app.delay(app, app.owner,
                             {key: 0
                              for key in app.structure.keys()})
         return HttpResponse(status=201)
     except ValueError as e:
         logger.info(e)
         raise DryccException("Unsupported block type: %s" % kwargs["type"])
Example #18
0
    def rollback(self, user, version=None):
        try:
            # if no version is provided then grab version from object
            version = (self.version - 1) if version is None else int(version)

            if version < 1:
                raise DryccException('version cannot be below 0')
            elif version == 1:
                raise DryccException('Cannot roll back to initial release.')

            prev = self.app.release_set.get(version=version)
            if prev.failed:
                raise DryccException('Cannot roll back to failed release.')
            latest_version = self.app.release_set.latest().version
            new_release = self.new(
                user,
                build=prev.build,
                config=prev.config,
                summary="{} rolled back to v{}".format(user, version),
                source_version='v{}'.format(version)
            )

            if self.build is not None:
                self.app.deploy(new_release, force_deploy=True)
            return new_release
        except Exception as e:
            # check if the exception is during create or publish
            if ('new_release' not in locals() and 'latest_version' in locals() and
                    self.app.release_set.latest().version == latest_version+1):
                new_release = self.app.release_set.latest()
            if 'new_release' in locals():
                new_release.failed = True
                new_release.summary = "{} performed roll back to a release that failed".format(self.owner)  # noqa
                # Get the exception that has occured
                new_release.exception = "error: {}".format(str(e))
                new_release.save()
            raise DryccException(str(e)) from e