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)
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)