Git Product home page Git Product logo

containerregistry's Introduction

containerregistry

Build Status

A set of Python libraries and tools for interacting with a Docker Registry.

Bazel users see rules_docker, which relies heavily on these tools.

puller.par

$ bazel run @containerregistry//:puller.par -- --help
usage: puller.par [-h] --name NAME --directory DIRECTORY [--os OS]
                   [--os-version OS_VERSION]
                   [--os-features [OS_FEATURES [OS_FEATURES ...]]]
                   [--architecture ARCHITECTURE] [--variant VARIANT]
                   [--features [FEATURES [FEATURES ...]]]
                   [--client-config-dir CLIENT_CONFIG_DIR]
                   [--stderrthreshold STDERRTHRESHOLD]

Pull images from a Docker Registry, faaaaast.

optional arguments:
  -h, --help            show this help message and exit
  --name NAME           The name of the docker image to pull and save.
                        Supports fully-qualified tag or digest references.
  --directory DIRECTORY
                        Where to save the image's files.
  --os OS               For multi-platform manifest lists, specifies the
                        operating system.
  --os-version OS_VERSION
                        For multi-platform manifest lists, specifies the
                        operating system version.
  --os-features [OS_FEATURES [OS_FEATURES ...]]
                        For multi-platform manifest lists, specifies operating
                        system features.
  --architecture ARCHITECTURE
                        For multi-platform manifest lists, specifies the CPU
                        architecture.
  --variant VARIANT     For multi-platform manifest lists, specifies the CPU
                        variant.
  --features [FEATURES [FEATURES ...]]
                        For multi-platform manifest lists, specifies CPU
                        features.
  --client-config-dir CLIENT_CONFIG_DIR
                        The path to the directory where the client
                        configuration files are located. Overiddes the value
                        from DOCKER_CONFIG
  --stderrthreshold STDERRTHRESHOLD
                        Write log events at or above this level to stderr.

pusher.par

$ bazel run @containerregistry//:pusher.par -- --help
usage: pusher.par [-h] --name NAME [--tarball TARBALL] [--config CONFIG]
                   [--manifest MANIFEST] [--digest DIGEST] [--layer LAYER]
                   [--stamp-info-file STAMP_INFO_FILE] [--oci]
                   [--client-config-dir CLIENT_CONFIG_DIR]
                   [--stderrthreshold STDERRTHRESHOLD]

Push images to a Docker Registry, faaaaaast.

optional arguments:
  -h, --help            show this help message and exit
  --name NAME           The name of the docker image to push.
  --tarball TARBALL     An optional legacy base image tarball.
  --config CONFIG       The path to the file storing the image config.
  --manifest MANIFEST   The path to the file storing the image manifest.
  --digest DIGEST       The list of layer digest filenames in order.
  --layer LAYER         The list of layer filenames in order.
  --stamp-info-file STAMP_INFO_FILE
                        A list of files from which to read substitutions to
                        make in the provided --name, e.g. {BUILD_USER}
  --oci                 Push the image with an OCI Manifest.
  --client-config-dir CLIENT_CONFIG_DIR
                        The path to the directory where the client
                        configuration files are located. Overiddes the value
                        from DOCKER_CONFIG
  --stderrthreshold STDERRTHRESHOLD
                        Write log events at or above this level to stderr.

importer.par

$ bazel run @containerregistry//:importer.par -- --help
usage: importer.par [-h] --tarball TARBALL [--format {tar,tar.gz}] --directory
                    DIRECTORY [--stderrthreshold STDERRTHRESHOLD]

Import images from a tarball into our faaaaaast format.

optional arguments:
  -h, --help            show this help message and exit
  --tarball TARBALL     The tarball containing the docker image to rewrite
                        into our fast on-disk format.
  --format {tar,tar.gz}
                        The form in which to save layers.
  --directory DIRECTORY
                        Where to save the image's files.
  --stderrthreshold STDERRTHRESHOLD
                        Write log events at or above this level to stderr.

flatten.par

$ bazel run @containerregistry//:flatten.par -- --help
usage: flatten.par [-h] [--tarball TARBALL] [--config CONFIG]
                   [--digest DIGEST] [--layer LAYER]
                   [--uncompressed_layer UNCOMPRESSED_LAYER]
                   [--diff_id DIFF_ID] [--filesystem FILESYSTEM]
                   [--metadata METADATA] [--stderrthreshold STDERRTHRESHOLD]

Flatten container images.

optional arguments:
  -h, --help            show this help message and exit
  --tarball TARBALL     An optional legacy base image tarball.
  --config CONFIG       The path to the file storing the image config.
  --digest DIGEST       The list of layer digest filenames in order.
  --layer LAYER         The list of compressed layer filenames in order.
  --uncompressed_layer UNCOMPRESSED_LAYER
                        The list of uncompressed layer filenames in order.
  --diff_id DIFF_ID     The list of diff_ids in order.
  --filesystem FILESYSTEM
                        The name of where to write the filesystem tarball.
  --metadata METADATA   The name of where to write the container startup
                        metadata.
  --stderrthreshold STDERRTHRESHOLD
                        Write log events at or above this level to stderr.

appender.par

$ bazel run @containerregistry//:appender.par -- --help
usage: appender.par [-h] --src-image SRC_IMAGE --tarball TARBALL --dst-image
                    DST_IMAGE [--stderrthreshold STDERRTHRESHOLD]

Append tarballs to an image in a Docker Registry.

optional arguments:
  -h, --help            show this help message and exit
  --src-image SRC_IMAGE
                        The name of the docker image to append to.
  --tarball TARBALL     The tarball to append.
  --dst-image DST_IMAGE
                        The name of the new image.
  --stderrthreshold STDERRTHRESHOLD
                        Write log events at or above this level to stderr.

digester.par

$ bazel run @containerregistry//:digester.par -- --help
usage: digester.par [-h] [--tarball TARBALL] --output-digest OUTPUT_DIGEST
                    [--config CONFIG] [--manifest MANIFEST] [--digest DIGEST]
                    [--layer LAYER] [--oci]
                    [--stderrthreshold STDERRTHRESHOLD]

Calculate digest for a container image.

optional arguments:
  -h, --help            show this help message and exit
  --tarball TARBALL     An optional legacy base image tarball.
  --output-digest OUTPUT_DIGEST
                        Filename to store digest in.
  --config CONFIG       The path to the file storing the image config.
  --manifest MANIFEST   The path to the file storing the image manifest.
  --digest DIGEST       The list of layer digest filenames in order.
  --layer LAYER         The list of layer filenames in order.
  --oci                 Image has an OCI Manifest.
  --stderrthreshold STDERRTHRESHOLD
                        Write log events at or above this level to stderr.

containerregistry's People

Contributors

deft-code avatar dekkagaijin avatar dlorenc avatar drigz avatar jonjohnsonjr avatar kaylanguyen avatar mattmoor avatar pcj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

containerregistry's Issues

Travis fails on all PRs

It looks like the Travis configuration uses secret environment variables to log into various docker repositories, but Travis only exports these variables on postsubmit runs (to prevent them from being stolen).

As a result, all PRs fail tests, which is not a great user experience.

Name option parsing

bazel-bin/external/containerregistry/puller.par --name index.docker.io/openjdk --directory .

I don't really know any other interpretation of the name option.

Running command line: bazel-bin/external/containerregistry/puller.par --name index.docker.io/openjdk --directory .
Traceback (most recent call last):
  File "/home/barriosj/miniconda2/lib/python2.7/runpy.py", line 174, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "/home/barriosj/miniconda2/lib/python2.7/runpy.py", line 72, in _run_code
    exec code in run_globals
  File "/tmp/barriosj/705e5906a0d5fed56361de3c80f1e60f/execroot/containerregistry/bazel-out/k8-fastbuild/bin/external/containerregistry/puller.par/__main__.py", line 133, in <module>
  File "/tmp/barriosj/705e5906a0d5fed56361de3c80f1e60f/execroot/containerregistry/bazel-out/k8-fastbuild/bin/external/containerregistry/puller.par/__main__.py", line 80, in main
  File "/tmp/barriosj/705e5906a0d5fed56361de3c80f1e60f/execroot/containerregistry/bazel-out/k8-fastbuild/bin/external/containerregistry/puller.par/containerregistry/client/docker_name_.py", line 211, in __init__
  File "/tmp/barriosj/705e5906a0d5fed56361de3c80f1e60f/execroot/containerregistry/bazel-out/k8-fastbuild/bin/external/containerregistry/puller.par/containerregistry/client/docker_name_.py", line 80, in _check_tag
  File "/tmp/barriosj/705e5906a0d5fed56361de3c80f1e60f/execroot/containerregistry/bazel-out/k8-fastbuild/bin/external/containerregistry/puller.par/containerregistry/client/docker_name_.py", line 64, in _check_element
containerregistry.client.docker_name_.BadNameException: Invalid tag: , must be at least 1 characters
ERROR: Non-zero return code '1' from command: Process exited with status 1

Suggested improvements to Bazel external dependency handling (non-conflicting names, .bzl file)

This library's current definition of Bazel external dependencies makes it awkward to integrate into other projects. There are two main issues:

  1. Defining deps in WORKSPACE prevents them from being imported into other workspaces with load(), so I have to copy-paste a bunch of implementation details. Other downstreams have similar issues, for example rules_docker/container/container.bzl needs full copies of same dep definitions.

  2. Python import path handling gets very confused if the name of the dependency is the same as the library being imported. There are workarounds such as renaming files (as you're doing for six), but a better solution is to give the deps non-conflicting names. A Java-style domain-plus-path format is common among OSS and Google-managed libraries.

Example code:

# containerregistry/build/deps.bzl
def deps():
  native.new_http_archive(
    name = "org_pypi_python_six",
    url = "https://pypi.python.org/packages/source/s/six/six-1.9.0.tar.gz",
    sha256 = "e24052411fc4fbd1f672635537c3fc2330d9481b18c0317695b46259512c91d5",
    strip_prefix = "six-1.9.0",
    build_file_content = """py_library(
        name = "six",
        srcs = ["six.py"],
        visibility = ["//visibility:public"],
    )""",
  )
  new_git_repository(
    name = "com_github_httplib2_httplib2",
    remote = "https://github.com/httplib2/httplib2",
    commit = "1ae16e0e8e868e4b81225631ee37c6555493014b", # v0.10.3
    build_file_content = """py_library(
        name = "python2_httplib2",
        srcs = glob(["python2/httplib2/**/*.py"]),
        data = ["python2/httplib2/cacerts.txt"],
        imports = ["python2"],
        visibility = ["//visibility:public"]
    )""",
  )
  new_git_repository(
      name = "com_github_google_oauth2client",
      remote = "https://github.com/google/oauth2client",
      commit = "a731be362014d61630044c46495c5b750437ab88", # v4.0.0
      build_file_content = """py_library(
          name = "oauth2client",
          srcs = glob(["oauth2client/**/*.py"]),
          visibility = ["//visibility:public"],
          deps = [
              "@com_github_httplib2_httplib2//:python2_httplib2",
              "@org_pypi_python_six//:six",
          ]
      )""",
  )

  # ... and so on

The puller --platform flag doesn't support platform variants

The --platform flag I added to the puller (#113) doesn't seem to work with some of the official images like busybox, since those manifests use platform variants for some architectures:

{
   "schemaVersion": 2,
   "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
   "manifests": [
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 527,
         "digest": "sha256:915f390a8912e16d4beb8689720a17348f3f6d1a7b659697df850ab625ea29d5",
         "platform": {
            "architecture": "amd64",
            "os": "linux"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 527,
         "digest": "sha256:11a6b4baf996d8e52a332fbe7117aca1aae2b3068c7106f5b1065c16e8660895",
         "platform": {
            "architecture": "arm",
            "os": "linux",
            "variant": "v5"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 527,
         "digest": "sha256:d4ae670481a474fda03b2e026a111972a5f459b86e0c57af1ec72d6045f57a27",
         "platform": {
            "architecture": "arm",
            "os": "linux",
            "variant": "v6"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 527,
         "digest": "sha256:8777c62bb60d20e3b98216abe38ea1be721badd997f3840295eccd46f2281a2b",
         "platform": {
            "architecture": "arm",
            "os": "linux",
            "variant": "v7"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 527,
         "digest": "sha256:adeafb67bdeab6f8ab07a46bc7b789d9ae508aac3b0933fb165e3ceb1f36e3e2",
         "platform": {
            "architecture": "arm64",
            "os": "linux",
            "variant": "v8"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 527,
         "digest": "sha256:1ce7a47326ea36f5ba9bf7689cb1737cf79aef81577671d9774deb7378694e54",
         "platform": {
            "architecture": "386",
            "os": "linux"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 528,
         "digest": "sha256:f7d5e9a7fa573052bbce9ad2d59b1c63b848f091a246a8c1e54ccc8178e1b794",
         "platform": {
            "architecture": "ppc64le",
            "os": "linux"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 528,
         "digest": "sha256:d6d388d3ef21f3a75e49606dc0da5ec92da6f181bea347473b7f01904da09175",
         "platform": {
            "architecture": "s390x",
            "os": "linux"
         }
      }
   ]
}

If you try to pull with --platform=linux/amd, it fails, presumably since the platform doesn't match fully.

$ bazel-bin/puller --name index.docker.io/library/busybox:latest --directory /tmp/busybox --platform=linux/arm
F1119 19:28:06.679466   74570 fast_puller_.py:128] Error pulling and saving image index.docker.io/library/busybox:latest: Could not resolve manifest list to compatible manifest

Docker's cli somehow handles this, apparently downloading the v7 variant, but I don't know how it does that.

I can think of a few ways to address this:

  1. Remove --platform, replacing it with --os, --architecture, and --variant. It's most explicit, and perhaps is what I should have done all along, but technically is a breaking change. (I'm not sure if anyone is using --platform yet though, since it's pretty new.)
  2. Extend --platform to support variants somehow, either
    a. with an optional /variant suffix (e.g. linux/arm/v7), or
    b. by translating special values like those listed in https://github.com/docker-library/go-dockerlibrary/blob/master/architecture/oci-platform.go, e.g. arm32v7 -> {arm, v7}
  3. Emulate whatever the docker cli is doing, though I don't like this one as much, since it doesn't seem to be documented, and I can't figure out how to make it download e.g. the arm v5 variant image.

I feel like the best options are either 1 or 2a. Thoughts?

(Note that the place I intend to use this, bazelbuild/rules_docker#544, already separates out os and architecture, so I'll probably add a third attribute there, variant, or maybe even turn it into a dict.)

cc @KaylaNguyen @nlopezgi

auth error when using default config.json from docker 2.0.0.3

The config.json client config file generated by docker 2.0.0.3 (the latest stable version) on macOS generates a config file that seems to cause errors here.

I get this error:

Error resolving credentials for index.docker.io/library/mysql@sha256:8c15b2612051244d0a2b6ceb6f9bf82ddc0e909555c1067c098e5f935e2751a7: Unsupported entry in "auth" section of Docker config: {}

with the below config.json:

{
  "stackOrchestrator" : "swarm",
  "credSstore" : "osxkeychain",
  "auths" : {
    "https://index.docker.io/v1/" : {

    }
  }
}

Forgive me if this isn't specific enough to find the problem. I'm working with this project through my use of rules_docker. (I made a ticket over there, too bazelbuild/rules_docker#704)

container_pull authentication failure

I'm having an issue with container_pull from private Docker Registry based on Sonatype OSS Nexus Repository Manager:

containerregistry.client.v2_2.docker_http_.BadStateException: Unexpected "www-authenticate" challenge type: BASIC
 (/usr/bin/python /private/var/tmp/_bazel_eric/35e4f788de46e2946a456376a62aa566/external/puller/file/puller.par --directory /private/var/tmp/_bazel_eric/35e4f788de46e2946a456376a62aa566/external/base/image --name suxen.vipdmp.com/com.veon/jvm-builder@sha256:e3aefc80adf5c8d403a1887d55f53c54ded0cdaf3b1c3e95bb861526b9cb515f).

Looking at Python source code, I see, that it only accepts Basic challenge type and not BASIC
I suppose the check should be case-insensitive.

foreign layers not pulled properly

The windows images seem to use foreign layers hosted by Microsoft. For example, the windows 10.0.17134.228 image for docker's hello-world image has this manifest:

{
        "schemaVersion": 2,
        "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
        "config": {
                "mediaType": "application/vnd.docker.container.image.v1+json",
                "size": 1882,
                "digest": "sha256:abe16c070a65267297b0550b0e0b3644e6c859199cb93f5badf899885bbed1fc"
        },
        "layers": [
                {
                        "mediaType": "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip",
                        "size": 92818888,
                        "digest": "sha256:e46172273a4e4384e1eec7fb01091c828a256ea0f87b30f61381fba9bc511371",
                        "urls": [
                                "https://go.microsoft.com/fwlink/?linkid=873594"
                        ]
                },
                {
                        "mediaType": "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip",
                        "size": 48632111,
                        "digest": "sha256:1f7d468f830cb0ed4beb8edc9438f18096e8c682e56a35242f60e6c61b718b30",
                        "urls": [
                                "https://go.microsoft.com/fwlink/?linkid=2009809"
                        ]
                },
                {
                        "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                        "size": 1664,
                        "digest": "sha256:35655e48c5c5c1c21f19073b2594d74b5023ae2cd84bf7f7f45acb886e1b5e34"
                },
                {
                        "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                        "size": 945,
                        "digest": "sha256:8159aad5f944a2967427ce18283248707fb15071b202e00de4ffd105e9ed6ace"
                }
        ]
}

Trying to download this image will fail, since it's trying to download the foreign image from the docker registry, rather than following the provided URLs:

F1120 15:03:28.953516  191191 fast_puller_.py:117] Error pulling and saving image index.docker.io/library/hello-world@sha256:670f0dc439e4669bc602cc571af9f72271ea3b39002416fe2398feb36a7ac20d: response: {'status': '404', 'content-length': '157', 'strict-transport-security': 'max-age=31536000', 'docker-distribution-api-version': 'registry/2.0', 'date': 'Tue, 20 Nov 2018 23:03:28 GMT', 'content-type': 'application/json; charset=utf-8'}
blob unknown to registry: sha256:e46172273a4e4384e1eec7fb01091c828a256ea0f87b30f61381fba9bc511371

Release 0.0.23

Sorry for the issue spam, but I didn't see another way to contact the team. Would it be possible to go ahead and release 0.0.23? I'm particularly looking forward to picking up the changes from #47. Thanks!

404 when pulling public docker.io image

Attempting to pull a public docker image from docker.io returns a 404 page:

/usr/bin/python .../external/puller/file/puller.par --directory .../external/ubuntu_16_04/image --name docker.io/ubuntu:16.04
F0422 11:22:50.921171  102973 __main__.py:125] Error pulling and saving image docker.io/ubuntu:16.04: response: {'status': '404', 'content-length': '36852', 'strict-transport-security': 'max-age=31536000', 'x-drupal-cache': 'HIT', 'x-content-type-options': 'nosniff', 'content-language': 'en', 'transfer-encoding': 'chunked', 'expires': 'Sun, 19 Nov 1978 05:00:00 GMT', 'vary': 'Cookie,Accept-Encoding', '-content-encoding': 'gzip', 'server': 'Apache', 'last-modified': 'Sun, 22 Apr 2018 18:13:20 GMT', 'connection': 'keep-alive', 'etag': '"1524420800-1"', 'link': '<https://www.docker.com/>; rel="canonical",<https://www.docker.com/>; rel="shortlink"', 'cache-control': 'public, max-age=86400', 'date': 'Sun, 22 Apr 2018 18:22:50 GMT', 'x-frame-options': 'SAMEORIGIN', 'content-type': 'text/html; charset=utf-8', 'x-xss-protection': '1; mode=block'}
<!doctype html>
<html class="no-js" lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="description" content="Docker is an open platform for developers and sysadmins to build, ship, and run distributed applications, whether on laptops, data center VMs, or the cloud." />
<meta name="keywords" content="docker, docker open source, docker platform, distributed applications, microservices, containers, docker containers, docker software, docker virtualization" />
<link rel="canonical" href="https://www.docker.com/" />
<link rel="shortlink" href="https://www.docker.com/" />
<meta property="og:site_name" content="Docker" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://www.docker.com/" />
<meta property="og:title" content="Docker" />
<meta property="og:image" content="https://www.docker.com/sites/default/files/social/docker_facebook_share.png" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@docker" />
<meta name="twitter:url" content="https://www.docker.com/" />
<meta name="twitter:title" content="Docker" />
<meta name="twitter:image" content="https://www.docker.com/sites/default/files/social/docker_twitter_share_new.png?4362984378" />
	<meta charset="utf-8"/>
	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
	<title>Page not found | Docker</title>
  <link rel="apple-touch-icon" sizes="57x57" href="/favicon/apple-icon-57x57.png">
  <link rel="apple-touch-icon" sizes="60x60" href="/favicon/apple-icon-60x60.png">
  <link rel="apple-touch-icon" sizes="72x72" href="/favicon/apple-icon-72x72.png">
  <link rel="apple-touch-icon" sizes="76x76" href="/favicon/apple-icon-76x76.png">
  <link rel="apple-touch-icon" sizes="114x114" href="/favicon/apple-icon-114x114.png">
  <link rel="apple-touch-icon" sizes="120x120" href="/favicon/apple-icon-120x120.png">
  <link rel="apple-touch-icon" sizes="144x144" href="/favicon/apple-icon-144x144.png">
  <link rel="apple-touch-icon" sizes="152x152" href="/favicon/apple-icon-152x152.png">
  <link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-icon-180x180.png">
  <link rel="icon" type="image/png" sizes="192x192"  href="/favicon/android-icon-192x192.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png">
  <link rel="icon" type="image/png" sizes="96x96" href="/favicon/favicon-96x96.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png">
  <link rel="manifest" href="/favicon/manifest.json">
  <link rel="mask-icon" href="/favicon/safari-pinned-tab.svg" color="#5bbad5">
  <meta name="msapplication-TileColor" content="#ffffff">
  <meta name="msapplication-TileImage" content="/favicon/ms-icon-144x144.png">
  <meta name="theme-color" content="#ffffff">
  <meta name="description" content=" Page not found">
	<link type="text/css" rel="stylesheet" href="https://www.docker.com/sites/default/files/css/css_kShW4RPmRstZ3SpIC-ZvVGNFVAi0WEMuCnI0ZkYIaFw.css" media="all" />
<link type="text/css" rel="stylesheet" href="https://www.docker.com/sites/default/files/css/css_5YbdHr5Ydl82DxADBrSxdn1QRG2JoYejHCFqvNm0E3w.css" media="all" />
<link type="text/css" rel="stylesheet" href="https://www.docker.com/sites/default/files/css/css_MnXiytJtb186Ydycnpwpw34cuUsHaKc80ey5LiQXhSY.css" media="all" />
<link type="text/css" rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,700" media="all" />
<link type="text/css" rel="stylesheet" href="https://www.docker.com/sites/default/files/css/css_oi4miyiebMIZuiZvsYzPOUJH2KK_1PkmnlbJT9Gr-Lk.css" media="all" />
<style type="text/css" media="all">
<!--/*--><![CDATA[/*><!--*/
.before_hero_section .container{}.inner-wrap{position:inherit;}.heronav_section.affix+.before_hero_section{top:-600px;margin:0 0 -600px 0;}.it_next_step .container>h2:after{height:4px;width:64px;opacity:0.2;background-color:#066DA5;content:"";display:block;margin:auto;margin-top:20px;}.it_next_step .container>p:nth-of-type(2){border-top:1px solid rgba(68,93,110,0.2);padding-top:40px;}#details_section .details_tip{width:23px;padding:0px 7px;font-family:Open Sans,sans-serif;font-weight:700;}.node-type-legal .title_section p{max-width:600px;}.sidebarnav_section ul li{max-width:200px;}.page-node-17497 .legal_content_section table{border:none;}.page-node-17497 .legal_content_section tbody{border:none;}.page-node-17497 .legal_content_section table td,.page-node-17497  .legal_content_section table th{border:none;padding:10px 10px 10px 0;}.page-node-17497 .legal_content_section table td img{display:block;}@media (max-width:600px){.page-node-17497 .legal_content_section table td{display:block;width:90%;}.page-node-17497 .legal_content_section table{width:100%!important;}}.home_middle_section .company_items li a img{max-width:100%;max-height:40px;}#dockercon_banner_wrap{}.page-node-1 #dockercon_banner_wrap{}#dockercon_banner_wrap img{max-width:300px;}.page-node-1 #dockercon_banner_wrap img{max-width:415px;}@media (max-width:500px){.page-node-1 #dockercon_banner_wrap img{max-width:100%;}}.title_section.with_heronav  #dockercon_banner_wrap{bottom:49px!important;}.title_section.without_heronav  #dockercon_banner_wrap{bottom:-1px!important;}.node-type-it-page .animation_slides #dockercon_banner_wrap{bottom:49px!important;}.animation_slides .kubernetes_gradient{background-image:radial-gradient(circle at 47% 9%,#3e825f,#1a4172);}.animation_slides .red_gradient{background-image:radial-gradient(circle at 47% -28%,#ef544b,#282b43);background-image:radial-gradient(circle at 47% 0%,#ef544b -15%,#282b43 60%);}.title_section.kubernetes_gradient img{max-height:800px;width:150%;max-width:inherit;left:-50%;top:-150px;position:absolute;z-index:-1;}.title_section.red_gradient img{width:100%;max-width:480px;left:3%;top:-50px;position:absolute;z-index:-1;}.title_section.red_gradient .main-button{background-color:rgba(255,255,255,0.3);box-shadow:0 1px 0 0 rgba(255,255,255,0.1);}.title_section.red_gradient .main-button:hover{background-color:rgba(255,255,255,0.2);}@media (max-width:680px){.animation_slides .title_section h1{margin-left:auto;margin-right:auto;}.animation_slides .slick-dots{}}@media (max-width:767px){.before_hero_section{padding:130px 0 200px 0;}#dockercon_banner_wrap{bottom:-50px!important;margin-top:0px;position:initial;height:90px;}#dockercon_banner_wrap a{position:absolute;bottom:0;right:0;}.title_section.with_heronav  #dockercon_banner_wrap a{bottom:49px;}.title_section.without_heronav  #dockercon_banner_wrap a{bottom:0px;}.partner_inside .content{padding:0 15px 20px;}}.career_job_openings_section ul.items>li .left{min-height:90px;}@media (max-width:1199px){.title_section .button{margin:20px 20px 0 0!important;}.main-header .desktop div>.nav-main>li{padding:15px 14px;}}.animation_slides .slick-slide.slick-current .container{display:block;}.heronav_section .arrow{height:50px;margin-top:-25px;}.node-type-page-alibaba p{font-size:16px;line-height:26px;}.partner_inside_overview{padding:50px 0;}.partner_inside_overview h2{font-size:36px;line-height:40px;max-width:800px;}.partner_inside_overview .logo{max-width:290px;}.partner_inside_overview .col-md-6+.col-md-6{text-align:right;}.partner_inside_overview .button{max-width:300px;}.partner_inside_overview .col-md-12 .button{max-width:inherit;width:initial;}.partner_inside_resources_section{padding:20px 0;}.partner_inside_resources_section .item{max-width:200px;margin:auto;}.partner_inside_resources_section ul.items>li+li{border-left:1px solid #ccc;}.partner_inside_get_started_section{padding:20px 0;}.partner_inside_about_section{padding:20px 0 100px 0;}.partner_inside_about_section h2{font-size:36px;}.partners_tabs ul li{max-width:20%;}.resource_sorting{display:none;}.logged-in .resource_sorting{display:block;}.resources_section .resource_sorting .resources_link img{display:none;}.resource_sorting .views-table{width:100%;border:1px solid #ccc;}.resource_sorting .views-table thead{display:none;}.resource_sorting .draggable a.tabledrag-handle{position:absolute;width:100%;height:100%;top:0;z-index:100;opacity:0;}.resource_sorting .views-table tr.even,.resource_sorting .views-table tr.odd{padding:6px 10px 6px 20px;width:100%;display:inline-block;float:left;position:relative;}.resources_section .resource_sorting .resources_link{width:100%;margin-bottom:2px;}.resources_section .resource_sorting .resources_link span{text-align:left;padding:0px 10px;}.view_resource .view tbody{border-top:none;}.view_resource .views-table{width:100%;}.view_resource .views-table thead{display:none;}.view_resource .views-table tr.even,.view_resource .views-table tr.odd{background-color:transparent;border-bottom:none;padding:0;width:25%;display:inline-block;float:left;position:relative;}.page-node-4199 .view_resource .views-table tr:nth-child(n+5){display:none;}.view_resource .views-table tr.even .resources_link,.view_resource .views-table tr.odd .resources_link{width:100%;max-width:200px;}.view_resource .draggable a.tabledrag-handle{position:absolute;opacity:0;}.view_resource .form-actions{display:none;}.resources_items>.resources_link{display:none;}.tabledrag-toggle-weight-wrapper{display:none;}.resources_section .view .form-submit{text-decoration:none;outline:0;-webkit-transition:all .4s ease-in-out;-moz-transition:all .4s ease-in-out;-o-transition:all .4s ease-in-out;transition:all .4s ease-in-out;font-family:'Geomanist Book';font-size:14px;padding:8px 60px 8px 60px;display:inline-block;min-width:150px;text-align:center;border-radius:1px;margin-bottom:20px;box-shadow:0 1px 0 0 rgba(0,0,0,0.2);border:1px solid rgba(0,0,0,0.1);color:#FFF;text-shadow:0 1px 0 rgba(0,0,0,0.2);background-color:#009bff;}.resources_section .view .form-submit:hover{background-color:#106C9E;box-shadow:0 1px 0 0 rgba(0,0,0,0.1);border:1px solid rgba(0,0,0,0.1);color:#FFFFFF;}.page-node-17573{}.page-node-17573 h2{font-family:Geomanist Light;font-size:36px;text-transform:uppercase;}.page-node-17573 .section .container>h2:after{display:none;}.page-node-17573 .other_resources .media_image{height:86px;}.page-node-17573 .other_resources .media_image img{max-width:80px;max-height:70px;}.page-node-17573 .section ul.items.other_resources li h3{font-family:'Open Sans',sans-serif;line-height:18px;font-size:14px;font-weight:bold;}.page-node-17573 ul.items.other_resources li p{font-size:14px;line-height:18px;}.page-node-17573 .it_hello_world_section .hello-world-text{max-width:360px;margin:0 auto;text-align:left;}.page-node-17573 .it_hello_world_section .hello-world-text p{max-width:inherit;margin:inherit;}.page-node-17573 ul.items.other_resources>li .media_content{max-width:240px;}.page-node-17573 section.section .container>ul.items{max-width:1030px;margin-left:auto;margin-right:auto;margin-bottom:0;}.page-node-17573 section.it_start_section{padding-top:80px;}.page-node-17573 section.it_save_section{padding-bottom:80px;position:relative;}.page-node-17573 section.it_save_section:before{content:"";position:absolute;left:0;right:0;top:0;height:50%;background-color:#f3f3f3;z-index:-1;}.page-node-17573 .it_save_section .container>p{max-width:800px;margin-left:auto;margin-right:auto;max-width:490px;}.page-node-17573 .it_save_section .it_save_item{background-color:#061F2F;padding:60px 16px;color:#FFFFFF;}.page-node-17573 .it_save_section .it_save_item h2{font-family:Geomanist;font-size:64px;margin-bottom:0;text-align:center;color:#FFFFFF;}.page-node-17573 .it_save_section .it_save_item h3{font-family:'Open Sans',sans-serif;color:#3AAFEF;max-width:110px;margin-left:auto;margin-right:auto;}.page-node-17573 .it_windows_section{text-align:center;}.page-node-17573 .it_windows_section .container>h2{max-width:490px;margin-left:auto;margin-right:auto;}.page-node-17573 .it_windows_section .container>p{max-width:490px;margin-left:auto;margin-right:auto;}.node-type-it-page.page-node-17573 .title_section img{margin:0;max-height:initial;max-width:inherit;position:absolute;width:120%;top:-50px;right:0;left:-50px;}.page-node-17573 .animation_slides .title_section{background-image:radial-gradient(circle,#00c7d0 0%,#0075b5 100%);}.page-node-17597 .flexible_content .text_center{text-align:left;}.page-node-17597 .flexible_content hr{margin-top:50px;margin-bottom:50px;}.page-node-17597 .flexible_content h4{margin-bottom:20px;}.page-node-17597 .flexible_content ul{margin-bottom:20px;}.page-node-17597 .flexible_content ul li{margin:5px 0;}.career_job_openings_section ul.items{max-width:800px;margin:auto;}.career_job_openings_section ul.items>li{padding:30px 30px 30px;display:inline-block;float:none;margin-left:-2px;margin-right:-2px;}@media (min-width:992px){.career_job_openings_section ul.items>li{width:33.33%;}}.career_job_openings_section ul.items>li .left{text-align:center;}.fedsummitpage #agenda #agenda_table .agenda-row .detail{font-size:13px;color:#445d6e;line-height:22px;}.fedsummitpage #agenda #agenda_table .agenda-row .detail p{font-size:13px;line-height:22px;padding:0 10px 0 0;}.fedsummitpage #agenda #agenda_table .agenda-row .detail ul{font-weight:300;}#agenda_table .agenda-row .session_details{margin-left:120px;width:calc(100% - 250px);float:left;padding:20px 0px 0px 0;}.fedsummitpage #agenda #agenda_table .agenda-row .detail .two-event{padding-right:15px;}#agenda_table .agenda-row .session_details .session_tem{width:50%;padding-right:15px;display:inline-block;margin-left:-1px;margin-right:-1px;vertical-align:top;}#agenda_table .agenda-row .session_details ul{font-family:Open Sans;font-size:13px;line-height:1.85;font-weight:500;}#agenda_table .agenda-row .session_details p{margin-bottom:0;}.fedsummitpage #agenda #agenda_table .agenda-row .detail .two-event{padding-right:0;}.page-node-17593 #featured_speakers{display:block !important;}.enterprise_company{max-width:600px;margin:50px auto 0 auto;display:table;}.enterprise_company .enterprise_company_logo{width:200px;display:table-cell;padding-right:30px;}.enterprise_company .enterprise_company_content{display:table-cell;text-align:left;vertical-align:top;}.enterprise_company .enterprise_company_content a{text-align:right;display:block;}.enterprise_container_management_section .enterprise_company{margin:0px auto 50px auto;}.enterprise_security_section  .enterprise_company{margin:100px auto 0px auto;}.page-node-17630 .whatisdocker_section5_section{background-color:#f3f3f3;}.pricing_plan_chart>tbody>tr.bg_gray>td{background-color:#ececec;}.title_section.blue_gradient img{max-height:800px;max-width:inherit;left:-13%;top:-150px;position:absolute;z-index:-1;}.animation_slides .title_section.blue_gradient p{margin-left:0;}.title_section.blue_gradient .col-xs-12{width:100%;}.title_section.blue_gradient .col-xs-0{position:absolute;right:0;}.title_section.blue_gradient .main-button{background-color:rgba(255,255,255,0.2);box-shadow:0 1px 0 0 rgba(0,0,0,0.1);border:1px solid rgba(255,255,255,0.1);color:#FFF;text-shadow:0 1px 0 rgba(0,0,0,0.2);}.title_section.blue_gradient .main-button:hover{background-color:rgba(255,255,255,0.3);color:#FFF;}.title_section.blue_gradient .second-button{background-color:rgba(0,0,0,0.2);box-shadow:0 1px 0 0 rgba(0,0,0,0.1);padding:12px 35px 10px 35px;border:none;}.title_section.blue_gradient .second-button:hover{background-color:rgba(0,0,0,0.3);color:#FFF;}.disable-button{background-color:rgba(0,0,0,0.1);-webkit-filter:grayscale(100%);filter:grayscale(100%);cursor:default;}

/*]]>*/-->
</style>
  <script type="text/javascript" src="//code.jquery.com/jquery-1.10.2.min.js"></script>
<script type="text/javascript">
<!--//--><![CDATA[//><!--
window.jQuery || document.write("<script src='/sites/all/modules/jquery_update/replace/jquery/1.10/jquery.min.js'>\x3C/script>")
//--><!]]>
</script>
<script type="text/javascript" src="https://www.docker.com/sites/default/files/js/js_V1ZuwJK9uzfm6fFffOcHHubfxnimoxnbgG58pvTQdpY.js"></script>
<script type="text/javascript" src="https://use.fontawesome.com/8a432be07c.js"></script>
<script type="text/javascript" src="//app-sj05.marketo.com/js/forms2/js/forms2.min.js"></script>
<script type="text/javascript">
<!--//--><![CDATA[//><!--

//--><!]]>
</script>
<script type="text/javascript">
<!--//--><![CDATA[//><!--
jQuery.extend(Drupal.settings, {"basePath":"\/","pathPrefix":"","ajaxPageState":{"theme":"docker","theme_token":"NItb-jRbOL0gy7DpTG-wm3dSnvKgUgoguyDIXwJnFfE","js":{"sites\/all\/themes\/docker\/assets\/js\/jquery-1.11.3.min.js":1,"sites\/all\/themes\/docker\/assets\/js\/jquery-migrate-1.4.1.min.js":1,"sites\/all\/themes\/docker\/assets\/js\/bootstrap.min.js":1,"sites\/all\/themes\/docker\/assets\/js\/affix.js":1,"sites\/all\/themes\/docker\/assets\/js\/jquery.ba-bbq.js":1,"sites\/all\/themes\/docker\/assets\/js\/jquery.smooth-scroll.min.js":1,"sites\/all\/themes\/docker\/assets\/js\/jquery.cubeportfolio.min.js":1,"sites\/all\/themes\/docker\/assets\/js\/jquery.magnific-popup.min.js":1,"sites\/all\/themes\/docker\/assets\/js\/jquery.flexslider-min.js":1,"sites\/all\/themes\/docker\/assets\/js\/jquery.matchHeight.min.js":1,"sites\/all\/themes\/docker\/assets\/js\/wow.min.js":1,"sites\/all\/themes\/docker\/assets\/js\/slick.min.js":1,"sites\/all\/themes\/docker\/assets\/js\/script.js":1,"0":1,"\/\/code.jquery.com\/jquery-1.10.2.min.js":1,"1":1,"misc\/jquery.once.js":1,"misc\/drupal.js":1,"https:\/\/use.fontawesome.com\/8a432be07c.js":1,"\/\/app-sj05.marketo.com\/js\/forms2\/js\/forms2.min.js":1,"2":1},"css":{"modules\/system\/system.base.css":1,"modules\/system\/system.messages.css":1,"modules\/system\/system.theme.css":1,"modules\/comment\/comment.css":1,"sites\/all\/modules\/date\/date_api\/date.css":1,"sites\/all\/modules\/date\/date_popup\/themes\/datepicker.1.7.css":1,"modules\/field\/theme\/field.css":1,"modules\/node\/node.css":1,"modules\/search\/search.css":1,"modules\/user\/user.css":1,"sites\/all\/modules\/views\/css\/views.css":1,"sites\/all\/modules\/ckeditor\/css\/ckeditor.css":1,"sites\/all\/modules\/ctools\/css\/ctools.css":1,"https:\/\/fonts.googleapis.com\/css?family=Open+Sans:300,400,700":1,"sites\/all\/themes\/docker\/assets\/css\/bootstrap.min.css":1,"sites\/all\/themes\/docker\/assets\/css\/cubeportfolio.min.css":1,"sites\/all\/themes\/docker\/assets\/css\/magnific-popup.css":1,"sites\/all\/themes\/docker\/assets\/css\/animate.css":1,"sites\/all\/themes\/docker\/assets\/css\/flexslider.css":1,"sites\/all\/themes\/docker\/assets\/css\/slick.css":1,"sites\/all\/themes\/docker\/assets\/css\/style.css":1,"sites\/all\/themes\/docker\/assets\/css\/style-a.css":1,"sites\/all\/themes\/docker\/assets\/css\/responsive.css":1,"sites\/all\/themes\/docker\/assets\/css\/style-dane.css":1,"0":1}}});
//--><!]]>
</script>



  <script type='application/ld+json'>
  {
    "@context": "http://www.schema.org",
    "@type": "WebSite",
    "name": "Docker",
    "url": "https://www.docker.com"
  }
  </script>
  <!-- Start Analytics.js Code -->
  <script type="text/javascript">
    !function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","debug","page","once","off","on"];analytics.factory=function(t){return function(){var e=Array.prototype.slice.call(arguments);e.unshift(t);analytics.push(e);return analytics}};for(var t=0;t<analytics.methods.length;t++){var e=analytics.methods[t];analytics[e]=analytics.factory(e)}analytics.load=function(t){var e=document.createElement("script");e.type="text/javascript";e.async=!0;e.src=("https:"===document.location.protocol?"https://":"http://")+"cdn.segment.com/analytics.js/v1/"+t+"/analytics.min.js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(e,n)};analytics.SNIPPET_VERSION="4.0.0";
    analytics.load("IyBu5RsBo9R8UuqWPhCkO8yEHXeZYnKa");
    analytics.page();
    }}();
  </script>
  <!-- End Analytics.js Code -->
<style>
<!--/*--><![CDATA[/* ><!--*/



/*--><!]]>*/
</style><!-- Global site tag (gtag.js) - Google AdWords: 842916875 -->
<script async src="https://www.googletagmanager.com/gtag/js?id=AW-842916875"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'AW-842916875');
</script>
<script type="text/javascript">
(function() {
var didInit = false;
function initMunchkin() {
if(didInit === false)
{ didInit = true; Munchkin.init('929-FJL-178'); }
}
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = '//munchkin.marketo.net/munchkin.js';
s.onreadystatechange = function() {
if (this.readyState == 'complete' || this.readyState == 'loaded')
{ initMunchkin(); }
};
s.onload = initMunchkin;
document.getElementsByTagName('head')[0].appendChild(s);
})();
</script>
</head>
<body class="html not-front not-logged-in no-sidebars page-node page-node- page-node-4213 path-404" ng-app="Docker" data-spy="scroll" data-target=".heronav_section" data-offset="70">
<div itemscope itemprop="organization" itemtype="http://schema.org/Organization">
  <meta itemprop="url" content="https://www.docker.com">
  <meta itemprop="name" content="Docker">
  <meta itemprop="sameAs" content="https://www.linkedin.com/company/docker">
  <meta itemprop="sameAs" content="https://twitter.com/docker">
  <meta itemprop="logo" content="https://www.docker.com/sites/all/themes/docker/assets/images/brand-full.svg">
</div>
<div itemscope itemtype="http://schema.org/WebSite">
  <meta itemprop="url" content="https://www.docker.com/"/>
  <meta itemprop="name" content="Docker">
</div>

		<div class="off-canvas-wrap" data-offcanvas>
		<div class="inner-wrap">
										<header class="main-header">
	<!-- navigation -->
	<!-- mobile navigation -->
	<div class="header">
		<div class="fixed_div">
			<div class="burger-container">
				<div id="burger">
					<div class="bar topBar"></div>
					<div class="bar btmBar"></div>
				</div>
			</div>
			<div class="icon icon-apple">
				<a href="/"><img class="brand-full" src="/sites/all/themes/docker/assets/images/brand-full.svg" alt="Docker" title="Docker"></a>
				<a href="/"><img class="brand-simple" src="/sites/all/themes/docker/assets/images/brand.svg" alt="Docker" title="Docker"></a>
			</div>
		</div>
		<div class="scroll_div">

			<ul class="nav-main"><li class="first leaf"><a href="/what-docker">What is Docker?</a></li>
<li class="leaf"><a href="/get-docker">Product</a></li>
<li class="leaf"><a href="/docker-community">Community</a></li>
<li class="last expanded has-submenu"><a href="/" class="nolinkhere">Support</a><ul class="nav-main"><li class="first leaf"><a href="https://success.docker.com/" target="_blank">Customer Portal</a></li>
<li class="leaf"><a href="https://docs.docker.com/" target="_blank">Documentation</a></li>
<li class="leaf"><a href="https://success.docker.com/support" target="_blank">Support</a></li>
<li class="leaf"><a href="https://training.docker.com/instructor-led-training" target="_blank">Training</a></li>
<li class="last leaf"><a href="https://success.docker.com/certification" target="_blank">Certification</a></li>
</ul></li>
</ul>
			<ul class="nav-user"><li class="first leaf"><a href="https://hub.docker.com/">Create Docker ID</a></li>
<li class="last leaf"><a href="https://hub.docker.com/sso/start/?next=https://www.docker.com/">Sign In</a></li>
</ul>
			<div class="shop icon icon-bag"></div>
		</div>
	</div>
	<!-- mobile navigation end -->
	<!-- full width navigation -->
	<div class="container">
		<div class="row desktop">
			<div class="col-xs-2">
				<a href="/"><img class="logo" src="/sites/all/themes/docker/assets/images/brand-full.svg" alt="Docker" title="Docker"></a>
				<a href="/"><img class="logo_safari" src="/sites/all/themes/docker/assets/images/brand-full.svg" alt="Docker" title="Docker"></a>
			</div>
			<div class="col-xs-10">
				<ul class="nav-user"><li class="first leaf"><a href="https://hub.docker.com/">Create Docker ID</a></li>
<li class="last leaf"><a href="https://hub.docker.com/sso/start/?next=https://www.docker.com/">Sign In</a></li>
</ul>

        <form action="/v2/ubuntu/manifests/16.04" method="post" id="search-block-form" accept-charset="UTF-8"><div><div class="container-inline">
      <h2 class="element-invisible">Search form</h2>
    <div class="form-item form-type-textfield form-item-search-block-form">
  <label class="element-invisible" for="edit-search-block-form--2">Search </label>
 <input type="text" id="edit-search-block-form--2" name="search_block_form" value="" size="15" maxlength="128" class="form-text" />
</div>
<div class="form-actions form-wrapper" id="edit-actions"><input type="submit" id="edit-submit" name="op" value="Search" class="form-submit" /></div><input type="hidden" name="form_build_id" value="form-TXhePs7bRwHeRkGAACf6c1iGadzev6dXMnczMEOIl8A" />
<input type="hidden" name="form_id" value="search_block_form" />
  <input type="submit" id="edit-close" name="op" value="Close" class="form-submit" />
</div>
</div></form><a class="search_box_icon" id="search_box_icon" href="#"></a>
				<a class="nav_xb" href="#" title=""></a>
				<ul class="nav-main"><li class="first leaf"><a href="/what-docker">What is Docker?</a></li>
<li class="leaf"><a href="/get-docker">Product</a></li>
<li class="leaf"><a href="/docker-community">Community</a></li>
<li class="last expanded has-submenu"><a href="/" class="nolinkhere">Support</a><ul class="nav-main"><li class="first leaf"><a href="https://success.docker.com/" target="_blank">Customer Portal</a></li>
<li class="leaf"><a href="https://docs.docker.com/" target="_blank">Documentation</a></li>
<li class="leaf"><a href="https://success.docker.com/support" target="_blank">Support</a></li>
<li class="leaf"><a href="https://training.docker.com/instructor-led-training" target="_blank">Training</a></li>
<li class="last leaf"><a href="https://success.docker.com/certification" target="_blank">Certification</a></li>
</ul></li>
</ul>			</div>
		</div>
	</div>
	<!-- full width navigation end -->

</header>						<section class="title_section 404_class nopadding">
<div class="header_padding"></div>
</section>
<script src="https://code.createjs.com/easeljs-0.8.1.min.js"></script>
<script src="https://code.createjs.com/tweenjs-0.6.1.min.js"></script>
<script src="https://code.createjs.com/movieclip-0.8.1.min.js"></script>
<script src="https://code.createjs.com/preloadjs-0.6.1.min.js"></script>
<script src="/sites/all/themes/docker/assets/js/docker-404.js"></script>
<script>
var canvas, stage, exportRoot;
function init404() {
	createjs.MotionGuidePlugin.install();

	canvas = document.getElementById("canvas");
	images = images||{};

	var loader = new createjs.LoadQueue(false);
	loader.addEventListener("fileload", handleFileLoad);
	loader.addEventListener("complete", handleComplete);
	loader.loadManifest(lib.properties.manifest);
}

function handleFileLoad(evt) {
	if (evt.item.type == "image") { images[evt.item.id] = evt.result; }
}

function handleComplete(evt) {
	exportRoot = new lib.docker404();

	stage = new createjs.Stage(canvas);
	stage.addChild(exportRoot);
	stage.update();

	createjs.Ticker.setFPS(lib.properties.fps);
	createjs.Ticker.addEventListener("tick", stage);
}
window.onload = init404;
</script>
<div class="page404" style="margin-left: -15px; margin-right: -15px;">
<canvas id="canvas" width="1920" height="1080" style="background-color:#2A7CBC;max-width:100%;width:100%;margin:0;padding:0"></canvas>
</div>			<a class="exit-off-canvas"></a>
		</div>
	</div>
			<section class="newsletter_section">
	<div class="container">
		<div class="newsletter">
			<form id="mktoForm_1038"></form>
		</div>
	</div>
</section>
<footer class="main_footer">
	<div class="container">
		<div class="top_footer">
			<div class="row">
				<div class="col-xs-12 col-sm-3 col-md-3">
					<ul class="footer_links links_1"><li class="first leaf"><a href="/what-docker">What is Docker</a></li>
<li class="leaf"><a href="/what-container">What is a Container</a></li>
<li class="leaf"><a href="/customers">Customers</a></li>
<li class="leaf"><a href="/industry-government">For Government</a></li>
<li class="leaf"><a href="https://www.docker.com/itpro">For IT Pros</a></li>
<li class="leaf"><a href="https://www.docker.com/find-partner">Find a Partner</a></li>
<li class="leaf"><a href="https://www.docker.com/partners/partner-program">Become a Partner</a></li>
<li class="leaf"><a href="/company">About Docker</a></li>
<li class="leaf"><a href="/company/management">Management</a></li>
<li class="leaf"><a href="/news-and-press">Press &amp; News</a></li>
<li class="last leaf"><a href="/careers">Careers</a></li>
</ul>
				</div>
				<div class="col-xs-12 col-sm-3 col-md-3">
					<ul class="footer_links links_2"><li class="first leaf"><a href="/get-docker">Product</a></li>
<li class="leaf"><a href="/pricing">Pricing</a></li>
<li class="leaf"><a href="/community-edition">Community Edition</a></li>
<li class="leaf"><a href="/enterprise-edition">Enterprise Edition </a></li>
<li class="leaf"><a href="https://cloud.docker.com/" target="_blank">Docker Cloud</a></li>
<li class="last leaf"><a href="https://store.docker.com/" target="_blank">Docker Store</a></li>
</ul>
				</div>
				<!--<div class=" hide col-xs-12 col-sm-3 col-md-3">

				</div>-->
				<div class="col-xs-12 col-sm-3 col-md-3">
					<ul class="footer_links links_5"><li class="first leaf"><a href="https://docs.docker.com/" target="_blank">Documentation</a></li>
<li class="leaf"><a href="https://blog.docker.com/" target="_blank">Blog</a></li>
<li class="leaf"><a href="https://blog.docker.com/feed/" target="_blank">RSS Feed</a></li>
<li class="leaf"><a href="https://training.docker.com/" target="_blank">Training</a></li>
<li class="leaf"><a href="https://success.docker.com/kbase" target="_blank">Knowledge Base</a></li>
<li class="last leaf"><a href="/products/resources">Resources</a></li>
</ul>
				</div>
				<div class="col-xs-12 col-sm-3 col-md-3">
					<ul class="footer_links links_4"><li class="first leaf"><a href="https://events.docker.com">Events</a></li>
<li class="leaf"><a href="/docker-community">Community</a></li>
<li class="leaf"><a href="/open-source-0">Open Source</a></li>
<li class="leaf"><a href="https://forums.docker.com/" target="_blank">Forums</a></li>
<li class="leaf"><a href="/docker-captains">Docker Captains</a></li>
<li class="leaf"><a href="/community-partnerships">Scholarships</a></li>
<li class="last leaf"><a href="https://blog.docker.com/curated/" target="_blank">Community News</a></li>
</ul>
				</div>
			</div>
			<div class="footer-nav">
				<nav class="footer_sub_nav">
									</nav>
			</div>
		</div>
		<div class="bottom_footer">
			<div class="footer-copyright">
				<p class="copyright">
					Copyright © 2018 Docker Inc. All rights reserved.				</p>
			</div>
			<div class="footer_social_nav">
				<ul class="nav-social"><li class="first leaf"><a href="http://twitter.com/docker" class="fa fa-twitter">Twitter</a></li>
<li class="leaf"><a href="http://www.youtube.com/user/dockerrun" class="fa fa-youtube">Youtube</a></li>
<li class="leaf"><a href="https://plus.google.com/u/0/communities/108146856671494713993" class="fa fa-google-plus">Google</a></li>
<li class="leaf"><a href="https://github.com/docker/docker" class="fa fa-github">Github</a></li>
<li class="leaf"><a href="https://www.linkedin.com/company/docker" class="fa fa-linkedin">Linkedin</a></li>
<li class="leaf"><a href="https://www.facebook.com/docker.run" class="fa fa-facebook">Facebook</a></li>
<li class="leaf"><a href="http://www.reddit.com/r/docker" class="fa fa-reddit-alien">Reddit</a></li>
<li class="last leaf"><a href="http://www.slideshare.net/docker" class="fa fa-slideshare">Slideshare</a></li>
</ul>
			</div>
		</div>
	</div>
</footer>
<script type="text/javascript" charset="utf-8">
  var _eiq = _eiq || [];
  var _engagio_settings = {
    accountId: "0381e85d5b97e7a3954996fedd3cbfcb68c98e1e"
  };
  (function() {
    var ei = document.createElement('script'); ei.type = 'text/javascript'; ei.async = true;
    ei.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'web-analytics.engagio.com/js/ei.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ei, s);
  })();
</script>

	<script type="text/javascript" src="https://www.docker.com/sites/default/files/js/js_b8tpEfzvJ0uR9YhDDa_ym-wD1KOrSh8nRe0uAjA8MkI.js"></script>
<script type="text/javascript">
<!--//--><![CDATA[//><!--

//--><!]]>
</script>
	<script>var $ = jQuery.noConflict();</script>
  <script>
				MktoForms2.loadForm("//app-sj05.marketo.com", "929-FJL-178", 1038, function(form) {
					form.onSuccess(function(values, followUpUrl) {
						location.href = "/thank-you-subscribing-docker-newsletter";
						return false;
					});
				});
				MktoForms2.whenReady(function(form){
				/*	$('#mktoForm_1038 #Email').attr('placeholder', 'Enter your email'); */
				$('.newsletter').find("#Email").each(function(ev){
					if(!$(this).val()) {
						$(this).attr("placeholder", "Subscribe to our newsletter");
					}
				});
				$(".newsletter button.mktoButton").text('Subscribe');
				});
  </script>
	<!-- Google Tag Manager -->
	<noscript><iframe src="//www.googletagmanager.com/ns.html?id=GTM-PSVHG8" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
	<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
	new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
	j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
	'//www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
	})(window,document,'script','dataLayer','GTM-PSVHG8');</script>
	<!-- End Google Tag Manager -->

	<!-- AdRoll Pixel Code -->
	<script type="text/javascript">
			adroll_adv_id = "DQN5LU2LWJERZKNXIV22Z2";
			adroll_pix_id = "SC67VPE7UZFTXBS5QTGXWO";
			/* OPTIONAL: provide email to improve user identification */
			/* adroll_email = "[email protected]"; */
			(function () {
					var _onload = function(){
							if (document.readyState && !/loaded|complete/.test(document.readyState)){setTimeout(_onload, 10);return}
							if (!window.__adroll_loaded){__adroll_loaded=true;setTimeout(_onload, 50);return}
							var scr = document.createElement("script");
							var host = (("https:" == document.location.protocol) ? "https://s.adroll.com" : "http://a.adroll.com");
							scr.setAttribute('async', 'true');
							scr.type = "text/javascript";
							scr.src = host + "/j/roundtrip.js";
							((document.getElementsByTagName('head') || [null])[0] ||
									document.getElementsByTagName('script')[0].parentNode).appendChild(scr);
					};
					if (window.addEventListener) {window.addEventListener('load', _onload, false);}
					else {window.attachEvent('onload', _onload)}
			}());
	</script>
	<!-- End AdRoll Pixel Code -->

	<!-- BrightFunnel Digital Attribution Code -->
	<script type="text/javascript">
		var bfId="fQUlbcLzIiHXC91h",
					bfSession = 0.041666666666666664;
		(function() {
					var script = document.createElement('script');
					script.type = 'text/javascript';
					script.async = true;
					script.src = ('https:' === document.location.protocol ? 'https://' : 'http://' ) + 'munchkin.brightfunnel.com/js/build/bf-munchkin.min.js?tstamp=' + new Date().getTime();
					var top = document.getElementsByTagName('script')[0];
					top.parentNode.insertBefore(script, top);
			})();
		</script>
	<!-- end Digital Attribution Code -->


	<!-- LinkedIn Pixel Code -->
	<script type="text/javascript"> _linkedin_data_partner_id = "20029"; </script><script type="text/javascript"> (function(){var s = document.getElementsByTagName("script")[0]; var b = document.createElement("script"); b.type = "text/javascript";b.async = true; b.src = "https://snap.licdn.com/li.lms-analytics/insight.min.js"; s.parentNode.insertBefore(b, s);})(); </script> <noscript> <img height="1" width="1" style="display:none;" alt="" src="https://dc.ads.linkedin.com/collect/?pid=20029&fmt=gif" /> </noscript>
	<!-- End LinkedIn Pixel Code -->

	<!-- Twitter universal website tag code -->
	<script>
	!function(e,t,n,s,u,a){e.twq||(s=e.twq=function(){s.exe?s.exe.apply(s,arguments):s.queue.push(arguments);
	},s.version='1.1',s.queue=[],u=t.createElement(n),u.async=!0,u.src='//static.ads-twitter.com/uwt.js',
	a=t.getElementsByTagName(n)[0],a.parentNode.insertBefore(u,a))}(window,document,'script');
	// Insert Twitter Pixel ID and Standard Event data below
	twq('init','nv75j');
	twq('track','PageView');
	</script>
	<!-- End Twitter universal website tag code -->

</body>
</html>
: <no details provided>

Improve Documentation

The documentation includes examples of using the auth helpers for google cloud, aws, and azure, which together seems to indicate that it'd be expected for those to all work. However, aside from gcr the README doesn't clearly state whether the tools are expected to work with dockerhub. I spent some time trying yesterday and it doesn't seem to work, but I don't know if that's as intended.

NO_PROXY support

can these utilities respect the NO_PROXY environment variable in the docker_rules? or is there a way to specify this ?

fast_puller_.py pulls an image with an unknown SHA256 from quay.io

I'm using the fast puller to pull an image from quay.io, but according to the SHA256 in the digest file, it pulls some other image.

All of the following calls yield the same result, where the SHA256 of the image being pulled is sha256:955b57c68705a57290f6e34b00e40ee7ec33e0138d39bf0b8759f7a2f709e70b:

$ python puller.par --name quay.io/coreos/kube-state-metrics:v1.5.0 --directory .
$ python puller.par --name quay.io/coreos/kube-state-metrics:latest --directory .
$ python puller.par --name quay.io/coreos/kube-state-metrics@sha256:b7a3143bd1eb7130759c9259073b9f239d0eeda09f5210f1cd31f1a530599ea1 --directory .
$ python puller.par --name quay.io/coreos/kube-state-metrics@sha256:f686e2b6c67d4eeee782eada446a1720ae14e7ade8fc4e4e254844262affd7fa --directory .

When trying to pull the image specifying the SHA256 of the image that was actually pulled, like so:

$ python puller.par --name quay.io/coreos/kube-state-metrics@sha256:955b57c68705a57290f6e34b00e40ee7ec33e0138d39bf0b8759f7a2f709e70b --directory .

I'm getting the following error:

F0401 19:17:02.952016  171301 __main__.py:147] Error pulling and saving image quay.io/coreos/kube-state-metrics@sha256:955b57c68705a57290f6e34b00e40ee7ec33e0138d39bf0b8759f7a2f709e70b: response: {'status': '404', 'content-length': '82', 'server': 'nginx/1.14.2', 'connection': 'keep-alive', 'date': 'Mon, 01 Apr 2019 23:17:02 GMT', 'content-type': 'application/json'}
manifest unknown: {}

Support for HTTP

Right now I think all you do is check if we are on localhost and then assume for all other cases that the user wants to use HTTPS. I think people should be able to explicitly set this behavior.

Bump version?

rules_docker brings down pre-built containerregistry par dependencies like so:

# The release of the github.com/google/containerregistry to consume.
CONTAINERREGISTRY_RELEASE = "v0.0.26"

def repositories():
  """Download dependencies of container rules."""
  excludes = native.existing_rules().keys()

  if "puller" not in excludes:
    native.http_file(
      name = "puller",
      url = ("https://storage.googleapis.com/containerregistry-releases/" +
             CONTAINERREGISTRY_RELEASE + "/puller.par"),
      sha256 = "42309ba47bb28d1e1b81ef72789dcca396095e191d4f0e49e2e23c297edd27fb",
      executable = True,
    )

Can someone tag a new release v0.0.27 with #77 included and push it up to https://storage.googleapis.com/containerregistry-releases?

Thx in advance, LMK if I can help somehow.

fast_pusher is racy

The biggest source of flakyness in our ci build (using bazel) is fast_pusher running into this:

F1016 05:27:50.844152   15925 fast_pusher_.py:186] Error publishing eu.gcr.io/my-project/my-image:daily-2018-10-16-2c53248c: Error fetching credential for gcloud, exit status: 1
ERROR: gcloud crashed (OperationalError): database is locked

If you would like to report this issue, please run the following command:
  gcloud feedback

To check gcloud for common problems, please run the following command:
  gcloud info --run-diagnostics

As a workaround we've tried to add

docker-credential-gcloud get https://eu.gcr.io

to make sure we refreshed credentials before running a series of parallel push commands, but this is not sufficient :/

Any ideas?

Pushes to gitlab fail with SSL error

Re-opening bazelbuild/rules_docker#820 as the issue seems to be at this layer.

Bazel version

Build label: 0.24.1
Build target: bazel-out/darwin-opt/bin/src/main/java/com/google/devtools/build/lib/bazel/BazelServer_deploy.jar

rules_docker version

HEAD

containerregistry version:

v0.0.36

Issue:

When attempting to run a docker push through a container_push rule to a registry.gitlab.com target, I am recieving the following error:
registry.gitlab.com/gauntletwizard/bazel-go:{BUILD_EMBED_LABEL} was resolved to registry.gitlab.com/gauntletwizard/bazel-go:foo
F0430 20:27:05.873595 4586 fast_pusher_.py:194] Error publishing registry.gitlab.com/gauntletwizard/bazel-go:foo: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:727)
This happens in both CI and local images (Running under Gitlab CI and a local build on OSX)

This is new as of <24hrs ago; Previously, builds worked fine. Other users are seeing the same issue, though their timing was different. I've had a successful push this morning, well after that post was made.

Pushes and pulls using the docker tool are working correctly. Browsing to https://registry.gitlab.com on my local machine shows a valid SSL certificate.

Example image:

https://gitlab.com/gauntletwizard/bazel-go/blob/master/BUILD#L47

bazel run @containerregistry//:pusher -- --name registry.gitlab.com/gauntletwizard/bazel-go:foo --config $PWD/bazel-bin/dockerimage.0.config

Argument "ca_certs" for fast_puller and fast_pusher

I'd like to be able to tell puller and pusher which ca_certs to use. Right now the default ones
from "cacerts.txt" of httplib2 are used.

httplib2.Http constructor has argument "ca_certs"

We have an on-premise docker-registry with a local certificate.
Right now I'm appending our cert to the cacerts.txt of httplib2 but I don't like that :-)

Memory consumption of pusher

We use CircleCI as our CI service which imposes a 4GB memory limit. When pushing large docker images (>1GB) pusher will consume several gigs of memory causing the kernel to kill the process upon memory exhaustion. Pusher should restrict active threads or total memory consumption to fit within the limits of the host, or provide configuration mechanisms to limit resource usage.

INFO: Running command line: bazel-bin/datastax-enterprise-v5.1.7-image-push

asia.gcr.io/vorstella-noc-dev/datastax-enterprise:v5.1.7-{GIT_VERSION}-134 was resolved to asia.gcr.io/vorstella-noc-dev/datastax-enterprise:v5.1.7-2e4767a-134
asia.gcr.io/vorstella-noc-dev/datastax-enterprise:v5.1.7-2e4767a-134 was published with digest: sha256:93ce4c62d1ac3792a14eac10fbcf5a433a68c7602a5ddf9c2f05f69f0a378166
asia.gcr.io/vorstella-noc-dev/datastax-enterprise:v5.1.7-{GIT_VERSION} was resolved to asia.gcr.io/vorstella-noc-dev/datastax-enterprise:v5.1.7-2e4767a
asia.gcr.io/vorstella-noc-dev/datastax-enterprise:v5.1.7-2e4767a was published with digest: sha256:93ce4c62d1ac3792a14eac10fbcf5a433a68c7602a5ddf9c2f05f69f0a378166
asia.gcr.io/vorstella-noc-dev/datastax-enterprise:v5.1.7 was resolved to asia.gcr.io/vorstella-noc-dev/datastax-enterprise:v5.1.7
asia.gcr.io/vorstella-noc-dev/datastax-enterprise:v5.1.7 was published with digest: sha256:93ce4c62d1ac3792a14eac10fbcf5a433a68c7602a5ddf9c2f05f69f0a378166
/home/circleci/.cache/bazel/_bazel_circleci/f16e36219ef33c22efc2ad20f3e3775c/execroot/distroless/bazel-out/k8-fastbuild/bin/datastax-enterprise-v5.1.7-image-push.runfiles/distroless/datastax-enterprise-v5.1.7-image-push.7.push: line 26: 24978 Killed                  ${RUNFILES}/distroless/../containerregistry/pusher --name=containers.prod.noc.vorstella.com/vorstella-noc-dev/datastax-enterprise:v5.1.7-{GIT_VERSION}-134 --stamp-info-file=${RUNFILES}/distroless/stable-status.txt --stamp-info-file=${RUNFILES}/distroless/volatile-status.txt --config=${RUNFILES}/distroless/datastax-enterprise/datastax-enterprise.config --digest=${RUNFILES}/distroless/base/base-layer.tar.gz.sha256 --digest=${RUNFILES}/distroless/base/debug-layer.tar.gz.sha256 --digest=${RUNFILES}/distroless/cc/debug-layer.tar.gz.sha256 --digest=${RUNFILES}/distroless/java-oracle/debug-layer.tar.gz.sha256 --digest=${RUNFILES}/distroless/datastax-enterprise/datastax-enterprise-layer.tar.gz.sha256 --layer=${RUNFILES}/distroless/base/base-layer.tar.gz --layer=${RUNFILES}/distroless/base/debug-layer.tar.gz --layer=${RUNFILES}/distroless/cc/debug-layer.tar.gz --layer=${RUNFILES}/distroless/java-oracle/debug-layer.tar.gz --layer=${RUNFILES}/distroless/datastax-enterprise/datastax-enterprise-layer.tar.gz "$@"
E0427 15:05:08.664109   24983 docker_session_.py:335] Error during upload of: containers.prod.noc.vorstella.com/vorstella-noc-dev/datastax-enterprise:v5.1.7-2e4767a
F0427 15:05:08.667324   24983 fast_pusher_.py:162] Error publishing containers.prod.noc.vorstella.com/vorstella-noc-dev/datastax-enterprise:v5.1.7-2e4767a: 
containers.prod.noc.vorstella.com/vorstella-noc-dev/datastax-enterprise:v5.1.7-{GIT_VERSION} was resolved to containers.prod.noc.vorstella.com/vorstella-noc-dev/datastax-enterprise:v5.1.7-2e4767a
/home/circleci/.cache/bazel/_bazel_circleci/f16e36219ef33c22efc2ad20f3e3775c/execroot/distroless/bazel-out/k8-fastbuild/bin/datastax-enterprise-v5.1.7-image-push.runfiles/distroless/datastax-enterprise-v5.1.7-image-push.8.push: line 26: 24979 Killed                  ${RUNFILES}/distroless/../containerregistry/pusher --name=containers.prod.noc.vorstella.com/vorstella-noc-dev/datastax-enterprise:v5.1.7 --stamp-info-file=${RUNFILES}/distroless/stable-status.txt --stamp-info-file=${RUNFILES}/distroless/volatile-status.txt --config=${RUNFILES}/distroless/datastax-enterprise/datastax-enterprise.config --digest=${RUNFILES}/distroless/base/base-layer.tar.gz.sha256 --digest=${RUNFILES}/distroless/base/debug-layer.tar.gz.sha256 --digest=${RUNFILES}/distroless/cc/debug-layer.tar.gz.sha256 --digest=${RUNFILES}/distroless/java-oracle/debug-layer.tar.gz.sha256 --digest=${RUNFILES}/distroless/datastax-enterprise/datastax-enterprise-layer.tar.gz.sha256 --layer=${RUNFILES}/distroless/base/base-layer.tar.gz --layer=${RUNFILES}/distroless/base/debug-layer.tar.gz --layer=${RUNFILES}/distroless/cc/debug-layer.tar.gz --layer=${RUNFILES}/distroless/java-oracle/debug-layer.tar.gz --layer=${RUNFILES}/distroless/datastax-enterprise/datastax-enterprise-layer.tar.gz "$@"
us.gcr.io/vorstella-noc-dev/datastax-enterprise:v5.1.7 was resolved to us.gcr.io/vorstella-noc-dev/datastax-enterprise:v5.1.7
us.gcr.io/vorstella-noc-dev/datastax-enterprise:v5.1.7 was published with digest: sha256:93ce4c62d1ac3792a14eac10fbcf5a433a68c7602a5ddf9c2f05f69f0a378166
us.gcr.io/vorstella-noc-dev/datastax-enterprise:v5.1.7-{GIT_VERSION}-134 was resolved to us.gcr.io/vorstella-noc-dev/datastax-enterprise:v5.1.7-2e4767a-134
us.gcr.io/vorstella-noc-dev/datastax-enterprise:v5.1.7-2e4767a-134 was published with digest: sha256:93ce4c62d1ac3792a14eac10fbcf5a433a68c7602a5ddf9c2f05f69f0a378166
us.gcr.io/vorstella-noc-dev/datastax-enterprise:v5.1.7-{GIT_VERSION} was resolved to us.gcr.io/vorstella-noc-dev/datastax-enterprise:v5.1.7-2e4767a
us.gcr.io/vorstella-noc-dev/datastax-enterprise:v5.1.7-2e4767a was published with digest: sha256:93ce4c62d1ac3792a14eac10fbcf5a433a68c7602a5ddf9c2f05f69f0a378166
ERROR: Non-zero return code '1' from command: Process exited with status 1

save.py: digest file writing thread bugs

There are two bugs:

  1. fast() is not waiting for the threads writing the digest and manifest.json files to complete. In my use case the digest file is left empty, which is due to bug no. 2.
  2. After adding my fix (see below) I got this stacktrace (redacted in parts):
Traceback (most recent call last):
...
  File ".../containerregistry/client/v2_2/save.py", line 270, in fast
    future.result()
  File "<embedded stdlib>/concurrent/futures/_base.py", line 425, in result
    return self.__get_result()
  File "<embedded stdlib>/concurrent/futures/_base.py", line 384, in __get_result
    raise self._exception
  File "<embedded stdlib>/concurrent/futures/thread.py", line 56, in run
    result = self.fn(*self.args, **self.kwargs)
  File ".../containerregistry/client/v2_2/save.py", line 183, in write_file
    f.write(accessor(arg))
TypeError: a bytes-like object is required, not 'str'

Code in question:

executor.submit(write_file, os.path.join(directory, 'digest'),
lambda unused: image.digest(), 'unused')
executor.submit(write_file, os.path.join(directory, 'manifest.json'),
lambda unused: image.manifest().encode('utf8'),
'unused')

My fix (note additional .encode('utf8'):

    digest_file = os.path.join(directory, 'digest')
    f = executor.submit(write_file, digest_file,
                        lambda unused: image.digest().encode('utf8'), 'unused')
    future_to_params[f] = digest_file
    manifest_file = os.path.join(directory, 'manifest.json')
    f = executor.submit(write_file, manifest_file,
                        lambda unused: image.manifest().encode('utf8'),
                        'unused')
    future_to_params[f] = manifest_file

forced url server specification fails to match for docker-credentials-secretservice

a49af36 introduced a compulsory url prefix, without any checking, into the credentials helper for referencing server entries.

On systems with docker-credentials-secretservice, required on ubuntu 18.04 through docker-compose and provided by golang-docker-credential-helpers, docker login <registry> strips any registry url down to store credentials with only the hostname as a key.

This means that with the compulsory https:// prefix, values cannot be retrieved through containerregistry for any system where the credsStore is secretservice, and through its own mechanism of searching out credential stores, this adoption of secretstore as a container cannot be avoided if it is on the $PATH when docker login is invoked (or even if the secretstore credsStore were to be preferred). rules_docker is obviously afflicted through both its direct dependency on containerregistry, and the puller par binary.

Can the compulsory url prefix be dropped and pathways to stores that require the url (gcr as observed) be required to provide the format needed by the selected credsStore, making this an implementation detail?

flatten isn't published for v0.0.26

In an effort to use published artifacts (to fix an issue with @six dependencies for rules_docker), flatten.par has not been published.

https://storage.googleapis.com/containerregistry-releases/<release>/flatten.par is missing.

Is this expected? There are published artifacts available for:

  • appender.par
  • puller.par
  • pusher.par
  • importer.par

This would allow rules_docker to use the published artifacts, and not require all of the dependencies for containerregistry to be available to rules_docker and to the parent WORKSPACE.

containerregistry in Pypi

containerregistry v0 in Pypi is created by @mattmoor and containerregistry-ccwienk is created by @ccwienk, seem the containerregistry v0 is not updated, and containerregistry-ccwienk now v0.2.3. Any plan to update containerregistry or containerregistry-ccwienk?

But there are some strict requirements for containerregistry-ccwienk package as below.

httplib2<0.13,>=0.11.3
futures<3.2,>=3.1.1
six<1.13,>=1.9
rsa>=3.1.4

is that possiable to relax restrictions the requirements to get rid of the "<" as below

httplib2>=0.11.3
futures>=3.1.1
six>=1.9
rsa>=3.1.4

Ping failures should have clearer error messages.

If you have a simple typo in your registry domain (e.g. usr.gcr.io) then you'll get a cryptic callstack ending in a failure to self._Ping().

Given that the error message doesn't even print the domain, you basically need an expert to understand why this might be failing.

ValueError: Single '}' encountered in format string

Howdy,

INFO: Running command line: bazel-bin/cmd/translation/push_image

Traceback (most recent call last):
  File "/tmp/build/cache/execroot/__main__/bazel-out/k8-fastbuild/bin/cmd/translation/push_image.runfiles/__main__/../containerregistry/tools/fast_pusher_.py", line 141, in <module>
    main()
  File "/tmp/build/cache/execroot/__main__/bazel-out/k8-fastbuild/bin/cmd/translation/push_image.runfiles/__main__/../containerregistry/tools/fast_pusher_.py", line 96, in main
    name = Tag(args.name, args.stamp_info_file)
  File "/tmp/build/cache/execroot/__main__/bazel-out/k8-fastbuild/bin/cmd/translation/push_image.runfiles/__main__/../containerregistry/tools/fast_pusher_.py", line 78, in Tag
    formatted_name = name.format(**format_args)
ValueError: Single '}' encountered in format string
�[31m�[1mERROR: �[0mNon-zero return code '1' from command: Process exited with status 1

Googled my way to nowhere and thought I'd ask here, may be someone had or will get this. :)

I'll keep on trying and share any fix if I do find some.

Thanks for any help !

Pushes to gitlab fail with SSL error

from bazelbuild/rules_docker#820:

When attempting to run a docker push through a container_push rule to a registry.gitlab.com target, I am recieving the following error:
registry.gitlab.com/gauntletwizard/bazel-go:{BUILD_EMBED_LABEL} was resolved to registry.gitlab.com/gauntletwizard/bazel-go:foo
F0430 20:27:05.873595 4586 fast_pusher_.py:194] Error publishing registry.gitlab.com/gauntletwizard/bazel-go:foo: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:727)

Users report they have made no change to their dependencies. Error is coming from containerregistry libraries so posting here. My suspicion is something changed in registry.gitlab.com that makes it so that containerregistry is no longer being able to validate certificates.

Bearer token refreshing is broken if running as library with Python3

Context

I am using parts of the containerregistry coding as a Python library (i.e. I am not using the stand-alone version built with Bazel via CLI). Using code runs with Python3.6.

Problem

_Refresh class (client/v2_2/docker_http_.py) parses the result of responses returned by httplib2.Http.request invocations. Those appear to behave differently on Python2 and Python3, such that on Python2, an instance of str is returned, whereas on Python3, it is an instance of byte.

json.load however will not accept byte, as an input, so the code breaks when running in my usage scenario.

Workaround

I already created a Pull request that solves the problem in my usage context and is very probable to not break other scenerios: #116

Remove oauth2client

This library is deprecated:

https://google-auth.readthedocs.io/en/latest/oauth2client-deprecation.html

Its presence complicates use of containerregistry in Python3. Fortunately, this is the only import:

from oauth2client import client as oauth2client

And I can't even find a use of that import in docker_creds_. The only other reference in that file is a comment:

# WORKAROUND...
# The python oauth2client library only loads the credential from an
# on-disk cache the first time 'refresh()' is called, and doesn't
# actually 'Force a refresh of access_token' as advertised.
# This call will load the credential, and the call below will refresh
# it as needed. If the credential is unexpired, the call below will
# simply return a cache of this refresh.

Given that oauth2client doesn't seem to actually be used for anything and that removal would reduce the list of deps, simplify Python3 support, shall we?

Specify location of ~/.docker/config.json

The docker CLI has a --config flag that allows one to specify an alternate location for the docker config directory. I am primarily interested in implementing push/pull basic auth support in rules_docker bazelbuild/rules_docker#526 and would benefit from such functionality here.

As the pusher.par already has a --config flag for a different purpose, I am proposing a flag named --client_config or similar to configure the directory where the config.json file is. This new feature would largely affect https://github.com/google/containerregistry/blob/master/client/docker_creds_.py#L222-L228.

Any objections to this? Working on a PR now.

Fetching dependencies assumes python2

Specifically, see this line:

strip_prefix = "httplib2-0.10.3/python2/httplib2/",

The httplib2 package ships two separate trees for python 2/3 compat, and we are exclusively using the python2 tree. This means that if a user is trying to run this code under python 3, it will fail, since that tree is not compatible with python3.

I'm not sure the right answer here. In an ideal world, Bazel would expose the python version to us, and we could include the right tree based on that. Perhaps we could fix bazelbuild/rules_python#33 and then use pip to import these dependencies, rather than fetching the archives directly.

Alternatively, we could move from httplib2 to urllib (or the python 2/3 compatible version in six.moves.urllib), but it turns out that oauth2client also uses httplib2, so we'd still need it for transitive dependency resolution.

Until this is fixed, it is unlikely that python 3 users can use these rules.

Building puller is not reproduceable

The first time I run

bazel build -s --verbose_failures //:pusher.par

everything works fine
but the second time I run it there is an error

SUBCOMMAND: # //:pusher.par [action 'Building par file //:pusher.par']
(cd /tmp/bazel/output/execroot/containerregistry && \
  exec env - \
  bazel-out/host/bin/external/subpar/compiler/compiler.par --manifest_file bazel-out/k8-fastbuild/bin/pusher.par_SOURCES --outputpar bazel-out/k8-fastbuild/bin/pusher.par --stub_file bazel-out/k8-fastbuild/bin/pusher tools/fast_pusher_.py)
ERROR: /home/barriosj/containerregistry/BUILD.bazel:36:1: Building par file //:pusher.par failed (Exit 1): compiler.par failed: error executing command 
  (cd /tmp/bazel/output/execroot/containerregistry && \
  exec env - \
  bazel-out/host/bin/external/subpar/compiler/compiler.par --manifest_file bazel-out/k8-fastbuild/bin/pusher.par_SOURCES --outputpar bazel-out/k8-fastbuild/bin/pusher.par --stub_file bazel-out/k8-fastbuild/bin/pusher tools/fast_pusher_.py)

Use --sandbox_debug to see verbose messages from the sandbox
Traceback (most recent call last):
  File "/home/barriosj/miniconda2/lib/python2.7/runpy.py", line 174, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "/home/barriosj/miniconda2/lib/python2.7/runpy.py", line 72, in _run_code
    exec code in run_globals
  File "bazel-out/host/bin/external/subpar/compiler/compiler.par/__main__.py", line 28, in <module>
  File "bazel-out/host/bin/external/subpar/compiler/compiler.par/subpar/compiler/cli.py", line 114, in main
  File "bazel-out/host/bin/external/subpar/compiler/compiler.par/subpar/compiler/python_archive.py", line 113, in create
  File "bazel-out/host/bin/external/subpar/compiler/compiler.par/subpar/compiler/python_archive.py", line 250, in write_zip_data
  File "bazel-out/host/bin/external/subpar/compiler/compiler.par/subpar/compiler/stored_resource.py", line 51, in store
  File "/home/barriosj/miniconda2/lib/python2.7/zipfile.py", line 1126, in write
    st = os.stat(filename)
OSError: [Errno 2] No such file or directory: 'bazel-bin/pusher.runfiles/__init__.py'
Target //:pusher.par failed to build
INFO: Elapsed time: 0.396s, Critical Path: 0.22s
FAILED: Build did NOT complete successfully

image_digester.py fails to run on Python3-default systems

On python3-only systems, using image_digester.py fails with erorrs indicating that httplib2 tries to use an API that is Python 2-specific:

Traceback (most recent call last):
  File ".../execroot/__main__/bazel-out/host/bin/external/containerregistry/digester.runfiles/containerregistry/tools/image_digester_.py", line 28, in <module>
    from containerregistry.client.v2_2 import docker_image as v2_2_image
  File ".../execroot/__main__/bazel-out/host/bin/external/containerregistry/digester.runfiles/containerregistry/client/__init__.py", line 23, in <module>
    from containerregistry.client import docker_creds_
  File ".../execroot/__main__/bazel-out/host/bin/external/containerregistry/digester.runfiles/containerregistry/client/docker_creds_.py", line 31, in <module>
    import httplib2
  File ".../execroot/__main__/bazel-out/host/bin/external/containerregistry/digester.runfiles/httplib2/__init__.py", line 28, in <module>
    import email.FeedParser
ModuleNotFoundError: No module named 'email.FeedParser'

This is likely due to inclusion of a python2-specific httplib2:

strip_prefix = "httplib2-0.11.3/python2/httplib2/",

Why faaaaaast?

Trying to get my head around this repo. I've copied the usage output below for these three tools. Specifically, the importer.par says Import images from a tarball into our faaaaaast format..

Can someone explain what the "faaaaaast format" is, why it is fast, and what is that being measured relative to?

Thanks!

$  bazel run @containerregistry//:pusher.par -- --help
usage: pusher.par [-h] [--name NAME] [--tarball TARBALL] [--config CONFIG]
                  [--digest DIGEST] [--layer LAYER]
                  [--stamp-info-file STAMP_INFO_FILE] [--oci]
                  [--stderrthreshold STDERRTHRESHOLD]

Push images to a Docker Registry, faaaaaast.

optional arguments:
  -h, --help            show this help message and exit
  --name NAME           The name of the docker image to push.
  --tarball TARBALL     An optional legacy base image tarball.
  --config CONFIG       The path to the file storing the image config.
  --digest DIGEST       The list of layer digest filenames in order.
  --layer LAYER         The list of layer filenames in order.
  --stamp-info-file STAMP_INFO_FILE
                        A list of files from which to read substitutions to
                        make in the provided --name, e.g. {BUILD_USER}
  --oci                 Push the image with an OCI Manifest.
  --stderrthreshold STDERRTHRESHOLD
                        Write log events at or above this level to stderr.
$ bazel run @containerregistry//:puller.par -- --help
usage: puller.par [-h] [--name NAME] [--directory DIRECTORY]
                  [--stderrthreshold STDERRTHRESHOLD]

Pull images from a Docker Registry, faaaaast.

optional arguments:
  -h, --help            show this help message and exit
  --name NAME           The name of the docker image to pull and save.
                        Supports fully-qualified tag or digest references.
  --directory DIRECTORY
                        Where to save the image's files.
  --stderrthreshold STDERRTHRESHOLD
                        Write log events at or above this level to stderr.
$ bazel run @containerregistry//:importer.par -- --help
usage: importer.par [-h] [--tarball TARBALL] [--format {tar,tar.gz}]
                    [--directory DIRECTORY]
                    [--stderrthreshold STDERRTHRESHOLD]

Import images from a tarball into our faaaaaast format.

optional arguments:
  -h, --help            show this help message and exit
  --tarball TARBALL     The tarball containing the docker image to rewrite
                        into our fast on-disk format.
  --format {tar,tar.gz}
                        The form in which to save layers.
  --directory DIRECTORY
                        Where to save the image's files.
  --stderrthreshold STDERRTHRESHOLD
                        Write log events at or above this level to stderr.

Cache downloaded layers

Currently we are using bazel over GCB and we have a performance issue with rules_docker where images are downloaded on every invocation

We would like to solve this issue by introducing a cache mechanism to containerregistery puller. Downloaded layers will be stored and fetched from this cache. The cache will be by layer so different images will be able to reuse layers that were already downloaded from different images

Use of `glob(**)` in top-level BUILD.bazel breaks builds in dirty workspaces

(copied/moved from google/subpar#29)

BUILD.bazel contains this target:

py_library(
   name = "containerregistry",
   srcs = glob(["**/*.py"]),
   deps = [ ... ]   
)

In a clean workspace, that glob picks up only the intended Python sources. But the build creates bazel-* symlinks, which are then detected by the glob and break later builds:

$ bazel clean
$ bazel build :pusher.par
INFO: Found 1 target...
Target //:pusher.par up-to-date:
  bazel-bin/pusher.par
INFO: Elapsed time: 1.992s, Critical Path: 0.54s
$ bazel build :pusher.par
INFO: Found 1 target...
ERROR: /Users/jmillikin/src/containerregistry/BUILD.bazel:36:1: Building par file //:pusher.par failed: I/O exception during sandboxed execution: /private/var/tmp/_bazel_jmillikin/165d82f9b8e48612b4a588f395fdde11/execroot/containerregistry/bazel-bin (No such file or directory).
Target //:pusher.par failed to build
Use --verbose_failures to see the command lines of failed build steps.
INFO: Elapsed time: 0.503s, Critical Path: 0.06s

This can be fixed by using a more verbose glob over the directories you expect to exist:

py_library(
   name = "containerregistry",
   srcs = glob([
     "__init__.py",
     "client/**/*.py",
     "tools/**/*.py",
     "transform/**/*.py",
     "transport/**/*.py",
   ]), 

Another solution is to move the containerregistry target to a subdir, so it won't pick up stray build artifacts.

Puller Slowness

It took my local machine 24 minutes to pull a 1.3GB image from our private docker registry. Is this expected behavior?

Allow pushing when layers are from a remote location (RBE)

Hi,
I’m not a container expert so I might be way off but I’d really like to be able to use the pusher from a machine which doesn’t have the layers on it.

Context:
I’m using GCB+RBE+Bazel (rules_docker) to build and test our code.
We’re currently integrating publishing from rules_docker and its current assumption is that I’ve built locally and so the layers are available.
I’d like to be able to run the pusher on different machines and so parallelize publishing as well as be able to start before the build for the entire repo finishes.
ResultStore gives us the ability to poll a bazel build and know what’s ready as well as the URLs on RBE.

Is there a technical option of using the pusher with URLs from RBE?

cc @nlopezgi since he often has very wise insights :)

fast_puller_.py pulls the wrong image from GCR

I'm using the fast puller to pull an image from GCR, but according to the SHA256 in the digest file, it pulls the wrong image.

I used the fast puller as

$ python puller.par --name gcr.io/google-containers/debian-iptables@sha256:cd81b1a8f40149b5061735927d2a2cf4b90fc27a52fc4cc66889b373368b6ef6 --directory .

and

$ python puller.par --name gcr.io/google-containers/debian-iptables:v11.0 --directory .

This failure seems to occur for the tagged images in the same repository, where it ends up pulling the oldest image that was pushed on the same day. For both commands the digest file contains sha256:d4ff8136b9037694a3165a7fff6a91e7fc828741b8ea1eda226d4d9ea5d23abb

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.