Пример #1
0
def _get_tickets(manifest, container_dir):
    """Get tickets."""
    principals = set(manifest.get('tickets', []))
    if not principals:
        return False

    tkts_spool_dir = os.path.join(container_dir, 'root', 'var', 'spool',
                                  'tickets')

    try:
        tickets.request_tickets(context.GLOBAL.zk.conn, manifest['name'],
                                tkts_spool_dir, principals)
    except Exception:
        _LOGGER.exception('Exception processing tickets.')
        raise exc.ContainerSetupError('Get tickets error',
                                      app_abort.AbortedReason.TICKETS)

    # Check that all requested tickets are valid.
    for princ in principals:
        krbcc_file = os.path.join(tkts_spool_dir, princ)
        if not tickets.krbcc_ok(krbcc_file):
            _LOGGER.error('Missing or expired tickets: %s, %s', princ,
                          krbcc_file)
            raise exc.ContainerSetupError(princ,
                                          app_abort.AbortedReason.TICKETS)
        _LOGGER.info('Ticket ok: %s, %s', princ, krbcc_file)

    return True
Пример #2
0
def _fetch_image(client, image):
    """Fetch image from local file or registry
    returns image metadata object
    """
    (scheme, path) = _parse_image_name(image)

    if scheme == 'file':
        try:
            image_meta = _load_image(client, path)
        except docker.errors.ImageNotFound:
            raise exc.ContainerSetupError(
                'Failed to load image file {}'.format(image),
                app_abort.AbortedReason.IMAGE)
    elif scheme == 'docker':
        # simulate docker pull logic, if tag not provided, assume latest
        if ':' not in path:
            path += ':latest'

        try:
            image_meta = _pull_image(client, path)
        except docker.errors.ImageNotFound:
            raise exc.ContainerSetupError(
                'Fail to pull {}, check image name or disk size'.format(image),
                app_abort.AbortedReason.IMAGE)
    else:
        raise exc.ContainerSetupError(
            'Unrecognized image name {}'.format(image),
            app_abort.AbortedReason.IMAGE)

    return image_meta
Пример #3
0
def add_manifest_features(manifest, runtime, tm_env):
    """Configure optional container features."""
    for feature in manifest.get('features', []):
        if not features.feature_exists(feature):
            _LOGGER.error('Unable to load feature: %s', feature)
            raise exc.ContainerSetupError(
                msg='Unsupported feature: {}'.format(feature),
                reason='feature',
            )

        feature_mod = features.get_feature(feature)(tm_env)

        if not feature_mod.applies(manifest, runtime):
            _LOGGER.error('Feature does not apply: %s', feature)
            raise exc.ContainerSetupError(
                msg='Unsupported feature: {}'.format(feature),
                reason='feature',
            )
        try:
            feature_mod.configure(manifest)
        except Exception:
            _LOGGER.exception('Error configuring feature: %s', feature)
            raise exc.ContainerSetupError(
                msg='Error configuring feature: {}'.format(feature),
                reason='feature',
            )
Пример #4
0
def mount_bind(newroot, target, source=None, recursive=True, read_only=True):
    """Bind mounts `source` to `newroot/target` so that `source` is accessed
    when reaching `newroot/target`.

    If a directory, the source will be mounted using --rbind.
    """
    # Ensure root directory exists
    if not os.path.exists(newroot):
        raise exc.ContainerSetupError('Path %r does not exist' % newroot)

    if source is None:
        source = target

    target = fs.norm_safe(target)
    source = fs.norm_safe(source)

    # Make sure target directory exists.
    if not os.path.exists(source):
        raise exc.ContainerSetupError('Source path %r does not exist' % source)

    mnt_flags = [mount.MS_BIND]

    # Use --rbind for directories and --bind for files.
    if recursive and os.path.isdir(source):
        mnt_flags.append(mount.MS_REC)

    # Strip leading /, ensure that mount is relative path.
    while target.startswith('/'):
        target = target[1:]

    # Create mount directory, make sure it does not exists.
    target_fp = os.path.join(newroot, target)
    if os.path.isdir(source):
        fs.mkdir_safe(target_fp)
    else:
        fs.mkfile_safe(target_fp)

    res = mount.mount(source=source,
                      target=target_fp,
                      fs_type=None,
                      mnt_flags=mnt_flags)

    if res == 0 and read_only:
        res = mount.mount(source=None,
                          target=target_fp,
                          fs_type=None,
                          mnt_flags=(mount.MS_BIND, mount.MS_RDONLY,
                                     mount.MS_REMOUNT))

    return res
Пример #5
0
def mount_bind(newroot, mount, target=None, bind_opt=None):
    """Mounts directory in the new root.

    Call to mount should be done before chrooting into new root.

    Unless specified, the target directory will be mounted using --rbind.
    """
    # Ensure root directory exists
    if not os.path.exists(newroot):
        raise exc.ContainerSetupError('Path %s does not exist' % newroot)

    if target is None:
        target = mount

    mount = norm_safe(mount)
    target = norm_safe(target)

    # Make sure target directory exists.
    if not os.path.exists(target):
        raise exc.ContainerSetupError('Target path %s does not exist' % target)

    # If bind_opt is not explicit, use --rbind for directories and
    # --bind for files.
    if bind_opt is None:
        if os.path.isdir(target):
            bind_opt = '--rbind'
        else:
            bind_opt = '--bind'

    # Strip leading /, ensure that mount is relative path.
    while mount.startswith('/'):
        mount = mount[1:]

    # Create mount directory, make sure it does not exists.
    mount_fp = os.path.join(newroot, mount)
    if os.path.isdir(target):
        mkdir_safe(mount_fp)
    else:
        mkfile_safe(mount_fp)

    subproc.check_call(
        [
            'mount',
            '-n',
            bind_opt,
            target,
            mount_fp
        ]
    )
Пример #6
0
def _allocate_sockets(environment, host_ip, sock_type, count):
    """Return a list of `count` socket bound to an ephemeral port.
    """
    # TODO(boysson): this should probably be abstracted away
    if environment == 'prod':
        port_pool = range(iptables.PROD_PORT_LOW, iptables.PROD_PORT_HIGH + 1)
    else:
        port_pool = range(iptables.NONPROD_PORT_LOW,
                          iptables.NONPROD_PORT_HIGH + 1)

    port_pool = random.sample(port_pool, iptables.PORT_SPAN)

    # socket objects are closed on GC so we need to return
    # them and expect the caller to keep them around while needed
    sockets = []

    for real_port in port_pool:
        if len(sockets) == count:
            break

        socket_ = socket.socket(socket.AF_INET, sock_type)
        socket_.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        try:
            socket_.bind((host_ip, real_port))
        except socket.error as err:
            if err.errno == errno.EADDRINUSE:
                continue
            raise

        sockets.append(socket_)
    else:
        raise exc.ContainerSetupError('run.alloc_sockets:{0} < {1}'.format(
            len(sockets), count))

    return sockets
Пример #7
0
def _get_gmsa(tm_env, client, app, container_args):
    """Waits on GMSA details and adds the credential spec to the args.
    """
    check = gmsa.HostGroupCheck(tm_env)

    count = 0
    found = False
    while count < 60:
        found = check.host_in_proid_group(app.proid)

        if found:
            break

        count += 1
        time.sleep(1)

    if not found:
        raise exc.ContainerSetupError(
            'Image {0} was not found'.format(app.image),
            app_abort.AbortedReason.GMSA)

    path = credential_spec.generate(app.proid, container_args['name'], client)

    container_args['security_opt'] = ['credentialspec={}'.format(path)]
    container_args['hostname'] = app.proid
Пример #8
0
    def run(self):
        """Prepares container environment and exec's container

        The function is intended to be invoked from 'run' script and never
        returns.

        :returns:
            This function never returns
        """
        manifest_file = os.path.join(self.container_dir, appcfg.APP_JSON)
        manifest = app_manifest.read(manifest_file)
        if not self._can_run(manifest):
            raise exc.ContainerSetupError(
                'Runtime {0} does not support {1}.'.format(
                    self.__class__.__name__, manifest.get('type')))

        # Intercept SIGTERM from supervisor, so that initialization is not
        # left in broken state.
        terminated = utils.make_signal_flag(utils.term_signal())

        unique_name = appcfg.manifest_unique_name(manifest)
        watchdog_name = 'app_run-%s' % unique_name
        self.watchdog = self.tm_env.watchdogs.create(
            watchdog_name, self.run_timeout(manifest),
            'Run of {container_dir!r} stalled'.format(
                container_dir=self.container_dir))

        self._run(manifest, self.watchdog, terminated)
Пример #9
0
    def test__run_aborted(self):
        """Tests docker runtime run when app has been aborted."""
        # Access to a protected member
        # pylint: disable=W0212
        docker_runtime = runtime.DockerRuntime(self.tm_env, self.container_dir)

        treadmill.runtime.docker.runtime._create_container.side_effect = \
            docker.errors.ImageNotFound('test')

        with self.assertRaises(treadmill.exc.ContainerSetupError) as context:
            docker_runtime._run(self.manifest)

            self.assertEqual(app_abort.AbortedReason.IMAGE,
                             context.exception.why)

        app_abort.report_aborted.reset_mock()

        app_presence = mock.Mock()
        treadmill.presence.EndpointPresence.return_value = app_presence
        app_presence.register.side_effect = exc.ContainerSetupError('test')

        with self.assertRaises(treadmill.exc.ContainerSetupError) as context:
            docker_runtime._run(self.manifest)

            self.assertEqual(app_abort.AbortedReason.PRESENCE,
                             context.exception.why)
Пример #10
0
    def run(self, terminated):
        """Prepares container environment and exec's container

        The function is intended to be invoked from 'run' script and never
        returns.

        :param terminated:
            Flag where terminated signal will accumulate.
        :param terminated:
            ``set``
        :returns:
            This function never returns
        """
        manifest_file = os.path.join(self.container_dir, _APP_YML)
        manifest = app_manifest.read(manifest_file)
        if not self._can_run(manifest):
            raise exc.ContainerSetupError(
                'Runtime {0} does not support {1}.'.format(
                    self.__class__.__name__,
                    manifest.get('type')
                )
            )

        watchdog_name = 'app_run-%s' % os.path.basename(self.container_dir)
        self.watchdog = self.tm_env.watchdogs.create(
            watchdog_name, '60s',
            'Run of {0} stalled'.format(self.container_dir))

        self._run(manifest, self.watchdog, terminated)
Пример #11
0
def _start_service_sup(container_dir):
    """Safely start services supervisor."""
    try:
        supervisor.control_service(
            os.path.join(container_dir, 'sys', 'start_container'),
            supervisor.ServiceControlAction.once)
    except subproc.CalledProcessError:
        raise exc.ContainerSetupError('start_container')
Пример #12
0
    def _run(self, manifest):
        context.GLOBAL.zk.conn.add_listener(zkutils.exit_on_lost)

        with lc.LogContext(_LOGGER, self._service.name,
                           lc.ContainerAdapter) as log:
            log.info('Running %r', self._service.directory)

            _sockets = runtime.allocate_network_ports(
                '0.0.0.0', manifest
            )

            app = runtime.save_app(manifest, self._service.data_dir)

            app_presence = presence.EndpointPresence(
                context.GLOBAL.zk.conn,
                manifest
            )

            app_presence.register_identity()
            app_presence.register_running()

            try:
                client = self._get_client()

                try:
                    container = _create_container(
                        self._tm_env,
                        self._get_config(),
                        client,
                        app
                    )
                except docker.errors.ImageNotFound:
                    raise exc.ContainerSetupError(
                        'Image {0} was not found'.format(app.image),
                        app_abort.AbortedReason.IMAGE
                    )

                container.start()
                container.reload()

                _LOGGER.info('Container is running.')
                app_presence.register_endpoints()
                appevents.post(
                    self._tm_env.app_events_dir,
                    events.ServiceRunningTraceEvent(
                        instanceid=app.name,
                        uniqueid=app.uniqueid,
                        service='docker'
                    )
                )

                while container.status == 'running':
                    container.wait(timeout=10)
                    container.reload()
            finally:
                _LOGGER.info('Stopping zookeeper.')
                context.GLOBAL.zk.conn.stop()
Пример #13
0
    def _run(self, manifest):
        context.GLOBAL.zk.conn.add_listener(zkutils.exit_on_lost)

        with lc.LogContext(_LOGGER, self._service.name,
                           lc.ContainerAdapter) as log:
            log.info('Running %r', self._service.directory)

            manifest['ephemeral_ports']['tcp'] = []
            manifest['ephemeral_ports']['udp'] = []

            _create_docker_log_symlink(self._service.data_dir)

            app = runtime.save_app(manifest, self._service.data_dir)

            volume_mapping = self._get_volume_mapping()

            app_presence = presence.EndpointPresence(context.GLOBAL.zk.conn,
                                                     manifest)

            app_presence.register_identity()
            app_presence.register_running()

            client = self._get_client()

            try:
                container = _create_container(self._tm_env, self._get_config(),
                                              client, app, volume_mapping)
            except docker.errors.ImageNotFound:
                raise exc.ContainerSetupError(
                    'Image {0} was not found'.format(app.image),
                    app_abort.AbortedReason.IMAGE)

            container.start()
            container.reload()

            _update_network_info_in_manifest(container, manifest)
            # needs to share manifest with container
            if volume_mapping:
                container_data_dir = next(iter(volume_mapping))
                runtime.save_app(manifest,
                                 container_data_dir,
                                 app_json='app.json')

            _LOGGER.info('Container is running.')
            app_presence.register_endpoints()
            trace.post(
                self._tm_env.app_events_dir,
                events.ServiceRunningTraceEvent(instanceid=app.name,
                                                uniqueid=app.uniqueid,
                                                service='docker'))

            _print_container_logs(container)
Пример #14
0
    def run(self, name, image, entrypoint, cmd, **args):
        """Run
        """
        client = self._get_client()
        if 'volumes' in args:
            args['volumes'] = _transform_volumes(args['volumes'])

        if 'envdirs' in args:
            args['environment'] = _read_environ(args.pop('envdirs'))

        # simulate docker pull logic, if tag not provided, assume latest
        if ':' not in image:
            image += ':latest'

        try:
            image_meta = _pull_image(client, image)
        except docker.errors.ImageNotFound:
            raise exc.ContainerSetupError(
                'Fail to pull {}, check image name or disk size'.format(image),
                app_abort.AbortedReason.IMAGE)

        container = _create_container(client, name, image, image_meta,
                                      entrypoint, cmd, **args)

        # TODO: start docker container event
        container.start()

        container.reload()
        logs_gen = container.logs(stdout=True,
                                  stderr=True,
                                  stream=True,
                                  follow=True)

        _LOGGER.info('Container %s is running', name)
        while container.status == 'running':
            try:
                for log_lines in logs_gen:
                    sys.stderr.write(log_lines)
            except socket.error:
                pass

            container.reload()

        # container.wait returns dict with key 'StatusCode'
        rc = container.wait()['StatusCode']
        if os.WIFSIGNALED(rc):
            # Process died with a signal in docker
            sig = os.WTERMSIG(rc)
            os.kill(os.getpid(), sig)

        else:
            utils.sys_exit(os.WEXITSTATUS(rc))
Пример #15
0
 def _run(self, manifest):
     try:
         app_run.run(
             tm_env=self._tm_env,
             runtime_config=self._config,
             container_dir=self._service.data_dir,
             manifest=manifest
         )
     except services.ResourceServiceTimeoutError as err:
         raise exc.ContainerSetupError(
             err.message,
             app_abort.AbortedReason.TIMEOUT
         )
Пример #16
0
def mount_docker_daemon_path(newroot_norm, app):
    """Mount tmpfs for docker
    """
    if not _has_docker(app):
        return

    # /etc/docker as temp fs as dockerd create /etc/docker/key.json
    try:
        fs_linux.mount_tmpfs(newroot_norm, '/etc/docker')
    except FileNotFoundError as err:
        _LOGGER.error('Failed to mount docker tmpfs: %s', err)
        # this exception is caught by sproc run to generate abort event
        raise exc.ContainerSetupError(
            msg=str(err),
            reason=app_abort.AbortedReason.UNSUPPORTED,
        )
Пример #17
0
    def run(self):
        """Prepares container environment and exec's container

        The function is intended to be invoked from 'run' script and never
        returns.

        :returns:
            This function never returns
        """
        manifest_file = os.path.join(self._service.data_dir, appcfg.APP_JSON)
        manifest = app_manifest.read(manifest_file)
        if not self._can_run(manifest):
            raise exc.ContainerSetupError('invalid_type',
                                          app_abort.AbortedReason.INVALID_TYPE)

        self._run(manifest)
Пример #18
0
def _create_container(client, name, image_meta, entrypoint, cmd, ulimit,
                      **args):
    """Create docker container from given app.
    """
    # if success,  pull returns an image object
    container_args = {
        'name': name,
        'image': image_meta.id,
        'command': cmd,
        'entrypoint': entrypoint,
        'detach': True,
        'stdin_open': True,
        'tty': True,
        'network_mode': 'host',
        'pid_mode': 'host',
        'ipc_mode': 'host',
        'ulimits': ulimit,
        # XXX: uts mode not supported by python lib yet
        # 'uts_mode': 'host',
    }

    # assign user argument
    user = _get_image_user(image_meta.attrs)
    if user is None or user == '':
        uid = os.getuid()
        gid = os.getgid()
        container_args['user'] = '******'.format(uid, gid)

    # add additonal container args
    for key, value in six.iteritems(args):
        if value is not None:
            container_args[key] = value

    try:
        # The container might exist already
        container = client.containers.get(name)
        container.remove(force=True)
        # TODO: remove container event
    except docker.errors.NotFound:
        pass
    except docker.errors.APIError as err:
        raise exc.ContainerSetupError(
            'Fail to remove container {}: {}'.format(name, err),
            app_abort.AbortedReason.PRESENCE)

    _LOGGER.info('Run docker: %r', container_args)
    return client.containers.create(**container_args)
Пример #19
0
def _start_service_sup(tm_env, manifest, container_dir):
    """Safely start services supervisor."""
    for service in manifest['services']:
        if service['trace']:
            appevents.post(
                tm_env.app_events_dir,
                traceevents.ServiceRunningTraceEvent(
                    instanceid=manifest['name'],
                    uniqueid=manifest['uniqueid'],
                    service=service['name']))

    try:
        supervisor.control_service(
            os.path.join(container_dir, 'sys', 'start_container'),
            supervisor.ServiceControlAction.once)
    except subproc.CalledProcessError:
        raise exc.ContainerSetupError('start_container')
Пример #20
0
def _create_ephemeral_with_retry(zkclient, path, data):
    """Create ephemeral node with retry."""
    prev_data = None
    for _ in range(0, _EPHEMERAL_RETRY_COUNT):
        try:
            return zkutils.create(zkclient,
                                  path,
                                  data,
                                  acl=[_SERVERS_ACL],
                                  ephemeral=True)
        except kazoo.client.NodeExistsError:
            prev_data = zkutils.get_default(zkclient, path)
            _LOGGER.warning('Node exists, will retry: %s, data: %r', path,
                            prev_data)
            time.sleep(_EPHEMERAL_RETRY_INTERVAL)

    raise exc.ContainerSetupError('%s:%s' % (path, prev_data),
                                  app_abort.AbortedReason.PRESENCE)
Пример #21
0
def _get_tickets(appname, app, container_dir):
    """Get tickets."""
    tkts_spool_dir = os.path.join(
        container_dir, 'root', 'var', 'spool', 'tickets')

    reply = tickets.request_tickets(context.GLOBAL.zk.conn, appname)
    if reply:
        tickets.store_tickets(reply, tkts_spool_dir)

    # Check that all requested tickets are valid.
    for princ in app.get('tickets', []):
        krbcc_file = os.path.join(tkts_spool_dir, princ)
        if not tickets.krbcc_ok(krbcc_file):
            _LOGGER.error('Missing or expired tickets: %s, %s',
                          princ, krbcc_file)
            raise exc.ContainerSetupError('tickets.%s' % princ)
        else:
            _LOGGER.info('Ticket ok: %s, %s', princ, krbcc_file)
Пример #22
0
    def run(self, name, image, entrypoint, cmd, **args):
        """Run
        """
        client = self._get_client()
        try:
            if 'volumes' in args:
                args['volumes'] = _transform_volumes(args['volumes'])

            if 'envdirs' in args:
                args['environment'] = _read_environ(args.pop('envdirs'))

            container = _create_container(client, name, image, entrypoint, cmd,
                                          **args)

        except docker.errors.ImageNotFound:
            raise exc.ContainerSetupError(
                'Image {0} was not found'.format(image),
                app_abort.AbortedReason.IMAGE)

        container.start()
        container.reload()
        logs_gen = container.logs(stdout=True,
                                  stderr=True,
                                  stream=True,
                                  follow=True)

        _LOGGER.info('Container %s is running', name)
        while container.status == 'running':
            try:
                for log_lines in logs_gen:
                    sys.stderr.write(log_lines)
            except socket.error:
                pass

            container.reload()

        rc = container.wait()
        if os.WIFSIGNALED(rc):
            # Process died with a signal in docker
            sig = os.WTERMSIG(rc)
            os.kill(os.getpid(), sig)

        else:
            utils.sys_exit(os.WEXITSTATUS(rc))
Пример #23
0
def _create_ephemeral_with_retry(zkclient, path, data):
    """Create ephemeral node with retry."""
    prev_data = None
    for _ in range(0, 5):
        try:
            return zkutils.create(zkclient,
                                  path,
                                  data,
                                  acl=[_SERVERS_ACL],
                                  ephemeral=True)
        except kazoo.client.NodeExistsError:
            prev_data, metadata = zkutils.get_default(zkclient,
                                                      path,
                                                      need_metadata=True)
            _LOGGER.warn('Node exists, will retry: %s, data: %r, metadata: %r',
                         path, prev_data, metadata)
            time.sleep(_EPHEMERAL_RETRY_INTERVAL)

    raise exc.ContainerSetupError('presence.%s:%s' % (path, prev_data))
Пример #24
0
def _allocate_sockets(environment, host_ip, sock_type, count):
    """Return a list of `count` socket bound to an ephemeral port.
    """
    # TODO: this should probably be abstracted away
    if environment == 'prod':
        port_pool = six.moves.range(PROD_PORT_LOW, PROD_PORT_HIGH + 1)
    else:
        port_pool = six.moves.range(NONPROD_PORT_LOW, NONPROD_PORT_HIGH + 1)

    port_pool = random.sample(port_pool, PORT_SPAN)

    # socket objects are closed on GC so we need to return
    # them and expect the caller to keep them around while needed
    sockets = []

    for real_port in port_pool:
        if len(sockets) == count:
            break

        socket_ = socket.socket(socket.AF_INET, sock_type)
        try:
            socket_.bind((host_ip, real_port))
            if sock_type == socket.SOCK_STREAM:
                socket_.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                socket_.listen(0)
        except socket.error as err:
            if err.errno == errno.EADDRINUSE:
                continue
            raise

        if six.PY3:
            # We want the sockets to survive an execv
            socket_.set_inheritable(True)

        sockets.append(socket_)
    else:
        raise exc.ContainerSetupError('{0} < {1}'.format(len(sockets), count),
                                      app_abort.AbortedReason.PORTS)

    return sockets
Пример #25
0
def _get_keytabs(manifest, container_dir, locker_pattern):
    """Get keytabs."""
    # keytab of 'proid:service' is not fetched from keytab locker
    keytabs = [kt for kt in manifest.get('keytabs', []) if ':' not in kt]
    _LOGGER.debug('keytabs to fetch from locker: %r', keytabs)
    if not keytabs:
        return

    kts_spool_dir = os.path.join(container_dir, 'root', 'var', 'spool',
                                 'keytabs')

    try:
        kt_client.request_keytabs(
            context.GLOBAL.zk.conn,
            manifest['name'],
            kts_spool_dir,
            locker_pattern,
        )
    except Exception:
        _LOGGER.exception('Exception processing keytabs.')
        raise exc.ContainerSetupError('Get keytabs error',
                                      app_abort.AbortedReason.KEYTABS)
Пример #26
0
def _get_keytabs(manifest, container_dir, locker_pattern):
    """Get keytabs."""
    # keytab of 'proid:service' is not fetched from keytab locker
    keytabs_to_fetch = set()
    kt_specs = set()
    for kt in manifest.get('keytabs', []):
        if ':' not in kt:
            keytabs_to_fetch.add(kt)
        else:
            kt_specs.add(kt)

    _LOGGER.debug('keytabs to fetch from locker: %r', keytabs_to_fetch)
    if not keytabs_to_fetch:
        return

    kts_spool_dir = os.path.join(container_dir, 'root', 'var', 'spool',
                                 'keytabs')

    try:
        kt_client.request_keytabs(
            context.GLOBAL.zk.conn,
            manifest['name'],
            kts_spool_dir,
            locker_pattern,
        )
    except Exception:
        _LOGGER.exception('Exception processing keytabs.')
        raise exc.ContainerSetupError('Get keytabs error',
                                      app_abort.AbortedReason.KEYTABS)

    # add fetched host keytabs to /etc/krb5.keytab
    kt_dest = os.path.join(container_dir, 'overlay', 'etc', 'krb5.keytab')
    keytabs.add_keytabs_to_file(kts_spool_dir, 'host', kt_dest)

    # add fetched keytabs to keytab spec file
    for kt_spec in kt_specs:
        owner, princ = kt_spec.split(':', 1)
        kt_dest = os.path.join(kts_spool_dir, owner)
        keytabs.add_keytabs_to_file(kts_spool_dir, princ, kt_dest, owner)
Пример #27
0
    def _run(self, manifest):
        context.GLOBAL.zk.conn.add_listener(zkutils.exit_on_lost)

        with lc.LogContext(_LOGGER, self._service.name,
                           lc.ContainerAdapter) as log:
            log.info('Running %r', self._service.directory)

            manifest['ephemeral_ports']['tcp'] = []
            manifest['ephemeral_ports']['udp'] = []

            # create container_data dir
            container_data_dir = os.path.join(self._service.data_dir,
                                              'container_data')

            log.info('container_data %r', container_data_dir)

            fs.mkdir_safe(container_data_dir)

            # volume mapping config : read-only mapping
            volume_mapping = {
                container_data_dir: {
                    'bind': 'c:\\container_data',
                    'mode': 'ro'
                }
            }

            app = runtime.save_app(manifest, self._service.data_dir)

            app_presence = presence.EndpointPresence(context.GLOBAL.zk.conn,
                                                     manifest)

            app_presence.register_identity()
            app_presence.register_running()

            client = self._get_client()

            try:
                container = _create_container(self._tm_env, self._get_config(),
                                              client, app, volume_mapping)
            except docker.errors.ImageNotFound:
                raise exc.ContainerSetupError(
                    'Image {0} was not found'.format(app.image),
                    app_abort.AbortedReason.IMAGE)

            container.start()
            container.reload()

            _update_network_info_in_manifest(container, manifest)
            runtime.save_app(manifest, container_data_dir, app_json='app.json')

            _LOGGER.info('Container is running.')
            app_presence.register_endpoints()
            trace.post(
                self._tm_env.app_events_dir,
                events.ServiceRunningTraceEvent(instanceid=app.name,
                                                uniqueid=app.uniqueid,
                                                service='docker'))

            while container.status == 'running':
                container.wait(timeout=10)
                container.reload()
Пример #28
0
def make_fsroot(root_dir, app):
    """Initializes directory structure for the container in a new root.

    The container uses pretty much a blank a FHS 3 layout.

     - Bind directories in parent / (with exceptions - see below.)
     - Skip /tmp, create /tmp in the new root with correct permissions.
     - Selectively create / bind /var.
       - /var/tmp (new)
       - /var/log (new)
       - /var/spool - create empty with dirs.
     - Bind everything in /var, skipping /spool/tickets

     tm_env is used to deliver abort events
     """
    newroot_norm = fs.norm_safe(root_dir)

    emptydirs = [
        '/bin',
        '/dev',
        '/etc',
        '/home',
        '/lib',
        '/lib64',
        '/opt',
        '/proc',
        '/root',
        '/run',
        '/sbin',
        '/sys',
        '/tmp',
        '/usr',
        '/var/cache',
        '/var/empty',
        '/var/empty/sshd',
        '/var/lib',
        '/var/lock',
        '/var/log',
        '/var/opt',
        '/var/spool',
        '/var/tmp',
        '/var/spool/keytabs',
        '/var/spool/tickets',
        '/var/spool/tokens',
        # for SSS
        '/var/lib/sss',
    ]

    stickydirs = [
        '/opt',
        '/run',
        '/tmp',
        '/var/cache',
        '/var/lib',
        '/var/lock',
        '/var/log',
        '/var/opt',
        '/var/tmp',
        '/var/spool/keytabs',
        '/var/spool/tickets',
        '/var/spool/tokens',
    ]

    # these folders are shared with underlying host and other containers,
    mounts = [
        '/bin',
        '/etc',  # TODO: Add /etc/opt
        '/lib',
        '/lib64',
        '/root',
        '/sbin',
        '/usr',
        # for SSS
        '/var/lib/sss',
        # TODO: Remove below once PAM UDS is implemented
        os.path.expandvars('${TREADMILL_APPROOT}/env'),
        os.path.expandvars('${TREADMILL_APPROOT}/spool'),
    ]

    for directory in emptydirs:
        fs.mkdir_safe(newroot_norm + directory)

    for directory in stickydirs:
        os.chmod(newroot_norm + directory, 0o777 | stat.S_ISVTX)

    # /var/empty must be owned by root and not group or world-writable.
    os.chmod(os.path.join(newroot_norm, 'var/empty'), 0o711)

    fs_linux.mount_bind(newroot_norm,
                        os.path.join(os.sep, 'sys'),
                        source='/sys',
                        recursive=True,
                        read_only=False)

    make_dev(newroot_norm)

    # Per FHS3 /var/run should be a symlink to /run which should be tmpfs
    fs.symlink_safe(os.path.join(newroot_norm, 'var', 'run'), '/run')
    # We create an unbounded tmpfs mount so that runtime data can be written to
    # it, counting against the memory limit of the container.
    fs_linux.mount_tmpfs(newroot_norm, '/run')

    # Make shared directories/files readonly to container
    for mount in mounts:
        if os.path.exists(mount):
            fs_linux.mount_bind(newroot_norm,
                                mount,
                                recursive=True,
                                read_only=True)

    if hasattr(app, 'docker') and app.docker:
        # If unable to mount docker directory, we throw Aborted events
        try:
            _mount_docker_tmpfs(newroot_norm)
        except FileNotFoundError as err:
            _LOGGER.error('Failed to mount docker tmpfs: %s', err)
            # this exception is caught by sproc run to generate abort event
            raise exc.ContainerSetupError(
                msg=str(err),
                reason=app_abort.AbortedReason.UNSUPPORTED,
            )