Ejemplo n.º 1
0
 def start_supervisor(self):
     """
     Starts the container and runs Supervisor inside the container.
     """
     command = '/usr/bin/supervisord -n'
     self.logger.info("Starting process supervisor (and SSH server) ..")
     # Select the Docker image to use as a base for the container.
     image = self.find_image(self.image) or self.find_image(self.base)
     self.logger.verbose("Creating container from image: %r", image)
     # Start the container with the given command.
     result = self.client.create_container(image=image.unique_name,
                                           command=command,
                                           hostname=self.hostname,
                                           ports=['22'])
     container_ids = [c['Id'] for c in self.client.containers(all=True)]
     self.session.container_id = self.expand_id(result['Id'], container_ids)
     self.logger.verbose("Created container: %s", summarize_id(self.session.container_id))
     for text in result.get('Warnings', []):
         logger.warn("%s", text)
     # Start the command inside the container.
     self.logger.verbose("Running command: %s", command)
     self.client.start(self.session.container_id)
     # Make the output from the container visible to the user.
     self.session.remote_terminal = RemoteTerminal(self.session.container_id)
     self.session.remote_terminal.attach()
     # Persist association between (repository, tag) and container id.
     with self.config as state:
         state['containers'][self.image.key] = self.session.container_id
Ejemplo n.º 2
0
 def __repr__(self):
     """
     Provide a textual representation of an :py:class:`Image` object.
     """
     properties = ["repository=%r" % self.repository,
                   "tag=%r" % self.tag]
     if self.id:
         properties.append("id=%r" % summarize_id(self.id))
     return "Image(%s)" % ", ".join(properties)
Ejemplo n.º 3
0
 def ssh_endpoint(self):
     """
     Wait for the container to become reachable over SSH_ and get a tuple
     with the IP address and port number that can be used to connect to the
     container over SSH.
     """
     self.check_active()
     if self.session.ssh_endpoint:
         return self.session.ssh_endpoint
     # Get the local port connected to the container.
     host_port = int(self.client.port(self.session.container_id, '22'))
     self.logger.debug("Configured port redirection for container %s: %s:%i -> %s:%i",
                       summarize_id(self.session.container_id),
                       socket.gethostname(), host_port,
                       self.hostname, 22)
     # Give the container time to finish the SSH server installation.
     self.logger.verbose("Waiting for SSH connection to %s (max %i seconds) ..",
                         summarize_id(self.session.container_id), self.timeout)
     global_timeout = time.time() + self.timeout
     ssh_timer = humanfriendly.Timer()
     while time.time() < global_timeout:
         for ip_address in find_local_ip_addresses():
             # Try to open an SSH connection to the container.
             self.logger.debug("Connecting to container over SSH at %s:%s ..", ip_address, host_port)
             command = self.get_ssh_client_command(ip_address, host_port) + ['true']
             ssh_client = subprocess.Popen(command, stdin=open(os.devnull), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
             # Give this attempt at most 10 seconds to succeed.
             inner_timeout = time.time() + 10
             while time.time() < inner_timeout:
                 if ssh_client.poll() is not None:
                     break
                 time.sleep(0.1)
             else:
                 self.logger.debug("Attempt to connect timed out!")
             if ssh_client.returncode == 0:
                 # At this point we have successfully connected!
                 self.session.ssh_endpoint = (ip_address, host_port)
                 self.logger.debug("Connected to %s at %s using SSH in %s.",
                                   self.image.name, self.session.ssh_endpoint,
                                   ssh_timer)
                 return self.session.ssh_endpoint
             time.sleep(1)
     msg = "Time ran out while waiting to connect to container %s over SSH! (Most likely something went wrong while initializing the container..)"
     raise SecureShellTimeout, msg % self.image.name
Ejemplo n.º 4
0
    def find_container(self):
        """
        Check to see if the current :py:class:`Container` has an associated
        Docker container that is currently running.

        :returns: ``True`` when a running container exists, ``False``
                  otherwise.
        """
        if not self.session.container_id:
            self.logger.verbose("Looking for running container ..")
            state = self.config.load()
            container_id = state['containers'].get(self.image.key)
            # Make sure the container is still running.
            if container_id in [c['Id'] for c in self.client.containers()]:
                self.session.container_id = container_id
                self.logger.info("Found running container: %s", summarize_id(container_id))
        return bool(self.session.container_id)
Ejemplo n.º 5
0
def find_base_image(client):
    """
    Find the id of the base image that's used by Redock to create new
    containers. If the image doesn't exist yet it will be created using
    :py:func:`create_base_image()`.

    :param client: Connection to Docker (instance of :py:class:`docker.Client`)
    :returns: The unique id of the base image.
    """
    logger.verbose("Looking for base image ..")
    image_id = find_named_image(client, BASE_IMAGE_REPO, BASE_IMAGE_TAG)
    if image_id:
        logger.verbose("Found base image: %s", summarize_id(image_id))
        return image_id
    else:
        logger.verbose("No base image found, creating it ..")
        return create_base_image(client)
Ejemplo n.º 6
0
def create_base_image(client):
    """
    Create the base image that's used by Redock to create new containers. This
    base image differs from the ubuntu:precise_ image (on which it is based) on
    a couple of points:

    - Automatic installation of recommended packages is disabled to conserve
      disk space.

    - The Ubuntu package mirror is set to a geographically close location to
      speed up downloading of system packages (see
      :py:func:`redock.utils.select_ubuntu_mirror()`).

    - The package list is updated to make sure apt-get_ installs the most up to
      date packages.

    - The following system packages are installed:

      language-pack-en-base_
        In a base Docker Ubuntu 12.04 image lots of commands complain loudly
        about the locale_. This silences the warnings by fixing the problem
        (if you want to call it that).

      openssh-server_
        After creating a new container Redock will connect to it over SSH_,
        so having an SSH server installed is a good start :-)

      supervisor_
        The base Docker Ubuntu 12.04 image has init_ (upstart_) disabled.
        Indeed we don't need all of the bagage that comes with init but it is
        nice to have a process runner for the SSH_ server (and eventually maybe
        more).

    - The initscripts_ and upstart_ system packages are marked 'on hold' so
      that apt-get_ will not upgrade them. This makes it possible to run
      ``apt-get dist-upgrade`` inside containers.

    - An SSH_ key pair is generated and the SSH public key is installed inside
      the base image so that Redock can connect to the container over SSH (you
      need ssh-keygen_ installed).

    - Supervisor_ is configured to automatically start the SSH_ server.

    :param client: Connection to Docker (instance of :py:class:`docker.Client`)
    :returns: The unique id of the base image.

    .. _apt-get: http://manpages.ubuntu.com/manpages/precise/man8/apt-get.8.html
    .. _init: http://manpages.ubuntu.com/manpages/precise/man8/init.8.html
    .. _initscripts: http://packages.ubuntu.com/precise/initscripts
    .. _language-pack-en-base: http://packages.ubuntu.com/precise/language-pack-en-base
    .. _locale: http://en.wikipedia.org/wiki/Locale
    .. _openssh-server: http://packages.ubuntu.com/precise/openssh-server
    .. _ssh-keygen: http://manpages.ubuntu.com/manpages/precise/man1/ssh-keygen.1.html
    .. _supervisor: http://packages.ubuntu.com/precise/supervisor
    .. _ubuntu:precise: https://index.docker.io/_/ubuntu/
    .. _upstart: http://packages.ubuntu.com/precise/upstart
    """
    download_image(client, 'ubuntu', 'precise')
    creation_timer = Timer()
    logger.info("Initializing base image (this can take a few minutes but you only have to do it once) ..")
    command = ' && '.join([
        'echo %s > /etc/apt/apt.conf.d/90redock' % pipes.quote(APT_CONFIG.strip()),
        'echo %s > /etc/apt/sources.list' % pipes.quote(SOURCES_LIST.format(mirror=select_ubuntu_mirror()).strip()),
        'apt-get update',
        'DEBIAN_FRONTEND=noninteractive apt-get install -q -y language-pack-en-base openssh-server supervisor',
        'apt-get clean', # Don't keep the +/- 20 MB of *.deb archives after installation.
        # Make it possible to run `apt-get dist-upgrade'.
        # https://help.ubuntu.com/community/PinningHowto#Introduction_to_Holding_Packages
        'apt-mark hold initscripts upstart',
        # Install the generated SSH public key.
        'mkdir -p /root/.ssh',
        'echo %s > /root/.ssh/authorized_keys' % pipes.quote(get_ssh_public_key()),
        # Create the Supervisor configuration for the SSH server.
        'echo %s > /etc/supervisor/conf.d/ssh-server.conf' % pipes.quote(SUPERVISOR_CONFIG.strip())])
    logger.debug("Generated command line: %s", command)
    result = client.create_container(image='ubuntu:precise',
                                     command='bash -c %s' % pipes.quote(command),
                                     hostname='redock-template',
                                     ports=['22'])
    container_id = result['Id']
    for text in result.get('Warnings', []):
      logger.warn("%s", text)
    logger.verbose("Created container %s.", summarize_id(container_id))
    client.start(container_id)
    with RemoteTerminal(container_id):
        logger.info("Waiting for initialization to finish ..")
        client.wait(container_id)
        logger.info("Finished initialization in %s.", creation_timer)
    commit_timer = Timer()
    logger.info("Saving initialized container as new base image ..")
    result = client.commit(container_id, repository='redock', tag='base')
    logger.info("Done! Committed base image as %s in %s.", summarize_id(result['Id']), commit_timer)
    return result['Id']