def _create_new_repo(self, args): if not args.ini or not os.path.exists(args.ini): raise Abort('An ini file is needed for new apps') app = App.from_ini(args.ini) if not app: raise Abort('Cannot continue with flawed ini file') if args.component_id: component_id = args.component_id else: component_id = '%s_%s' % (app.id, date.today().strftime('%Y%m%d')) return self._build_repo_dir(app, component_id, args.path, args.ucs_version)
def _upgrade_release(self, app, release): process = self._execute_container_script(app, 'update_release', _credentials=False, release=release) if not process or process.returncode != 0: raise Abort('Release upgrade script failed')
def _register_database(self, app): database_connector = DatabaseConnector.get_connector(app) if database_connector: try: database_connector.create_database() except DatabaseError as exc: raise Abort(str(exc))
def set_ini_value(self, section, attr, value, parser): if not re.match('^[a-zA-Z0-9_]+$', attr): raise Abort('May not use %s as attribute' % attr) try: items = parser.items(section) except NoSectionError: items = [] for name, old_value in items: if attr.lower() == name.lower(): if attr != name: self.warn('Using %s instead of %s as attribute' % (name, attr)) attr = name self.log('%s: Overwriting %r' % (attr, old_value)) self.debug('Setting [%s]%s=%r' % (section, attr, value)) if value is None: try: parser.remove_option(section, attr) except NoSectionError: pass else: try: parser.add_section(section) except DuplicateSectionError: pass except ValueError: section = 'DEFAULT' parser.set(section, attr, value)
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 main(self, args): if args.meta: ini_file = args.app.get_cache_file('meta') else: ini_file = args.app.get_ini_file() parser = read_ini_file(ini_file, CaseSensitiveConfigParser) for section, attr, value in args.attrs: attribute = args.app.get_attr(underscore(attr)) self.process(args.app, attribute, section, camelcase(attr), value, parser) self.log('Rewriting %s' % ini_file) with NamedTemporaryFile('w+b') as tmp_ini_file: parser.write(tmp_ini_file) tmp_ini_file.flush() if not args.meta: for locale in ['en', 'de']: new_app = App.from_ini(tmp_ini_file.name, locale=locale, cache=args.app.get_app_cache_obj()) if new_app is None: raise Abort( 'ini file would be malformed. Not saving attributes!' ) shutil.copy2(tmp_ini_file.name, ini_file) os.chmod(ini_file, 0o644) args.app.get_app_cache_obj().clear_cache()
def _remove_app(self, app, args): if not app.docker: super(Remove, self)._remove_app(app, args) else: if app.plugin_of: raise Abort('Uninstallation of a plugin is not supported!') else: self._remove_docker_container(app, args)
def _upgrade_app(self, app, args): process = self._execute_container_script(app, 'update_app_version', args) if not process or process.returncode != 0: raise Abort('App upgrade script failed') self._register_app(app, args) self._call_join_script(app, args) self.old_app = app
def verify(self): if ucr_is_false('appcenter/index/verify'): return try: verify(self.app, self.image) except Exception as exc: self.logger.fatal(str(exc)) raise Abort()
def _remove_docker_container(self, app, args): if args.backup: if self._backup_container(app, backup_data='move') is False: raise Abort('Could not backup container!') docker = self._get_docker(app) if docker.container: Stop.call(app=app) docker.stop() docker.rm()
def main(self, args): docker = self._get_docker(args.app) docker_exec = ['docker', 'exec'] commands = args.commands[:] if not commands: commands = shlex.split(args.app.docker_shell_command) args.interactive = True args.tty = True if args.interactive: docker_exec.append('-i') if args.tty: docker_exec.append('-t') if not commands: raise Abort('Cannot run command: No command specified') if not app_is_running(args.app): raise Abort( 'Cannot run command: %s is not running in a container' % args.app.id) self.debug('Calling %s' % commands[0]) return subprocess.call(docker_exec + [docker.container] + commands)
def _setup_docker_image(self, app, args): self._execute_container_script(app, 'restore_data_before_setup', _credentials=False) if app.docker_script_setup: process = self._execute_container_script(app, 'setup', args) if not process or process.returncode != 0: raise Abort(_('Setup script failed!')) self._execute_container_script(app, 'restore_data_after_setup', _credentials=False)
def _load_index_json(self, app_cache): index_json_gz_filename = os.path.join(app_cache.get_cache_dir(), '.index.json.gz') if not ucr_is_false('appcenter/index/verify'): detached_sig_path = index_json_gz_filename + '.gpg' (rc, gpg_error) = gpg_verify(index_json_gz_filename, detached_sig_path) if rc: if gpg_error: self.fatal(gpg_error) raise Abort('Signature verification for %s failed' % index_json_gz_filename) with gzip_open(index_json_gz_filename, 'rb') as fgzip: content = fgzip.read() return loads(content)
def _register_attributes(self, app, args): # FIXME: there is no better lib function than this snippet schema_file = app.get_share_file('schema') if os.path.exists(schema_file): self.log('Registering schema %s' % schema_file) lo, pos = self._get_ldap_connection(args) with self._get_password_file(args) as password_file: create_recursive_container('cn=ldapschema,cn=univention,%s' % ucr_get('ldap/base'), lo, pos) schema_obj = UniventionLDAPSchema(ucr_instance()) userdn = self._get_userdn(args) udm_passthrough_options = ['--binddn', userdn, '--bindpwdfile', password_file] opts = Values() opts.packagename = 'appcenter-app-%s' % app.id opts.packageversion = app.version opts.ucsversionstart = None opts.ucsversionend = None os.environ['UNIVENTION_APP_IDENTIFIER'] = app.id try: schema_obj.register(schema_file, opts, udm_passthrough_options) except SystemExit as exc: if exc.code == 4: self.warn('A newer version of %s has already been registered. Skipping...' % schema_file) else: raise Abort('Registration of schema extension failed (Code: %s)' % exc.code) else: if not schema_obj.wait_for_activation(): raise Abort('Registering schema file %s failed' % schema_file) finally: del os.environ['UNIVENTION_APP_IDENTIFIER'] # and this is what should be there after one line of lib.register_schema(schema_file) app = app.get_app_cache_obj().copy(locale='en').find_by_component_id(app.component_id) attributes, __ = get_extended_attributes(app) if attributes: for i, attribute in enumerate(attributes): self.log('Registering attribute %s' % attribute.name) create_extended_attribute(attribute, app, i + 1, lo, pos)
def _install_only_master_packages_remotely(self, app, host, is_master, args): if args.install_master_packages_remotely: self.log('Installing some packages of %s on %s' % (app.id, host)) else: self.warn( 'Not installing packages on %s. Please make sure that these packages are installed by calling "univention-app install "%s=%s" --only-master-packages" on the host' % (host, app.id, app.version)) return username = '******' % host try: if args.noninteractive: raise Abort() password = self._get_password_for(username) with self._get_password_file(password=password) as password_file: if not password_file: raise Abort() # TODO: fallback if univention-app is not installed process = self._subprocess([ '/usr/sbin/univention-ssh', password_file, username, 'univention-app', 'install', '%s=%s' % (app.id, app.version), '--only-master-packages', '--noninteractive', '--do-not-send-info' ]) if process.returncode != 0: self.warn( 'Installing master packages for %s on %s failed!' % (app.id, host)) except Abort: if is_master: self.fatal('This is the DC master. Cannot continue!') raise else: self.warn( 'This is a DC backup. Continuing anyway, please rerun univention-app install %s --only-master-packages there later!' % (app.id))
def main(self, args): component_id = args.component_id app_id = None if args.new: component_id, app_id = self._create_new_repo(args) meta_inf_dir = os.path.join(args.path, 'meta-inf', args.ucs_version) if LooseVersion(args.ucs_version) >= '4.1': if app_id is None: if args.ini: ini_file = args.ini else: for root, dirnames, filenames in os.walk(meta_inf_dir): for filename in filenames: if filename == '%s.ini' % component_id: ini_file = os.path.join(root, filename) break else: continue break else: raise Abort( 'Could not determine app id. Specify an --ini file!' ) app = App.from_ini(ini_file) app_id = app.id meta_inf_dir = os.path.join(meta_inf_dir, app_id) mkdir(meta_inf_dir) repo_dir = os.path.join(args.path, 'univention-repository', args.ucs_version, 'maintained', 'component', component_id) mkdir(repo_dir) if args.unmaintained: version = ucr_get('version/version') if args.ucs_version != version: self.fatal( 'Cannot easily set up unmaintained packages for %s (need %s). You need to download them into the repository manually. Sorry!' % (args.ucs_version, version)) else: self._copy_unmaintained_packages(repo_dir, args) app = self._copy_meta_files(component_id, meta_inf_dir, repo_dir, args) if app and args.packages: self._handle_packages(app, repo_dir, args) self._generate_repo_index_files(repo_dir) self._generate_meta_index_files(args) self.log('Component is: %s' % component_id)
def _do_it(self, app, args): if self._install_only_master_packages(args): self._install_master_packages(app, unregister_if_uninstalled=True) else: self._register_files(app) self.percentage = 5 self._register_app(app, args) self.percentage = 10 self._register_database(app) self.percentage = 15 self._register_attributes(app, args) self.percentage = 25 if self._install_app(app, args): self.percentage = 80 self._call_join_script(app, args) ucr_save({'appcenter/prudence/docker/%s' % app.id: 'yes'}) else: raise Abort('Failed to install the App')
def main(self, args): app = args.app if app._docker_prudence_is_true(): apps = [ _app for _app in Apps().get_all_apps_with_id(app.id) if not _app.docker ] if apps: app = sorted(apps)[-1] self.warn( 'Using %s instead of %s because docker is to be ignored' % (app, args.app)) else: raise Abort( 'Cannot use %s as docker is to be ignored, yet, only non-docker versions could be found' % args.app) args.app = app return self.do_it(args)
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() docker.pull() self.log('Saving data from old container (%s)' % self.old_app) old_docker = self._get_docker(self.old_app) old_container = old_docker.container if self._backup_container(self.old_app, backup_data='copy') is False: raise Abort('Could not backup container!') self._had_image_upgrade = True self.log('Setting up new container (%s)' % app) ucr_save({app.ucr_image_key: None}) args.set_vars = self._get_config(self.old_app, args) self._install_new_app(app, args) self.log('Removing old container') if old_container: docker_rm(old_container) self._register_app(app, args) self._call_join_script(app, args) self.old_app = app
def _show_license(self, app, args): if self._show_file(app, 'license_agreement', args, agree=True) is False: raise Abort()
def _copy_meta_files(self, component_id, meta_inf_dir, repo_dir, args): ini_file = os.path.join(meta_inf_dir, '%s.ini' % component_id) if args.clear: if os.path.exists(repo_dir): rmdir(repo_dir) if os.path.exists(ini_file): os.unlink(ini_file) if args.ini: self.copy_file(args.ini, ini_file) if not os.path.exists(ini_file): self.warn('Stopping here due to no ini file') return app_en = App.from_ini(ini_file, 'en') app_de = App.from_ini(ini_file, 'de') if args.clear: self._build_repo_dir(app_en, component_id, args.path, args.ucs_version) if not app_en or not app_de: raise Abort('Cannot continue with flawed ini file') if args.logo: if LooseVersion(args.ucs_version) >= '4.1': parser = ConfigParser() parser.read(ini_file) try: logo_fname = parser.get('Application', 'Logo') except NoOptionError: self.fatal('No Logo specified in ini file!') else: self.copy_file(args.logo, os.path.join(meta_inf_dir, logo_fname)) else: self.copy_file( args.logo, os.path.join(meta_inf_dir, '%s.png' % component_id)) if args.logo_detail_page: parser = ConfigParser() parser.read(ini_file) try: logo_detail_fname = parser.get('Application', 'LogoDetailPage') except NoOptionError: self.fatal('No Logo specified in ini file!') self.copy_file(args.logo_detail_page, os.path.join(meta_inf_dir, logo_detail_fname)) if args.screenshot: self.copy_file(args.screenshot[0], os.path.join(meta_inf_dir, app_en.screenshot)) if len(args.screenshot) > 1: self.copy_file(args.screenshot[1], os.path.join(meta_inf_dir, app_de.screenshot)) if args.thumbnails: thumbnails = [] for thumbnail in app_en.thumbnails + app_de.thumbnails: if thumbnail in thumbnails: continue if thumbnail.startswith('http'): continue thumbnails.append(thumbnail) for i, thumbnail in enumerate(args.thumbnails): try: self.copy_file(thumbnail, os.path.join(meta_inf_dir, thumbnails[i])) except IndexError: raise Abort( 'The ini file must state as much Thumbnails= as --thumbnails are given' ) if args.readme: for readme in args.readme: self.copy_file(readme, repo_dir) if args.license: for license in args.license: self.copy_file(license, repo_dir) if args.ucr: self.copy_file( args.ucr, os.path.join(repo_dir, 'univention-config-registry-variables')) if args.schema: self.copy_file(args.schema, os.path.join(repo_dir, 'schema')) if args.attributes: self.copy_file(args.attributes, os.path.join(repo_dir, 'attributes')) if args.preinst: self.copy_file(args.preinst, os.path.join(repo_dir, 'preinst')) if args.join: self.copy_file(args.join, os.path.join(repo_dir, 'inst')) if args.prerm: self.copy_file(args.prerm, os.path.join(repo_dir, 'prerm')) if args.unjoin: self.copy_file(args.unjoin, os.path.join(repo_dir, 'uinst')) if args.init: self.copy_file(args.init, os.path.join(repo_dir, 'init')) if args.setup: self.copy_file(args.setup, os.path.join(repo_dir, 'setup')) if args.store_data: self.copy_file(args.store_data, os.path.join(repo_dir, 'store_data')) if args.restore_data_before_setup: self.copy_file(args.restore_data_before_setup, os.path.join(repo_dir, 'restore_data_before_setup')) if args.restore_data_after_setup: self.copy_file(args.restore_data_after_setup, os.path.join(repo_dir, 'restore_data_after_setup')) if args.update_available: self.copy_file(args.update_available, os.path.join(repo_dir, 'update_available')) if args.update_packages: self.copy_file(args.update_packages, os.path.join(repo_dir, 'update_packages')) if args.update_release: self.copy_file(args.update_release, os.path.join(repo_dir, 'update_release')) if args.update_app_version: self.copy_file(args.update_app_version, os.path.join(repo_dir, 'update_app_version')) if args.env: self.copy_file(args.env, os.path.join(repo_dir, 'env')) return app_en
def _show_post_readme(self, app, args): if self._show_file(app, self.post_readme, args, confirm=True) is False: raise Abort()
def _upgrade_packages(self, app, args): process = self._execute_container_script(app, 'update_packages', args) if not process or process.returncode != 0: raise Abort('Package upgrade script failed')
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() self.log('Downloading app image %s' % docker.image) docker.pull() self.log('Initializing app image') hostname = explode_dn(hostdn, 1)[0] set_vars = (args.set_vars or {}).copy() configure = get_action('configure') for variable in configure.list_config(app): if variable['value'] is not None and variable['id'] not in set_vars: set_vars[variable['id']] = variable['value'] # default set_vars['docker/host/name'] = '%s.%s' % (ucr_get('hostname'), ucr_get('domainname')) set_vars['ldap/hostdn'] = 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 Abort(str(exc)) container = docker.create(hostname, set_vars) self.log('Preconfiguring container %s' % container) autostart = 'yes' if not Start.call(app=app): raise Abort('Unable to start the container!') 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) self._copy_files_into_container(app, '/etc/timezone', '/etc/localtime', database_password_file) configure.call(app=app, autostart=autostart, set_vars=set_vars)