github.com/jiasir/deis@v1.12.2/controller/registry/dockerclient.py (about)

     1  # -*- coding: utf-8 -*-
     2  """Support the Deis workflow by manipulating and publishing Docker images."""
     3  
     4  from __future__ import unicode_literals
     5  import io
     6  import logging
     7  
     8  from django.conf import settings
     9  from rest_framework.exceptions import PermissionDenied
    10  from simpleflock import SimpleFlock
    11  import docker
    12  
    13  logger = logging.getLogger(__name__)
    14  
    15  
    16  class DockerClient(object):
    17      """Use the Docker API to pull, tag, build, and push images to deis-registry."""
    18  
    19      FLOCKFILE = '/tmp/controller-pull'
    20  
    21      def __init__(self):
    22          self.client = docker.Client(version='auto')
    23          self.registry = settings.REGISTRY_HOST + ':' + str(settings.REGISTRY_PORT)
    24  
    25      def publish_release(self, source, config, target, deis_registry):
    26          """Update a source Docker image with environment config and publish it to deis-registry."""
    27          # get the source repository name and tag
    28          src_name, src_tag = docker.utils.parse_repository_tag(source)
    29          # get the target repository name and tag
    30          name, tag = docker.utils.parse_repository_tag(target)
    31          # strip any "http://host.domain:port" prefix from the target repository name,
    32          # since we always publish to the Deis registry
    33          name = strip_prefix(name)
    34  
    35          # pull the source image from the registry
    36          # NOTE: this relies on an implementation detail of deis-builder, that
    37          # the image has been uploaded already to deis-registry
    38          if deis_registry:
    39              repo = "{}/{}".format(self.registry, src_name)
    40          else:
    41              repo = src_name
    42          self.pull(repo, src_tag)
    43  
    44          # tag the image locally without the repository URL
    45          image = "{}:{}".format(repo, src_tag)
    46          self.tag(image, src_name, tag=src_tag)
    47  
    48          # build a Docker image that adds a "last-mile" layer of environment
    49          config.update({'DEIS_APP': name, 'DEIS_RELEASE': tag})
    50          self.build(source, config, name, tag)
    51  
    52          # push the image to deis-registry
    53          self.push("{}/{}".format(self.registry, name), tag)
    54  
    55      def build(self, source, config, repo, tag):
    56          """Add a "last-mile" layer of environment config to a Docker image for deis-registry."""
    57          check_blacklist(repo)
    58          env = ' '.join("{}='{}'".format(
    59              k, v.encode('unicode-escape').replace("'", "\\'")) for k, v in config.viewitems())
    60          dockerfile = "FROM {}\nENV {}".format(source, env)
    61          f = io.BytesIO(dockerfile.encode('utf-8'))
    62          target_repo = "{}/{}:{}".format(self.registry, repo, tag)
    63          logger.info("Building Docker image {}".format(target_repo))
    64          with SimpleFlock(self.FLOCKFILE, timeout=1200):
    65              stream = self.client.build(fileobj=f, tag=target_repo, stream=True, rm=True)
    66              log_output(stream)
    67  
    68      def pull(self, repo, tag):
    69          """Pull a Docker image into the local storage graph."""
    70          check_blacklist(repo)
    71          logger.info("Pulling Docker image {}:{}".format(repo, tag))
    72          with SimpleFlock(self.FLOCKFILE, timeout=1200):
    73              stream = self.client.pull(repo, tag=tag, stream=True, insecure_registry=True)
    74              log_output(stream)
    75  
    76      def push(self, repo, tag):
    77          """Push a local Docker image to a registry."""
    78          logger.info("Pushing Docker image {}:{}".format(repo, tag))
    79          stream = self.client.push(repo, tag=tag, stream=True, insecure_registry=True)
    80          log_output(stream)
    81  
    82      def tag(self, image, repo, tag):
    83          """Tag a local Docker image with a new name and tag."""
    84          check_blacklist(repo)
    85          logger.info("Tagging Docker image {} as {}:{}".format(image, repo, tag))
    86          if not self.client.tag(image, repo, tag=tag, force=True):
    87              raise docker.errors.DockerException("tagging failed")
    88  
    89  
    90  def check_blacklist(repo):
    91      """Check a Docker repository name for collision with deis/* components."""
    92      blacklisted = [  # NOTE: keep this list up to date!
    93          'builder', 'cache', 'controller', 'database', 'logger', 'logspout',
    94          'publisher', 'registry', 'router', 'store-admin', 'store-daemon',
    95          'store-gateway', 'store-metadata', 'store-monitor', 'swarm', 'mesos-master',
    96          'mesos-marathon', 'mesos-slave', 'zookeeper',
    97      ]
    98      if any("deis/{}".format(c) in repo for c in blacklisted):
    99          raise PermissionDenied("Repository name {} is not allowed".format(repo))
   100  
   101  
   102  def log_output(stream):
   103      """Log a stream at DEBUG level, and raise DockerException if it contains "error"."""
   104      for chunk in stream:
   105          logger.debug(chunk)
   106          # error handling requires looking at the response body
   107          if '"error"' in chunk.lower():
   108              raise docker.errors.DockerException(chunk)
   109  
   110  
   111  def strip_prefix(name):
   112      """Strip the schema and host:port from a Docker repository name."""
   113      paths = name.split('/')
   114      return '/'.join(p for p in paths if p and '.' not in p and ':' not in p)
   115  
   116  
   117  def publish_release(source, config, target, deis_registry):
   118  
   119      client = DockerClient()
   120      return client.publish_release(source, config, target, deis_registry)