Esempio n. 1
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 DeisException('version cannot be below 0')
            elif version == 1:
                raise DeisException('Cannot roll back to initial release.')

            prev = self.app.release_set.get(version=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:
            if 'new_release' in locals():
                new_release.delete()
            raise DeisException(str(e)) from e
Esempio n. 2
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 DeisException('version cannot be below 0')
            elif version == 1:
                raise DeisException('Cannot roll back to initial release.')

            prev = self.app.release_set.get(version=version)
            if prev.failed:
                raise DeisException('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
                new_release.save()
            raise DeisException(str(e)) from e
Esempio n. 3
0
    def new(self, user, config, build, summary=None, source_version='latest'):
        """
        Create a new application release using the provided Build and Config
        on behalf of a user.

        Releases start at v1 and auto-increment.
        """
        # construct fully-qualified target image
        new_version = self.app.release_set.latest().version + 1
        # create new release and auto-increment version
        release = Release.objects.create(
            owner=user, app=self.app, config=config,
            build=build, version=new_version, summary=summary
        )

        try:
            release.publish()
        except DeisException as e:
            # If we cannot publish this app, just log and carry on
            self.app.log(e)
            pass
        except RegistryException as e:
            self.app.log(e)
            raise DeisException(str(e)) from e

        return release
    def save(self, *args, **kwargs):
        self.summary = []
        previous_settings = None
        try:
            previous_settings = self.app.appsettings_set.latest()
        except AppSettings.DoesNotExist:
            pass

        try:
            self.update_maintenance(previous_settings)
            self.update_routable(previous_settings)
            self.update_whitelist(previous_settings)
            self.update_autoscale(previous_settings)
        except (UnprocessableEntity, NotFound):
            raise
        except Exception as e:
            self.delete()
            raise DeisException(str(e)) from e

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

        summary = ' '.join(self.summary)
        self.app.log('summary of app setting changes: {}'.format(summary), logging.DEBUG)
        return super(AppSettings, self).save(**kwargs)
Esempio n. 5
0
    def set_tags(self, previous_config):
        """verify the tags exist on any nodes as labels"""
        if not self.tags:
            if settings.DEIS_DEFAULT_CONFIG_TAGS:
                try:
                    tags = json.loads(settings.DEIS_DEFAULT_CONFIG_TAGS)
                    self.tags = tags
                except json.JSONDecodeError:
                    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 DeisException(message)
Esempio n. 6
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
                new_release.save()
            else:
                self.delete()

            raise DeisException(str(e)) from e
Esempio n. 7
0
    def get_port(self, routable=False):
        """
        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:
            deis_registry = bool(self.build.source_based)
            envs = self.config.values
            creds = self.get_registry_auth()

            port = None
            # Only care about port for routable application
            if not routable:
                return port

            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:
                if envs.get('PORT', None) is None:
                    self.app.log(
                        'Private registry detected but no $PORT defined. Defaulting to $PORT 5000',
                        logging.WARNING)  # noqa
                    return 5000

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

            # If the user provides PORT
            if envs.get('PORT', None):
                return envs.get('PORT')

            # discover port from docker image
            port = docker_get_port(self.image, deis_registry, creds)
            if port is None:
                msg = "Expose a port or make the app non routable by changing the process type"
                self.app.log(msg, logging.ERROR)
                raise DeisException(msg)

            return port
        except Exception as e:
            raise DeisException(str(e)) from e
Esempio n. 8
0
 def check_image_access(self):
     try:
         deis_registry = bool(self.build.source_based)
         if deis_registry:
             return  # we always have access to our own registry
         creds = self.get_registry_auth()
         docker_check_access(self.image, creds)
     except Exception as e:
         raise DeisException(str(e)) from e
Esempio n. 9
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:
            deis_registry = bool(self.build.source_based)
            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):
                if envs.get('PORT', None) is None:
                    if not self.app.appsettings_set.latest().routable:
                        return None
                    raise DeisException(
                        '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
            if envs.get('PORT', None):
                return int(envs.get('PORT'))

            # discover port from docker image
            if deis_registry:
                creds = self._get_private_registry_creds()
            port = docker_get_port(self.image, creds)
            if port is None and self.app.appsettings_set.latest().routable:
                msg = "Expose a port or make the app non routable by changing the process type"
                self.app.log(msg, logging.ERROR)
                raise DeisException(msg)

            return port
        except Exception as e:
            raise DeisException(str(e)) from e
Esempio n. 10
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 DeisException(
                'PORT needs to be set in the config '
                'when using a private registry')
Esempio n. 11
0
    def publish(self):
        if self.build is None:
            raise DeisException(
                'No build associated with this release to publish')

        # If the build has a SHA, assume it's from deis-builder and in the deis-registry already
        if self.build.source_based:
            return

        # Builder pushes to internal registry, exclude SHA based images from being returned early
        registry = self.config.registry
        if (registry.get('username', None) and registry.get('password', None)
                and
                # SHA means it came from a git push (builder)
                not self.build.sha and
                # hostname tells Builder where to push images
                not registry.get('hostname', None)) or (
                    settings.REGISTRY_LOCATION != 'on-cluster'):
            self.app.log(
                '{} exists in the target registry. Using image for release {} of app {}'
                .format(self.build.image, self.version, self.app))  # noqa
            return

        # return image if it is already in the registry, test host and then host + port
        if (self.build.image.startswith(settings.REGISTRY_HOST)
                or self.build.image.startswith(settings.REGISTRY_URL)):
            self.app.log(
                '{} exists in the target registry. Using image for release {} of app {}'
                .format(self.build.image, self.version, self.app))  # noqa
            return

        # add tag if it was not provided
        source_image = self.build.image
        if ':' not in source_image:
            source_image = "{}:{}".format(source_image, self.build.version)

        # if build is source based then it was pushed into the deis registry
        deis_registry = bool(self.build.source_based)
        publish_release(source_image, self.image, deis_registry,
                        self.get_registry_auth())