Exemple #1
0
def cleanup(config_ids, managed_by, cont_cmd='podman', default_runtime=None,
            log_level=None, log_file=None):
    """Delete containers no longer applied, rename others to preferred name.

    :param list config_ids: List of config IDs still applied. All containers
                            managed by this tool will be deleted if their
                            config ID is not specified in this list.
    :param str managed_by: Name of the tool managing the containers. Only
                           containers labelled with this will be modified.
    :param str cont_cmd: Optional override to the container command to run.
    :param str default_runtime: (deprecated) does nothing.
    :param int log_level: optional log level for loggers
    :param int log_file: optional log file for messages
    """
    log = common.configure_logging(__name__, log_level, log_file)
    if default_runtime:
        log.warning("DEPRECATION: 'default_runtime' does nothing, "
                    "use 'cont_cmd' instead")

    if cont_cmd == 'podman':
        r = runner.PodmanRunner(managed_by, cont_cmd=cont_cmd, log=log)
        log.warning("paunch cleanup is partially supported with podman")
    else:
        r = runner.DockerRunner(managed_by, cont_cmd=cont_cmd, log=log)

    r.delete_missing_configs(config_ids)
    r.rename_containers()
Exemple #2
0
def delete(config_ids, managed_by, cont_cmd='podman', default_runtime=None,
           log_level=None, log_file=None):
    """Delete containers with the specified config IDs.

    :param list config_ids: List of config IDs to delete the containers for.
    :param str managed_by: Name of the tool managing the containers. Only
                           containers labelled with this will be modified.
    :param str cont_cmd: Optional override to the container command to run.
    :param str default_runtime: (deprecated) does nothing.
    """
    log = common.configure_logging(__name__, log_level, log_file)
    if default_runtime:
        log.warning("DEPRECATION: 'default_runtime' does nothing, "
                    "use 'cont_cmd' instead")

    if not config_ids:
        log.warn('No config IDs specified')

    if cont_cmd == 'podman':
        r = runner.PodmanRunner(managed_by, cont_cmd=cont_cmd, log=log)
        log.warning("paunch cleanup is partially supported with podman")
    else:
        r = runner.DockerRunner(managed_by, cont_cmd=cont_cmd, log=log)

    for conf_id in config_ids:
        r.remove_containers(conf_id)
Exemple #3
0
def list(managed_by, cont_cmd='podman', default_runtime=None,
         log_level=None, log_file=None):
    """List all containers associated with all config IDs.

    :param str managed_by: Name of the tool managing the containers. Only
                           containers labelled with this will be modified.
    :param str cont_cmd: Optional override to the container command to run.
    :param str default_runtime: (deprecated) does nothing.
    :param int log_level: optional log level for loggers
    :param int log_file: optional log file for messages

    :returns a dict where the key is the config ID and the value is a list of
             'podman inspect' dicts for each container.
    :rtype: defaultdict(list)
    """
    log = common.configure_logging(__name__, log_level, log_file)
    if default_runtime:
        log.warning("DEPRECATION: 'default_runtime' does nothing, "
                    "use 'cont_cmd' instead")

    if cont_cmd == 'podman':
        r = runner.PodmanRunner(managed_by, cont_cmd=cont_cmd, log=log)
    else:
        r = runner.DockerRunner(managed_by, cont_cmd=cont_cmd, log=log)

    return r.list_configs()
Exemple #4
0
def apply(config_id, config, managed_by, labels=None, docker_cmd=None):
    """Execute supplied container configuration.

    :param str config_id: Unique config ID, should not be re-used until any
                          running containers with that config ID have been
                          deleted.
    :param dict config: Configuration data describing container actions to
                        apply.
    :param str managed_by: Name of the tool managing the containers. Only
                           containers labelled with this will be modified.
    :param dict labels: Optional keys/values of labels to apply to containers
                        created with this invocation.
    :param str docker_cmd: Optional override to the docker command to run.

    :returns (list, list, int) lists of stdout and stderr for each execution,
                               and a single return code representing the
                               overall success of the apply.
    :rtype: tuple
    """
    r = runner.DockerRunner(managed_by, docker_cmd=docker_cmd)
    builder = compose1.ComposeV1Builder(config_id=config_id,
                                        config=config,
                                        runner=r,
                                        labels=labels)
    return builder.apply()
Exemple #5
0
def apply(config_id, config, managed_by, labels=None, cont_cmd='podman',
          default_runtime=None, log_level=None, log_file=None,
          cont_log_path=None, healthcheck_disabled=False):
    """Execute supplied container configuration.

    :param str config_id: Unique config ID, should not be re-used until any
                          running containers with that config ID have been
                          deleted.
    :param dict config: Configuration data describing container actions to
                        apply.
    :param str managed_by: Name of the tool managing the containers. Only
                           containers labelled with this will be modified.
    :param dict labels: Optional keys/values of labels to apply to containers
                        created with this invocation.
    :param str cont_cmd: Optional override to the container command to run.
    :param str default_runtime: (deprecated) does nothing.
    :param int log_level: optional log level for loggers
    :param str log_file: optional log file for messages
    :param str cont_log_path: optional log path for containers. Works only for
                              podman engine. Must be an absolute path.
    :param bool healthcheck_disabled: optional boolean to disable container
                                      healthcheck.

    :returns (list, list, int) lists of stdout and stderr for each execution,
                               and a single return code representing the
                               overall success of the apply.
    :rtype: tuple
    """
    log = common.configure_logging(__name__, log_level, log_file)
    if default_runtime:
        log.warning("DEPRECATION: 'default_runtime' does nothing, "
                    "use 'cont_cmd' instead")

    if cont_cmd == 'podman':
        r = runner.PodmanRunner(managed_by, cont_cmd=cont_cmd, log=log)
        builder = podman.PodmanBuilder(
            config_id=config_id,
            config=config,
            runner=r,
            labels=labels,
            log=log,
            cont_log_path=cont_log_path,
            healthcheck_disabled=healthcheck_disabled
        )
    else:
        r = runner.DockerRunner(managed_by, cont_cmd=cont_cmd, log=log)
        builder = compose1.ComposeV1Builder(
            config_id=config_id,
            config=config,
            runner=r,
            labels=labels,
            log=log
        )
    return builder.apply()
Exemple #6
0
def cleanup(config_ids, managed_by, docker_cmd=None):
    """Delete containers no longer applied, rename others to preferred name.

    :param list config_ids: List of config IDs still applied. All containers
                            managed by this tool will be deleted if their
                            config ID is not specified in this list.
    :param str managed_by: Name of the tool managing the containers. Only
                           containers labelled with this will be modified.
    :param str docker_cmd: Optional override to the docker command to run.
    """
    r = runner.DockerRunner(managed_by, docker_cmd=docker_cmd)
    r.delete_missing_configs(config_ids)
    r.rename_containers()
Exemple #7
0
    SH_SCRIPT = '/var/lib/container-puppet/container-puppet.sh'
    CONTAINER_CLI = os.environ.get('CONTAINER_CLI', 'podman')
    CONTAINER_LOG_STDOUT_PATH = os.environ.get('CONTAINER_LOG_STDOUT_PATH',
                                               '/var/log/containers/stdouts')
    CLI_CMD = '/usr/bin/' + CONTAINER_CLI
    LOG = get_logger()
    LOG.info('Running container-puppet')
    CONFIG_VOLUME_PREFIX = os.path.abspath(
        os.environ.get('CONFIG_VOLUME_PREFIX', '/var/lib/config-data'))
    CHECK_MODE = int(os.environ.get('CHECK_MODE', 0))
    LOG.debug('CHECK_MODE: %s' % CHECK_MODE)
    if CONTAINER_CLI == 'docker':
        CLI_DCMD = ['--volume', PUPPETS]
        ENV = {}
        RUNNER = containers_runner.DockerRunner('container-puppet',
                                                cont_cmd='docker',
                                                log=LOG)
    elif CONTAINER_CLI == 'podman':
        # podman doesn't allow relabeling content in /usr and
        # doesn't support named volumes
        CLI_DCMD = ['--security-opt', 'label=disable', '--volume', PUPPETS]
        # podman need to find dependent binaries that are in environment
        ENV = {'PATH': os.environ['PATH']}
        RUNNER = containers_runner.PodmanRunner('container-puppet',
                                                cont_cmd='podman',
                                                log=LOG)
    else:
        LOG.error('Invalid CONTAINER_CLI: %s' % CONTAINER_CLI)
        raise SystemExit()

    config_file = os.environ.get(
Exemple #8
0
 def setUp(self):
     super(TestBaseRunner, self).setUp()
     self.runner = runner.DockerRunner('tester')
     self.podman_runner = runner.PodmanRunner('tester')
Exemple #9
0
def debug(config_id, container_name, action, config, managed_by, labels=None,
          cont_cmd='podman', default_runtime=None, log_level=None,
          log_file=None):
    """Execute supplied container configuration.

    :param str config_id: Unique config ID, should not be re-used until any
                          running containers with that config ID have been
                          deleted.
    :param str container_name: Name of the container in the config you
                               wish to manipulate.
    :param str action: Action to take.
    :param dict config: Configuration data describing container actions to
                        apply.
    :param str managed_by: Name of the tool managing the containers. Only
                           containers labeled with this will be modified.
    :param dict labels: Optional keys/values of labels to apply to containers
                        created with this invocation.
    :param str cont_cmd: Optional override to the container command to run.
    :param str default_runtime: (deprecated) does nothing.
    :param int log_level: optional log level for loggers
    :param int log_file: optional log file for messages

    :returns integer return value from running command or failure for any
             other reason.
    :rtype: int
    """
    log = common.configure_logging(__name__, log_level, log_file)
    if default_runtime:
        log.warning("DEPRECATION: 'default_runtime' does nothing, "
                    "use 'cont_cmd' instead")

    if cont_cmd == 'podman':
        r = runner.PodmanRunner(managed_by, cont_cmd=cont_cmd, log=log)
        builder = podman.PodmanBuilder(
            config_id=config_id,
            config=config,
            runner=r,
            labels=labels,
            log=log
        )
    else:
        r = runner.DockerRunner(managed_by, cont_cmd=cont_cmd, log=log)
        builder = compose1.ComposeV1Builder(
            config_id=config_id,
            config=config,
            runner=r,
            labels=labels,
            log=log
        )
    if action == 'print-cmd':
        cmd = [
            r.cont_cmd,
            'run',
            '--name',
            r.unique_container_name(container_name)
        ]
        builder.container_run_args(cmd, container_name)
        print(' '.join(cmd))
    elif action == 'run':
        cmd = [
            r.cont_cmd,
            'run',
            '--name',
            r.unique_container_name(container_name)
        ]
        builder.container_run_args(cmd, container_name)
        return r.execute_interactive(cmd, log)
    elif action == 'dump-yaml':
        print(yaml.safe_dump(config, default_flow_style=False))
    elif action == 'dump-json':
        print(json.dumps(config, indent=4))
    else:
        raise ValueError('action should be one of: "dump-json", "dump-yaml"',
                         '"print-cmd", or "run"')
Exemple #10
0
# inside of a container.

import glob
import json
import logging
import os
import subprocess
import sys
import tempfile
import time
import multiprocessing

from paunch import runner as containers_runner

logger = None
RUNNER = containers_runner.DockerRunner('docker-puppet')


def get_logger():
    global logger
    if logger is None:
        logger = logging.getLogger()
        ch = logging.StreamHandler(sys.stdout)
        if os.environ.get('DEBUG', False):
            logger.setLevel(logging.DEBUG)
            ch.setLevel(logging.DEBUG)
        else:
            logger.setLevel(logging.INFO)
            ch.setLevel(logging.INFO)
        formatter = logging.Formatter('%(asctime)s %(levelname)s: '
                                      '%(process)s -- %(message)s')
Exemple #11
0
 def setUp(self):
     super(TestDockerRunner, self).setUp()
     self.runner = runner.DockerRunner('tester')
Exemple #12
0
    def test_apply_failed_pull(self):
        orig_call = tenacity.wait.wait_random_exponential.__call__
        orig_argspec = inspect.getargspec(orig_call)
        config = {
            'one': {
                'start_order': 0,
                'image': 'centos:7',
            },
            'two': {
                'start_order': 1,
                'image': 'centos:7',
            },
            'three': {
                'start_order': 2,
                'image': 'centos:6',
            },
            'four': {
                'start_order': 10,
                'image': 'centos:7',
            },
            'four_ls': {
                'action': 'exec',
                'start_order': 20,
                'command': ['four', 'ls', '-l', '/']
            }
        }

        r = runner.DockerRunner(managed_by='tester', cont_cmd='docker')
        exe = mock.Mock()
        exe.side_effect = [
            ('exists', '', 0),  # inspect for image centos:6
            ('', '', 1),  # inspect for missing image centos:7
            ('Pulling centos:7', 'ouch', 1),  # pull centos:7 failure
            ('Pulling centos:7', 'ouch', 1),  # pull centos:7 retry 2
            ('Pulling centos:7', 'ouch', 1),  # pull centos:7 retry 3
            ('Pulling centos:7', 'ouch', 1),  # pull centos:7 retry 4
        ]
        r.execute = exe

        with mock.patch('tenacity.wait.wait_random_exponential.__call__') as f:
            f.return_value = 0
            with mock.patch('inspect.getargspec') as mock_args:
                mock_args.return_value = orig_argspec
                builder = compose1.ComposeV1Builder('foo', config, r)

        stdout, stderr, deploy_status_code = builder.apply()
        self.assertEqual(1, deploy_status_code)
        self.assertEqual(['Pulling centos:7'], stdout)
        self.assertEqual(['ouch'], stderr)

        exe.assert_has_calls([
            # inspect existing image centos:6
            mock.call([
                'docker', 'inspect', '--type', 'image', '--format', 'exists',
                'centos:6'
            ], mock.ANY, False),
            # inspect and pull missing image centos:7
            mock.call([
                'docker', 'inspect', '--type', 'image', '--format', 'exists',
                'centos:7'
            ], mock.ANY, False),
            mock.call(['docker', 'pull', 'centos:7'], mock.ANY),
        ])
Exemple #13
0
    def test_apply(self):
        orig_call = tenacity.wait.wait_random_exponential.__call__
        orig_argspec = inspect.getargspec(orig_call)
        config = {
            'one': {
                'start_order': 0,
                'image': 'centos:7',
            },
            'two': {
                'start_order': 1,
                'image': 'centos:7',
            },
            'three': {
                'start_order': 2,
                'image': 'centos:6',
            },
            'four': {
                'start_order': 10,
                'image': 'centos:7',
            },
            'four_ls': {
                'action': 'exec',
                'start_order': 20,
                'command': ['four', 'ls', '-l', '/']
            }
        }

        r = runner.DockerRunner(managed_by='tester', cont_cmd='docker')
        exe = mock.Mock()
        exe.side_effect = [
            ('exists', '', 0),  # inspect for image centos:6
            ('', '', 1),  # inspect for missing image centos:7
            ('Pulled centos:7', 'ouch', 1),  # pull centos:6 fails
            ('Pulled centos:7', '', 0),  # pull centos:6 succeeds
            ('', '', 0),  # ps for delete_missing_and_updated container_names
            ('', '', 0),  # ps for after delete_missing_and_updated renames
            ('', '', 0),  # ps to only create containers which don't exist
            ('Created one-12345678', '', 0),
            ('Created two-12345678', '', 0),
            ('Created three-12345678', '', 0),
            ('Created four-12345678', '', 0),
            ('a\nb\nc', '', 0)
        ]
        r.discover_container_name = lambda n, c: '%s-12345678' % n
        r.unique_container_name = lambda n: '%s-12345678' % n
        r.execute = exe

        with mock.patch('tenacity.wait.wait_random_exponential.__call__') as f:
            f.return_value = 0
            with mock.patch('inspect.getargspec') as mock_args:
                mock_args.return_value = orig_argspec
                builder = compose1.ComposeV1Builder('foo', config, r)

        stdout, stderr, deploy_status_code = builder.apply()
        self.assertEqual(0, deploy_status_code)
        self.assertEqual([
            'Pulled centos:7', 'Created one-12345678', 'Created two-12345678',
            'Created three-12345678', 'Created four-12345678', 'a\nb\nc'
        ], stdout)
        self.assertEqual([], stderr)

        exe.assert_has_calls([
            # inspect existing image centos:6
            mock.call([
                'docker', 'inspect', '--type', 'image', '--format', 'exists',
                'centos:6'
            ], mock.ANY, False),
            # inspect and pull missing image centos:7
            mock.call([
                'docker', 'inspect', '--type', 'image', '--format', 'exists',
                'centos:7'
            ], mock.ANY, False),
            # first pull attempt fails
            mock.call(['docker', 'pull', 'centos:7'], mock.ANY),
            # second pull attempt succeeds
            mock.call(['docker', 'pull', 'centos:7'], mock.ANY),
            # ps for delete_missing_and_updated container_names
            mock.call([
                'docker', 'ps', '-a', '--filter', 'label=managed_by=tester',
                '--filter', 'label=config_id=foo', '--format',
                '{{.Names}} {{.Label "container_name"}}'
            ], mock.ANY),
            # ps for after delete_missing_and_updated renames
            mock.call([
                'docker', 'ps', '-a', '--filter', 'label=managed_by=tester',
                '--format', '{{.Names}} {{.Label "container_name"}}'
            ], mock.ANY),
            # ps to only create containers which don't exist
            mock.call([
                'docker', 'ps', '-a', '--filter', 'label=managed_by=tester',
                '--filter', 'label=config_id=foo', '--format',
                '{{.Names}} {{.Label "container_name"}}'
            ], mock.ANY),
            # run one
            mock.call([
                'docker', 'run', '--name', 'one-12345678', '--label',
                'config_id=foo', '--label', 'container_name=one', '--label',
                'managed_by=tester', '--label',
                'config_data=%s' % json.dumps(config['one']), '--detach=true',
                'centos:7'
            ], mock.ANY),
            # run two
            mock.call([
                'docker', 'run', '--name', 'two-12345678', '--label',
                'config_id=foo', '--label', 'container_name=two', '--label',
                'managed_by=tester', '--label',
                'config_data=%s' % json.dumps(config['two']), '--detach=true',
                'centos:7'
            ], mock.ANY),
            # run three
            mock.call([
                'docker', 'run', '--name', 'three-12345678', '--label',
                'config_id=foo', '--label', 'container_name=three', '--label',
                'managed_by=tester', '--label',
                'config_data=%s' % json.dumps(config['three']),
                '--detach=true', 'centos:6'
            ], mock.ANY),
            # run four
            mock.call([
                'docker', 'run', '--name', 'four-12345678', '--label',
                'config_id=foo', '--label', 'container_name=four', '--label',
                'managed_by=tester', '--label',
                'config_data=%s' % json.dumps(config['four']), '--detach=true',
                'centos:7'
            ], mock.ANY),
            # execute within four
            mock.call(['docker', 'exec', 'four-12345678', 'ls', '-l', '/'],
                      mock.ANY),
        ])
Exemple #14
0
    def test_apply_idempotency(self):
        config = {
            # not running yet
            'one': {
                'start_order': 0,
                'image': 'centos:7',
            },
            # running, but with a different config
            'two': {
                'start_order': 1,
                'image': 'centos:7',
            },
            # running with the same config
            'three': {
                'start_order': 2,
                'image': 'centos:7',
            },
            # not running yet
            'four': {
                'start_order': 10,
                'image': 'centos:7',
            },
            'four_ls': {
                'action': 'exec',
                'start_order': 20,
                'command': ['four', 'ls', '-l', '/']
            }
        }

        r = runner.DockerRunner(managed_by='tester', cont_cmd='docker')
        exe = mock.Mock()
        exe.side_effect = [
            # inspect for image centos:7
            ('exists', '', 0),
            # ps for delete_missing_and_updated container_names
            ('''five five
six six
two-12345678 two
three-12345678 three''', '', 0),
            # rm five
            ('', '', 0),
            # rm six
            ('', '', 0),
            # inspect two
            ('{"start_order": 1, "image": "centos:6"}', '', 0),
            # rm two, changed config data
            ('', '', 0),
            # inspect three
            ('{"start_order": 2, "image": "centos:7"}', '', 0),
            # ps for after delete_missing_and_updated renames
            ('', '', 0),
            # ps to only create containers which don't exist
            ('three-12345678 three', '', 0),
            ('Created one-12345678', '', 0),
            ('Created two-12345678', '', 0),
            ('Created four-12345678', '', 0),
            ('a\nb\nc', '', 0)
        ]
        r.discover_container_name = lambda n, c: '%s-12345678' % n
        r.unique_container_name = lambda n: '%s-12345678' % n
        r.execute = exe

        builder = compose1.ComposeV1Builder('foo', config, r)
        stdout, stderr, deploy_status_code = builder.apply()
        self.assertEqual(0, deploy_status_code)
        self.assertEqual([
            'Created one-12345678', 'Created two-12345678',
            'Created four-12345678', 'a\nb\nc'
        ], stdout)
        self.assertEqual([], stderr)

        exe.assert_has_calls([
            # inspect image centos:7
            mock.call([
                'docker', 'inspect', '--type', 'image', '--format', 'exists',
                'centos:7'
            ], mock.ANY, False),
            # ps for delete_missing_and_updated container_names
            mock.call([
                'docker', 'ps', '-a', '--filter', 'label=managed_by=tester',
                '--filter', 'label=config_id=foo', '--format',
                '{{.Names}} {{.Label "container_name"}}'
            ], mock.ANY),
            # rm containers not in config
            mock.call(['docker', 'rm', '-f', 'five'], mock.ANY),
            mock.call(['docker', 'rm', '-f', 'six'], mock.ANY),
            # rm two, changed config
            mock.call([
                'docker', 'inspect', '--type', 'container', '--format',
                '{{index .Config.Labels "config_data"}}', 'two-12345678'
            ], mock.ANY, False),
            mock.call(['docker', 'rm', '-f', 'two-12345678'], mock.ANY),
            # check three, config hasn't changed
            mock.call([
                'docker', 'inspect', '--type', 'container', '--format',
                '{{index .Config.Labels "config_data"}}', 'three-12345678'
            ], mock.ANY, False),
            # ps for after delete_missing_and_updated renames
            mock.call([
                'docker', 'ps', '-a', '--filter', 'label=managed_by=tester',
                '--format', '{{.Names}} {{.Label "container_name"}}'
            ], mock.ANY),
            # ps to only create containers which don't exist
            mock.call([
                'docker', 'ps', '-a', '--filter', 'label=managed_by=tester',
                '--filter', 'label=config_id=foo', '--format',
                '{{.Names}} {{.Label "container_name"}}'
            ], mock.ANY),
            # run one
            mock.call([
                'docker', 'run', '--name', 'one-12345678', '--label',
                'config_id=foo', '--label', 'container_name=one', '--label',
                'managed_by=tester', '--label',
                'config_data=%s' % json.dumps(config['one']), '--detach=true',
                'centos:7'
            ], mock.ANY),
            # run two
            mock.call([
                'docker', 'run', '--name', 'two-12345678', '--label',
                'config_id=foo', '--label', 'container_name=two', '--label',
                'managed_by=tester', '--label',
                'config_data=%s' % json.dumps(config['two']), '--detach=true',
                'centos:7'
            ], mock.ANY),
            # don't run three, its already running
            # run four
            mock.call([
                'docker', 'run', '--name', 'four-12345678', '--label',
                'config_id=foo', '--label', 'container_name=four', '--label',
                'managed_by=tester', '--label',
                'config_data=%s' % json.dumps(config['four']), '--detach=true',
                'centos:7'
            ], mock.ANY),
            # execute within four
            mock.call(['docker', 'exec', 'four-12345678', 'ls', '-l', '/'],
                      mock.ANY),
        ])