def walk_string(s):
        try:
            as_tag = docker_name.Tag(s)
            if as_tag in overrides:
                return str(overrides[as_tag])

            # Resolve the tag to digest using the standard
            # Docker keychain logic.
            creds = docker_creds.DefaultKeychain.Resolve(as_tag)
            with v2_2_image.FromRegistry(as_tag, creds, transport) as img:
                if img.exists():
                    digest = str(
                        docker_name.Digest('{repository}@{digest}'.format(
                            repository=as_tag.as_repository(),
                            digest=util.Digest(img.manifest()))))
                else:
                    # If the tag doesn't exists as v2.2, then try as v2.
                    with v2_image.FromRegistry(as_tag, creds,
                                               transport) as img:
                        digest = str(
                            docker_name.Digest('{repository}@{digest}'.format(
                                repository=as_tag.as_repository(),
                                digest=v2_util.Digest(img.manifest()))))

            # Make sure we consistently resolve all instances of a tag,
            # since it is technically possible to have a race here.
            overrides[as_tag] = digest
            return digest
        except:
            return s
Beispiel #2
0
  def images(self):
    """Returns a list of tuples whose elements are (name, platform, image).

    Raises:
      InvalidMediaTypeError: a child with an unexpected media type was found.
    """
    manifests = json.loads(self.manifest())['manifests']
    results = []
    for entry in manifests:
      digest = entry['digest']
      name = docker_name.Digest('{base}@{digest}'.format(
          base=self._name.as_repository(), digest=digest))

      # TODO(user): Support Image Index.
      if entry['mediaType'] == docker_http.MANIFEST_LIST_MIME:
        image = FromRegistry(name, self._creds, self._original_transport)
      elif entry['mediaType'] == docker_http.MANIFEST_SCHEMA2_MIME:
        image = v2_2_image.FromRegistry(name, self._creds,
                                        self._original_transport)
      else:
        raise InvalidMediaTypeError('Invalid media type: ' + entry['mediaType'])

      platform = Platform(entry['platform']) if 'platform' in entry else None
      results.append((name, platform, image))
    return results
def main():
    args = parser.parse_args()

    if not args.name or not args.directory:
        raise Exception('--name and --directory are required arguments.')

    transport = transport_pool.Http(httplib2.Http, size=_THREADS)

    if '@' in args.name:
        name = docker_name.Digest(args.name)
    else:
        name = docker_name.Tag(args.name)

    # Resolve the appropriate credential to use based on the standard Docker
    # client logic.
    creds = docker_creds.DefaultKeychain.Resolve(name)

    with v2_2_image.FromRegistry(name, creds, transport) as v2_2_img:
        if v2_2_img.exists():
            save.fast(v2_2_img, args.directory, threads=_THREADS)
            return

    with v2_image.FromRegistry(name, creds, transport) as v2_img:
        with v2_compat.V22FromV2(v2_img) as v2_2_img:
            save.fast(v2_2_img, args.directory, threads=_THREADS)
            return
Beispiel #4
0
    def images(self):
        """Returns a list of tuples whose elements are (name, platform, image).

    Raises:
      InvalidMediaTypeError: a child with an unexpected media type was found.
    """
        manifests = json.loads(self.manifest())['manifests']
        results = []
        for entry in manifests:
            digest = entry['digest']
            base = self._name.as_repository()  # pytype: disable=attribute-error
            name = docker_name.Digest('{base}@{digest}'.format(base=base,
                                                               digest=digest))
            media_type = entry['mediaType']

            if media_type in docker_http.MANIFEST_LIST_MIMES:
                image = FromRegistry(name, self._creds,
                                     self._original_transport)
            elif media_type in docker_http.SUPPORTED_MANIFEST_MIMES:
                image = v2_2_image.FromRegistry(name, self._creds,
                                                self._original_transport,
                                                [media_type])
            else:
                raise InvalidMediaTypeError('Invalid media type: ' +
                                            media_type)

            platform = Platform(
                entry['platform']) if 'platform' in entry else None
            results.append((name, platform, image))
        return results
Beispiel #5
0
def main():
  args = parser.parse_args()

  if not args.name or not args.directory:
    raise Exception('--name and --directory are required arguments.')

  transport = transport_pool.Http(httplib2.Http, size=_THREADS)

  if '@' in args.name:
    name = docker_name.Digest(args.name)
  else:
    name = docker_name.Tag(args.name)

  # OCI Image Manifest is compatible with Docker Image Manifest Version 2,
  # Schema 2. We indicate support for both formats by passing both media types
  # as 'Accept' headers.
  #
  # For reference:
  #   OCI: https://github.com/opencontainers/image-spec
  #   Docker: https://docs.docker.com/registry/spec/manifest-v2-2/
  accept = docker_http.SUPPORTED_MANIFEST_MIMES

  # Resolve the appropriate credential to use based on the standard Docker
  # client logic.
  creds = docker_creds.DefaultKeychain.Resolve(name)

  with v2_2_image.FromRegistry(name, creds, transport, accept) as v2_2_img:
    if v2_2_img.exists():
      save.fast(v2_2_img, args.directory, threads=_THREADS)
      return

  with v2_image.FromRegistry(name, creds, transport) as v2_img:
    with v2_compat.V22FromV2(v2_img) as v2_2_img:
      save.fast(v2_2_img, args.directory, threads=_THREADS)
      return
Beispiel #6
0
 def __iter__(self):
   results = []
   for (platform, manifest) in self._images:
     name = docker_name.Digest('{base}@{digest}'.format(
         base=self._name.as_repository(), digest=manifest.digest()))
     results.append((name, platform, manifest))
   return iter(results)
Beispiel #7
0
def main():
    args = parser.parse_args()

    if not args.name or not args.tarball:
        raise Exception('--name and --tarball are required arguments.')

    transport = transport_pool.Http(httplib2.Http, size=8)

    if '@' in args.name:
        name = docker_name.Digest(args.name)
    else:
        name = docker_name.Tag(args.name)

    # Resolve the appropriate credential to use based on the standard Docker
    # client logic.
    creds = docker_creds.DefaultKeychain.Resolve(name)

    with tarfile.open(name=args.tarball, mode='w') as tar:
        with v2_2_image.FromRegistry(name, creds, transport) as v2_2_img:
            if v2_2_img.exists():
                save.tarball(_make_tag_if_digest(name), v2_2_img, tar)
                return

        with v2_image.FromRegistry(name, creds, transport) as v2_img:
            with v2_compat.V22FromV2(v2_img) as v2_2_img:
                save.tarball(_make_tag_if_digest(name), v2_2_img, tar)
                return
Beispiel #8
0
def MakeSignaturePayloadDict(container_image_url):
    """Creates a dict representing a JSON signature object to sign.

  Args:
    container_image_url: See `containerregistry.client.docker_name.Digest` for
      artifact URL validation and parsing details.  `container_image_url` must
      be a fully qualified image URL with a valid sha256 digest.

  Returns:
    Dictionary of nested dictionaries and strings, suitable for passing to
    `json.dumps` or similar.
  """
    url = _ReplaceImageUrlScheme(image_url=container_image_url, scheme='')
    try:
        repo_digest = docker_name.Digest(url)
    except docker_name.BadNameException as e:
        raise BadImageUrlError(e)
    return {
        'critical': {
            'identity': {
                'docker-reference': six.text_type(repo_digest.as_repository()),
            },
            'image': {
                'docker-manifest-digest': repo_digest.digest,
            },
            'type': 'Google cloud binauthz container signature',
        },
    }
Beispiel #9
0
def GetDigestFromName(image_name):
  """Gets a digest object given a repository, tag or digest.

  Args:
    image_name: A docker image reference, possibly underqualified.

  Returns:
    a docker_name.Digest object.

  Raises:
    InvalidImageNameError: If no digest can be resolved.
  """
  tag_or_digest = GetDockerImageFromTagOrDigest(image_name)
  # If we got a digest, then just return it.
  if isinstance(tag_or_digest, docker_name.Digest):
    return tag_or_digest

  # If we got a tag, resolve it to a digest.
  def ResolveV2Tag(tag):
    with v2_image.FromRegistry(
        basic_creds=CredentialProvider(), name=tag,
        transport=http.Http()) as v2_img:
      if v2_img.exists():
        return v2_img.digest()
      return None

  def ResolveV22Tag(tag):
    with v2_2_image.FromRegistry(
        basic_creds=CredentialProvider(),
        name=tag,
        transport=http.Http(),
        accepted_mimes=v2_2_docker_http.SUPPORTED_MANIFEST_MIMES) as v2_2_img:
      if v2_2_img.exists():
        return v2_2_img.digest()
      return None

  def ResolveManifestListTag(tag):
    with docker_image_list.FromRegistry(
        basic_creds=CredentialProvider(), name=tag,
        transport=http.Http()) as manifest_list:
      if manifest_list.exists():
        return manifest_list.digest()
      return None

  # Resolve as manifest list, then v2.2, then v2.1 because for compatibility:
  # - manifest lists can be rewritten to v2.2 "default" images.
  # - v2.2 manifests can be rewritten to v2.1 manifests.
  sha256 = (
      ResolveManifestListTag(tag_or_digest) or ResolveV22Tag(tag_or_digest) or
      ResolveV2Tag(tag_or_digest))
  if not sha256:
    raise InvalidImageNameError(
        '[{0}] is not a valid name. Expected tag in the form "base:tag" or '
        '"tag" or digest in the form "sha256:<digest>"'.format(image_name))

  return docker_name.Digest('{registry}/{repository}@{sha256}'.format(
      registry=tag_or_digest.registry,
      repository=tag_or_digest.repository,
      sha256=sha256))
Beispiel #10
0
    def testDigest(self):
        repo = 'gcr.io/google-appengine/java-compat'
        hex_str = '01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b'
        expected_digest = docker_name.Digest('{repo}@sha256:{hex_str}'.format(
            repo=repo, hex_str=hex_str))
        digest = util.GetDigestFromName(str(expected_digest))

        self.assertEqual(expected_digest, digest)
Beispiel #11
0
def Publish(transport,
            image_chroot,
            name=None,
            tarball=None,
            config=None,
            digest=None,
            layer=None):
    if not name:
        raise Exception('Expected "name" kwarg')

    if not config and (layer or digest):
        raise Exception(
            name +
            ': Using "layer" or "digest" requires "config" to be specified.')

    if config:
        with open(config, 'r') as reader:
            config = reader.read()
    elif tarball:
        with v2_2_image.FromTarball(tarball) as base:
            config = base.config_file()
    else:
        raise Exception(name +
                        ': Either "config" or "tarball" must be specified.')

    if digest or layer:
        digest = digest.split(',')
        layer = layer.split(',')
        if len(digest) != len(layer):
            raise Exception(
                name + ': "digest" and "layer" must have matching lengths.')
    else:
        digest = []
        layer = []

    name_to_replace = name
    if image_chroot:
        name_to_publish = docker_name.Tag(os.path.join(image_chroot, name),
                                          strict=False)
    else:
        # Without a chroot, the left-hand-side must be a valid tag.
        name_to_publish = docker_name.Tag(name, strict=False)

    # Resolve the appropriate credential to use based on the standard Docker
    # client logic.
    creds = docker_creds.DefaultKeychain.Resolve(name_to_publish)

    with v2_2_session.Push(name_to_publish, creds, transport,
                           threads=_THREADS) as session:
        with v2_2_image.FromDisk(config,
                                 zip(digest or [], layer or []),
                                 legacy_base=tarball) as v2_2_img:
            session.upload(v2_2_img)

            return (name_to_replace,
                    docker_name.Digest('{repository}@{digest}'.format(
                        repository=name_to_publish.as_repository(),
                        digest=v2_2_img.digest())))
Beispiel #12
0
def _parse_image_reference(image_reference):
    util.check_type(image_reference, str)

    if '@' in image_reference:
        name = docker_name.Digest(image_reference)
    else:
        name = docker_name.Tag(image_reference)

    return name
Beispiel #13
0
def NormalizeArtifactUrl(artifact_url):
    """Normalizes given URL by ensuring the scheme is https."""
    url_without_scheme = _ReplaceImageUrlScheme(artifact_url, scheme='')
    try:
        # The validation logic in `docker_name` silently produces incorrect results
        # if the passed URL has a scheme.
        docker_name.Digest(url_without_scheme)
    except docker_name.BadNameException as e:
        raise BadImageUrlError(e)
    return _ReplaceImageUrlScheme(artifact_url, scheme='https')
Beispiel #14
0
    def _get_tags(self, repo, digest):
        full_digest = repo + '@sha256:' + digest

        name = docker_name.Digest(full_digest)
        creds = docker_creds.DefaultKeychain.Resolve(name)
        transport = transport_pool.Http(httplib2.Http)

        with docker_image.FromRegistry(name, creds, transport) as img:
            return img.tags()
        raise AssertionError('Unable to get tags from {0}'.format(full_digest))
def NormalizeArtifactUrl(artifact_url):
    """Normalizes given URL by ensuring the scheme is https."""
    if '//' not in artifact_url:
        artifact_url = '//' + artifact_url
    parsed_url = urlparse.urlparse(artifact_url)
    url = urlparse.ParseResult('https', *parsed_url[1:]).geturl()
    try:
        docker_name.Digest(url)  # Just check over the URL.
    except docker_name.BadNameException as e:
        raise BadImageUrlError(e)
    return url
Beispiel #16
0
def main():
  logging_setup.DefineCommandLineArgs(parser)
  args = parser.parse_args()
  logging_setup.Init(args=args)

  if not args.name or not args.tarball:
    logging.fatal('--name and --tarball are required arguments.')
    sys.exit(1)

  retry_factory = retry.Factory()
  retry_factory = retry_factory.WithSourceTransportCallable(httplib2.Http)
  transport = transport_pool.Http(retry_factory.Build, size=8)

  if '@' in args.name:
    name = docker_name.Digest(args.name)
  else:
    name = docker_name.Tag(args.name)

  # OCI Image Manifest is compatible with Docker Image Manifest Version 2,
  # Schema 2. We indicate support for both formats by passing both media types
  # as 'Accept' headers.
  #
  # For reference:
  #   OCI: https://github.com/opencontainers/image-spec
  #   Docker: https://docs.docker.com/registry/spec/manifest-v2-2/
  accept = docker_http.SUPPORTED_MANIFEST_MIMES

  # Resolve the appropriate credential to use based on the standard Docker
  # client logic.
  try:
    creds = docker_creds.DefaultKeychain.Resolve(name)
  # pylint: disable=broad-except
  except Exception as e:
    logging.fatal('Error resolving credentials for %s: %s', name, e)
    sys.exit(1)

  try:
    with tarfile.open(name=args.tarball, mode='w') as tar:
      logging.info('Pulling v2.2 image from %r ...', name)
      with v2_2_image.FromRegistry(name, creds, transport, accept) as v2_2_img:
        if v2_2_img.exists():
          save.tarball(_make_tag_if_digest(name), v2_2_img, tar)
          return

      logging.info('Pulling v2 image from %r ...', name)
      with v2_image.FromRegistry(name, creds, transport) as v2_img:
        with v2_compat.V22FromV2(v2_img) as v2_2_img:
          save.tarball(_make_tag_if_digest(name), v2_2_img, tar)
          return
  # pylint: disable=broad-except
  except Exception as e:
    logging.fatal('Error pulling and saving image %s: %s', name, e)
    sys.exit(1)
Beispiel #17
0
def main():
    digest = 'fake.gcr.io/test/test@sha256:' + DIGEST
    tag = 'fake.gcr.io/test/test:tag'
    src_name = docker_name.Digest(digest)
    dest_name = docker_name.Tag(tag)
    creds = docker_creds.DefaultKeychain.Resolve(src_name)
    transport = transport_pool.Http(httplib2.Http)

    with docker_image.FromRegistry(src_name, creds, transport) as src_img:
        if src_img.exists():
            creds = docker_creds.DefaultKeychain.Resolve(dest_name)
            with docker_session.Push(dest_name, creds, transport) as push:
                    push.upload(src_img)
    def get_existing_tags(self, full_repo, digest):
        full_digest = full_repo + '@sha256:' + digest
        existing_tags = []

        name = docker_name.Digest(full_digest)
        creds = docker_creds.DefaultKeychain.Resolve(name)
        transport = transport_pool.Http(httplib2.Http)

        with docker_image.FromRegistry(name, creds, transport) as img:
            if img.exists():
                existing_tags = img.tags()
            else:
                logging.debug("""Unable to get existing tags for {0}
                        as the image can't be found""".format(full_digest))
        return existing_tags
Beispiel #19
0
    def testRepoV22(self, list_registry, v2_2_registry, v2_registry):
        repo = 'gcr.io/google-appengine/java-compat'
        hex_str = '01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b'
        expected_digest = docker_name.Digest('{repo}@sha256:{hex_str}'.format(
            repo=repo, hex_str=hex_str))

        v2_registry.return_value.__enter__.return_value.exists.return_value = False
        v2_2_registry.return_value.__enter__.return_value.exists.return_value = True
        v2_2_registry.return_value.__enter__.return_value.digest.return_value = (
            expected_digest.digest)
        list_registry.return_value.__enter__.return_value.exists.return_value = (
            False)

        digest = util.GetDigestFromName(repo)
        self.assertEqual(expected_digest, digest)
Beispiel #20
0
    def _del_img_from_gcr(self, img_name):
        img_tag = docker_name.Tag(img_name)
        creds = docker_creds.DefaultKeychain.Resolve(img_tag)
        transport = transport_pool.Http(httplib2.Http, size=_THREADS)
        with docker_image.FromRegistry(img_tag, creds,
                                       transport) as base_image:
            img_digest = docker_name.Digest(''.join(
                [self._name.split(":")[0], "@",
                 str(base_image.digest())]))

        logging.info('Deleting tag {0}'.format(img_tag))
        docker_session.Delete(img_tag, creds, transport)
        logging.info('Deleting image {0}'.format(img_digest))
        docker_session.Delete(img_digest, creds, transport)
        return
Beispiel #21
0
 def test_tag_to_digest_not_cached(self):
     with v2_2_image.FromTarball(
             TestData(
                 'io_bazel_rules_k8s/examples/hellogrpc/cc/server/server.tar'
             )) as img:
         # Add a fake exists method to look like FromRegistry
         img.exists = lambda: True
         with mock.patch.object(v2_2_image,
                                'FromRegistry',
                                return_value=img):
             tag = docker_name.Tag('gcr.io/foo/bar:baz')
             expected_digest = docker_name.Digest('gcr.io/foo/bar@' +
                                                  img.digest())
             actual_digest = resolver.TagToDigest(tag, {}, _BAD_TRANSPORT)
             self.assertEqual(actual_digest, str(expected_digest))
def main():
  args = parser.parse_args()

  if not args.name:
    raise Exception('--name is a required arguments.')

  # This library can support push-by-digest, but the likelihood of a user
  # correctly providing us with the digest without using this library
  # directly is essentially nil.
  name = Tag(args.name, args.stamp_info_file)

  if not args.config and (args.layer or args.digest):
    raise Exception(
        'Using --layer or --digest requires --config to be specified.')

  if not args.config and not args.tarball:
    raise Exception('Either --config or --tarball must be specified.')

  # If config is specified, use that.  Otherwise, fallback on reading
  # the config from the tarball.
  config = args.config
  if args.config:
    with open(args.config, 'r') as reader:
      config = reader.read()
  elif args.tarball:
    with v2_2_image.FromTarball(args.tarball) as base:
      config = base.config_file()

  if len(args.digest or []) != len(args.layer or []):
    raise Exception('--digest and --layer must have matching lengths.')

  transport = transport_pool.Http(httplib2.Http, size=_THREADS)

  # Resolve the appropriate credential to use based on the standard Docker
  # client logic.
  creds = docker_creds.DefaultKeychain.Resolve(name)

  with docker_session.Push(name, creds, transport, threads=_THREADS) as session:
    with v2_2_image.FromDisk(config, zip(args.digest or [], args.layer or []),
                             legacy_base=args.tarball) as v2_2_img:
      session.upload(v2_2_img)

      print '%s=%s' % (
        name, docker_name.Digest('{repository}@{digest}'.format(
          repository=name.as_repository(),
          digest=util.Digest(v2_2_img.manifest()))))
Beispiel #23
0
def GetDigestFromName(image_name):
    """Gets a digest object given a repository, tag or digest.

  Args:
    image_name: A docker image reference, possibly underqualified.

  Returns:
    a docker_name.Digest object.

  Raises:
    InvalidImageNameError: If no digest can be resolved.
  """
    tag_or_digest = GetDockerImageFromTagOrDigest(image_name)
    # If we got a digest, then just return it.
    if isinstance(tag_or_digest, docker_name.Digest):
        return tag_or_digest

    # If we got a tag, resolve it to a digest.
    def ResolveV2Tag(tag):
        with v2_image.FromRegistry(basic_creds=CredentialProvider(),
                                   name=tag,
                                   transport=http.Http()) as v2_img:
            if v2_img.exists():
                return v2_img.digest()
            return None

    def ResolveV22Tag(tag):
        with v2_2_image.FromRegistry(basic_creds=CredentialProvider(),
                                     name=tag,
                                     transport=http.Http()) as v2_2_img:
            if v2_2_img.exists():
                return v2_2_img.digest()
            return None

    # Resolve v2.2 first because we will exist via a compatibility layer.
    sha256 = ResolveV22Tag(tag_or_digest) or ResolveV2Tag(tag_or_digest)
    if not sha256:
        raise InvalidImageNameError(
            '[{0}] is not a valid name.'.format(image_name))

    return docker_name.Digest('{registry}/{repository}@{sha256}'.format(
        registry=tag_or_digest.registry,
        repository=tag_or_digest.repository,
        sha256=sha256))
Beispiel #24
0
def GetDockerImageFromTagOrDigest(image_name):
    """Gets an image object given either a tag or a digest.

  Args:
    image_name: Either a fully qualified tag or a fully qualified digest.
      Defaults to latest if no tag specified.

  Returns:
    Either a docker_name.Tag or a docker_name.Digest object.

  Raises:
    InvalidImageNameError: Given digest could not be resolved to a full digest.
  """
    if not IsFullySpecified(image_name):
        image_name += ':latest'

    try:
        return ValidateImagePathAndReturn(docker_name.Tag(image_name))
    except docker_name.BadNameException:
        pass

    parts = image_name.split('@', 1)
    if len(parts) == 2:
        if not parts[1].startswith('sha256:'):
            raise InvalidImageNameError(
                '[{0}] digest must be of the form "sha256:<digest>".'.format(
                    image_name))

        # If the full digest wasn't specified, check if what was passed
        # in is a valid digest prefix.
        # 7 for 'sha256:' and 64 for the full digest
        if len(parts[1]) < 7 + 64:
            resolved = GetDockerDigestFromPrefix(image_name)
            if resolved == image_name:
                raise InvalidImageNameError(
                    '[{0}] could not be resolved to a full digest.'.format(
                        image_name))
            image_name = resolved
    try:
        return ValidateImagePathAndReturn(docker_name.Digest(image_name))
    except docker_name.BadNameException:
        raise InvalidImageNameError(
            '[{0}] digest must be of the form "sha256:<digest>".'.format(
                image_name))
    def add_tags(self, digest, tag, dry_run):
        if dry_run:
            logging.debug('Would have tagged {0} with {1}'.format(digest, tag))
            return

        src_name = docker_name.Digest(digest)
        dest_name = docker_name.Tag(tag)
        creds = docker_creds.DefaultKeychain.Resolve(src_name)
        transport = transport_pool.Http(httplib2.Http)

        with docker_image.FromRegistry(src_name, creds, transport) as src_img:
            if src_img.exists():
                creds = docker_creds.DefaultKeychain.Resolve(dest_name)
                logging.debug('Tagging {0} with {1}'.format(digest, tag))
                with docker_session.Push(dest_name, creds, transport) as push:
                    push.upload(src_img)
            else:
                logging.debug("""Unable to tag {0}
                    as the image can't be found""".format(digest))
Beispiel #26
0
def GetDockerImageFromTagOrDigest(image_name):
  """Gets an image object given either a tag or a digest.

  Args:
    image_name: Either a fully qualified tag or a fully qualified digest.
      Defaults to latest if no tag specified.

  Returns:
    Either a docker_name.Tag or a docker_name.Digest object.
  """
  if not IsFullySpecified(image_name):
    image_name += ':latest'

  try:
    return docker_name.Tag(image_name)
  except docker_name.BadNameException:
    pass
  # If the full digest wasn't specified, check if what was passed
  # in is a valid digest prefix.
  # 7 for 'sha256:' and 64 for the full digest
  parts = image_name.split('@', 1)
  if len(parts) == 2 and len(parts[1]) < 7 + 64:
    image_name = GetDockerDigestFromPrefix(image_name)
  return docker_name.Digest(image_name)
def main():
  logging_setup.DefineCommandLineArgs(parser)
  args = parser.parse_args()
  logging_setup.Init(args=args)

  if not args.name or not args.directory:
    logging.fatal('--name and --directory are required arguments.')

  retry_factory = retry.Factory()
  retry_factory = retry_factory.WithSourceTransportCallable(httplib2.Http)
  transport = transport_pool.Http(retry_factory.Build, size=_THREADS)

  if '@' in args.name:
    name = docker_name.Digest(args.name)
  else:
    name = docker_name.Tag(args.name)

  # OCI Image Manifest is compatible with Docker Image Manifest Version 2,
  # Schema 2. We indicate support for both formats by passing both media types
  # as 'Accept' headers.
  #
  # For reference:
  #   OCI: https://github.com/opencontainers/image-spec
  #   Docker: https://docs.docker.com/registry/spec/manifest-v2-2/
  accept = docker_http.SUPPORTED_MANIFEST_MIMES

  # Resolve the appropriate credential to use based on the standard Docker
  # client logic.
  try:
    creds = docker_creds.DefaultKeychain.Resolve(name)
  # pylint: disable=broad-except
  except Exception as e:
    logging.fatal('Error resolving credentials for %s: %s', name, e)
    sys.exit(1)

  try:
    logging.info('Pulling manifest list from %r ...', name)
    with image_list.FromRegistry(name, creds, transport) as img_list:
      if img_list.exists():
        platform = image_list.Platform({
            'architecture': _PROCESSOR_ARCHITECTURE,
            'os': _OPERATING_SYSTEM,
        })
        # pytype: disable=wrong-arg-types
        with img_list.resolve(platform) as default_child:
          save.fast(default_child, args.directory, threads=_THREADS)
          return
        # pytype: enable=wrong-arg-types

    logging.info('Pulling v2.2 image from %r ...', name)
    with v2_2_image.FromRegistry(name, creds, transport, accept) as v2_2_img:
      if v2_2_img.exists():
        save.fast(v2_2_img, args.directory, threads=_THREADS)
        return

    logging.info('Pulling v2 image from %r ...', name)
    with v2_image.FromRegistry(name, creds, transport) as v2_img:
      with v2_compat.V22FromV2(v2_img) as v2_2_img:
        save.fast(v2_2_img, args.directory, threads=_THREADS)
        return
  # pylint: disable=broad-except
  except Exception as e:
    logging.fatal('Error pulling and saving image %s: %s', name, e)
    sys.exit(1)
def main():
    logging_setup.DefineCommandLineArgs(parser)
    args = parser.parse_args()
    logging_setup.Init(args=args)

    # retry if error acc to retry plan
    retry_factory = retry.Factory()
    retry_factory = retry_factory.WithSourceTransportCallable(httplib2.Http)
    transport = transport_pool.Http(retry_factory.Build, size=_THREADS)

    if '@' in args.name:
        name = docker_name.Digest(args.name)
    else:
        name = docker_name.Tag(args.name)

    # If the user provided a client config directory, instruct the keychain
    # resolver to use it to look for the docker client config
    if args.client_config_dir is not None:
        docker_creds.DefaultKeychain.setCustomConfigDir(args.client_config_dir)

    # OCI Image Manifest is compatible with Docker Image Manifest Version 2,
    # Schema 2. We indicate support for both formats by passing both media types
    # as 'Accept' headers.
    #
    # For reference:
    #   OCI: https://github.com/opencontainers/image-spec
    #   Docker: https://docs.docker.com/registry/spec/manifest-v2-2/
    accept = docker_http.SUPPORTED_MANIFEST_MIMES

    # Resolve the appropriate credential to use based on the standard Docker
    # client logic.
    try:
        # check username/paswd match up
        creds = docker_creds.DefaultKeychain.Resolve(name)
    # pylint: disable=broad-except
    except Exception as e:
        logging.fatal('Error resolving credentials for %s: %s', name, e)
        sys.exit(1)

    try:
        logging.info('Pulling manifest list from %r ...', name)
        # (image, [(.sha, tar)])
        with image_list.FromRegistry(name, creds, transport) as img_list:
            if img_list.exists():
                # populate docker_image_list.Platform object (runtime requirements for an object)
                # from the provided args
                # see: docker image manifest list
                platform = platform_args.FromArgs(args)
                # pytype: disable=wrong-arg-types
                with img_list.resolve(platform) as default_child:
                    #
                    save.fast(
                        # create new filesystem (directory) with config files and sha/tars (_save.py)
                        default_child,
                        args.directory,
                        threads=_THREADS,
                        cache_directory=args.cache)
                    return
                # pytype: enable=wrong-arg-types

        logging.info('Pulling v2.2 image from %r ...', name)
        with v2_2_image.FromRegistry(name, creds, transport,
                                     accept) as v2_2_img:
            if v2_2_img.exists():
                save.fast(v2_2_img,
                          args.directory,
                          threads=_THREADS,
                          cache_directory=args.cache)
                return

        logging.info('Pulling v2 image from %r ...', name)
        with v2_image.FromRegistry(name, creds, transport) as v2_img:
            with v2_compat.V22FromV2(v2_img) as v2_2_img:
                save.fast(v2_2_img,
                          args.directory,
                          threads=_THREADS,
                          cache_directory=args.cache)
                return
    # pylint: disable=broad-except
    except Exception as e:
        logging.fatal('Error pulling and saving image %s: %s', name, e)
        sys.exit(1)
    def reconcile_tags(self, data, dry_run):
        for project in data['projects']:

            default_registry = project['base_registry']
            registries = project.get('additional_registries', [])
            registries.append(default_registry)

            default_repo = os.path.join(default_registry,
                                        project['repository'])

            for image in project['images']:
                digest = self.get_digest_from_prefix(default_repo,
                                                     image['digest'])

                default_digest = default_repo + '@sha256:' + digest
                default_name = docker_name.Digest(default_digest)
                default_creds = (
                    docker_creds.DefaultKeychain.Resolve(default_name))
                transport = transport_pool.Http(httplib2.Http)

                # Bail out if the digest in the config file doesn't exist.
                with docker_image.FromRegistry(default_name, default_creds,
                                               transport) as img:

                    if not img.exists():
                        logging.debug('Could not retrieve  ' +
                                      '{0}'.format(default_digest))
                        return

                for registry in registries:

                    full_repo = os.path.join(registry, project['repository'])
                    full_digest = full_repo + '@sha256:' + digest
                    name = docker_name.Digest(full_digest)
                    creds = docker_creds.DefaultKeychain.Resolve(name)

                    with docker_image.FromRegistry(name, creds,
                                                   transport) as img:
                        if img.exists():

                            existing_tags = img.tags()
                            logging.debug('Existing Tags: ' +
                                          '{0}'.format(existing_tags))

                            manifests = img.manifests()
                            tagged_digest = self.get_tagged_digest(
                                manifests, image['tag'])

                            # Don't retag an image if the tag already exists
                            if tagged_digest.startswith('sha256:'):
                                tagged_digest = tagged_digest[len('sha256:'):]
                            if tagged_digest.startswith(digest):
                                logging.debug(
                                    'Skipping tagging %s with %s as '
                                    'that tag already exists.', digest,
                                    image['tag'])
                                continue

                        # We can safely retag now.
                        full_tag = full_repo + ':' + image['tag']
                        self.add_tags(default_digest, full_tag, dry_run)

                logging.debug(self.get_existing_tags(default_repo, digest))
Beispiel #30
0
 def fully_qualify_digest(digest):
     return docker_name.Digest('{registry}/{repo}@{digest}'.format(
         registry=tag.registry, repo=tag.repository, digest=digest))