def _upgrade_image(self, app, args):
        docker = self._get_docker(app)

        self.log('Verifying Docker registry manifest for app image %s' %
                 docker.image)
        docker.verify()
        self.log('Pulling Docker image %s' % docker.image)
        docker.pull()
        self.log('Saving data from old container (%s)' % self.old_app)
        Start.call(app=self.old_app)
        settings = self._get_configure_settings(self.old_app,
                                                filter_action=False)
        settings.update(args.set_vars or {})
        args.set_vars = settings
        old_docker = self._get_docker(self.old_app)
        old_docker.cp_from_container('/etc/machine.secret', app.secret_on_host)
        if self._backup_container(self.old_app) is False:
            raise UpgradeBackupFailed()
        self.log('Removing old container')
        if old_docker.container:
            old_docker.rm()
        self._had_image_upgrade = True
        self.log('Setting up new container (%s)' % app)
        ucr_save({app.ucr_image_key: None})
        old_configure = args.configure
        args.configure = False
        self._install_new_app(app, args)
        self._update_converter_service(app)
        args.configure = old_configure
        args.set_vars = settings
        self._configure(app, args)
        self._register_app(app, args)
        self._call_join_script(app, args)
        self.old_app = app
	def _revert(self, app, args):
		if self._had_image_upgrade:
			try:
				remove = get_action('remove')
				install = get_action('install')
				password = self._get_password(args, ask=False)
				remove.call(app=app, noninteractive=args.noninteractive, username=args.username, password=password, send_info=False, skip_checks=[], backup=False)
				install.call(app=self.old_app, noninteractive=args.noninteractive, username=args.username, password=password, send_info=False, skip_checks=[])
			except Exception:
				pass
		else:
			Start.call_safe(app=self.old_app)
Beispiel #3
0
 def _backup_container(self, app, backup_data=False):
     docker = self._get_docker(app)
     if docker.exists():
         if not Start.call(app=app):
             self.fatal('Starting the container for %s failed' % app)
             return False
         if not self._store_data(app):
             self.fatal('Storing data for %s failed' % app)
             return False
         image_name = 'appcenter-backup-%s:%d' % (app.id, time.time())
         if backup_data == 'copy':
             shutil.copytree(app.get_data_dir(),
                             os.path.join(BACKUP_DIR, image_name, 'data'),
                             symlinks=True)
             shutil.copytree(app.get_conf_dir(),
                             os.path.join(BACKUP_DIR, image_name, 'conf'),
                             symlinks=True)
         elif backup_data == 'move':
             shutil.move(app.get_data_dir(),
                         os.path.join(BACKUP_DIR, image_name, 'data'))
             shutil.move(app.get_conf_dir(),
                         os.path.join(BACKUP_DIR, image_name, 'conf'))
         if not Stop.call(app=app):
             self.fatal('Stopping the container for %s failed' % app)
             return False
         image_id = docker.commit(image_name)
         self.log('Backed up %s as %s. ID: %s' %
                  (app, image_name, image_id))
         return image_id
     else:
         self.fatal('No container found. Unable to backup')
 def _docker_upgrade_mode(self, app):
     mode = None
     detail = None
     if self.old_app.docker and app.plugin_of:
         if app > self.old_app:
             mode, detail = 'app', app.version
         return mode, detail
     if not self.old_app.docker:
         return 'docker', None
     if not Start.call(app=self.old_app):
         raise Abort(
             'Could not start the app container. It needs to be running to be upgraded!'
         )
     mode = self._execute_container_script(
         self.old_app, 'update_available', _credentials=False,
         _output=True) or ''
     mode = mode.strip()
     if mode.startswith('release:'):
         mode, detail = 'release', mode[8:].strip()
     if mode not in ['packages', 'release']:
         # packages and release first!
         if app > self.old_app:
             mode, detail = 'app', app.version
         if self.old_app.get_docker_image_name(
         ) not in app.get_docker_images():
             mode, detail = 'image', app.get_docker_image_name()
     return mode, detail
 def _docker_upgrade_mode(self, app):
     mode = None
     detail = None
     if self.old_app.docker and app.plugin_of:
         if app > self.old_app:
             mode, detail = 'app', app.version
         return mode, detail
     if not self.old_app.docker:
         return 'docker', None
     if not Start.call(app=self.old_app):
         raise UpgradeStartContainerFailed()
     result = self._execute_container_script(app,
                                             'update_available',
                                             credentials=False,
                                             output=True)
     if result is not None:
         process, log = result
         if process.returncode != 0:
             self.fatal('%s: Searching for App upgrade failed!' % app)
             return None, None
         mode = '\n'.join(log.stdout())
         if mode:
             mode = mode.strip()
     else:
         mode = ''
     if mode.startswith('release:'):
         mode, detail = 'release', mode[8:].strip()
     if mode not in ['packages', 'release']:
         # packages and release first!
         if app > self.old_app:
             mode, detail = 'app', app.version
         if self.old_app.get_docker_image_name(
         ) not in app.get_docker_images():
             mode, detail = 'image', app.get_docker_image_name()
     return mode, detail
 def _upgrade_image(self, app, args):
     docker = self._get_docker(app)
     if args.pull_image:
         self.log('Verifying Docker registry manifest for app image %s' %
                  docker.image)
         docker.verify()
         self.log('Pulling Docker image %s' % docker.image)
         docker.backup_run_file()
         docker.pull()
     else:
         docker.setup_docker_files()
     self.log('Saving data from old container (%s)' % self.old_app)
     Start.call(app=self.old_app)
     settings = self._get_configure_settings(self.old_app,
                                             filter_action=False)
     settings.update(args.set_vars or {})
     args.set_vars = settings
     old_docker = self._get_docker(self.old_app)
     old_docker.cp_from_container('/etc/machine.secret', app.secret_on_host)
     if self._backup_container(self.old_app) is False:
         raise UpgradeBackupFailed()
     self.log('Removing old container')
     if old_docker.container:
         old_docker.rm()
     self._had_image_upgrade = True
     self.log('Setting up new container (%s)' % app)
     ucr_save({app.ucr_image_key: None})
     old_configure = args.configure
     args.configure = False
     self._install_new_app(app, args)
     self._update_converter_service(app)
     args.configure = old_configure
     args.set_vars = settings
     self._configure(app, args)
     self._register_app(app, args)
     self._call_join_script(app, args)
     if args.remove_image:
         self.log('Trying to remove old image')
         try:
             if old_docker.rmi() != 0:
                 self.log(
                     'Failed to remove old image. Continuing anyway...')
         except Exception as exc:
             self.warn('Error while removing old image: %s' % exc)
     self.old_app = app
    def _backup_container(self, app, backup_data=False, commit_image=True):
        docker = self._get_docker(app)
        if docker.exists():
            # New backup
            image_repo = 'appcenter-backup-%s' % app.id
            image_name = '%s:%d' % (image_repo, time.time())

            if not Start.call(app=app):
                self.fatal('Starting the container for %s failed' % app)
                return False
            if not self._store_data(app):
                self.fatal('Storing data for %s failed' % app)
                return False
            if not Stop.call(app=app):
                self.fatal('Stopping the container for %s failed' % app)
                return False

            if backup_data == 'copy':
                # not used atm
                shutil.copytree(app.get_data_dir(),
                                os.path.join(BACKUP_DIR, image_name, 'data'),
                                symlinks=True)
                shutil.copytree(app.get_conf_dir(),
                                os.path.join(BACKUP_DIR, image_name, 'conf'),
                                symlinks=True)
            elif backup_data == 'move':
                shutil.move(app.get_data_dir(),
                            os.path.join(BACKUP_DIR, image_name, 'data'))
                shutil.move(app.get_conf_dir(),
                            os.path.join(BACKUP_DIR, image_name, 'conf'))

            # Cleanup old backups
            max_number_of_backups = 1
            backed_up_dirs = glob(os.path.join(BACKUP_DIR, image_repo + ':*'))
            self.debug('Found dirs: %r' % backed_up_dirs)
            for dirname in sorted(backed_up_dirs,
                                  reverse=True)[max_number_of_backups:]:
                self.log('Removing backup %s' % dirname)
                try:
                    shutil.rmtree(dirname)
                except EnvironmentError as exc:
                    self.warn('Unable to remove backup directory: %s' % exc)
                    self.warn('Continuing anyway')
                image = os.path.basename(dirname)
                if rmi(image) != 0:
                    self.log('Unable to remove image %s' % image)
                    self.log('Probably never created')
        else:
            self.fatal('No container found. Unable to backup')
 def _backup_container(self, app, remove=False):
     docker = self._get_docker(app)
     if docker.exists():
         if not Start.call(app=app):
             self.fatal('Starting the container for %s failed' % app)
             return False
         if not self._store_data(app):
             self.fatal('Storing data for %s failed' % app)
             return False
         if not Stop.call(app=app):
             self.fatal('Stopping the container for %s failed' % app)
             return False
         if remove:
             # New backup
             image_repo = 'appcenter-backup-%s' % app.id
             image_name = '%s:%d' % (image_repo, time.time())
             shutil.move(app.get_conf_dir(),
                         os.path.join(BACKUP_DIR, image_name, 'conf'))
     else:
         self.fatal('No container found. Unable to run store_data!')
    def _start_docker_image(self, app, hostdn, password, args):
        docker = self._get_docker(app)
        if not docker:
            return

        self.log('Verifying Docker registry manifest for app image %s' %
                 docker.image)
        docker.verify()

        if args.pull_image:
            docker.pull()

        self.log('Initializing app image')
        hostname = explode_dn(hostdn, 1)[0]
        set_vars = (args.set_vars or {}).copy()
        after_image_configuration = {}
        for setting in app.get_settings():
            if setting.should_go_into_image_configuration(app):
                if setting.name not in set_vars:
                    set_vars[setting.name] = setting.get_initial_value(app)
            else:
                try:
                    after_image_configuration[setting.name] = set_vars.pop(
                        setting.name)
                except KeyError:
                    after_image_configuration[
                        setting.name] = setting.get_initial_value(app)
        set_vars['docker/host/name'] = '%s.%s' % (ucr_get('hostname'),
                                                  ucr_get('domainname'))
        set_vars['ldap/hostdn'] = hostdn
        if app.docker_env_ldap_user:
            set_vars[app.docker_env_ldap_user] = hostdn
        set_vars['server/role'] = app.docker_server_role
        set_vars['update/warning/releasenotes'] = 'no'
        ucr_keys_list = list(ucr_keys())
        for var in [
                'nameserver.*', 'repository/online/server',
                'repository/app_center/server', 'update/secure_apt',
                'appcenter/index/verify', 'ldap/base', 'ldap/server.*',
                'ldap/master.*', 'locale.*', 'domainname'
        ]:
            for key in ucr_keys_list:
                if re.match(var, key):
                    set_vars[key] = ucr_get(key)
        if ucr_is_true('appcenter/docker/container/proxy/settings',
                       default=True):
            if ucr_get('proxy/http'):
                set_vars['proxy/http'] = ucr_get('proxy/http')
                set_vars['http_proxy'] = ucr_get('proxy/http')
            if ucr_get('proxy/https'):
                set_vars['proxy/https'] = ucr_get('proxy/https')
                set_vars['https_proxy'] = ucr_get('proxy/https')
            if ucr_get('proxy/no_proxy'):
                set_vars['proxy/no_proxy'] = ucr_get('proxy/no_proxy')
                set_vars['no_proxy'] = ucr_get('proxy/no_proxy')
        set_vars['updater/identify'] = 'Docker App'
        database_connector = DatabaseConnector.get_connector(app)
        database_password_file = None
        if database_connector:
            try:
                database_password = database_connector.get_db_password()
                database_password_file = database_connector.get_db_password_file(
                )
                if database_password:
                    set_vars[
                        app.
                        docker_env_database_host] = database_connector.get_db_host(
                        )
                    db_port = database_connector.get_db_port()
                    if db_port:
                        set_vars[app.docker_env_database_port] = db_port
                    set_vars[
                        app.
                        docker_env_database_name] = database_connector.get_db_name(
                        )
                    set_vars[
                        app.
                        docker_env_database_user] = database_connector.get_db_user(
                        )
                    if app.docker_env_database_password_file:
                        set_vars[
                            app.
                            docker_env_database_password_file] = database_password_file
                    else:
                        set_vars[
                            app.
                            docker_env_database_password] = database_password
                autostart_variable = database_connector.get_autostart_variable(
                )
                if autostart_variable:
                    set_vars[autostart_variable] = 'no'
            except DatabaseError as exc:
                raise DatabaseConnectorError(str(exc))

        container = docker.create(hostname, set_vars)
        self.log('Preconfiguring container %s' % container)
        autostart = 'yes'
        if not Start.call(app=app):
            raise DockerCouldNotStartContainer(str(Status.get_status(app)))
        time.sleep(3)
        if not docker.is_running():
            dlogs = docker.dockerd_logs()
            clogs = docker.logs()
            inspect = docker.inspect_container()
            msg = """
The container for {app} could not be started!

docker logs {container}:
{clogs}

dockerd logs:
{dlogs}

docker inspect:
{state}
{graphdriver}""".format(app=app,
                        container=docker.container,
                        clogs=clogs,
                        dlogs=dlogs,
                        state=inspect.get('State'),
                        graphdriver=inspect.get('GraphDriver'))
            raise AppCenterErrorContainerStart(msg)
        # copy password files
        if os.path.isfile(app.secret_on_host):
            # we can not use docker-cp here, as we support read-only containers too :-(
            f_name = docker.path('/etc/machine.secret')
            f_dir = os.path.dirname(f_name)
            # if the container start takes a little longer the f_dir may not exist yet
            # so wait max 60s
            for i in xrange(0, 12):
                if os.path.isdir(f_dir):
                    break
                time.sleep(5)
            try:
                with open(f_name, 'w+b') as f:
                    os.chmod(f_name, 0o600)
                    f.write(password)
            except Exception as exc:
                raise DockerCouldNotStartContainer(
                    'Could not copy machine.secret to container: %s (%s)' %
                    (str(exc), docker.logs()))
        if database_password_file:
            docker.cp_to_container(database_password_file,
                                   database_password_file)
        # update timezone in container
        logfile_logger = get_logfile_logger('docker.base')
        docker.execute('rm',
                       '-f',
                       '/etc/timezone',
                       '/etc/localtime',
                       _logger=logfile_logger)
        docker.cp_to_container('/etc/timezone',
                               '/etc/timezone',
                               _logger=logfile_logger)
        docker.cp_to_container('/etc/localtime',
                               '/etc/localtime',
                               _logger=logfile_logger)
        # configure app
        after_image_configuration.update(set_vars)
        configure = get_action('configure')
        configure.call(app=app,
                       autostart=autostart,
                       run_script='no',
                       set_vars=after_image_configuration)
    def _start_docker_image(self, app, hostdn, password, args):
        docker = self._get_docker(app)
        if not docker:
            return

        self.log('Verifying Docker registry manifest for app image %s' %
                 docker.image)
        docker.verify()

        if args.pull_image:
            self.log('Downloading app image %s' % docker.image)
            if not docker.pull():
                raise DockerImagePullFailed(docker.image)

        self.log('Initializing app image')
        hostname = explode_dn(hostdn, 1)[0]
        set_vars = (args.set_vars or {}).copy()
        after_image_configuration = {}
        for setting in app.get_settings():
            if setting.should_go_into_image_configuration(app):
                if setting.name not in set_vars:
                    set_vars[setting.name] = setting.get_initial_value()
            else:
                try:
                    after_image_configuration[setting.name] = set_vars.pop(
                        setting.name)
                except KeyError:
                    pass
        set_vars['docker/host/name'] = '%s.%s' % (ucr_get('hostname'),
                                                  ucr_get('domainname'))
        set_vars['ldap/hostdn'] = hostdn
        if app.docker_env_ldap_user:
            set_vars[app.docker_env_ldap_user] = hostdn
        set_vars['server/role'] = app.docker_server_role
        set_vars['update/warning/releasenotes'] = 'no'
        ucr_keys_list = list(ucr_keys())
        for var in [
                'nameserver.*', 'repository/online/server',
                'repository/app_center/server', 'update/secure_apt',
                'appcenter/index/verify', 'ldap/master.*', 'locale.*',
                'domainname'
        ]:
            for key in ucr_keys_list:
                if re.match(var, key):
                    set_vars[key] = ucr_get(key)
        if ucr_is_true('appcenter/docker/container/proxy/settings',
                       default=True):
            if ucr_get('proxy/http'):
                set_vars['proxy/http'] = ucr_get('proxy/http')
                set_vars['http_proxy'] = ucr_get('proxy/http')
            if ucr_get('proxy/https'):
                set_vars['proxy/https'] = ucr_get('proxy/https')
                set_vars['https_proxy'] = ucr_get('proxy/https')
            if ucr_get('proxy/no_proxy'):
                set_vars['proxy/no_proxy'] = ucr_get('proxy/no_proxy')
                set_vars['no_proxy'] = ucr_get('proxy/no_proxy')
        set_vars['updater/identify'] = 'Docker App'
        database_connector = DatabaseConnector.get_connector(app)
        database_password_file = None
        if database_connector:
            try:
                database_password = database_connector.get_db_password()
                database_password_file = database_connector.get_db_password_file(
                )
                if database_password:
                    set_vars[
                        app.
                        docker_env_database_host] = database_connector.get_db_host(
                        )
                    db_port = database_connector.get_db_port()
                    if db_port:
                        set_vars[app.docker_env_database_port] = db_port
                    set_vars[
                        app.
                        docker_env_database_name] = database_connector.get_db_name(
                        )
                    set_vars[
                        app.
                        docker_env_database_user] = database_connector.get_db_user(
                        )
                    if app.docker_env_database_password_file:
                        set_vars[
                            app.
                            docker_env_database_password_file] = database_password_file
                    else:
                        set_vars[
                            app.
                            docker_env_database_password] = database_password
                autostart_variable = database_connector.get_autostart_variable(
                )
                if autostart_variable:
                    set_vars[autostart_variable] = 'no'
            except DatabaseError as exc:
                raise DatabaseConnectorError(str(exc))

        container = docker.create(hostname, set_vars)
        self.log('Preconfiguring container %s' % container)
        autostart = 'yes'
        if not Start.call(app=app):
            raise DockerCouldNotStartContainer()
        time.sleep(3)
        if not docker.is_running():
            dlogs = docker.dockerd_logs()
            clogs = docker.logs()
            inspect = docker.inspect_container()
            msg = """
The container for {app} could not be started!

docker logs {container}:
{clogs}

dockerd logs:
{dlogs}

docker inspect:
{state}
{graphdriver}""".format(app=app,
                        container=docker.container,
                        clogs='\n'.join(clogs),
                        dlogs='\n'.join(dlogs),
                        state=inspect.get('State'),
                        graphdriver=inspect.get('GraphDriver'))
            raise AppCenterErrorContainerStart(msg)
        if password:
            with open(docker.path('/etc/machine.secret'), 'w+b') as f:
                f.write(password)
        docker.cp_to_container('/etc/timezone', '/etc/timezone')
        docker.cp_to_container('/etc/localtime', '/etc/localtime')
        if database_password_file:
            docker.cp_to_container(database_password_file,
                                   database_password_file)
        after_image_configuration.update(set_vars)
        configure = get_action('configure')
        configure.call(app=app,
                       autostart=autostart,
                       run_script='no',
                       set_vars=after_image_configuration)