<commit_before># Copyright (C) 2013-2014 SignalFuse, Inc.
# Copyright (C) 2015 SignalFx, Inc.
#
# Docker container orchestration utility.

from __future__ import print_function

import collections
import json
import time
try:
    import urlparse
except ImportError:
    # Try for Python3
    from urllib import parse as urlparse

from .. import audit
from .. import exceptions
from ..termoutput import green, blue, red, time_ago


CONTAINER_STATUS_FMT = '{:<25s} '
TASK_RESULT_FMT = '{:<10s}'


class Task:
    """Base class for tasks acting on containers."""

    def __init__(self, action, o, container):
        """Initialize the base task parameters.

        Args:
            o (termoutput.OutputFormatter): the output formatter used for task
                output.
            container (entities.Container): the container the task operates on.
        """
        self.action = action
        self.o = o
        self.container = container

    def _wait_for_status(self, cond, retries=10):
        """Wait for the container's status to comply to the given condition."""
        while retries >= 0:
            status = self.container.status(refresh=True)
            if cond(status):
                return True
            retries -= 1
            if retries >= 0:
                time.sleep(0.5)
        return False

    def _check_for_state(self, state, cond):
        """Wait for the container to reach the given lifecycle state by executing
        the corresponding, configured lifecycle checks, taking into account the
        container state (through _wait_for_status) while the checks wait for
        the target status to be reached.

        Args:
            state (string): the target lifecycle state.
            cond (lambda): a lambda function that takes in the container's
                status (from inspection) and returns True if it conforms to the
                target desired lifecycle state.
        """
        checks = self.container.start_lifecycle_checks(state)
        if not checks:
            return self._wait_for_status(cond)

        # Wait for all checks to complete
        while not checks.ready():
            checks.wait(1)
            if not self._wait_for_status(cond, retries=1):
                return False

        # Check results
        for check in checks.get():
            if not check:
                return False

        return True

    def run(self, auditor=None):
        if auditor:
            auditor.action(action=self.action, level=audit.DEBUG,
                           what=self.container)

        try:
            self._run()
            if auditor:
                auditor.success(action=self.action, level=audit.DEBUG,
                                what=self.container)
        except Exception as e:
            if auditor:
                auditor.error(action=self.action, what=self.container,
                              message=e)
            exceptions.raise_with_tb()

    def _run(self):
        raise NotImplementedError


class StatusTask(Task):
    """Check for and display a container's status."""

    def __init__(self, o, container):
        Task.__init__(self, 'status', o, container)

    def _run(self):
        self.o.reset()
        self.o.pending('checking...')

        try:
            if self.container.is_running():
                self.o.commit(green(CONTAINER_STATUS_FMT.format(
                    self.container.shortid_and_tag)))
                self.o.commit(green('running{}'.format(
                    time_ago(self.container.started_at))))
            else:
                self.o.commit(CONTAINER_STATUS_FMT.format(
                    self.container.shortid_and_tag))
                self.o.commit(red('down{}'.format(
                    time_ago(self.container.finished_at))))
        except Exception:
            self.o.commit(CONTAINER_STATUS_FMT.format('-'))
            self.o.commit(red(TASK_RESULT_FMT.format('host down')))
            return


class StartTask(Task):
    """Start a container, refreshing the image if requested.

    If reuse is True, the container will not be removed and re-created
    if it exists."""

    def __init__(self, o, container, registries={}, refresh=False,
                 reuse=False):
        Task.__init__(self, 'start', o, container)
        self._registries = registries
        self._refresh = refresh
        self._reuse = reuse

    def _run(self):
        self.o.reset()
        error = None
        try:
            # TODO: None is used to indicate that no action was performed
            # because the container and its application were already
            # running. This makes the following code not very nice and this
            # could be improved.
            result = self._create_and_start_container()
            if result is None:
                self.o.commit(blue('up{}'.format(
                    time_ago(self.container.started_at))))
            elif result:
                self.o.commit(green('started'))
            else:
                self.o.commit(red('service did not start!'))

            if result is False:
                error = [
                    ('Halting start sequence because {} failed to start!'
                        .format(self.container)),
                    self.container.ship.backend.logs(self.container.id)]
                raise exceptions.ContainerOrchestrationException(
                    self.container, '\n'.join(error).strip())
        except Exception:
            self.o.commit(red('failed to start container!'))
            raise

    def _create_and_start_container(self):
        """Start the container.

        If the container and its application are already running, no action is
        performed and the function returns None to indicate that. Otherwise, a
        new container must be created and started. To achieve this, any
        existing container of the same name is first removed. Then, if
        necessary or if requested, the container image is pulled from its
        registry. Finally, the container is created and started, configured as
        necessary. We then wait for the application to start and return True or
        False depending on whether the start was successful."""
        self.o.pending('checking service...')
        if self.container.is_running():
            self.o.commit(blue(CONTAINER_STATUS_FMT.format(
                self.container.shortid_and_tag)))
            # We use None as a special marker showing the container and the
            # application were already running.
            return None

        # Otherwise we need to start it.
        if (not self._reuse) or (not self.container.status()):
            CleanTask(self.o, self.container, standalone=False).run()

            # Check if the image is available, or if we need to pull it down.
            image = self.container.get_image_details()
            if self._refresh or \
                not filter(
                    lambda i: self.container.image in i['RepoTags'],
                    self.container.ship.backend.images(image['repository'])):
                PullTask(self.o, self.container, self._registries,
                         standalone=False).run()

            # Create and start the container.
            ports = self.container.ports \
                and list(map(lambda p: tuple(p['exposed'].split('/')),
                             self.container.ports.values())) \
                or None<commit_msg>Handle the presence of a matching image repository with no tag<commit_after># Copyright (C) 2013-2014 SignalFuse, Inc.
# Copyright (C) 2015 SignalFx, Inc.
#
# Docker container orchestration utility.

from __future__ import print_function

import collections
import json
import time
try:
    import urlparse
except ImportError:
    # Try for Python3
    from urllib import parse as urlparse

from .. import audit
from .. import exceptions
from ..termoutput import green, blue, red, time_ago


CONTAINER_STATUS_FMT = '{:<25s} '
TASK_RESULT_FMT = '{:<10s}'


class Task:
    """Base class for tasks acting on containers."""

    def __init__(self, action, o, container):
        """Initialize the base task parameters.

        Args:
            o (termoutput.OutputFormatter): the output formatter used for task
                output.
            container (entities.Container): the container the task operates on.
        """
        self.action = action
        self.o = o
        self.container = container

    def _wait_for_status(self, cond, retries=10):
        """Wait for the container's status to comply to the given condition."""
        while retries >= 0:
            status = self.container.status(refresh=True)
            if cond(status):
                return True
            retries -= 1
            if retries >= 0:
                time.sleep(0.5)
        return False

    def _check_for_state(self, state, cond):
        """Wait for the container to reach the given lifecycle state by executing
        the corresponding, configured lifecycle checks, taking into account the
        container state (through _wait_for_status) while the checks wait for
        the target status to be reached.

        Args:
            state (string): the target lifecycle state.
            cond (lambda): a lambda function that takes in the container's
                status (from inspection) and returns True if it conforms to the
                target desired lifecycle state.
        """
        checks = self.container.start_lifecycle_checks(state)
        if not checks:
            return self._wait_for_status(cond)

        # Wait for all checks to complete
        while not checks.ready():
            checks.wait(1)
            if not self._wait_for_status(cond, retries=1):
                return False

        # Check results
        for check in checks.get():
            if not check:
                return False

        return True

    def run(self, auditor=None):
        if auditor:
            auditor.action(action=self.action, level=audit.DEBUG,
                           what=self.container)

        try:
            self._run()
            if auditor:
                auditor.success(action=self.action, level=audit.DEBUG,
                                what=self.container)
        except Exception as e:
            if auditor:
                auditor.error(action=self.action, what=self.container,
                              message=e)
            exceptions.raise_with_tb()

    def _run(self):
        raise NotImplementedError


class StatusTask(Task):
    """Check for and display a container's status."""

    def __init__(self, o, container):
        Task.__init__(self, 'status', o, container)

    def _run(self):
        self.o.reset()
        self.o.pending('checking...')

        try:
            if self.container.is_running():
                self.o.commit(green(CONTAINER_STATUS_FMT.format(
                    self.container.shortid_and_tag)))
                self.o.commit(green('running{}'.format(
                    time_ago(self.container.started_at))))
            else:
                self.o.commit(CONTAINER_STATUS_FMT.format(
                    self.container.shortid_and_tag))
                self.o.commit(red('down{}'.format(
                    time_ago(self.container.finished_at))))
        except Exception:
            self.o.commit(CONTAINER_STATUS_FMT.format('-'))
            self.o.commit(red(TASK_RESULT_FMT.format('host down')))
            return


class StartTask(Task):
    """Start a container, refreshing the image if requested.

    If reuse is True, the container will not be removed and re-created
    if it exists."""

    def __init__(self, o, container, registries={}, refresh=False,
                 reuse=False):
        Task.__init__(self, 'start', o, container)
        self._registries = registries
        self._refresh = refresh
        self._reuse = reuse

    def _run(self):
        self.o.reset()
        error = None
        try:
            # TODO: None is used to indicate that no action was performed
            # because the container and its application were already
            # running. This makes the following code not very nice and this
            # could be improved.
            result = self._create_and_start_container()
            if result is None:
                self.o.commit(blue('up{}'.format(
                    time_ago(self.container.started_at))))
            elif result:
                self.o.commit(green('started'))
            else:
                self.o.commit(red('service did not start!'))

            if result is False:
                error = [
                    ('Halting start sequence because {} failed to start!'
                        .format(self.container)),
                    self.container.ship.backend.logs(self.container.id)]
                raise exceptions.ContainerOrchestrationException(
                    self.container, '\n'.join(error).strip())
        except Exception:
            self.o.commit(red('failed to start container!'))
            raise

    def _create_and_start_container(self):
        """Start the container.

        If the container and its application are already running, no action is
        performed and the function returns None to indicate that. Otherwise, a
        new container must be created and started. To achieve this, any
        existing container of the same name is first removed. Then, if
        necessary or if requested, the container image is pulled from its
        registry. Finally, the container is created and started, configured as
        necessary. We then wait for the application to start and return True or
        False depending on whether the start was successful."""
        self.o.pending('checking service...')
        if self.container.is_running():
            self.o.commit(blue(CONTAINER_STATUS_FMT.format(
                self.container.shortid_and_tag)))
            # We use None as a special marker showing the container and the
            # application were already running.
            return None

        # Otherwise we need to start it.
        if (not self._reuse) or (not self.container.status()):
            CleanTask(self.o, self.container, standalone=False).run()

            # Check if the image is available, or if we need to pull it down.
            image = self.container.get_image_details()
            if self._refresh or \
                not filter(
                    lambda i: self.container.image in (i['RepoTags'] or []),
                    self.container.ship.backend.images(image['repository'])):
                PullTask(self.o, self.container, self._registries,
                         standalone=False).run()

            # Create and start the container.
            ports = self.container.ports \
                and list(map(lambda p: tuple(p['exposed'].split('/')),
                             self.container.ports.values())) \
                or None
{'fraction_non_alphanumeric': 0.08536991409735634, 'fraction_numerical': 0.003462742225477792, 'mean_word_length': 1.8687679083094555, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3588217', 'n_tokens_mistral': 3869, 'n_tokens_neox': 3672, 'n_words': 1220}