From caee4a99c9cef1f62c48aabc8b39331a369a3408 Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Mon, 17 Oct 2016 09:53:40 -0400 Subject: [PATCH 01/11] Vendor containers/image and containers/storage Vendor updated containers/image and containers/storage, along with any new dependencies they drag in, and updated versions of other dependencies that happen to get pulled in. github.com/coreos/go-systemd/daemon/SdNotify() now takes a boolean to control whether or not it unsets the NOTIFY_SOCKET variable from the calling process's environment. Adapt. github.com/opencontainers/runtime-tools/generate/Generator.AddProcessEnv() now takes the environment variable name and value as two arguments, not one. Adapt. Signed-off-by: Nalin Dahyabhai --- cmd/ocid/daemon_linux.go | 2 +- hack/.vendor-helpers.sh | 12 +- hack/vendor.sh | 8 + server/container_create.go | 3 +- vendor/github.com/BurntSushi/toml/.travis.yml | 1 + vendor/github.com/BurntSushi/toml/COMPATIBLE | 1 + vendor/github.com/BurntSushi/toml/COPYING | 1 + vendor/github.com/BurntSushi/toml/Makefile | 1 + vendor/github.com/BurntSushi/toml/README.md | 9 +- .../toml/cmd/toml-test-decoder/COPYING | 1 + .../toml/cmd/toml-test-encoder/COPYING | 1 + .../BurntSushi/toml/cmd/tomlv/COPYING | 1 + .../containernetworking/cni/libcni/api.go | 3 + .../containernetworking/cni/libcni/conf.go | 3 +- .../cni/pkg/invoke/args.go | 3 + .../containernetworking/cni/pkg/ns/ns.go | 3 + .../cni/pkg/version/plugin.go | 3 + .../containers/image/copy/compression.go | 3 +- .../github.com/containers/image/copy/copy.go | 74 +-- .../image/directory/directory_dest.go | 10 +- .../image/directory/directory_src.go | 6 +- .../image/directory/directory_transport.go | 11 +- .../image/directory/explicitfilepath/path.go | 7 +- .../image/docker/daemon/daemon_dest.go | 26 +- .../image/docker/daemon/daemon_src.go | 30 +- .../image/docker/daemon/daemon_transport.go | 17 +- .../image/docker/daemon/daemon_types.go | 2 +- .../containers/image/docker/docker_client.go | 62 ++- .../containers/image/docker/docker_image.go | 3 +- .../image/docker/docker_image_dest.go | 41 +- .../image/docker/docker_image_src.go | 21 +- .../image/docker/docker_transport.go | 9 +- .../containers/image/docker/lookaside.go | 13 +- .../docker/policyconfiguration/naming.go | 8 +- .../image/docker/reference/reference.go | 35 +- .../containers/image/image/docker_list.go | 9 +- .../containers/image/image/docker_schema1.go | 27 +- .../containers/image/image/docker_schema2.go | 22 +- .../containers/image/image/manifest.go | 2 +- .../containers/image/image/memory.go | 8 +- .../github.com/containers/image/image/oci.go | 12 +- .../containers/image/image/unparsed.go | 7 +- .../containers/image/manifest/manifest.go | 2 +- .../containers/image/oci/layout/oci_dest.go | 18 +- .../containers/image/oci/layout/oci_src.go | 2 +- .../image/oci/layout/oci_transport.go | 20 +- .../image/openshift/openshift-copies.go | 36 +- .../containers/image/openshift/openshift.go | 14 +- .../image/openshift/openshift_transport.go | 11 +- .../containers/image/signature/docker.go | 2 +- .../containers/image/signature/mechanism.go | 3 - .../image/signature/policy_config.go | 5 +- .../containers/image/signature/policy_eval.go | 5 +- .../image/signature/policy_eval_signedby.go | 11 +- .../containers/image/signature/signature.go | 5 +- .../containers/image/storage/storage_image.go | 28 +- .../image/storage/storage_transport.go | 11 +- .../containers/image/transports/transports.go | 5 +- .../containers/image/types/types.go | 5 +- .../storage/pkg/devicemapper/devmapper_log.go | 6 +- .../pkg/devicemapper/devmapper_wrapper.go | 4 +- .../storage/storageversion/version_lib.go | 2 +- .../coreos/go-systemd/daemon/sdnotify.go | 27 +- .../coreos/go-systemd/daemon/watchdog.go | 72 +++ .../docker/docker/pkg/stringid/stringid.go | 14 +- .../docker/pkg/stringutils/stringutils.go | 2 +- .../docker/pkg/truncindex/truncindex.go | 12 +- vendor/github.com/mtrmac/gpgme/gpgme.go | 10 +- .../opencontainers/go-digest/.mailmap | 1 + .../opencontainers/go-digest/.pullapprove.yml | 12 + .../opencontainers/go-digest/.travis.yml | 4 + .../opencontainers/go-digest/CONTRIBUTING.md | 72 +++ .../opencontainers/go-digest/LICENSE.code | 191 ++++++++ .../opencontainers/go-digest/LICENSE.docs | 425 ++++++++++++++++++ .../opencontainers/go-digest/MAINTAINERS | 9 + .../opencontainers/go-digest/README.md | 104 +++++ .../opencontainers/go-digest/algorithm.go | 144 ++++++ .../opencontainers/go-digest/digest.go | 140 ++++++ .../opencontainers/go-digest/digester.go | 25 ++ .../opencontainers/go-digest/doc.go | 42 ++ .../opencontainers/go-digest/verifiers.go | 31 ++ .../image-spec/specs-go/v1/config.go | 12 +- .../image-spec/specs-go/v1/layout.go | 3 + .../image-spec/specs-go/version.go | 2 +- .../runtime-tools/generate/generate.go | 23 +- vendor/github.com/pborman/uuid/README.md | 2 +- vendor/github.com/pborman/uuid/json.go | 34 -- vendor/github.com/pborman/uuid/marshal.go | 83 ++++ vendor/github.com/pkg/errors/.gitignore | 24 + vendor/github.com/pkg/errors/.travis.yml | 11 + vendor/github.com/pkg/errors/LICENSE | 23 + vendor/github.com/pkg/errors/README.md | 52 +++ vendor/github.com/pkg/errors/appveyor.yml | 32 ++ vendor/github.com/pkg/errors/errors.go | 269 +++++++++++ vendor/github.com/pkg/errors/stack.go | 178 ++++++++ vendor/github.com/rajatchopra/ocicni/LICENSE | 191 ++++++++ vendor/github.com/rajatchopra/ocicni/noop.go | 2 - .../github.com/rajatchopra/ocicni/ocicni.go | 40 +- vendor/github.com/rajatchopra/ocicni/types.go | 1 - vendor/github.com/rajatchopra/ocicni/util.go | 5 +- 100 files changed, 2636 insertions(+), 404 deletions(-) create mode 100644 vendor/github.com/coreos/go-systemd/daemon/watchdog.go create mode 100644 vendor/github.com/opencontainers/go-digest/.mailmap create mode 100644 vendor/github.com/opencontainers/go-digest/.pullapprove.yml create mode 100644 vendor/github.com/opencontainers/go-digest/.travis.yml create mode 100644 vendor/github.com/opencontainers/go-digest/CONTRIBUTING.md create mode 100644 vendor/github.com/opencontainers/go-digest/LICENSE.code create mode 100644 vendor/github.com/opencontainers/go-digest/LICENSE.docs create mode 100644 vendor/github.com/opencontainers/go-digest/MAINTAINERS create mode 100644 vendor/github.com/opencontainers/go-digest/README.md create mode 100644 vendor/github.com/opencontainers/go-digest/algorithm.go create mode 100644 vendor/github.com/opencontainers/go-digest/digest.go create mode 100644 vendor/github.com/opencontainers/go-digest/digester.go create mode 100644 vendor/github.com/opencontainers/go-digest/doc.go create mode 100644 vendor/github.com/opencontainers/go-digest/verifiers.go delete mode 100644 vendor/github.com/pborman/uuid/json.go create mode 100644 vendor/github.com/pborman/uuid/marshal.go create mode 100644 vendor/github.com/pkg/errors/.gitignore create mode 100644 vendor/github.com/pkg/errors/.travis.yml create mode 100644 vendor/github.com/pkg/errors/LICENSE create mode 100644 vendor/github.com/pkg/errors/README.md create mode 100644 vendor/github.com/pkg/errors/appveyor.yml create mode 100644 vendor/github.com/pkg/errors/errors.go create mode 100644 vendor/github.com/pkg/errors/stack.go create mode 100644 vendor/github.com/rajatchopra/ocicni/LICENSE diff --git a/cmd/ocid/daemon_linux.go b/cmd/ocid/daemon_linux.go index 8667bd0e..94ecb61f 100644 --- a/cmd/ocid/daemon_linux.go +++ b/cmd/ocid/daemon_linux.go @@ -8,7 +8,7 @@ import ( ) func sdNotify() { - if _, err := systemdDaemon.SdNotify("READY=1"); err != nil { + if _, err := systemdDaemon.SdNotify(true, "READY=1"); err != nil { logrus.Warnf("Failed to sd_notify systemd: %v", err) } } diff --git a/hack/.vendor-helpers.sh b/hack/.vendor-helpers.sh index 6227f24e..895ad9c1 100644 --- a/hack/.vendor-helpers.sh +++ b/hack/.vendor-helpers.sh @@ -60,7 +60,7 @@ clean() { local packages=($(GOPATH=$original_GOPATH go list -e ./... | grep -v "^${PROJECT}/vendor")) local platforms=( linux/amd64 linux/386 ) - local buildTags=( seccomp ) + local buildTagSets=( seccomp ) echo @@ -68,10 +68,12 @@ clean() { local IFS=$'\n' local imports=( $( for platform in "${platforms[@]}"; do - export GOOS="${platform%/*}"; - export GOARCH="${platform##*/}"; - go list -e -tags "$buildTags" -f '{{join .Deps "\n"}}' "${packages[@]}" - go list -e -tags "$buildTags" -f '{{join .TestImports "\n"}}' "${packages[@]}" + for buildTags in "" "${buildTagSets[@]}"; do + export GOOS="${platform%/*}"; + export GOARCH="${platform##*/}"; + go list -e -tags "$buildTags" -f '{{join .Deps "\n"}}' "${packages[@]}" + go list -e -tags "$buildTags" -f '{{join .TestImports "\n"}}' "${packages[@]}" + done done | grep -vE "^${PROJECT}" | sort -u ) ) # .TestImports does not include indirect dependencies, so do one more iteration. diff --git a/hack/vendor.sh b/hack/vendor.sh index db4a67cb..3fbf3f51 100755 --- a/hack/vendor.sh +++ b/hack/vendor.sh @@ -89,5 +89,13 @@ clone git github.com/gogo/protobuf 43a2e0b1c32252bfbbdf81f7faa7a88fb3fa4028 clone git github.com/gorilla/context v1.1 clone git golang.org/x/sys 8f0908ab3b2457e2e15403d3697c9ef5cb4b57a9 https://github.com/golang/sys.git clone git github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0 +clone git github.com/mistifyio/go-zfs master +clone git github.com/pborman/uuid master +clone git github.com/mtrmac/gpgme master +clone git gopkg.in/cheggaaa/pb.v1 master +clone git github.com/mattn/go-runewidth master +clone git github.com/docker/engine-api v0.4.0 +clone git github.com/pkg/errors master +clone git github.com/opencontainers/go-digest master clean diff --git a/server/container_create.go b/server/container_create.go index ccdc224a..bd219198 100644 --- a/server/container_create.go +++ b/server/container_create.go @@ -151,8 +151,7 @@ func (s *Server) createSandboxContainer(containerID string, containerName string if key == "" { continue } - env := fmt.Sprintf("%s=%s", key, value) - specgen.AddProcessEnv(env) + specgen.AddProcessEnv(key, value) } } diff --git a/vendor/github.com/BurntSushi/toml/.travis.yml b/vendor/github.com/BurntSushi/toml/.travis.yml index a4308ae4..43caf6d0 100644 --- a/vendor/github.com/BurntSushi/toml/.travis.yml +++ b/vendor/github.com/BurntSushi/toml/.travis.yml @@ -9,3 +9,4 @@ install: script: - export PATH="$PATH:$HOME/gopath/bin" - make test + diff --git a/vendor/github.com/BurntSushi/toml/COMPATIBLE b/vendor/github.com/BurntSushi/toml/COMPATIBLE index 94cb969a..21e0938c 100644 --- a/vendor/github.com/BurntSushi/toml/COMPATIBLE +++ b/vendor/github.com/BurntSushi/toml/COMPATIBLE @@ -1,2 +1,3 @@ Compatible with TOML version [v0.2.0](https://github.com/mojombo/toml/blob/master/versions/toml-v0.2.0.md) + diff --git a/vendor/github.com/BurntSushi/toml/COPYING b/vendor/github.com/BurntSushi/toml/COPYING index 5c93f456..5a8e3325 100644 --- a/vendor/github.com/BurntSushi/toml/COPYING +++ b/vendor/github.com/BurntSushi/toml/COPYING @@ -11,3 +11,4 @@ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. You just DO WHAT THE FUCK YOU WANT TO. + diff --git a/vendor/github.com/BurntSushi/toml/Makefile b/vendor/github.com/BurntSushi/toml/Makefile index 36961c91..3600848d 100644 --- a/vendor/github.com/BurntSushi/toml/Makefile +++ b/vendor/github.com/BurntSushi/toml/Makefile @@ -16,3 +16,4 @@ tags: push: git push origin master git push github master + diff --git a/vendor/github.com/BurntSushi/toml/README.md b/vendor/github.com/BurntSushi/toml/README.md index c6afff0e..5a5df637 100644 --- a/vendor/github.com/BurntSushi/toml/README.md +++ b/vendor/github.com/BurntSushi/toml/README.md @@ -1,9 +1,9 @@ ## TOML parser and encoder for Go with reflection TOML stands for Tom's Obvious, Minimal Language. This Go package provides a -reflection interface similar to Go's standard library `json` and `xml` +reflection interface similar to Go's standard library `json` and `xml` packages. This package also supports the `encoding.TextUnmarshaler` and -`encoding.TextMarshaler` interfaces so that you can define custom data +`encoding.TextMarshaler` interfaces so that you can define custom data representations. (There is an example of this below.) Spec: https://github.com/mojombo/toml @@ -87,7 +87,7 @@ type TOML struct { ### Using the `encoding.TextUnmarshaler` interface -Here's an example that automatically parses duration strings into +Here's an example that automatically parses duration strings into `time.Duration` values: ```toml @@ -120,7 +120,7 @@ for _, s := range favorites.Song { } ``` -And you'll also need a `duration` type that satisfies the +And you'll also need a `duration` type that satisfies the `encoding.TextUnmarshaler` interface: ```go @@ -217,3 +217,4 @@ Note that a case insensitive match will be tried if an exact match can't be found. A working example of the above can be found in `_examples/example.{go,toml}`. + diff --git a/vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING b/vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING index 5c93f456..5a8e3325 100644 --- a/vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING +++ b/vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING @@ -11,3 +11,4 @@ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. You just DO WHAT THE FUCK YOU WANT TO. + diff --git a/vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING b/vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING index 5c93f456..5a8e3325 100644 --- a/vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING +++ b/vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING @@ -11,3 +11,4 @@ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. You just DO WHAT THE FUCK YOU WANT TO. + diff --git a/vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING b/vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING index 5c93f456..5a8e3325 100644 --- a/vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING +++ b/vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING @@ -11,3 +11,4 @@ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. You just DO WHAT THE FUCK YOU WANT TO. + diff --git a/vendor/github.com/containernetworking/cni/libcni/api.go b/vendor/github.com/containernetworking/cni/libcni/api.go index 8ba78b26..dfc30cab 100644 --- a/vendor/github.com/containernetworking/cni/libcni/api.go +++ b/vendor/github.com/containernetworking/cni/libcni/api.go @@ -43,6 +43,9 @@ type CNIConfig struct { Path []string } +// CNIConfig implements the CNI interface +var _ CNI = &CNIConfig{} + // AddNetwork executes the plugin with the ADD command func (c *CNIConfig) AddNetwork(net *NetworkConfig, rt *RuntimeConf) (*types.Result, error) { pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path) diff --git a/vendor/github.com/containernetworking/cni/libcni/conf.go b/vendor/github.com/containernetworking/cni/libcni/conf.go index 984b1592..708686e6 100644 --- a/vendor/github.com/containernetworking/cni/libcni/conf.go +++ b/vendor/github.com/containernetworking/cni/libcni/conf.go @@ -55,7 +55,8 @@ func ConfFiles(dir string) ([]string, error) { if f.IsDir() { continue } - if filepath.Ext(f.Name()) == ".conf" { + fileExt := filepath.Ext(f.Name()) + if fileExt == ".conf" || fileExt == ".json" { confFiles = append(confFiles, filepath.Join(dir, f.Name())) } } diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/args.go b/vendor/github.com/containernetworking/cni/pkg/invoke/args.go index be28ba62..ba9d0c3b 100644 --- a/vendor/github.com/containernetworking/cni/pkg/invoke/args.go +++ b/vendor/github.com/containernetworking/cni/pkg/invoke/args.go @@ -47,6 +47,9 @@ type Args struct { Path string } +// Args implements the CNIArgs interface +var _ CNIArgs = &Args{} + func (args *Args) AsEnv() []string { env := os.Environ() pluginArgsStr := args.PluginArgsStr diff --git a/vendor/github.com/containernetworking/cni/pkg/ns/ns.go b/vendor/github.com/containernetworking/cni/pkg/ns/ns.go index 3246ebf3..220dd694 100644 --- a/vendor/github.com/containernetworking/cni/pkg/ns/ns.go +++ b/vendor/github.com/containernetworking/cni/pkg/ns/ns.go @@ -62,6 +62,9 @@ type netNS struct { closed bool } +// netNS implements the NetNS interface +var _ NetNS = &netNS{} + func getCurrentThreadNetNSPath() string { // /proc/self/ns/net returns the namespace of the main thread, not // of whatever thread this goroutine is running on. Make sure we diff --git a/vendor/github.com/containernetworking/cni/pkg/version/plugin.go b/vendor/github.com/containernetworking/cni/pkg/version/plugin.go index dc937b54..8a467281 100644 --- a/vendor/github.com/containernetworking/cni/pkg/version/plugin.go +++ b/vendor/github.com/containernetworking/cni/pkg/version/plugin.go @@ -36,6 +36,9 @@ type pluginInfo struct { SupportedVersions_ []string `json:"supportedVersions,omitempty"` } +// pluginInfo implements the PluginInfo interface +var _ PluginInfo = &pluginInfo{} + func (p *pluginInfo) Encode(w io.Writer) error { return json.NewEncoder(w).Encode(p) } diff --git a/vendor/github.com/containers/image/copy/compression.go b/vendor/github.com/containers/image/copy/compression.go index 820c7e99..03514b48 100644 --- a/vendor/github.com/containers/image/copy/compression.go +++ b/vendor/github.com/containers/image/copy/compression.go @@ -4,9 +4,10 @@ import ( "bytes" "compress/bzip2" "compress/gzip" - "errors" "io" + "github.com/pkg/errors" + "github.com/Sirupsen/logrus" ) diff --git a/vendor/github.com/containers/image/copy/copy.go b/vendor/github.com/containers/image/copy/copy.go index 555e378d..d27e634c 100644 --- a/vendor/github.com/containers/image/copy/copy.go +++ b/vendor/github.com/containers/image/copy/copy.go @@ -3,7 +3,6 @@ package copy import ( "bytes" "compress/gzip" - "errors" "fmt" "io" "io/ioutil" @@ -17,7 +16,8 @@ import ( "github.com/containers/image/signature" "github.com/containers/image/transports" "github.com/containers/image/types" - "github.com/docker/distribution/digest" + "github.com/opencontainers/go-digest" + "github.com/pkg/errors" ) // preferredManifestMIMETypes lists manifest MIME types in order of our preference, if we can't use the original manifest and need to convert. @@ -51,15 +51,15 @@ type imageCopier struct { // and set validationFailed to true if the source stream does not match expectedDigest. func newDigestingReader(source io.Reader, expectedDigest digest.Digest) (*digestingReader, error) { if err := expectedDigest.Validate(); err != nil { - return nil, fmt.Errorf("Invalid digest specification %s", expectedDigest) + return nil, errors.Errorf("Invalid digest specification %s", expectedDigest) } digestAlgorithm := expectedDigest.Algorithm() if !digestAlgorithm.Available() { - return nil, fmt.Errorf("Invalid digest specification %s: unsupported digest algorithm %s", expectedDigest, digestAlgorithm) + return nil, errors.Errorf("Invalid digest specification %s: unsupported digest algorithm %s", expectedDigest, digestAlgorithm) } return &digestingReader{ source: source, - digester: digestAlgorithm.New(), + digester: digestAlgorithm.Digester(), expectedDigest: expectedDigest, validationFailed: false, }, nil @@ -72,14 +72,14 @@ func (d *digestingReader) Read(p []byte) (int, error) { // Coverage: This should not happen, the hash.Hash interface requires // d.digest.Write to never return an error, and the io.Writer interface // requires n2 == len(input) if no error is returned. - return 0, fmt.Errorf("Error updating digest during verification: %d vs. %d, %v", n2, n, err) + return 0, errors.Wrapf(err, "Error updating digest during verification: %d vs. %d", n2, n) } } if err == io.EOF { actualDigest := d.digester.Digest() if actualDigest != d.expectedDigest { d.validationFailed = true - return 0, fmt.Errorf("Digest did not match, expected %s, got %s", d.expectedDigest, actualDigest) + return 0, errors.Errorf("Digest did not match, expected %s, got %s", d.expectedDigest, actualDigest) } } return n, err @@ -106,14 +106,14 @@ func Image(policyContext *signature.PolicyContext, destRef, srcRef types.ImageRe dest, err := destRef.NewImageDestination(options.DestinationCtx) if err != nil { - return fmt.Errorf("Error initializing destination %s: %v", transports.ImageName(destRef), err) + return errors.Wrapf(err, "Error initializing destination %s", transports.ImageName(destRef)) } defer dest.Close() destSupportedManifestMIMETypes := dest.SupportedManifestMIMETypes() rawSource, err := srcRef.NewImageSource(options.SourceCtx, destSupportedManifestMIMETypes) if err != nil { - return fmt.Errorf("Error initializing source %s: %v", transports.ImageName(srcRef), err) + return errors.Wrapf(err, "Error initializing source %s", transports.ImageName(srcRef)) } unparsedImage := image.UnparsedFromSource(rawSource) defer func() { @@ -124,17 +124,17 @@ func Image(policyContext *signature.PolicyContext, destRef, srcRef types.ImageRe // Please keep this policy check BEFORE reading any other information about the image. if allowed, err := policyContext.IsRunningImageAllowed(unparsedImage); !allowed || err != nil { // Be paranoid and fail if either return value indicates so. - return fmt.Errorf("Source image rejected: %v", err) + return errors.Wrap(err, "Source image rejected") } src, err := image.FromUnparsedImage(unparsedImage) if err != nil { - return fmt.Errorf("Error initializing image from source %s: %v", transports.ImageName(srcRef), err) + return errors.Wrapf(err, "Error initializing image from source %s", transports.ImageName(srcRef)) } unparsedImage = nil defer src.Close() if src.IsMultiImage() { - return fmt.Errorf("can not copy %s: manifest contains multiple images", transports.ImageName(srcRef)) + return errors.Errorf("can not copy %s: manifest contains multiple images", transports.ImageName(srcRef)) } var sigs [][]byte @@ -144,14 +144,14 @@ func Image(policyContext *signature.PolicyContext, destRef, srcRef types.ImageRe writeReport("Getting image source signatures\n") s, err := src.Signatures() if err != nil { - return fmt.Errorf("Error reading signatures: %v", err) + return errors.Wrap(err, "Error reading signatures") } sigs = s } if len(sigs) != 0 { writeReport("Checking if image destination supports signatures\n") if err := dest.SupportsSignatures(); err != nil { - return fmt.Errorf("Can not copy signatures: %v", err) + return errors.Wrap(err, "Can not copy signatures") } } @@ -182,17 +182,17 @@ func Image(policyContext *signature.PolicyContext, destRef, srcRef types.ImageRe pendingImage := src if !reflect.DeepEqual(manifestUpdates, types.ManifestUpdateOptions{InformationOnly: manifestUpdates.InformationOnly}) { if !canModifyManifest { - return fmt.Errorf("Internal error: copy needs an updated manifest but that was known to be forbidden") + return errors.Errorf("Internal error: copy needs an updated manifest but that was known to be forbidden") } manifestUpdates.InformationOnly.Destination = dest pendingImage, err = src.UpdatedImage(manifestUpdates) if err != nil { - return fmt.Errorf("Error creating an updated image manifest: %v", err) + return errors.Wrap(err, "Error creating an updated image manifest") } } manifest, _, err := pendingImage.Manifest() if err != nil { - return fmt.Errorf("Error reading manifest: %v", err) + return errors.Wrap(err, "Error reading manifest") } if err := ic.copyConfig(pendingImage); err != nil { @@ -202,33 +202,33 @@ func Image(policyContext *signature.PolicyContext, destRef, srcRef types.ImageRe if options != nil && options.SignBy != "" { mech, err := signature.NewGPGSigningMechanism() if err != nil { - return fmt.Errorf("Error initializing GPG: %v", err) + return errors.Wrap(err, "Error initializing GPG") } dockerReference := dest.Reference().DockerReference() if dockerReference == nil { - return fmt.Errorf("Cannot determine canonical Docker reference for destination %s", transports.ImageName(dest.Reference())) + return errors.Errorf("Cannot determine canonical Docker reference for destination %s", transports.ImageName(dest.Reference())) } writeReport("Signing manifest\n") newSig, err := signature.SignDockerManifest(manifest, dockerReference.String(), mech, options.SignBy) if err != nil { - return fmt.Errorf("Error creating signature: %v", err) + return errors.Wrap(err, "Error creating signature") } sigs = append(sigs, newSig) } writeReport("Writing manifest to image destination\n") if err := dest.PutManifest(manifest); err != nil { - return fmt.Errorf("Error writing manifest: %v", err) + return errors.Wrap(err, "Error writing manifest") } writeReport("Storing signatures\n") if err := dest.PutSignatures(sigs); err != nil { - return fmt.Errorf("Error writing signatures: %v", err) + return errors.Wrap(err, "Error writing signatures") } if err := dest.Commit(); err != nil { - return fmt.Errorf("Error committing the finished image: %v", err) + return errors.Wrap(err, "Error committing the finished image") } return nil @@ -293,14 +293,14 @@ func (ic *imageCopier) copyConfig(src types.Image) error { fmt.Fprintf(ic.reportWriter, "Copying config %s\n", srcInfo.Digest) configBlob, err := src.ConfigBlob() if err != nil { - return fmt.Errorf("Error reading config blob %s: %v", srcInfo.Digest, err) + return errors.Wrapf(err, "Error reading config blob %s", srcInfo.Digest) } destInfo, err := ic.copyBlobFromStream(bytes.NewReader(configBlob), srcInfo, nil, false) if err != nil { return err } if destInfo.Digest != srcInfo.Digest { - return fmt.Errorf("Internal error: copying uncompressed config blob %s changed digest to %s", srcInfo.Digest, destInfo.Digest) + return errors.Errorf("Internal error: copying uncompressed config blob %s changed digest to %s", srcInfo.Digest, destInfo.Digest) } } return nil @@ -319,7 +319,7 @@ func (ic *imageCopier) copyLayer(srcInfo types.BlobInfo) (types.BlobInfo, digest // Check if we already have a blob with this digest haveBlob, extantBlobSize, err := ic.dest.HasBlob(srcInfo) if err != nil && err != types.ErrBlobNotFound { - return types.BlobInfo{}, "", fmt.Errorf("Error checking for blob %s at destination: %v", srcInfo.Digest, err) + return types.BlobInfo{}, "", errors.Wrapf(err, "Error checking for blob %s at destination", srcInfo.Digest) } // If we already have a cached diffID for this blob, we don't need to compute it diffIDIsNeeded := ic.diffIDsAreNeeded && (ic.cachedDiffIDs[srcInfo.Digest] == "") @@ -327,13 +327,13 @@ func (ic *imageCopier) copyLayer(srcInfo types.BlobInfo) (types.BlobInfo, digest if haveBlob && !diffIDIsNeeded { // Check the blob sizes match, if we were given a size this time if srcInfo.Size != -1 && srcInfo.Size != extantBlobSize { - return types.BlobInfo{}, "", fmt.Errorf("Error: blob %s is already present, but with size %d instead of %d", srcInfo.Digest, extantBlobSize, srcInfo.Size) + return types.BlobInfo{}, "", errors.Errorf("Error: blob %s is already present, but with size %d instead of %d", srcInfo.Digest, extantBlobSize, srcInfo.Size) } srcInfo.Size = extantBlobSize // Tell the image destination that this blob's delta is being applied again. For some image destinations, this can be faster than using GetBlob/PutBlob blobinfo, err := ic.dest.ReapplyBlob(srcInfo) if err != nil { - return types.BlobInfo{}, "", fmt.Errorf("Error reapplying blob %s at destination: %v", srcInfo.Digest, err) + return types.BlobInfo{}, "", errors.Wrapf(err, "Error reapplying blob %s at destination", srcInfo.Digest) } fmt.Fprintf(ic.reportWriter, "Skipping fetch of repeat blob %s\n", srcInfo.Digest) return blobinfo, ic.cachedDiffIDs[srcInfo.Digest], err @@ -343,7 +343,7 @@ func (ic *imageCopier) copyLayer(srcInfo types.BlobInfo) (types.BlobInfo, digest fmt.Fprintf(ic.reportWriter, "Copying blob %s\n", srcInfo.Digest) srcStream, srcBlobSize, err := ic.rawSource.GetBlob(srcInfo) if err != nil { - return types.BlobInfo{}, "", fmt.Errorf("Error reading blob %s: %v", srcInfo.Digest, err) + return types.BlobInfo{}, "", errors.Wrapf(err, "Error reading blob %s", srcInfo.Digest) } defer srcStream.Close() @@ -356,7 +356,7 @@ func (ic *imageCopier) copyLayer(srcInfo types.BlobInfo) (types.BlobInfo, digest if diffIDIsNeeded { diffIDResult = <-diffIDChan if diffIDResult.err != nil { - return types.BlobInfo{}, "", fmt.Errorf("Error computing layer DiffID: %v", diffIDResult.err) + return types.BlobInfo{}, "", errors.Wrap(diffIDResult.err, "Error computing layer DiffID") } logrus.Debugf("Computed DiffID %s for layer %s", diffIDResult.digest, srcInfo.Digest) ic.cachedDiffIDs[srcInfo.Digest] = diffIDResult.digest @@ -441,7 +441,7 @@ func (ic *imageCopier) copyBlobFromStream(srcStream io.Reader, srcInfo types.Blo // read stream to the end, and validation does not happen. digestingReader, err := newDigestingReader(srcStream, srcInfo.Digest) if err != nil { - return types.BlobInfo{}, fmt.Errorf("Error preparing to verify blob %s: %v", srcInfo.Digest, err) + return types.BlobInfo{}, errors.Wrapf(err, "Error preparing to verify blob %s", srcInfo.Digest) } var destStream io.Reader = digestingReader @@ -449,7 +449,7 @@ func (ic *imageCopier) copyBlobFromStream(srcStream io.Reader, srcInfo types.Blo // This requires us to “peek ahead” into the stream to read the initial part, which requires us to chain through another io.Reader returned by detectCompression. decompressor, destStream, err := detectCompression(destStream) // We could skip this in some cases, but let's keep the code path uniform if err != nil { - return types.BlobInfo{}, fmt.Errorf("Error reading blob %s: %v", srcInfo.Digest, err) + return types.BlobInfo{}, errors.Wrapf(err, "Error reading blob %s", srcInfo.Digest) } isCompressed := decompressor != nil @@ -492,7 +492,7 @@ func (ic *imageCopier) copyBlobFromStream(srcStream io.Reader, srcInfo types.Blo // === Finally, send the layer stream to dest. uploadedInfo, err := ic.dest.PutBlob(destStream, inputInfo) if err != nil { - return types.BlobInfo{}, fmt.Errorf("Error writing blob: %v", err) + return types.BlobInfo{}, errors.Wrap(err, "Error writing blob") } // This is fairly horrible: the writer from getOriginalLayerCopyWriter wants to consumer @@ -503,15 +503,15 @@ func (ic *imageCopier) copyBlobFromStream(srcStream io.Reader, srcInfo types.Blo logrus.Debugf("Consuming rest of the original blob to satisfy getOriginalLayerCopyWriter") _, err := io.Copy(ioutil.Discard, originalLayerReader) if err != nil { - return types.BlobInfo{}, fmt.Errorf("Error reading input blob %s: %v", srcInfo.Digest, err) + return types.BlobInfo{}, errors.Wrapf(err, "Error reading input blob %s", srcInfo.Digest) } } if digestingReader.validationFailed { // Coverage: This should never happen. - return types.BlobInfo{}, fmt.Errorf("Internal error writing blob %s, digest verification failed but was ignored", srcInfo.Digest) + return types.BlobInfo{}, errors.Errorf("Internal error writing blob %s, digest verification failed but was ignored", srcInfo.Digest) } if inputInfo.Digest != "" && uploadedInfo.Digest != inputInfo.Digest { - return types.BlobInfo{}, fmt.Errorf("Internal error writing blob %s, blob with digest %s saved with digest %s", srcInfo.Digest, inputInfo.Digest, uploadedInfo.Digest) + return types.BlobInfo{}, errors.Errorf("Internal error writing blob %s, blob with digest %s saved with digest %s", srcInfo.Digest, inputInfo.Digest, uploadedInfo.Digest) } return uploadedInfo, nil } @@ -542,7 +542,7 @@ func determineManifestConversion(manifestUpdates *types.ManifestUpdateOptions, s _, srcType, err := src.Manifest() if err != nil { // This should have been cached?! - return fmt.Errorf("Error reading manifest: %v", err) + return errors.Wrap(err, "Error reading manifest") } if _, ok := supportedByDest[srcType]; ok { logrus.Debugf("Manifest MIME type %s is declared supported by the destination", srcType) diff --git a/vendor/github.com/containers/image/directory/directory_dest.go b/vendor/github.com/containers/image/directory/directory_dest.go index 2a8d102e..e0b0fe45 100644 --- a/vendor/github.com/containers/image/directory/directory_dest.go +++ b/vendor/github.com/containers/image/directory/directory_dest.go @@ -1,13 +1,13 @@ package directory import ( - "fmt" "io" "io/ioutil" "os" "github.com/containers/image/types" - "github.com/docker/distribution/digest" + "github.com/opencontainers/go-digest" + "github.com/pkg/errors" ) type dirImageDestination struct { @@ -69,7 +69,7 @@ func (d *dirImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobInfo } }() - digester := digest.Canonical.New() + digester := digest.Canonical.Digester() tee := io.TeeReader(stream, digester.Hash()) size, err := io.Copy(blobFile, tee) @@ -78,7 +78,7 @@ func (d *dirImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobInfo } computedDigest := digester.Digest() if inputInfo.Size != -1 && size != inputInfo.Size { - return types.BlobInfo{}, fmt.Errorf("Size mismatch when copying %s, expected %d, got %d", computedDigest, inputInfo.Size, size) + return types.BlobInfo{}, errors.Errorf("Size mismatch when copying %s, expected %d, got %d", computedDigest, inputInfo.Size, size) } if err := blobFile.Sync(); err != nil { return types.BlobInfo{}, err @@ -96,7 +96,7 @@ func (d *dirImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobInfo func (d *dirImageDestination) HasBlob(info types.BlobInfo) (bool, int64, error) { if info.Digest == "" { - return false, -1, fmt.Errorf(`"Can not check for a blob with unknown digest`) + return false, -1, errors.Errorf(`"Can not check for a blob with unknown digest`) } blobPath := d.ref.layerPath(info.Digest) finfo, err := os.Stat(blobPath) diff --git a/vendor/github.com/containers/image/directory/directory_src.go b/vendor/github.com/containers/image/directory/directory_src.go index 8500128d..8053c189 100644 --- a/vendor/github.com/containers/image/directory/directory_src.go +++ b/vendor/github.com/containers/image/directory/directory_src.go @@ -1,14 +1,14 @@ package directory import ( - "fmt" "io" "io/ioutil" "os" "github.com/containers/image/manifest" "github.com/containers/image/types" - "github.com/docker/distribution/digest" + "github.com/opencontainers/go-digest" + "github.com/pkg/errors" ) type dirImageSource struct { @@ -42,7 +42,7 @@ func (s *dirImageSource) GetManifest() ([]byte, string, error) { } func (s *dirImageSource) GetTargetManifest(digest digest.Digest) ([]byte, string, error) { - return nil, "", fmt.Errorf(`Getting target manifest not supported by "dir:"`) + return nil, "", errors.Errorf(`Getting target manifest not supported by "dir:"`) } // GetBlob returns a stream for the specified blob, and the blob’s size (or -1 if unknown). diff --git a/vendor/github.com/containers/image/directory/directory_transport.go b/vendor/github.com/containers/image/directory/directory_transport.go index 7a728e09..89d2565a 100644 --- a/vendor/github.com/containers/image/directory/directory_transport.go +++ b/vendor/github.com/containers/image/directory/directory_transport.go @@ -1,16 +1,17 @@ package directory import ( - "errors" "fmt" "path/filepath" "strings" + "github.com/pkg/errors" + "github.com/containers/image/directory/explicitfilepath" "github.com/containers/image/docker/reference" "github.com/containers/image/image" "github.com/containers/image/types" - "github.com/docker/distribution/digest" + "github.com/opencontainers/go-digest" ) // Transport is an ImageTransport for directory paths. @@ -33,7 +34,7 @@ func (t dirTransport) ParseReference(reference string) (types.ImageReference, er // scope passed to this function will not be "", that value is always allowed. func (t dirTransport) ValidatePolicyConfigurationScope(scope string) error { if !strings.HasPrefix(scope, "/") { - return fmt.Errorf("Invalid scope %s: Must be an absolute path", scope) + return errors.Errorf("Invalid scope %s: Must be an absolute path", scope) } // Refuse also "/", otherwise "/" and "" would have the same semantics, // and "" could be unexpectedly shadowed by the "/" entry. @@ -42,7 +43,7 @@ func (t dirTransport) ValidatePolicyConfigurationScope(scope string) error { } cleaned := filepath.Clean(scope) if cleaned != scope { - return fmt.Errorf(`Invalid scope %s: Uses non-canonical format, perhaps try %s`, scope, cleaned) + return errors.Errorf(`Invalid scope %s: Uses non-canonical format, perhaps try %s`, scope, cleaned) } return nil } @@ -153,7 +154,7 @@ func (ref dirReference) NewImageDestination(ctx *types.SystemContext) (types.Ima // DeleteImage deletes the named image from the registry, if supported. func (ref dirReference) DeleteImage(ctx *types.SystemContext) error { - return fmt.Errorf("Deleting images not implemented for dir: images") + return errors.Errorf("Deleting images not implemented for dir: images") } // manifestPath returns a path for the manifest within a directory using our conventions. diff --git a/vendor/github.com/containers/image/directory/explicitfilepath/path.go b/vendor/github.com/containers/image/directory/explicitfilepath/path.go index b4ff4d08..71136b88 100644 --- a/vendor/github.com/containers/image/directory/explicitfilepath/path.go +++ b/vendor/github.com/containers/image/directory/explicitfilepath/path.go @@ -1,9 +1,10 @@ package explicitfilepath import ( - "fmt" "os" "path/filepath" + + "github.com/pkg/errors" ) // ResolvePathToFullyExplicit returns the input path converted to an absolute, no-symlinks, cleaned up path. @@ -25,14 +26,14 @@ func ResolvePathToFullyExplicit(path string) (string, error) { // This can still happen if there is a filesystem race condition, causing the Lstat() above to fail but the later resolution to succeed. // We do not care to promise anything if such filesystem race conditions can happen, but we definitely don't want to return "."/".." components // in the resulting path, and especially not at the end. - return "", fmt.Errorf("Unexpectedly missing special filename component in %s", path) + return "", errors.Errorf("Unexpectedly missing special filename component in %s", path) } resolvedPath := filepath.Join(resolvedParent, file) // As a sanity check, ensure that there are no "." or ".." components. cleanedResolvedPath := filepath.Clean(resolvedPath) if cleanedResolvedPath != resolvedPath { // Coverage: This should never happen. - return "", fmt.Errorf("Internal inconsistency: Path %s resolved to %s still cleaned up to %s", path, resolvedPath, cleanedResolvedPath) + return "", errors.Errorf("Internal inconsistency: Path %s resolved to %s still cleaned up to %s", path, resolvedPath, cleanedResolvedPath) } return resolvedPath, nil default: // err != nil, unrecognized diff --git a/vendor/github.com/containers/image/docker/daemon/daemon_dest.go b/vendor/github.com/containers/image/docker/daemon/daemon_dest.go index 7c4576dd..8368fab2 100644 --- a/vendor/github.com/containers/image/docker/daemon/daemon_dest.go +++ b/vendor/github.com/containers/image/docker/daemon/daemon_dest.go @@ -4,7 +4,6 @@ import ( "archive/tar" "bytes" "encoding/json" - "errors" "fmt" "io" "io/ioutil" @@ -15,8 +14,9 @@ import ( "github.com/containers/image/docker/reference" "github.com/containers/image/manifest" "github.com/containers/image/types" - "github.com/docker/distribution/digest" "github.com/docker/engine-api/client" + "github.com/opencontainers/go-digest" + "github.com/pkg/errors" "golang.org/x/net/context" ) @@ -36,16 +36,16 @@ type daemonImageDestination struct { // newImageDestination returns a types.ImageDestination for the specified image reference. func newImageDestination(systemCtx *types.SystemContext, ref daemonReference) (types.ImageDestination, error) { if ref.ref == nil { - return nil, fmt.Errorf("Invalid destination docker-daemon:%s: a destination must be a name:tag", ref.StringWithinTransport()) + return nil, errors.Errorf("Invalid destination docker-daemon:%s: a destination must be a name:tag", ref.StringWithinTransport()) } namedTaggedRef, ok := ref.ref.(reference.NamedTagged) if !ok { - return nil, fmt.Errorf("Invalid destination docker-daemon:%s: a destination must be a name:tag", ref.StringWithinTransport()) + return nil, errors.Errorf("Invalid destination docker-daemon:%s: a destination must be a name:tag", ref.StringWithinTransport()) } c, err := client.NewClient(client.DefaultDockerHost, "1.22", nil, nil) // FIXME: overridable host if err != nil { - return nil, fmt.Errorf("Error initializing docker engine client: %v", err) + return nil, errors.Wrap(err, "Error initializing docker engine client") } reader, writer := io.Pipe() @@ -84,7 +84,7 @@ func imageLoadGoroutine(ctx context.Context, c *client.Client, reader *io.PipeRe resp, err := c.ImageLoad(ctx, reader, true) if err != nil { - err = fmt.Errorf("Error saving image to docker engine: %v", err) + err = errors.Wrap(err, "Error saving image to docker engine") return } defer resp.Body.Close() @@ -123,7 +123,7 @@ func (d *daemonImageDestination) SupportedManifestMIMETypes() []string { // SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures. // Note: It is still possible for PutSignatures to fail if SupportsSignatures returns nil. func (d *daemonImageDestination) SupportsSignatures() error { - return fmt.Errorf("Storing signatures for docker-daemon: destinations is not supported") + return errors.Errorf("Storing signatures for docker-daemon: destinations is not supported") } // ShouldCompressLayers returns true iff it is desirable to compress layer blobs written to this destination. @@ -170,7 +170,7 @@ func (d *daemonImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobI logrus.Debugf("… streaming done") } - digester := digest.Canonical.New() + digester := digest.Canonical.Digester() tee := io.TeeReader(stream, digester.Hash()) if err := d.sendFile(inputInfo.Digest.String(), inputInfo.Size, tee); err != nil { return types.BlobInfo{}, err @@ -181,7 +181,7 @@ func (d *daemonImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobI func (d *daemonImageDestination) HasBlob(info types.BlobInfo) (bool, int64, error) { if info.Digest == "" { - return false, -1, fmt.Errorf(`"Can not check for a blob with unknown digest`) + return false, -1, errors.Errorf(`"Can not check for a blob with unknown digest`) } if blob, ok := d.blobs[info.Digest]; ok { return true, blob.Size, nil @@ -196,10 +196,10 @@ func (d *daemonImageDestination) ReapplyBlob(info types.BlobInfo) (types.BlobInf func (d *daemonImageDestination) PutManifest(m []byte) error { var man schema2Manifest if err := json.Unmarshal(m, &man); err != nil { - return fmt.Errorf("Error parsing manifest: %v", err) + return errors.Wrap(err, "Error parsing manifest") } if man.SchemaVersion != 2 || man.MediaType != manifest.DockerV2Schema2MediaType { - return fmt.Errorf("Unsupported manifest type, need a Docker schema 2 manifest") + return errors.Errorf("Unsupported manifest type, need a Docker schema 2 manifest") } layerPaths := []string{} @@ -280,14 +280,14 @@ func (d *daemonImageDestination) sendFile(path string, expectedSize int64, strea return err } if size != expectedSize { - return fmt.Errorf("Size mismatch when copying %s, expected %d, got %d", path, expectedSize, size) + return errors.Errorf("Size mismatch when copying %s, expected %d, got %d", path, expectedSize, size) } return nil } func (d *daemonImageDestination) PutSignatures(signatures [][]byte) error { if len(signatures) != 0 { - return fmt.Errorf("Storing signatures for docker-daemon: destinations is not supported") + return errors.Errorf("Storing signatures for docker-daemon: destinations is not supported") } return nil } diff --git a/vendor/github.com/containers/image/docker/daemon/daemon_src.go b/vendor/github.com/containers/image/docker/daemon/daemon_src.go index 2f487751..5a59cafb 100644 --- a/vendor/github.com/containers/image/docker/daemon/daemon_src.go +++ b/vendor/github.com/containers/image/docker/daemon/daemon_src.go @@ -4,7 +4,6 @@ import ( "archive/tar" "bytes" "encoding/json" - "fmt" "io" "io/ioutil" "os" @@ -12,8 +11,9 @@ import ( "github.com/containers/image/manifest" "github.com/containers/image/types" - "github.com/docker/distribution/digest" "github.com/docker/engine-api/client" + "github.com/opencontainers/go-digest" + "github.com/pkg/errors" "golang.org/x/net/context" ) @@ -49,13 +49,13 @@ type layerInfo struct { func newImageSource(ctx *types.SystemContext, ref daemonReference) (types.ImageSource, error) { c, err := client.NewClient(client.DefaultDockerHost, "1.22", nil, nil) // FIXME: overridable host if err != nil { - return nil, fmt.Errorf("Error initializing docker engine client: %v", err) + return nil, errors.Wrap(err, "Error initializing docker engine client") } // Per NewReference(), ref.StringWithinTransport() is either an image ID (config digest), or a !reference.NameOnly() reference. // Either way ImageSave should create a tarball with exactly one image. inputStream, err := c.ImageSave(context.TODO(), []string{ref.StringWithinTransport()}) if err != nil { - return nil, fmt.Errorf("Error loading image from docker engine: %v", err) + return nil, errors.Wrap(err, "Error loading image from docker engine") } defer inputStream.Close() @@ -145,7 +145,7 @@ func (s *daemonImageSource) openTarComponent(componentPath string) (io.ReadClose } if !header.FileInfo().Mode().IsRegular() { - return nil, fmt.Errorf("Error reading tar archive component %s: not a regular file", header.Name) + return nil, errors.Errorf("Error reading tar archive component %s: not a regular file", header.Name) } succeeded = true return &tarReadCloser{Reader: tarReader, backingFile: f}, nil @@ -174,7 +174,7 @@ func findTarComponent(inputFile io.Reader, path string) (*tar.Reader, *tar.Heade func (s *daemonImageSource) readTarComponent(path string) ([]byte, error) { file, err := s.openTarComponent(path) if err != nil { - return nil, fmt.Errorf("Error loading tar component %s: %v", path, err) + return nil, errors.Wrapf(err, "Error loading tar component %s", path) } defer file.Close() bytes, err := ioutil.ReadAll(file) @@ -203,7 +203,7 @@ func (s *daemonImageSource) ensureCachedDataIsPresent() error { } var parsedConfig dockerImage // Most fields ommitted, we only care about layer DiffIDs. if err := json.Unmarshal(configBytes, &parsedConfig); err != nil { - return fmt.Errorf("Error decoding tar config %s: %v", tarManifest.Config, err) + return errors.Wrapf(err, "Error decoding tar config %s", tarManifest.Config) } knownLayers, err := s.prepareLayerData(tarManifest, &parsedConfig) @@ -229,10 +229,10 @@ func (s *daemonImageSource) loadTarManifest() (*manifestItem, error) { } var items []manifestItem if err := json.Unmarshal(bytes, &items); err != nil { - return nil, fmt.Errorf("Error decoding tar manifest.json: %v", err) + return nil, errors.Wrap(err, "Error decoding tar manifest.json") } if len(items) != 1 { - return nil, fmt.Errorf("Unexpected tar manifest.json: expected 1 item, got %d", len(items)) + return nil, errors.Errorf("Unexpected tar manifest.json: expected 1 item, got %d", len(items)) } return &items[0], nil } @@ -240,7 +240,7 @@ func (s *daemonImageSource) loadTarManifest() (*manifestItem, error) { func (s *daemonImageSource) prepareLayerData(tarManifest *manifestItem, parsedConfig *dockerImage) (map[diffID]*layerInfo, error) { // Collect layer data available in manifest and config. if len(tarManifest.Layers) != len(parsedConfig.RootFS.DiffIDs) { - return nil, fmt.Errorf("Inconsistent layer count: %d in manifest, %d in config", len(tarManifest.Layers), len(parsedConfig.RootFS.DiffIDs)) + return nil, errors.Errorf("Inconsistent layer count: %d in manifest, %d in config", len(tarManifest.Layers), len(parsedConfig.RootFS.DiffIDs)) } knownLayers := map[diffID]*layerInfo{} unknownLayerSizes := map[string]*layerInfo{} // Points into knownLayers, a "to do list" of items with unknown sizes. @@ -253,7 +253,7 @@ func (s *daemonImageSource) prepareLayerData(tarManifest *manifestItem, parsedCo } layerPath := tarManifest.Layers[i] if _, ok := unknownLayerSizes[layerPath]; ok { - return nil, fmt.Errorf("Layer tarfile %s used for two different DiffID values", layerPath) + return nil, errors.Errorf("Layer tarfile %s used for two different DiffID values", layerPath) } li := &layerInfo{ // A new element in each iteration path: layerPath, @@ -284,7 +284,7 @@ func (s *daemonImageSource) prepareLayerData(tarManifest *manifestItem, parsedCo } } if len(unknownLayerSizes) != 0 { - return nil, fmt.Errorf("Some layer tarfiles are missing in the tarball") // This could do with a better error reporting, if this ever happened in practice. + return nil, errors.Errorf("Some layer tarfiles are missing in the tarball") // This could do with a better error reporting, if this ever happened in practice. } return knownLayers, nil @@ -310,7 +310,7 @@ func (s *daemonImageSource) GetManifest() ([]byte, string, error) { for _, diffID := range s.orderedDiffIDList { li, ok := s.knownLayers[diffID] if !ok { - return nil, "", fmt.Errorf("Internal inconsistency: Information about layer %s missing", diffID) + return nil, "", errors.Errorf("Internal inconsistency: Information about layer %s missing", diffID) } m.Layers = append(m.Layers, distributionDescriptor{ Digest: digest.Digest(diffID), // diffID is a digest of the uncompressed tarball @@ -331,7 +331,7 @@ func (s *daemonImageSource) GetManifest() ([]byte, string, error) { // out of a manifest list. func (s *daemonImageSource) GetTargetManifest(digest digest.Digest) ([]byte, string, error) { // How did we even get here? GetManifest() above has returned a manifest.DockerV2Schema2MediaType. - return nil, "", fmt.Errorf(`Manifest lists are not supported by "docker-daemon:"`) + return nil, "", errors.Errorf(`Manifest lists are not supported by "docker-daemon:"`) } // GetBlob returns a stream for the specified blob, and the blob’s size (or -1 if unknown). @@ -352,7 +352,7 @@ func (s *daemonImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, return stream, li.size, nil } - return nil, 0, fmt.Errorf("Unknown blob %s", info.Digest) + return nil, 0, errors.Errorf("Unknown blob %s", info.Digest) } // GetSignatures returns the image's signatures. It may use a remote (= slow) service. diff --git a/vendor/github.com/containers/image/docker/daemon/daemon_transport.go b/vendor/github.com/containers/image/docker/daemon/daemon_transport.go index 1492e2fe..c8e40aed 100644 --- a/vendor/github.com/containers/image/docker/daemon/daemon_transport.go +++ b/vendor/github.com/containers/image/docker/daemon/daemon_transport.go @@ -1,13 +1,12 @@ package daemon import ( - "errors" - "fmt" + "github.com/pkg/errors" "github.com/containers/image/docker/reference" "github.com/containers/image/image" "github.com/containers/image/types" - "github.com/docker/distribution/digest" + "github.com/opencontainers/go-digest" ) // Transport is an ImageTransport for images managed by a local Docker daemon. @@ -52,11 +51,11 @@ func ParseReference(refString string) (types.ImageReference, error) { // digest:hexstring is structurally the same as a reponame:tag (meaning docker.io/library/reponame:tag). // reference.ParseIDOrReference interprets such strings as digests. - if dgst, err := digest.ParseDigest(refString); err == nil { + if dgst, err := digest.Parse(refString); err == nil { // The daemon explicitly refuses to tag images with a reponame equal to digest.Canonical - but _only_ this digest name. // Other digest references are ambiguous, so refuse them. if dgst.Algorithm() != digest.Canonical { - return nil, fmt.Errorf("Invalid docker-daemon: reference %s: only digest algorithm %s accepted", refString, digest.Canonical) + return nil, errors.Errorf("Invalid docker-daemon: reference %s: only digest algorithm %s accepted", refString, digest.Canonical) } return NewReference(dgst, nil) } @@ -66,7 +65,7 @@ func ParseReference(refString string) (types.ImageReference, error) { return nil, err } if ref.Name() == digest.Canonical.String() { - return nil, fmt.Errorf("Invalid docker-daemon: reference %s: The %s repository name is reserved for (non-shortened) digest references", refString, digest.Canonical) + return nil, errors.Errorf("Invalid docker-daemon: reference %s: The %s repository name is reserved for (non-shortened) digest references", refString, digest.Canonical) } return NewReference("", ref) } @@ -78,14 +77,14 @@ func NewReference(id digest.Digest, ref reference.Named) (types.ImageReference, } if ref != nil { if reference.IsNameOnly(ref) { - return nil, fmt.Errorf("docker-daemon: reference %s has neither a tag nor a digest", ref.String()) + return nil, errors.Errorf("docker-daemon: reference %s has neither a tag nor a digest", ref.String()) } // A github.com/distribution/reference value can have a tag and a digest at the same time! // docker/reference does not handle that, so fail. _, isTagged := ref.(reference.NamedTagged) _, isDigested := ref.(reference.Canonical) if isTagged && isDigested { - return nil, fmt.Errorf("docker-daemon: references with both a tag and digest are currently not supported") + return nil, errors.Errorf("docker-daemon: references with both a tag and digest are currently not supported") } } return daemonReference{ @@ -175,5 +174,5 @@ func (ref daemonReference) DeleteImage(ctx *types.SystemContext) error { // Should this just untag the image? Should this stop running containers? // The semantics is not quite as clear as for remote repositories. // The user can run (docker rmi) directly anyway, so, for now(?), punt instead of trying to guess what the user meant. - return fmt.Errorf("Deleting images not implemented for docker-daemon: images") + return errors.Errorf("Deleting images not implemented for docker-daemon: images") } diff --git a/vendor/github.com/containers/image/docker/daemon/daemon_types.go b/vendor/github.com/containers/image/docker/daemon/daemon_types.go index c1bb9caa..03276aea 100644 --- a/vendor/github.com/containers/image/docker/daemon/daemon_types.go +++ b/vendor/github.com/containers/image/docker/daemon/daemon_types.go @@ -1,6 +1,6 @@ package daemon -import "github.com/docker/distribution/digest" +import "github.com/opencontainers/go-digest" // Various data structures. diff --git a/vendor/github.com/containers/image/docker/docker_client.go b/vendor/github.com/containers/image/docker/docker_client.go index 35ecb96e..9fca4100 100644 --- a/vendor/github.com/containers/image/docker/docker_client.go +++ b/vendor/github.com/containers/image/docker/docker_client.go @@ -4,7 +4,6 @@ import ( "crypto/tls" "encoding/base64" "encoding/json" - "errors" "fmt" "io" "io/ioutil" @@ -20,6 +19,7 @@ import ( "github.com/containers/storage/pkg/homedir" "github.com/docker/go-connections/sockets" "github.com/docker/go-connections/tlsconfig" + "github.com/pkg/errors" ) const ( @@ -101,7 +101,7 @@ func setupCertificates(dir string, tlsc *tls.Config) error { if strings.HasSuffix(f.Name(), ".crt") { systemPool, err := tlsconfig.SystemCertPool() if err != nil { - return fmt.Errorf("unable to get system cert pool: %v", err) + return errors.Wrap(err, "unable to get system cert pool") } tlsc.RootCAs = systemPool logrus.Debugf("crt: %s", fullPath) @@ -116,7 +116,7 @@ func setupCertificates(dir string, tlsc *tls.Config) error { keyName := certName[:len(certName)-5] + ".key" logrus.Debugf("cert: %s", fullPath) if !hasFile(fs, keyName) { - return fmt.Errorf("missing key %s for client certificate %s. Note that CA certificates should use the extension .crt", keyName, certName) + return errors.Errorf("missing key %s for client certificate %s. Note that CA certificates should use the extension .crt", keyName, certName) } cert, err := tls.LoadX509KeyPair(filepath.Join(dir, certName), filepath.Join(dir, keyName)) if err != nil { @@ -129,7 +129,7 @@ func setupCertificates(dir string, tlsc *tls.Config) error { certName := keyName[:len(keyName)-4] + ".cert" logrus.Debugf("key: %s", fullPath) if !hasFile(fs, certName) { - return fmt.Errorf("missing client certificate %s for key %s", certName, keyName) + return errors.Errorf("missing client certificate %s for key %s", certName, keyName) } } } @@ -240,7 +240,7 @@ func (c *dockerClient) makeRequestToResolvedURL(method, url string, headers map[ func (c *dockerClient) setupRequestAuth(req *http.Request) error { tokens := strings.SplitN(strings.TrimSpace(c.wwwAuthenticate), " ", 2) if len(tokens) != 2 { - return fmt.Errorf("expected 2 tokens in WWW-Authenticate: %d, %s", len(tokens), c.wwwAuthenticate) + return errors.Errorf("expected 2 tokens in WWW-Authenticate: %d, %s", len(tokens), c.wwwAuthenticate) } switch tokens[0] { case "Basic": @@ -264,18 +264,48 @@ func (c *dockerClient) setupRequestAuth(req *http.Request) error { return err } chs := parseAuthHeader(res.Header) + // We could end up in this "if" statement if the /v2/ call (during ping) + // returned 401 with a valid WWW-Authenticate=Bearer header. + // That doesn't **always** mean, however, that the specific API request + // (different from /v2/) actually needs to be authorized. + // One example of this _weird_ scenario happens with GCR.io docker + // registries. if res.StatusCode != http.StatusUnauthorized || chs == nil || len(chs) == 0 { - // no need for bearer? wtf? - return nil + // With gcr.io, the /v2/ call returns a 401 with a valid WWW-Authenticate=Bearer + // header but the repository could be _public_ (no authorization is needed). + // Hence, the registry response contains no challenges and the status + // code is not 401. + // We just skip this case as it's not standard on docker/distribution + // registries (https://github.com/docker/distribution/blob/master/docs/spec/api.md#api-version-check) + if res.StatusCode != http.StatusUnauthorized { + return nil + } + // gcr.io private repositories pull instead requires us to send user:pass pair in + // order to retrieve a token and setup the correct Bearer token. + // try again one last time with Basic Auth + testReq2 := *req + // Do not use the body stream, or we couldn't reuse it for the "real" call later. + testReq2.Body = nil + testReq2.ContentLength = 0 + testReq2.SetBasicAuth(c.username, c.password) + res, err := c.client.Do(&testReq2) + if err != nil { + return err + } + chs = parseAuthHeader(res.Header) + if res.StatusCode != http.StatusUnauthorized || chs == nil || len(chs) == 0 { + // no need for bearer? wtf? + return nil + } } // Arbitrarily use the first challenge, there is no reason to expect more than one. challenge := chs[0] if challenge.Scheme != "bearer" { // Another artifact of trying to handle WWW-Authenticate before it actually happens. - return fmt.Errorf("Unimplemented: WWW-Authenticate Bearer replaced by %#v", challenge.Scheme) + return errors.Errorf("Unimplemented: WWW-Authenticate Bearer replaced by %#v", challenge.Scheme) } realm, ok := challenge.Parameters["realm"] if !ok { - return fmt.Errorf("missing realm in bearer auth challenge") + return errors.Errorf("missing realm in bearer auth challenge") } service, _ := challenge.Parameters["service"] // Will be "" if not present scope, _ := challenge.Parameters["scope"] // Will be "" if not present @@ -286,7 +316,7 @@ func (c *dockerClient) setupRequestAuth(req *http.Request) error { req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) return nil } - return fmt.Errorf("no handler for %s authentication", tokens[0]) + return errors.Errorf("no handler for %s authentication", tokens[0]) // support docker bearer with authconfig's Auth string? see docker2aci } @@ -317,11 +347,11 @@ func (c *dockerClient) getBearerToken(realm, service, scope string) (string, err defer res.Body.Close() switch res.StatusCode { case http.StatusUnauthorized: - return "", fmt.Errorf("unable to retrieve auth token: 401 unauthorized") + return "", errors.Errorf("unable to retrieve auth token: 401 unauthorized") case http.StatusOK: break default: - return "", fmt.Errorf("unexpected http code: %d, URL: %s", res.StatusCode, authReq.URL) + return "", errors.Errorf("unexpected http code: %d, URL: %s", res.StatusCode, authReq.URL) } tokenBlob, err := ioutil.ReadAll(res.Body) if err != nil { @@ -365,7 +395,7 @@ func getAuth(ctx *types.SystemContext, registry string) (string, string, error) if os.IsNotExist(err) { return "", "", nil } - return "", "", fmt.Errorf("%s - %v", oldDockerCfgPath, err) + return "", "", errors.Wrap(err, oldDockerCfgPath) } j, err := ioutil.ReadFile(oldDockerCfgPath) @@ -377,7 +407,7 @@ func getAuth(ctx *types.SystemContext, registry string) (string, string, error) } } else if err != nil { - return "", "", fmt.Errorf("%s - %v", dockerCfgPath, err) + return "", "", errors.Wrap(err, dockerCfgPath) } // I'm feeling lucky @@ -414,7 +444,7 @@ func (c *dockerClient) ping() (*pingResponse, error) { defer resp.Body.Close() logrus.Debugf("Ping %s status %d", scheme+"://"+c.registry+"/v2/", resp.StatusCode) if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusUnauthorized { - return nil, fmt.Errorf("error pinging repository, response code %d", resp.StatusCode) + return nil, errors.Errorf("error pinging repository, response code %d", resp.StatusCode) } pr := &pingResponse{} pr.WWWAuthenticate = resp.Header.Get("WWW-Authenticate") @@ -427,7 +457,7 @@ func (c *dockerClient) ping() (*pingResponse, error) { pr, err = ping("http") } if err != nil { - err = fmt.Errorf("pinging docker registry returned %+v", err) + err = errors.Wrap(err, "pinging docker registry returned") if c.ctx != nil && c.ctx.DockerDisableV1Ping { return nil, err } diff --git a/vendor/github.com/containers/image/docker/docker_image.go b/vendor/github.com/containers/image/docker/docker_image.go index 59468755..ce769c0a 100644 --- a/vendor/github.com/containers/image/docker/docker_image.go +++ b/vendor/github.com/containers/image/docker/docker_image.go @@ -7,6 +7,7 @@ import ( "github.com/containers/image/image" "github.com/containers/image/types" + "github.com/pkg/errors" ) // Image is a Docker-specific implementation of types.Image with a few extra methods @@ -46,7 +47,7 @@ func (i *Image) GetRepositoryTags() ([]string, error) { defer res.Body.Close() if res.StatusCode != http.StatusOK { // print url also - return nil, fmt.Errorf("Invalid status code returned when fetching tags list %d", res.StatusCode) + return nil, errors.Errorf("Invalid status code returned when fetching tags list %d", res.StatusCode) } type tagsRes struct { Tags []string diff --git a/vendor/github.com/containers/image/docker/docker_image_dest.go b/vendor/github.com/containers/image/docker/docker_image_dest.go index 54df01d7..9761f58e 100644 --- a/vendor/github.com/containers/image/docker/docker_image_dest.go +++ b/vendor/github.com/containers/image/docker/docker_image_dest.go @@ -13,7 +13,8 @@ import ( "github.com/Sirupsen/logrus" "github.com/containers/image/manifest" "github.com/containers/image/types" - "github.com/docker/distribution/digest" + "github.com/opencontainers/go-digest" + "github.com/pkg/errors" ) type dockerImageDestination struct { @@ -57,7 +58,7 @@ func (d *dockerImageDestination) SupportedManifestMIMETypes() []string { // SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures. // Note: It is still possible for PutSignatures to fail if SupportsSignatures returns nil. func (d *dockerImageDestination) SupportsSignatures() error { - return fmt.Errorf("Pushing signatures to a Docker Registry is not supported") + return errors.Errorf("Pushing signatures to a Docker Registry is not supported") } // ShouldCompressLayers returns true iff it is desirable to compress layer blobs written to this destination. @@ -101,11 +102,11 @@ func (d *dockerImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobI return types.BlobInfo{Digest: inputInfo.Digest, Size: getBlobSize(res)}, nil case http.StatusUnauthorized: logrus.Debugf("... not authorized") - return types.BlobInfo{}, fmt.Errorf("not authorized to read from destination repository %s", d.ref.ref.RemoteName()) + return types.BlobInfo{}, errors.Errorf("not authorized to read from destination repository %s", d.ref.ref.RemoteName()) case http.StatusNotFound: // noop default: - return types.BlobInfo{}, fmt.Errorf("failed to read from destination repository %s: %v", d.ref.ref.RemoteName(), http.StatusText(res.StatusCode)) + return types.BlobInfo{}, errors.Errorf("failed to read from destination repository %s: %v", d.ref.ref.RemoteName(), http.StatusText(res.StatusCode)) } logrus.Debugf("... failed, status %d", res.StatusCode) } @@ -120,14 +121,14 @@ func (d *dockerImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobI defer res.Body.Close() if res.StatusCode != http.StatusAccepted { logrus.Debugf("Error initiating layer upload, response %#v", *res) - return types.BlobInfo{}, fmt.Errorf("Error initiating layer upload to %s, status %d", uploadURL, res.StatusCode) + return types.BlobInfo{}, errors.Errorf("Error initiating layer upload to %s, status %d", uploadURL, res.StatusCode) } uploadLocation, err := res.Location() if err != nil { - return types.BlobInfo{}, fmt.Errorf("Error determining upload URL: %s", err.Error()) + return types.BlobInfo{}, errors.Wrap(err, "Error determining upload URL") } - digester := digest.Canonical.New() + digester := digest.Canonical.Digester() sizeCounter := &sizeCounter{} tee := io.TeeReader(stream, io.MultiWriter(digester.Hash(), sizeCounter)) res, err = d.c.makeRequestToResolvedURL("PATCH", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, tee, inputInfo.Size, true) @@ -140,7 +141,7 @@ func (d *dockerImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobI uploadLocation, err = res.Location() if err != nil { - return types.BlobInfo{}, fmt.Errorf("Error determining upload URL: %s", err.Error()) + return types.BlobInfo{}, errors.Wrap(err, "Error determining upload URL") } // FIXME: DELETE uploadLocation on failure @@ -156,7 +157,7 @@ func (d *dockerImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobI defer res.Body.Close() if res.StatusCode != http.StatusCreated { logrus.Debugf("Error uploading layer, response %#v", *res) - return types.BlobInfo{}, fmt.Errorf("Error uploading layer to %s, status %d", uploadLocation, res.StatusCode) + return types.BlobInfo{}, errors.Errorf("Error uploading layer to %s, status %d", uploadLocation, res.StatusCode) } logrus.Debugf("Upload of layer %s complete", computedDigest) @@ -165,7 +166,7 @@ func (d *dockerImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobI func (d *dockerImageDestination) HasBlob(info types.BlobInfo) (bool, int64, error) { if info.Digest == "" { - return false, -1, fmt.Errorf(`"Can not check for a blob with unknown digest`) + return false, -1, errors.Errorf(`"Can not check for a blob with unknown digest`) } checkURL := fmt.Sprintf(blobsURL, d.ref.ref.RemoteName(), info.Digest.String()) @@ -181,7 +182,7 @@ func (d *dockerImageDestination) HasBlob(info types.BlobInfo) (bool, int64, erro return true, getBlobSize(res), nil case http.StatusUnauthorized: logrus.Debugf("... not authorized") - return false, -1, fmt.Errorf("not authorized to read from destination repository %s", d.ref.ref.RemoteName()) + return false, -1, errors.Errorf("not authorized to read from destination repository %s", d.ref.ref.RemoteName()) case http.StatusNotFound: logrus.Debugf("... not present") return false, -1, types.ErrBlobNotFound @@ -225,7 +226,7 @@ func (d *dockerImageDestination) PutManifest(m []byte) error { logrus.Debugf("Error body %s", string(body)) } logrus.Debugf("Error uploading manifest, status %d, %#v", res.StatusCode, res) - return fmt.Errorf("Error uploading manifest to %s, status %d", url, res.StatusCode) + return errors.Errorf("Error uploading manifest to %s, status %d", url, res.StatusCode) } return nil } @@ -239,18 +240,18 @@ func (d *dockerImageDestination) PutSignatures(signatures [][]byte) error { return nil } if d.c.signatureBase == nil { - return fmt.Errorf("Pushing signatures to a Docker Registry is not supported, and there is no applicable signature storage configured") + return errors.Errorf("Pushing signatures to a Docker Registry is not supported, and there is no applicable signature storage configured") } if d.manifestDigest.String() == "" { // This shouldn’t happen, ImageDestination users are required to call PutManifest before PutSignatures - return fmt.Errorf("Unknown manifest digest, can't add signatures") + return errors.Errorf("Unknown manifest digest, can't add signatures") } for i, signature := range signatures { url := signatureStorageURL(d.c.signatureBase, d.manifestDigest, i) if url == nil { - return fmt.Errorf("Internal error: signatureStorageURL with non-nil base returned nil") + return errors.Errorf("Internal error: signatureStorageURL with non-nil base returned nil") } err := d.putOneSignature(url, signature) if err != nil { @@ -265,7 +266,7 @@ func (d *dockerImageDestination) PutSignatures(signatures [][]byte) error { for i := len(signatures); ; i++ { url := signatureStorageURL(d.c.signatureBase, d.manifestDigest, i) if url == nil { - return fmt.Errorf("Internal error: signatureStorageURL with non-nil base returned nil") + return errors.Errorf("Internal error: signatureStorageURL with non-nil base returned nil") } missing, err := d.c.deleteOneSignature(url) if err != nil { @@ -295,9 +296,9 @@ func (d *dockerImageDestination) putOneSignature(url *url.URL, signature []byte) return nil case "http", "https": - return fmt.Errorf("Writing directly to a %s sigstore %s is not supported. Configure a sigstore-staging: location", url.Scheme, url.String()) + return errors.Errorf("Writing directly to a %s sigstore %s is not supported. Configure a sigstore-staging: location", url.Scheme, url.String()) default: - return fmt.Errorf("Unsupported scheme when writing signature to %s", url.String()) + return errors.Errorf("Unsupported scheme when writing signature to %s", url.String()) } } @@ -314,9 +315,9 @@ func (c *dockerClient) deleteOneSignature(url *url.URL) (missing bool, err error return false, err case "http", "https": - return false, fmt.Errorf("Writing directly to a %s sigstore %s is not supported. Configure a sigstore-staging: location", url.Scheme, url.String()) + return false, errors.Errorf("Writing directly to a %s sigstore %s is not supported. Configure a sigstore-staging: location", url.Scheme, url.String()) default: - return false, fmt.Errorf("Unsupported scheme when deleting signature from %s", url.String()) + return false, errors.Errorf("Unsupported scheme when deleting signature from %s", url.String()) } } diff --git a/vendor/github.com/containers/image/docker/docker_image_src.go b/vendor/github.com/containers/image/docker/docker_image_src.go index 8a85891f..563e6aa6 100644 --- a/vendor/github.com/containers/image/docker/docker_image_src.go +++ b/vendor/github.com/containers/image/docker/docker_image_src.go @@ -13,8 +13,9 @@ import ( "github.com/Sirupsen/logrus" "github.com/containers/image/manifest" "github.com/containers/image/types" - "github.com/docker/distribution/digest" "github.com/docker/distribution/registry/client" + "github.com/opencontainers/go-digest" + "github.com/pkg/errors" ) type dockerImageSource struct { @@ -139,7 +140,7 @@ func (s *dockerImageSource) getExternalBlob(urls []string) (io.ReadCloser, int64 resp, err = s.c.makeRequestToResolvedURL("GET", url, nil, nil, -1, false) if err == nil { if resp.StatusCode != http.StatusOK { - err = fmt.Errorf("error fetching external blob from %q: %d", url, resp.StatusCode) + err = errors.Errorf("error fetching external blob from %q: %d", url, resp.StatusCode) logrus.Debug(err) continue } @@ -173,7 +174,7 @@ func (s *dockerImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, } if res.StatusCode != http.StatusOK { // print url also - return nil, 0, fmt.Errorf("Invalid status code returned when fetching blob %d", res.StatusCode) + return nil, 0, errors.Errorf("Invalid status code returned when fetching blob %d", res.StatusCode) } return res.Body, getBlobSize(res), nil } @@ -195,7 +196,7 @@ func (s *dockerImageSource) GetSignatures() ([][]byte, error) { for i := 0; ; i++ { url := signatureStorageURL(s.c.signatureBase, manifestDigest, i) if url == nil { - return nil, fmt.Errorf("Internal error: signatureStorageURL with non-nil base returned nil") + return nil, errors.Errorf("Internal error: signatureStorageURL with non-nil base returned nil") } signature, missing, err := s.getOneSignature(url) if err != nil { @@ -234,7 +235,7 @@ func (s *dockerImageSource) getOneSignature(url *url.URL) (signature []byte, mis if res.StatusCode == http.StatusNotFound { return nil, true, nil } else if res.StatusCode != http.StatusOK { - return nil, false, fmt.Errorf("Error reading signature from %s: status %d", url.String(), res.StatusCode) + return nil, false, errors.Errorf("Error reading signature from %s: status %d", url.String(), res.StatusCode) } sig, err := ioutil.ReadAll(res.Body) if err != nil { @@ -243,7 +244,7 @@ func (s *dockerImageSource) getOneSignature(url *url.URL) (signature []byte, mis return sig, false, nil default: - return nil, false, fmt.Errorf("Unsupported scheme when reading signature from %s", url.String()) + return nil, false, errors.Errorf("Unsupported scheme when reading signature from %s", url.String()) } } @@ -276,9 +277,9 @@ func deleteImage(ctx *types.SystemContext, ref dockerReference) error { switch get.StatusCode { case http.StatusOK: case http.StatusNotFound: - return fmt.Errorf("Unable to delete %v. Image may not exist or is not stored with a v2 Schema in a v2 registry", ref.ref) + return errors.Errorf("Unable to delete %v. Image may not exist or is not stored with a v2 Schema in a v2 registry", ref.ref) default: - return fmt.Errorf("Failed to delete %v: %s (%v)", ref.ref, manifestBody, get.Status) + return errors.Errorf("Failed to delete %v: %s (%v)", ref.ref, manifestBody, get.Status) } digest := get.Header.Get("Docker-Content-Digest") @@ -297,7 +298,7 @@ func deleteImage(ctx *types.SystemContext, ref dockerReference) error { return err } if delete.StatusCode != http.StatusAccepted { - return fmt.Errorf("Failed to delete %v: %s (%v)", deleteURL, string(body), delete.Status) + return errors.Errorf("Failed to delete %v: %s (%v)", deleteURL, string(body), delete.Status) } if c.signatureBase != nil { @@ -309,7 +310,7 @@ func deleteImage(ctx *types.SystemContext, ref dockerReference) error { for i := 0; ; i++ { url := signatureStorageURL(c.signatureBase, manifestDigest, i) if url == nil { - return fmt.Errorf("Internal error: signatureStorageURL with non-nil base returned nil") + return errors.Errorf("Internal error: signatureStorageURL with non-nil base returned nil") } missing, err := c.deleteOneSignature(url) if err != nil { diff --git a/vendor/github.com/containers/image/docker/docker_transport.go b/vendor/github.com/containers/image/docker/docker_transport.go index 1284dc4b..00d0b7c9 100644 --- a/vendor/github.com/containers/image/docker/docker_transport.go +++ b/vendor/github.com/containers/image/docker/docker_transport.go @@ -7,6 +7,7 @@ import ( "github.com/containers/image/docker/policyconfiguration" "github.com/containers/image/docker/reference" "github.com/containers/image/types" + "github.com/pkg/errors" ) // Transport is an ImageTransport for Docker registry-hosted images. @@ -42,7 +43,7 @@ type dockerReference struct { // ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an Docker ImageReference. func ParseReference(refString string) (types.ImageReference, error) { if !strings.HasPrefix(refString, "//") { - return nil, fmt.Errorf("docker: image reference %s does not start with //", refString) + return nil, errors.Errorf("docker: image reference %s does not start with //", refString) } ref, err := reference.ParseNamed(strings.TrimPrefix(refString, "//")) if err != nil { @@ -55,7 +56,7 @@ func ParseReference(refString string) (types.ImageReference, error) { // NewReference returns a Docker reference for a named reference. The reference must satisfy !reference.IsNameOnly(). func NewReference(ref reference.Named) (types.ImageReference, error) { if reference.IsNameOnly(ref) { - return nil, fmt.Errorf("Docker reference %s has neither a tag nor a digest", ref.String()) + return nil, errors.Errorf("Docker reference %s has neither a tag nor a digest", ref.String()) } // A github.com/distribution/reference value can have a tag and a digest at the same time! // docker/reference does not handle that, so fail. @@ -64,7 +65,7 @@ func NewReference(ref reference.Named) (types.ImageReference, error) { _, isTagged := ref.(reference.NamedTagged) _, isDigested := ref.(reference.Canonical) if isTagged && isDigested { - return nil, fmt.Errorf("Docker references with both a tag and digest are currently not supported") + return nil, errors.Errorf("Docker references with both a tag and digest are currently not supported") } return dockerReference{ ref: ref, @@ -151,5 +152,5 @@ func (ref dockerReference) tagOrDigest() (string, error) { return ref.Tag(), nil } // This should not happen, NewReference above refuses reference.IsNameOnly values. - return "", fmt.Errorf("Internal inconsistency: Reference %s unexpectedly has neither a digest nor a tag", ref.ref.String()) + return "", errors.Errorf("Internal inconsistency: Reference %s unexpectedly has neither a digest nor a tag", ref.ref.String()) } diff --git a/vendor/github.com/containers/image/docker/lookaside.go b/vendor/github.com/containers/image/docker/lookaside.go index 743e172c..e8f3a5be 100644 --- a/vendor/github.com/containers/image/docker/lookaside.go +++ b/vendor/github.com/containers/image/docker/lookaside.go @@ -9,8 +9,9 @@ import ( "path/filepath" "strings" - "github.com/docker/distribution/digest" "github.com/ghodss/yaml" + "github.com/opencontainers/go-digest" + "github.com/pkg/errors" "github.com/Sirupsen/logrus" "github.com/containers/image/types" @@ -60,12 +61,12 @@ func configuredSignatureStorageBase(ctx *types.SystemContext, ref dockerReferenc url, err := url.Parse(topLevel) if err != nil { - return nil, fmt.Errorf("Invalid signature storage URL %s: %v", topLevel, err) + return nil, errors.Wrapf(err, "Invalid signature storage URL %s", topLevel) } // FIXME? Restrict to explicitly supported schemes? repo := ref.ref.FullName() // Note that this is without a tag or digest. if path.Clean(repo) != repo { // Coverage: This should not be reachable because /./ and /../ components are not valid in docker references - return nil, fmt.Errorf("Unexpected path elements in Docker reference %s for signature storage", ref.ref.String()) + return nil, errors.Errorf("Unexpected path elements in Docker reference %s for signature storage", ref.ref.String()) } url.Path = url.Path + "/" + repo return url, nil @@ -114,12 +115,12 @@ func loadAndMergeConfig(dirPath string) (*registryConfiguration, error) { var config registryConfiguration err = yaml.Unmarshal(configBytes, &config) if err != nil { - return nil, fmt.Errorf("Error parsing %s: %v", configPath, err) + return nil, errors.Wrapf(err, "Error parsing %s", configPath) } if config.DefaultDocker != nil { if mergedConfig.DefaultDocker != nil { - return nil, fmt.Errorf(`Error parsing signature storage configuration: "default-docker" defined both in "%s" and "%s"`, + return nil, errors.Errorf(`Error parsing signature storage configuration: "default-docker" defined both in "%s" and "%s"`, dockerDefaultMergedFrom, configPath) } mergedConfig.DefaultDocker = config.DefaultDocker @@ -128,7 +129,7 @@ func loadAndMergeConfig(dirPath string) (*registryConfiguration, error) { for nsName, nsConfig := range config.Docker { // includes config.Docker == nil if _, ok := mergedConfig.Docker[nsName]; ok { - return nil, fmt.Errorf(`Error parsing signature storage configuration: "docker" namespace "%s" defined both in "%s" and "%s"`, + return nil, errors.Errorf(`Error parsing signature storage configuration: "docker" namespace "%s" defined both in "%s" and "%s"`, nsName, nsMergedFrom[nsName], configPath) } mergedConfig.Docker[nsName] = nsConfig diff --git a/vendor/github.com/containers/image/docker/policyconfiguration/naming.go b/vendor/github.com/containers/image/docker/policyconfiguration/naming.go index 08892cb1..a40fa380 100644 --- a/vendor/github.com/containers/image/docker/policyconfiguration/naming.go +++ b/vendor/github.com/containers/image/docker/policyconfiguration/naming.go @@ -1,10 +1,10 @@ package policyconfiguration import ( - "errors" - "fmt" "strings" + "github.com/pkg/errors" + "github.com/containers/image/docker/reference" ) @@ -17,9 +17,9 @@ func DockerReferenceIdentity(ref reference.Named) (string, error) { digested, isDigested := ref.(reference.Canonical) switch { case isTagged && isDigested: // This should not happen, docker/reference.ParseNamed drops the tag. - return "", fmt.Errorf("Unexpected Docker reference %s with both a name and a digest", ref.String()) + return "", errors.Errorf("Unexpected Docker reference %s with both a name and a digest", ref.String()) case !isTagged && !isDigested: // This should not happen, the caller is expected to ensure !reference.IsNameOnly() - return "", fmt.Errorf("Internal inconsistency: Docker reference %s with neither a tag nor a digest", ref.String()) + return "", errors.Errorf("Internal inconsistency: Docker reference %s with neither a tag nor a digest", ref.String()) case isTagged: res = res + ":" + tagged.Tag() case isDigested: diff --git a/vendor/github.com/containers/image/docker/reference/reference.go b/vendor/github.com/containers/image/docker/reference/reference.go index de26e44c..fb9c0175 100644 --- a/vendor/github.com/containers/image/docker/reference/reference.go +++ b/vendor/github.com/containers/image/docker/reference/reference.go @@ -1,13 +1,16 @@ package reference import ( - "errors" - "fmt" "regexp" "strings" - "github.com/docker/distribution/digest" + // "opencontainers/go-digest" requires us to load the algorithms that we + // want to use into the binary (it calls .Available). + _ "crypto/sha256" + distreference "github.com/docker/distribution/reference" + "github.com/opencontainers/go-digest" + "github.com/pkg/errors" ) const ( @@ -55,14 +58,18 @@ type Canonical interface { func ParseNamed(s string) (Named, error) { named, err := distreference.ParseNamed(s) if err != nil { - return nil, fmt.Errorf("Error parsing reference: %q is not a valid repository/tag", s) + return nil, errors.Wrapf(err, "Error parsing reference: %q is not a valid repository/tag", s) } r, err := WithName(named.Name()) if err != nil { return nil, err } if canonical, isCanonical := named.(distreference.Canonical); isCanonical { - return WithDigest(r, canonical.Digest()) + r, err := distreference.WithDigest(r, canonical.Digest()) + if err != nil { + return nil, err + } + return &canonicalRef{namedRef{r}}, nil } if tagged, isTagged := named.(distreference.NamedTagged); isTagged { return WithTag(r, tagged.Tag()) @@ -97,16 +104,6 @@ func WithTag(name Named, tag string) (NamedTagged, error) { return &taggedRef{namedRef{r}}, nil } -// WithDigest combines the name from "name" and the digest from "digest" to form -// a reference incorporating both the name and the digest. -func WithDigest(name Named, digest digest.Digest) (Canonical, error) { - r, err := distreference.WithDigest(name, digest) - if err != nil { - return nil, err - } - return &canonicalRef{namedRef{r}}, nil -} - type namedRef struct { distreference.Named } @@ -133,7 +130,7 @@ func (r *taggedRef) Tag() string { return r.namedRef.Named.(distreference.NamedTagged).Tag() } func (r *canonicalRef) Digest() digest.Digest { - return r.namedRef.Named.(distreference.Canonical).Digest() + return digest.Digest(r.namedRef.Named.(distreference.Canonical).Digest()) } // WithDefaultTag adds a default tag to a reference if it only has a repo name. @@ -161,7 +158,7 @@ func ParseIDOrReference(idOrRef string) (digest.Digest, Named, error) { if err := validateID(idOrRef); err == nil { idOrRef = "sha256:" + idOrRef } - if dgst, err := digest.ParseDigest(idOrRef); err == nil { + if dgst, err := digest.Parse(idOrRef); err == nil { return dgst, nil, nil } ref, err := ParseNamed(idOrRef) @@ -207,14 +204,14 @@ var validHex = regexp.MustCompile(`^([a-f0-9]{64})$`) func validateID(id string) error { if ok := validHex.MatchString(id); !ok { - return fmt.Errorf("image ID %q is invalid", id) + return errors.Errorf("image ID %q is invalid", id) } return nil } func validateName(name string) error { if err := validateID(name); err == nil { - return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", name) + return errors.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", name) } return nil } diff --git a/vendor/github.com/containers/image/image/docker_list.go b/vendor/github.com/containers/image/image/docker_list.go index bfb3b570..47679415 100644 --- a/vendor/github.com/containers/image/image/docker_list.go +++ b/vendor/github.com/containers/image/image/docker_list.go @@ -2,13 +2,12 @@ package image import ( "encoding/json" - "errors" - "fmt" "runtime" "github.com/containers/image/manifest" "github.com/containers/image/types" - "github.com/docker/distribution/digest" + "github.com/opencontainers/go-digest" + "github.com/pkg/errors" ) type platformSpec struct { @@ -54,10 +53,10 @@ func manifestSchema2FromManifestList(src types.ImageSource, manblob []byte) (gen matches, err := manifest.MatchesDigest(manblob, targetManifestDigest) if err != nil { - return nil, fmt.Errorf("Error computing manifest digest: %v", err) + return nil, errors.Wrap(err, "Error computing manifest digest") } if !matches { - return nil, fmt.Errorf("Manifest image does not match selected manifest digest %s", targetManifestDigest) + return nil, errors.Errorf("Manifest image does not match selected manifest digest %s", targetManifestDigest) } return manifestInstanceFromBlob(src, manblob, mt) diff --git a/vendor/github.com/containers/image/image/docker_schema1.go b/vendor/github.com/containers/image/image/docker_schema1.go index c2ad9a7d..dce81a14 100644 --- a/vendor/github.com/containers/image/image/docker_schema1.go +++ b/vendor/github.com/containers/image/image/docker_schema1.go @@ -2,8 +2,6 @@ package image import ( "encoding/json" - "errors" - "fmt" "regexp" "strings" "time" @@ -11,7 +9,8 @@ import ( "github.com/containers/image/docker/reference" "github.com/containers/image/manifest" "github.com/containers/image/types" - "github.com/docker/distribution/digest" + "github.com/opencontainers/go-digest" + "github.com/pkg/errors" ) var ( @@ -54,7 +53,7 @@ func manifestSchema1FromManifest(manifest []byte) (genericManifest, error) { return nil, err } if mschema1.SchemaVersion != 1 { - return nil, fmt.Errorf("unsupported schema version %d", mschema1.SchemaVersion) + return nil, errors.Errorf("unsupported schema version %d", mschema1.SchemaVersion) } if len(mschema1.FSLayers) != len(mschema1.History) { return nil, errors.New("length of history not equal to number of layers") @@ -153,7 +152,7 @@ func (m *manifestSchema1) UpdatedImage(options types.ManifestUpdateOptions) (typ if options.LayerInfos != nil { // Our LayerInfos includes empty layers (where m.History.V1Compatibility->ThrowAway), so expect them to be included here as well. if len(copy.FSLayers) != len(options.LayerInfos) { - return nil, fmt.Errorf("Error preparing updated manifest: layer count changed from %d to %d", len(copy.FSLayers), len(options.LayerInfos)) + return nil, errors.Errorf("Error preparing updated manifest: layer count changed from %d to %d", len(copy.FSLayers), len(options.LayerInfos)) } for i, info := range options.LayerInfos { // (docker push) sets up m.History.V1Compatibility->{Id,Parent} based on values of info.Digest, @@ -171,7 +170,7 @@ func (m *manifestSchema1) UpdatedImage(options types.ManifestUpdateOptions) (typ case manifest.DockerV2Schema2MediaType: return copy.convertToManifestSchema2(options.InformationOnly.LayerInfos, options.InformationOnly.LayerDiffIDs) default: - return nil, fmt.Errorf("Conversion of image manifest from %s to %s is not implemented", manifest.DockerV2Schema1SignedMediaType, options.ManifestMIMEType) + return nil, errors.Errorf("Conversion of image manifest from %s to %s is not implemented", manifest.DockerV2Schema1SignedMediaType, options.ManifestMIMEType) } return memoryImageFromManifest(©), nil @@ -211,7 +210,7 @@ func fixManifestLayers(manifest *manifestSchema1) error { for _, img := range imgs { // skip IDs that appear after each other, we handle those later if _, exists := idmap[img.ID]; img.ID != lastID && exists { - return fmt.Errorf("ID %+v appears multiple times in manifest", img.ID) + return errors.Errorf("ID %+v appears multiple times in manifest", img.ID) } lastID = img.ID idmap[lastID] = struct{}{} @@ -222,7 +221,7 @@ func fixManifestLayers(manifest *manifestSchema1) error { manifest.FSLayers = append(manifest.FSLayers[:i], manifest.FSLayers[i+1:]...) manifest.History = append(manifest.History[:i], manifest.History[i+1:]...) } else if imgs[i].Parent != imgs[i+1].ID { - return fmt.Errorf("Invalid parent ID. Expected %v, got %v", imgs[i+1].ID, imgs[i].Parent) + return errors.Errorf("Invalid parent ID. Expected %v, got %v", imgs[i+1].ID, imgs[i].Parent) } } return nil @@ -230,7 +229,7 @@ func fixManifestLayers(manifest *manifestSchema1) error { func validateV1ID(id string) error { if ok := validHex.MatchString(id); !ok { - return fmt.Errorf("image ID %q is invalid", id) + return errors.Errorf("image ID %q is invalid", id) } return nil } @@ -239,16 +238,16 @@ func validateV1ID(id string) error { func (m *manifestSchema1) convertToManifestSchema2(uploadedLayerInfos []types.BlobInfo, layerDiffIDs []digest.Digest) (types.Image, error) { if len(m.History) == 0 { // What would this even mean?! Anyhow, the rest of the code depends on fsLayers[0] and history[0] existing. - return nil, fmt.Errorf("Cannot convert an image with 0 history entries to %s", manifest.DockerV2Schema2MediaType) + return nil, errors.Errorf("Cannot convert an image with 0 history entries to %s", manifest.DockerV2Schema2MediaType) } if len(m.History) != len(m.FSLayers) { - return nil, fmt.Errorf("Inconsistent schema 1 manifest: %d history entries, %d fsLayers entries", len(m.History), len(m.FSLayers)) + return nil, errors.Errorf("Inconsistent schema 1 manifest: %d history entries, %d fsLayers entries", len(m.History), len(m.FSLayers)) } if len(uploadedLayerInfos) != len(m.FSLayers) { - return nil, fmt.Errorf("Internal error: uploaded %d blobs, but schema1 manifest has %d fsLayers", len(uploadedLayerInfos), len(m.FSLayers)) + return nil, errors.Errorf("Internal error: uploaded %d blobs, but schema1 manifest has %d fsLayers", len(uploadedLayerInfos), len(m.FSLayers)) } if len(layerDiffIDs) != len(m.FSLayers) { - return nil, fmt.Errorf("Internal error: collected %d DiffID values, but schema1 manifest has %d fsLayers", len(layerDiffIDs), len(m.FSLayers)) + return nil, errors.Errorf("Internal error: collected %d DiffID values, but schema1 manifest has %d fsLayers", len(layerDiffIDs), len(m.FSLayers)) } rootFS := rootFS{ @@ -263,7 +262,7 @@ func (m *manifestSchema1) convertToManifestSchema2(uploadedLayerInfos []types.Bl var v1compat v1Compatibility if err := json.Unmarshal([]byte(m.History[v1Index].V1Compatibility), &v1compat); err != nil { - return nil, fmt.Errorf("Error decoding history entry %d: %v", v1Index, err) + return nil, errors.Wrapf(err, "Error decoding history entry %d", v1Index) } history[v2Index] = imageHistory{ Created: v1compat.Created, diff --git a/vendor/github.com/containers/image/image/docker_schema2.go b/vendor/github.com/containers/image/image/docker_schema2.go index 812d3369..a40d53b2 100644 --- a/vendor/github.com/containers/image/image/docker_schema2.go +++ b/vendor/github.com/containers/image/image/docker_schema2.go @@ -5,14 +5,14 @@ import ( "crypto/sha256" "encoding/hex" "encoding/json" - "fmt" "io/ioutil" "strings" "github.com/Sirupsen/logrus" "github.com/containers/image/manifest" "github.com/containers/image/types" - "github.com/docker/distribution/digest" + "github.com/opencontainers/go-digest" + "github.com/pkg/errors" ) // gzippedEmptyLayer is a gzip-compressed version of an empty tar file (1024 NULL bytes) @@ -82,7 +82,7 @@ func (m *manifestSchema2) ConfigInfo() types.BlobInfo { func (m *manifestSchema2) ConfigBlob() ([]byte, error) { if m.configBlob == nil { if m.src == nil { - return nil, fmt.Errorf("Internal error: neither src nor configBlob set in manifestSchema2") + return nil, errors.Errorf("Internal error: neither src nor configBlob set in manifestSchema2") } stream, _, err := m.src.GetBlob(types.BlobInfo{ Digest: m.ConfigDescriptor.Digest, @@ -99,7 +99,7 @@ func (m *manifestSchema2) ConfigBlob() ([]byte, error) { } computedDigest := digest.FromBytes(blob) if computedDigest != m.ConfigDescriptor.Digest { - return nil, fmt.Errorf("Download config.json digest %s does not match expected %s", computedDigest, m.ConfigDescriptor.Digest) + return nil, errors.Errorf("Download config.json digest %s does not match expected %s", computedDigest, m.ConfigDescriptor.Digest) } m.configBlob = blob } @@ -152,7 +152,7 @@ func (m *manifestSchema2) UpdatedImage(options types.ManifestUpdateOptions) (typ copy := *m // NOTE: This is not a deep copy, it still shares slices etc. if options.LayerInfos != nil { if len(copy.LayersDescriptors) != len(options.LayerInfos) { - return nil, fmt.Errorf("Error preparing updated manifest: layer count changed from %d to %d", len(copy.LayersDescriptors), len(options.LayerInfos)) + return nil, errors.Errorf("Error preparing updated manifest: layer count changed from %d to %d", len(copy.LayersDescriptors), len(options.LayerInfos)) } copy.LayersDescriptors = make([]descriptor, len(options.LayerInfos)) for i, info := range options.LayerInfos { @@ -167,7 +167,7 @@ func (m *manifestSchema2) UpdatedImage(options types.ManifestUpdateOptions) (typ case manifest.DockerV2Schema1SignedMediaType, manifest.DockerV2Schema1MediaType: return copy.convertToManifestSchema1(options.InformationOnly.Destination) default: - return nil, fmt.Errorf("Conversion of image manifest from %s to %s is not implemented", manifest.DockerV2Schema2MediaType, options.ManifestMIMEType) + return nil, errors.Errorf("Conversion of image manifest from %s to %s is not implemented", manifest.DockerV2Schema2MediaType, options.ManifestMIMEType) } return memoryImageFromManifest(©), nil @@ -193,7 +193,7 @@ func (m *manifestSchema2) convertToManifestSchema1(dest types.ImageDestination) haveGzippedEmptyLayer := false if len(imageConfig.History) == 0 { // What would this even mean?! Anyhow, the rest of the code depends on fsLayers[0] and history[0] existing. - return nil, fmt.Errorf("Cannot convert an image with 0 history entries to %s", manifest.DockerV2Schema1SignedMediaType) + return nil, errors.Errorf("Cannot convert an image with 0 history entries to %s", manifest.DockerV2Schema1SignedMediaType) } for v2Index, historyEntry := range imageConfig.History { parentV1ID = v1ID @@ -205,17 +205,17 @@ func (m *manifestSchema2) convertToManifestSchema1(dest types.ImageDestination) logrus.Debugf("Uploading empty layer during conversion to schema 1") info, err := dest.PutBlob(bytes.NewReader(gzippedEmptyLayer), types.BlobInfo{Digest: gzippedEmptyLayerDigest, Size: int64(len(gzippedEmptyLayer))}) if err != nil { - return nil, fmt.Errorf("Error uploading empty layer: %v", err) + return nil, errors.Wrap(err, "Error uploading empty layer") } if info.Digest != gzippedEmptyLayerDigest { - return nil, fmt.Errorf("Internal error: Uploaded empty layer has digest %#v instead of %s", info.Digest, gzippedEmptyLayerDigest) + return nil, errors.Errorf("Internal error: Uploaded empty layer has digest %#v instead of %s", info.Digest, gzippedEmptyLayerDigest) } haveGzippedEmptyLayer = true } blobDigest = gzippedEmptyLayerDigest } else { if nonemptyLayerIndex >= len(m.LayersDescriptors) { - return nil, fmt.Errorf("Invalid image configuration, needs more than the %d distributed layers", len(m.LayersDescriptors)) + return nil, errors.Errorf("Invalid image configuration, needs more than the %d distributed layers", len(m.LayersDescriptors)) } blobDigest = m.LayersDescriptors[nonemptyLayerIndex].Digest nonemptyLayerIndex++ @@ -239,7 +239,7 @@ func (m *manifestSchema2) convertToManifestSchema1(dest types.ImageDestination) fakeImage.ContainerConfig.Cmd = []string{historyEntry.CreatedBy} v1CompatibilityBytes, err := json.Marshal(&fakeImage) if err != nil { - return nil, fmt.Errorf("Internal error: Error creating v1compatibility for %#v", fakeImage) + return nil, errors.Errorf("Internal error: Error creating v1compatibility for %#v", fakeImage) } fsLayers[v1Index] = fsLayersSchema1{BlobSum: blobDigest} diff --git a/vendor/github.com/containers/image/image/manifest.go b/vendor/github.com/containers/image/image/manifest.go index df3e23f4..6b82b0be 100644 --- a/vendor/github.com/containers/image/image/manifest.go +++ b/vendor/github.com/containers/image/image/manifest.go @@ -3,8 +3,8 @@ package image import ( "time" - "github.com/docker/distribution/digest" "github.com/docker/engine-api/types/strslice" + "github.com/opencontainers/go-digest" "github.com/containers/image/manifest" "github.com/containers/image/types" diff --git a/vendor/github.com/containers/image/image/memory.go b/vendor/github.com/containers/image/image/memory.go index 9404db82..1a3faa02 100644 --- a/vendor/github.com/containers/image/image/memory.go +++ b/vendor/github.com/containers/image/image/memory.go @@ -1,7 +1,7 @@ package image import ( - "errors" + "github.com/pkg/errors" "github.com/containers/image/types" ) @@ -37,11 +37,7 @@ func (i *memoryImage) Close() { // Size returns the size of the image as stored, if known, or -1 if not. func (i *memoryImage) Size() (int64, error) { - s, err := i.serialize() - if err != nil { - return -1, err - } - return int64(len(s)), nil + return -1, nil } // Manifest is like ImageSource.GetManifest, but the result is cached; it is OK to call this however often you need. diff --git a/vendor/github.com/containers/image/image/oci.go b/vendor/github.com/containers/image/image/oci.go index 6ca627a9..969bb61a 100644 --- a/vendor/github.com/containers/image/image/oci.go +++ b/vendor/github.com/containers/image/image/oci.go @@ -2,13 +2,13 @@ package image import ( "encoding/json" - "fmt" "io/ioutil" "github.com/containers/image/manifest" "github.com/containers/image/types" - "github.com/docker/distribution/digest" + "github.com/opencontainers/go-digest" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" ) type manifestOCI1 struct { @@ -59,7 +59,7 @@ func (m *manifestOCI1) ConfigInfo() types.BlobInfo { func (m *manifestOCI1) ConfigBlob() ([]byte, error) { if m.configBlob == nil { if m.src == nil { - return nil, fmt.Errorf("Internal error: neither src nor configBlob set in manifestOCI1") + return nil, errors.Errorf("Internal error: neither src nor configBlob set in manifestOCI1") } stream, _, err := m.src.GetBlob(types.BlobInfo{ Digest: m.ConfigDescriptor.Digest, @@ -76,7 +76,7 @@ func (m *manifestOCI1) ConfigBlob() ([]byte, error) { } computedDigest := digest.FromBytes(blob) if computedDigest != m.ConfigDescriptor.Digest { - return nil, fmt.Errorf("Download config.json digest %s does not match expected %s", computedDigest, m.ConfigDescriptor.Digest) + return nil, errors.Errorf("Download config.json digest %s does not match expected %s", computedDigest, m.ConfigDescriptor.Digest) } m.configBlob = blob } @@ -125,7 +125,7 @@ func (m *manifestOCI1) UpdatedImage(options types.ManifestUpdateOptions) (types. copy := *m // NOTE: This is not a deep copy, it still shares slices etc. if options.LayerInfos != nil { if len(copy.LayersDescriptors) != len(options.LayerInfos) { - return nil, fmt.Errorf("Error preparing updated manifest: layer count changed from %d to %d", len(copy.LayersDescriptors), len(options.LayerInfos)) + return nil, errors.Errorf("Error preparing updated manifest: layer count changed from %d to %d", len(copy.LayersDescriptors), len(options.LayerInfos)) } copy.LayersDescriptors = make([]descriptor, len(options.LayerInfos)) for i, info := range options.LayerInfos { @@ -139,7 +139,7 @@ func (m *manifestOCI1) UpdatedImage(options types.ManifestUpdateOptions) (types. case manifest.DockerV2Schema2MediaType: return copy.convertToManifestSchema2() default: - return nil, fmt.Errorf("Conversion of image manifest from %s to %s is not implemented", imgspecv1.MediaTypeImageManifest, options.ManifestMIMEType) + return nil, errors.Errorf("Conversion of image manifest from %s to %s is not implemented", imgspecv1.MediaTypeImageManifest, options.ManifestMIMEType) } return memoryImageFromManifest(©), nil diff --git a/vendor/github.com/containers/image/image/unparsed.go b/vendor/github.com/containers/image/image/unparsed.go index 6cedc08e..1e1ee0b5 100644 --- a/vendor/github.com/containers/image/image/unparsed.go +++ b/vendor/github.com/containers/image/image/unparsed.go @@ -1,11 +1,10 @@ package image import ( - "fmt" - "github.com/containers/image/docker/reference" "github.com/containers/image/manifest" "github.com/containers/image/types" + "github.com/pkg/errors" ) // UnparsedImage implements types.UnparsedImage . @@ -56,10 +55,10 @@ func (i *UnparsedImage) Manifest() ([]byte, string, error) { digest := canonical.Digest() matches, err := manifest.MatchesDigest(m, digest) if err != nil { - return nil, "", fmt.Errorf("Error computing manifest digest: %v", err) + return nil, "", errors.Wrap(err, "Error computing manifest digest") } if !matches { - return nil, "", fmt.Errorf("Manifest does not match provided manifest digest %s", digest) + return nil, "", errors.Errorf("Manifest does not match provided manifest digest %s", digest) } } } diff --git a/vendor/github.com/containers/image/manifest/manifest.go b/vendor/github.com/containers/image/manifest/manifest.go index 0702caa9..430331f0 100644 --- a/vendor/github.com/containers/image/manifest/manifest.go +++ b/vendor/github.com/containers/image/manifest/manifest.go @@ -3,8 +3,8 @@ package manifest import ( "encoding/json" - "github.com/docker/distribution/digest" "github.com/docker/libtrust" + "github.com/opencontainers/go-digest" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" ) diff --git a/vendor/github.com/containers/image/oci/layout/oci_dest.go b/vendor/github.com/containers/image/oci/layout/oci_dest.go index 1c849e0d..4f4a12c0 100644 --- a/vendor/github.com/containers/image/oci/layout/oci_dest.go +++ b/vendor/github.com/containers/image/oci/layout/oci_dest.go @@ -2,16 +2,16 @@ package layout import ( "encoding/json" - "errors" - "fmt" "io" "io/ioutil" "os" "path/filepath" + "github.com/pkg/errors" + "github.com/containers/image/manifest" "github.com/containers/image/types" - "github.com/docker/distribution/digest" + "github.com/opencontainers/go-digest" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" ) @@ -44,7 +44,7 @@ func (d *ociImageDestination) SupportedManifestMIMETypes() []string { // SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures. // Note: It is still possible for PutSignatures to fail if SupportsSignatures returns nil. func (d *ociImageDestination) SupportsSignatures() error { - return fmt.Errorf("Pushing signatures for OCI images is not supported") + return errors.Errorf("Pushing signatures for OCI images is not supported") } // ShouldCompressLayers returns true iff it is desirable to compress layer blobs written to this destination. @@ -80,7 +80,7 @@ func (d *ociImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobInfo } }() - digester := digest.Canonical.New() + digester := digest.Canonical.Digester() tee := io.TeeReader(stream, digester.Hash()) size, err := io.Copy(blobFile, tee) @@ -89,7 +89,7 @@ func (d *ociImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobInfo } computedDigest := digester.Digest() if inputInfo.Size != -1 && size != inputInfo.Size { - return types.BlobInfo{}, fmt.Errorf("Size mismatch when copying %s, expected %d, got %d", computedDigest, inputInfo.Size, size) + return types.BlobInfo{}, errors.Errorf("Size mismatch when copying %s, expected %d, got %d", computedDigest, inputInfo.Size, size) } if err := blobFile.Sync(); err != nil { return types.BlobInfo{}, err @@ -114,7 +114,7 @@ func (d *ociImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobInfo func (d *ociImageDestination) HasBlob(info types.BlobInfo) (bool, int64, error) { if info.Digest == "" { - return false, -1, fmt.Errorf(`"Can not check for a blob with unknown digest`) + return false, -1, errors.Errorf(`"Can not check for a blob with unknown digest`) } blobPath, err := d.ref.blobPath(info.Digest) if err != nil { @@ -169,7 +169,7 @@ func createManifest(m []byte) ([]byte, string, error) { case imgspecv1.MediaTypeImageManifest: return m, mt, nil } - return nil, "", fmt.Errorf("unrecognized manifest media type %q", mt) + return nil, "", errors.Errorf("unrecognized manifest media type %q", mt) } func (d *ociImageDestination) PutManifest(m []byte) error { @@ -227,7 +227,7 @@ func ensureParentDirectoryExists(path string) error { func (d *ociImageDestination) PutSignatures(signatures [][]byte) error { if len(signatures) != 0 { - return fmt.Errorf("Pushing signatures for OCI images is not supported") + return errors.Errorf("Pushing signatures for OCI images is not supported") } return nil } diff --git a/vendor/github.com/containers/image/oci/layout/oci_src.go b/vendor/github.com/containers/image/oci/layout/oci_src.go index e1501b94..1b148d27 100644 --- a/vendor/github.com/containers/image/oci/layout/oci_src.go +++ b/vendor/github.com/containers/image/oci/layout/oci_src.go @@ -8,7 +8,7 @@ import ( "github.com/containers/image/manifest" "github.com/containers/image/types" - "github.com/docker/distribution/digest" + "github.com/opencontainers/go-digest" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" ) diff --git a/vendor/github.com/containers/image/oci/layout/oci_transport.go b/vendor/github.com/containers/image/oci/layout/oci_transport.go index 7faddf97..734af87c 100644 --- a/vendor/github.com/containers/image/oci/layout/oci_transport.go +++ b/vendor/github.com/containers/image/oci/layout/oci_transport.go @@ -1,7 +1,6 @@ package layout import ( - "errors" "fmt" "path/filepath" "regexp" @@ -11,7 +10,8 @@ import ( "github.com/containers/image/docker/reference" "github.com/containers/image/image" "github.com/containers/image/types" - "github.com/docker/distribution/digest" + "github.com/opencontainers/go-digest" + "github.com/pkg/errors" ) // Transport is an ImageTransport for OCI directories. @@ -43,16 +43,16 @@ func (t ociTransport) ValidatePolicyConfigurationScope(scope string) error { dir = scope[:sep] tag := scope[sep+1:] if !refRegexp.MatchString(tag) { - return fmt.Errorf("Invalid tag %s", tag) + return errors.Errorf("Invalid tag %s", tag) } } if strings.Contains(dir, ":") { - return fmt.Errorf("Invalid OCI reference %s: path contains a colon", scope) + return errors.Errorf("Invalid OCI reference %s: path contains a colon", scope) } if !strings.HasPrefix(dir, "/") { - return fmt.Errorf("Invalid scope %s: must be an absolute path", scope) + return errors.Errorf("Invalid scope %s: must be an absolute path", scope) } // Refuse also "/", otherwise "/" and "" would have the same semantics, // and "" could be unexpectedly shadowed by the "/" entry. @@ -62,7 +62,7 @@ func (t ociTransport) ValidatePolicyConfigurationScope(scope string) error { } cleaned := filepath.Clean(dir) if cleaned != dir { - return fmt.Errorf(`Invalid scope %s: Uses non-canonical path format, perhaps try with path %s`, scope, cleaned) + return errors.Errorf(`Invalid scope %s: Uses non-canonical path format, perhaps try with path %s`, scope, cleaned) } return nil } @@ -106,10 +106,10 @@ func NewReference(dir, tag string) (types.ImageReference, error) { // This is necessary to prevent directory paths returned by PolicyConfigurationNamespaces // from being ambiguous with values of PolicyConfigurationIdentity. if strings.Contains(resolved, ":") { - return nil, fmt.Errorf("Invalid OCI reference %s:%s: path %s contains a colon", dir, tag, resolved) + return nil, errors.Errorf("Invalid OCI reference %s:%s: path %s contains a colon", dir, tag, resolved) } if !refRegexp.MatchString(tag) { - return nil, fmt.Errorf("Invalid tag %s", tag) + return nil, errors.Errorf("Invalid tag %s", tag) } return ociReference{dir: dir, resolvedDir: resolved, tag: tag}, nil } @@ -191,7 +191,7 @@ func (ref ociReference) NewImageDestination(ctx *types.SystemContext) (types.Ima // DeleteImage deletes the named image from the registry, if supported. func (ref ociReference) DeleteImage(ctx *types.SystemContext) error { - return fmt.Errorf("Deleting images not implemented for oci: images") + return errors.Errorf("Deleting images not implemented for oci: images") } // ociLayoutPathPath returns a path for the oci-layout within a directory using OCI conventions. @@ -202,7 +202,7 @@ func (ref ociReference) ociLayoutPath() string { // blobPath returns a path for a blob within a directory using OCI image-layout conventions. func (ref ociReference) blobPath(digest digest.Digest) (string, error) { if err := digest.Validate(); err != nil { - return "", fmt.Errorf("unexpected digest reference %s: %v", digest, err) + return "", errors.Wrapf(err, "unexpected digest reference %s", digest) } return filepath.Join(ref.dir, "blobs", digest.Algorithm().String(), digest.Hex()), nil } diff --git a/vendor/github.com/containers/image/openshift/openshift-copies.go b/vendor/github.com/containers/image/openshift/openshift-copies.go index 84c8ea96..5b9dda3c 100644 --- a/vendor/github.com/containers/image/openshift/openshift-copies.go +++ b/vendor/github.com/containers/image/openshift/openshift-copies.go @@ -4,7 +4,6 @@ import ( "crypto/tls" "crypto/x509" "encoding/json" - "errors" "fmt" "io/ioutil" "net" @@ -18,6 +17,7 @@ import ( "github.com/ghodss/yaml" "github.com/imdario/mergo" + "github.com/pkg/errors" utilerrors "k8s.io/kubernetes/pkg/util/errors" "k8s.io/kubernetes/pkg/util/homedir" utilnet "k8s.io/kubernetes/pkg/util/net" @@ -348,20 +348,20 @@ func validateClusterInfo(clusterName string, clusterInfo clientcmdCluster) []err if len(clusterInfo.Server) == 0 { if len(clusterName) == 0 { - validationErrors = append(validationErrors, fmt.Errorf("default cluster has no server defined")) + validationErrors = append(validationErrors, errors.Errorf("default cluster has no server defined")) } else { - validationErrors = append(validationErrors, fmt.Errorf("no server found for cluster %q", clusterName)) + validationErrors = append(validationErrors, errors.Errorf("no server found for cluster %q", clusterName)) } } // Make sure CA data and CA file aren't both specified if len(clusterInfo.CertificateAuthority) != 0 && len(clusterInfo.CertificateAuthorityData) != 0 { - validationErrors = append(validationErrors, fmt.Errorf("certificate-authority-data and certificate-authority are both specified for %v. certificate-authority-data will override", clusterName)) + validationErrors = append(validationErrors, errors.Errorf("certificate-authority-data and certificate-authority are both specified for %v. certificate-authority-data will override", clusterName)) } if len(clusterInfo.CertificateAuthority) != 0 { clientCertCA, err := os.Open(clusterInfo.CertificateAuthority) defer clientCertCA.Close() if err != nil { - validationErrors = append(validationErrors, fmt.Errorf("unable to read certificate-authority %v for %v due to %v", clusterInfo.CertificateAuthority, clusterName, err)) + validationErrors = append(validationErrors, errors.Errorf("unable to read certificate-authority %v for %v due to %v", clusterInfo.CertificateAuthority, clusterName, err)) } } @@ -385,36 +385,36 @@ func validateAuthInfo(authInfoName string, authInfo clientcmdAuthInfo) []error { if len(authInfo.ClientCertificate) != 0 || len(authInfo.ClientCertificateData) != 0 { // Make sure cert data and file aren't both specified if len(authInfo.ClientCertificate) != 0 && len(authInfo.ClientCertificateData) != 0 { - validationErrors = append(validationErrors, fmt.Errorf("client-cert-data and client-cert are both specified for %v. client-cert-data will override", authInfoName)) + validationErrors = append(validationErrors, errors.Errorf("client-cert-data and client-cert are both specified for %v. client-cert-data will override", authInfoName)) } // Make sure key data and file aren't both specified if len(authInfo.ClientKey) != 0 && len(authInfo.ClientKeyData) != 0 { - validationErrors = append(validationErrors, fmt.Errorf("client-key-data and client-key are both specified for %v; client-key-data will override", authInfoName)) + validationErrors = append(validationErrors, errors.Errorf("client-key-data and client-key are both specified for %v; client-key-data will override", authInfoName)) } // Make sure a key is specified if len(authInfo.ClientKey) == 0 && len(authInfo.ClientKeyData) == 0 { - validationErrors = append(validationErrors, fmt.Errorf("client-key-data or client-key must be specified for %v to use the clientCert authentication method", authInfoName)) + validationErrors = append(validationErrors, errors.Errorf("client-key-data or client-key must be specified for %v to use the clientCert authentication method", authInfoName)) } if len(authInfo.ClientCertificate) != 0 { clientCertFile, err := os.Open(authInfo.ClientCertificate) defer clientCertFile.Close() if err != nil { - validationErrors = append(validationErrors, fmt.Errorf("unable to read client-cert %v for %v due to %v", authInfo.ClientCertificate, authInfoName, err)) + validationErrors = append(validationErrors, errors.Errorf("unable to read client-cert %v for %v due to %v", authInfo.ClientCertificate, authInfoName, err)) } } if len(authInfo.ClientKey) != 0 { clientKeyFile, err := os.Open(authInfo.ClientKey) defer clientKeyFile.Close() if err != nil { - validationErrors = append(validationErrors, fmt.Errorf("unable to read client-key %v for %v due to %v", authInfo.ClientKey, authInfoName, err)) + validationErrors = append(validationErrors, errors.Errorf("unable to read client-key %v for %v due to %v", authInfo.ClientKey, authInfoName, err)) } } } // authPath also provides information for the client to identify the server, so allow multiple auth methods in that case if (len(methods) > 1) && (!usingAuthPath) { - validationErrors = append(validationErrors, fmt.Errorf("more than one authentication method found for %v; found %v, only one is allowed", authInfoName, methods)) + validationErrors = append(validationErrors, errors.Errorf("more than one authentication method found for %v; found %v, only one is allowed", authInfoName, methods)) } return validationErrors @@ -518,7 +518,7 @@ func (rules *clientConfigLoadingRules) Load() (*clientcmdConfig, error) { continue } if err != nil { - errlist = append(errlist, fmt.Errorf("Error loading config file \"%s\": %v", filename, err)) + errlist = append(errlist, errors.Wrapf(err, "Error loading config file \"%s\"", filename)) continue } @@ -623,7 +623,7 @@ func resolveLocalPaths(config *clientcmdConfig) error { } base, err := filepath.Abs(filepath.Dir(cluster.LocationOfOrigin)) if err != nil { - return fmt.Errorf("Could not determine the absolute path of config file %s: %v", cluster.LocationOfOrigin, err) + return errors.Wrapf(err, "Could not determine the absolute path of config file %s", cluster.LocationOfOrigin) } if err := resolvePaths(getClusterFileReferences(cluster), base); err != nil { @@ -636,7 +636,7 @@ func resolveLocalPaths(config *clientcmdConfig) error { } base, err := filepath.Abs(filepath.Dir(authInfo.LocationOfOrigin)) if err != nil { - return fmt.Errorf("Could not determine the absolute path of config file %s: %v", authInfo.LocationOfOrigin, err) + return errors.Wrapf(err, "Could not determine the absolute path of config file %s", authInfo.LocationOfOrigin) } if err := resolvePaths(getAuthInfoFileReferences(authInfo), base); err != nil { @@ -706,7 +706,7 @@ func restClientFor(config *restConfig) (*url.URL, *http.Client, error) { // Kubernetes API. func defaultServerURL(host string, defaultTLS bool) (*url.URL, error) { if host == "" { - return nil, fmt.Errorf("host must be a URL or a host:port pair") + return nil, errors.Errorf("host must be a URL or a host:port pair") } base := host hostURL, err := url.Parse(base) @@ -723,7 +723,7 @@ func defaultServerURL(host string, defaultTLS bool) (*url.URL, error) { return nil, err } if hostURL.Path != "" && hostURL.Path != "/" { - return nil, fmt.Errorf("host must be a URL or a host:port pair: %q", base) + return nil, errors.Errorf("host must be a URL or a host:port pair: %q", base) } } @@ -793,7 +793,7 @@ func transportNew(config *restConfig) (http.RoundTripper, error) { // REMOVED: HTTPWrappersForConfig(config, rt) in favor of the caller setting HTTP headers itself based on restConfig. Only this inlined check remains. if len(config.Username) != 0 && len(config.BearerToken) != 0 { - return nil, fmt.Errorf("username/password or bearer token may be set, but not both") + return nil, errors.Errorf("username/password or bearer token may be set, but not both") } return rt, nil @@ -832,7 +832,7 @@ func tlsConfigFor(c *restConfig) (*tls.Config, error) { return nil, nil } if c.HasCA() && c.Insecure { - return nil, fmt.Errorf("specifying a root certificates file with the insecure flag is not allowed") + return nil, errors.Errorf("specifying a root certificates file with the insecure flag is not allowed") } if err := loadTLSFiles(c); err != nil { return nil, err diff --git a/vendor/github.com/containers/image/openshift/openshift.go b/vendor/github.com/containers/image/openshift/openshift.go index a8f66681..1fc0e24c 100644 --- a/vendor/github.com/containers/image/openshift/openshift.go +++ b/vendor/github.com/containers/image/openshift/openshift.go @@ -4,7 +4,6 @@ import ( "bytes" "crypto/rand" "encoding/json" - "errors" "fmt" "io" "io/ioutil" @@ -17,7 +16,8 @@ import ( "github.com/containers/image/manifest" "github.com/containers/image/types" "github.com/containers/image/version" - "github.com/docker/distribution/digest" + "github.com/opencontainers/go-digest" + "github.com/pkg/errors" ) // openshiftClient is configuration for dealing with a single image stream, for reading or writing. @@ -124,7 +124,7 @@ func (c *openshiftClient) doRequest(method, path string, requestBody []byte) ([] if statusValid { return nil, errors.New(status.Message) } - return nil, fmt.Errorf("HTTP error: status code: %d, body: %s", res.StatusCode, string(body)) + return nil, errors.Errorf("HTTP error: status code: %d, body: %s", res.StatusCode, string(body)) } return body, nil @@ -151,7 +151,7 @@ func (c *openshiftClient) getImage(imageStreamImageName string) (*image, error) func (c *openshiftClient) convertDockerImageReference(ref string) (string, error) { parts := strings.SplitN(ref, "/", 2) if len(parts) != 2 { - return "", fmt.Errorf("Invalid format of docker reference %s: missing '/'", ref) + return "", errors.Errorf("Invalid format of docker reference %s: missing '/'", ref) } return c.ref.dockerReference.Hostname() + "/" + parts[1], nil } @@ -267,7 +267,7 @@ func (s *openshiftImageSource) ensureImageIsResolved() error { } } if te == nil { - return fmt.Errorf("No matching tag found") + return errors.Errorf("No matching tag found") } logrus.Debugf("tag event %#v", te) dockerRefString, err := s.client.convertDockerImageReference(te.DockerImageReference) @@ -386,7 +386,7 @@ func (d *openshiftImageDestination) PutManifest(m []byte) error { func (d *openshiftImageDestination) PutSignatures(signatures [][]byte) error { if d.imageStreamImageName == "" { - return fmt.Errorf("Internal error: Unknown manifest digest, can't add signatures") + return errors.Errorf("Internal error: Unknown manifest digest, can't add signatures") } // Because image signatures are a shared resource in Atomic Registry, the default upload // always adds signatures. Eventually we should also allow removing signatures. @@ -418,7 +418,7 @@ sigExists: randBytes := make([]byte, 16) n, err := rand.Read(randBytes) if err != nil || n != 16 { - return fmt.Errorf("Error generating random signature ID: %v, len %d", err, n) + return errors.Wrapf(err, "Error generating random signature len %d", n) } signatureName = fmt.Sprintf("%s@%032x", d.imageStreamImageName, randBytes) if _, ok := existingSigNames[signatureName]; !ok { diff --git a/vendor/github.com/containers/image/openshift/openshift_transport.go b/vendor/github.com/containers/image/openshift/openshift_transport.go index 9e2b106c..ef92a7cd 100644 --- a/vendor/github.com/containers/image/openshift/openshift_transport.go +++ b/vendor/github.com/containers/image/openshift/openshift_transport.go @@ -9,6 +9,7 @@ import ( "github.com/containers/image/docker/reference" genericImage "github.com/containers/image/image" "github.com/containers/image/types" + "github.com/pkg/errors" ) // Transport is an ImageTransport for OpenShift registry-hosted images. @@ -36,7 +37,7 @@ var scopeRegexp = regexp.MustCompile("^[^/]*(/[^:/]*(/[^:/]*(:[^:/]*)?)?)?$") // scope passed to this function will not be "", that value is always allowed. func (t openshiftTransport) ValidatePolicyConfigurationScope(scope string) error { if scopeRegexp.FindStringIndex(scope) == nil { - return fmt.Errorf("Invalid scope name %s", scope) + return errors.Errorf("Invalid scope name %s", scope) } return nil } @@ -52,11 +53,11 @@ type openshiftReference struct { func ParseReference(ref string) (types.ImageReference, error) { r, err := reference.ParseNamed(ref) if err != nil { - return nil, fmt.Errorf("failed to parse image reference %q, %v", ref, err) + return nil, errors.Wrapf(err, "failed to parse image reference %q", ref) } tagged, ok := r.(reference.NamedTagged) if !ok { - return nil, fmt.Errorf("invalid image reference %s, %#v", ref, r) + return nil, errors.Errorf("invalid image reference %s, %#v", ref, r) } return NewReference(tagged) } @@ -65,7 +66,7 @@ func ParseReference(ref string) (types.ImageReference, error) { func NewReference(dockerRef reference.NamedTagged) (types.ImageReference, error) { r := strings.SplitN(dockerRef.RemoteName(), "/", 3) if len(r) != 2 { - return nil, fmt.Errorf("invalid image reference %s", dockerRef.String()) + return nil, errors.Errorf("invalid image reference %s", dockerRef.String()) } return openshiftReference{ namespace: r[0], @@ -146,5 +147,5 @@ func (ref openshiftReference) NewImageDestination(ctx *types.SystemContext) (typ // DeleteImage deletes the named image from the registry, if supported. func (ref openshiftReference) DeleteImage(ctx *types.SystemContext) error { - return fmt.Errorf("Deleting images not implemented for atomic: images") + return errors.Errorf("Deleting images not implemented for atomic: images") } diff --git a/vendor/github.com/containers/image/signature/docker.go b/vendor/github.com/containers/image/signature/docker.go index b90da5ff..9f8a40a0 100644 --- a/vendor/github.com/containers/image/signature/docker.go +++ b/vendor/github.com/containers/image/signature/docker.go @@ -6,7 +6,7 @@ import ( "fmt" "github.com/containers/image/manifest" - "github.com/docker/distribution/digest" + "github.com/opencontainers/go-digest" ) // SignDockerManifest returns a signature for manifest as the specified dockerReference, diff --git a/vendor/github.com/containers/image/signature/mechanism.go b/vendor/github.com/containers/image/signature/mechanism.go index 9b1b47ff..196ad927 100644 --- a/vendor/github.com/containers/image/signature/mechanism.go +++ b/vendor/github.com/containers/image/signature/mechanism.go @@ -76,9 +76,6 @@ func (m gpgSigningMechanism) ImportKeysFromBytes(blob []byte) ([]string, error) func (m gpgSigningMechanism) Sign(input []byte, keyIdentity string) ([]byte, error) { key, err := m.ctx.GetKey(keyIdentity, true) if err != nil { - if e, ok := err.(gpgme.Error); ok && e.Code() == gpgme.ErrorEOF { - return nil, fmt.Errorf("key %q not found", keyIdentity) - } return nil, err } inputData, err := gpgme.NewDataBytes(input) diff --git a/vendor/github.com/containers/image/signature/policy_config.go b/vendor/github.com/containers/image/signature/policy_config.go index adac6888..e4525795 100644 --- a/vendor/github.com/containers/image/signature/policy_config.go +++ b/vendor/github.com/containers/image/signature/policy_config.go @@ -15,11 +15,12 @@ package signature import ( "encoding/json" - "errors" "fmt" "io/ioutil" "path/filepath" + "github.com/pkg/errors" + "github.com/containers/image/docker/reference" "github.com/containers/image/transports" "github.com/containers/image/types" @@ -405,7 +406,7 @@ func (pr *prSignedBy) UnmarshalJSON(data []byte) error { case !gotKeyPath && !gotKeyData: return InvalidPolicyFormatError("At least one of keyPath and keyData mus be specified") default: // Coverage: This should never happen - return fmt.Errorf("Impossible keyPath/keyData presence combination!?") + return errors.Errorf("Impossible keyPath/keyData presence combination!?") } if err != nil { return err diff --git a/vendor/github.com/containers/image/signature/policy_eval.go b/vendor/github.com/containers/image/signature/policy_eval.go index 4828ecc8..ba1fcc2a 100644 --- a/vendor/github.com/containers/image/signature/policy_eval.go +++ b/vendor/github.com/containers/image/signature/policy_eval.go @@ -6,10 +6,9 @@ package signature import ( - "fmt" - "github.com/Sirupsen/logrus" "github.com/containers/image/types" + "github.com/pkg/errors" ) // PolicyRequirementError is an explanatory text for rejecting a signature or an image. @@ -95,7 +94,7 @@ const ( // changeContextState changes pc.state, or fails if the state is unexpected func (pc *PolicyContext) changeState(expected, new policyContextState) error { if pc.state != expected { - return fmt.Errorf(`"Invalid PolicyContext state, expected "%s", found "%s"`, expected, pc.state) + return errors.Errorf(`"Invalid PolicyContext state, expected "%s", found "%s"`, expected, pc.state) } pc.state = new return nil diff --git a/vendor/github.com/containers/image/signature/policy_eval_signedby.go b/vendor/github.com/containers/image/signature/policy_eval_signedby.go index 595634ce..9d914019 100644 --- a/vendor/github.com/containers/image/signature/policy_eval_signedby.go +++ b/vendor/github.com/containers/image/signature/policy_eval_signedby.go @@ -3,15 +3,16 @@ package signature import ( - "errors" "fmt" "io/ioutil" "os" "strings" + "github.com/pkg/errors" + "github.com/containers/image/manifest" "github.com/containers/image/types" - "github.com/docker/distribution/digest" + "github.com/opencontainers/go-digest" ) func (pr *prSignedBy) isSignatureAuthorAccepted(image types.UnparsedImage, sig []byte) (signatureAcceptanceResult, *Signature, error) { @@ -19,10 +20,10 @@ func (pr *prSignedBy) isSignatureAuthorAccepted(image types.UnparsedImage, sig [ case SBKeyTypeGPGKeys: case SBKeyTypeSignedByGPGKeys, SBKeyTypeX509Certificates, SBKeyTypeSignedByX509CAs: // FIXME? Reject this at policy parsing time already? - return sarRejected, nil, fmt.Errorf(`"Unimplemented "keyType" value "%s"`, string(pr.KeyType)) + return sarRejected, nil, errors.Errorf(`"Unimplemented "keyType" value "%s"`, string(pr.KeyType)) default: // This should never happen, newPRSignedBy ensures KeyType.IsValid() - return sarRejected, nil, fmt.Errorf(`"Unknown "keyType" value "%s"`, string(pr.KeyType)) + return sarRejected, nil, errors.Errorf(`"Unknown "keyType" value "%s"`, string(pr.KeyType)) } if pr.KeyPath != "" && pr.KeyData != nil { @@ -116,7 +117,7 @@ func (pr *prSignedBy) isRunningImageAllowed(image types.UnparsedImage) (bool, er // Huh?! This should not happen at all; treat it as any other invalid value. fallthrough default: - reason = fmt.Errorf(`Internal error: Unexpected signature verification result "%s"`, string(res)) + reason = errors.Errorf(`Internal error: Unexpected signature verification result "%s"`, string(res)) } rejections = append(rejections, reason) } diff --git a/vendor/github.com/containers/image/signature/signature.go b/vendor/github.com/containers/image/signature/signature.go index b0c6e444..b2705a6d 100644 --- a/vendor/github.com/containers/image/signature/signature.go +++ b/vendor/github.com/containers/image/signature/signature.go @@ -4,12 +4,13 @@ package signature import ( "encoding/json" - "errors" "fmt" "time" + "github.com/pkg/errors" + "github.com/containers/image/version" - "github.com/docker/distribution/digest" + "github.com/opencontainers/go-digest" ) const ( diff --git a/vendor/github.com/containers/image/storage/storage_image.go b/vendor/github.com/containers/image/storage/storage_image.go index 132c5ad5..4d009464 100644 --- a/vendor/github.com/containers/image/storage/storage_image.go +++ b/vendor/github.com/containers/image/storage/storage_image.go @@ -3,12 +3,12 @@ package storage import ( "bytes" "encoding/json" - "errors" - "fmt" "io" "io/ioutil" "time" + "github.com/pkg/errors" + "github.com/Sirupsen/logrus" "github.com/containers/image/image" "github.com/containers/image/manifest" @@ -16,7 +16,7 @@ import ( "github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/ioutils" "github.com/containers/storage/storage" - ddigest "github.com/docker/distribution/digest" + ddigest "github.com/opencontainers/go-digest" ) var ( @@ -78,7 +78,7 @@ func newImageSource(imageRef storageReference) (*storageImageSource, error) { } img, err := imageRef.transport.store.GetImage(id) if err != nil { - return nil, fmt.Errorf("error reading image %q: %v", id, err) + return nil, errors.Wrapf(err, "error reading image %q", id) } image := &storageImageSource{ imageRef: imageRef, @@ -90,7 +90,7 @@ func newImageSource(imageRef storageReference) (*storageImageSource, error) { SignatureSizes: []int{}, } if err := json.Unmarshal([]byte(img.Metadata), image); err != nil { - return nil, fmt.Errorf("error decoding metadata for source image: %v", err) + return nil, errors.Wrap(err, "error decoding metadata for source image") } return image, nil } @@ -150,10 +150,10 @@ func (s *storageImageDestination) PutBlob(stream io.Reader, blobinfo types.BlobI // Set up to read the whole blob (the initial snippet, plus the rest) // while digesting it with either the default, or the passed-in digest, // if one was specified. - hasher := ddigest.Canonical.New() + hasher := ddigest.Canonical.Digester() if digest.Validate() == nil { if a := digest.Algorithm(); a.Available() { - hasher = a.New() + hasher = a.Digester() } } hash := "" @@ -278,7 +278,7 @@ func (s *storageImageDestination) PutBlob(stream io.Reader, blobinfo types.BlobI func (s *storageImageDestination) HasBlob(blobinfo types.BlobInfo) (bool, int64, error) { if blobinfo.Digest == "" { - return false, -1, fmt.Errorf(`"Can not check for a blob with unknown digest`) + return false, -1, errors.Errorf(`"Can not check for a blob with unknown digest`) } for _, blob := range s.BlobList { if blob.Digest == blobinfo.Digest { @@ -468,7 +468,7 @@ func diffLayer(store storage.Store, layerID string) (rc io.ReadCloser, n int64, } if layer.Metadata != "" { if err := json.Unmarshal([]byte(layer.Metadata), &layerMeta); err != nil { - return nil, -1, fmt.Errorf("error decoding metadata for layer %q: %v", layerID, err) + return nil, -1, errors.Wrapf(err, "error decoding metadata for layer %q", layerID) } } if layerMeta.CompressedSize <= 0 { @@ -504,7 +504,7 @@ func (s *storageImageSource) GetSignatures() (signatures [][]byte, err error) { offset += length } if offset != len(signature) { - return nil, fmt.Errorf("signatures data contained %d extra bytes", len(signatures)-offset) + return nil, errors.Errorf("signatures data contained %d extra bytes", len(signatures)-offset) } return sigslice, nil } @@ -513,12 +513,12 @@ func (s *storageImageSource) getSize() (int64, error) { var sum int64 names, err := s.imageRef.transport.store.ListImageBigData(s.imageRef.id) if err != nil { - return -1, fmt.Errorf("error reading image %q: %v", s.imageRef.id, err) + return -1, errors.Wrapf(err, "error reading image %q", s.imageRef.id) } for _, name := range names { bigSize, err := s.imageRef.transport.store.GetImageBigDataSize(s.imageRef.id, name) if err != nil { - return -1, fmt.Errorf("error reading data blob size %q for %q: %v", name, s.imageRef.id, err) + return -1, errors.Wrapf(err, "error reading data blob size %q for %q", name, s.imageRef.id) } sum += bigSize } @@ -536,11 +536,11 @@ func (s *storageImageSource) getSize() (int64, error) { } if layer.Metadata != "" { if err := json.Unmarshal([]byte(layer.Metadata), &layerMeta); err != nil { - return -1, fmt.Errorf("error decoding metadata for layer %q: %v", layerID, err) + return -1, errors.Wrapf(err, "error decoding metadata for layer %q", layerID) } } if layerMeta.Size < 0 { - return -1, fmt.Errorf("size for layer %q is unknown, failing getSize()", layerID) + return -1, errors.Errorf("size for layer %q is unknown, failing getSize()", layerID) } sum += layerMeta.Size } diff --git a/vendor/github.com/containers/image/storage/storage_transport.go b/vendor/github.com/containers/image/storage/storage_transport.go index 71056ff1..661df103 100644 --- a/vendor/github.com/containers/image/storage/storage_transport.go +++ b/vendor/github.com/containers/image/storage/storage_transport.go @@ -1,17 +1,18 @@ package storage import ( - "errors" "path/filepath" "regexp" "strings" + "github.com/pkg/errors" + "github.com/Sirupsen/logrus" "github.com/containers/image/docker/reference" "github.com/containers/image/types" "github.com/containers/storage/storage" - "github.com/docker/distribution/digest" - ddigest "github.com/docker/distribution/digest" + "github.com/opencontainers/go-digest" + ddigest "github.com/opencontainers/go-digest" ) var ( @@ -94,7 +95,7 @@ func (s storageTransport) ParseStoreReference(store storage.Store, ref string) ( return nil, err } } - sum, err = digest.ParseDigest("sha256:" + refInfo[1]) + sum, err = digest.Parse("sha256:" + refInfo[1]) if err != nil { return nil, err } @@ -265,7 +266,7 @@ func (s storageTransport) ValidatePolicyConfigurationScope(scope string) error { if err != nil { return err } - _, err = ddigest.ParseDigest("sha256:" + scopeInfo[1]) + _, err = ddigest.Parse("sha256:" + scopeInfo[1]) if err != nil { return err } diff --git a/vendor/github.com/containers/image/transports/transports.go b/vendor/github.com/containers/image/transports/transports.go index d4141df0..04826c04 100644 --- a/vendor/github.com/containers/image/transports/transports.go +++ b/vendor/github.com/containers/image/transports/transports.go @@ -11,6 +11,7 @@ import ( "github.com/containers/image/openshift" "github.com/containers/image/storage" "github.com/containers/image/types" + "github.com/pkg/errors" ) // KnownTransports is a registry of known ImageTransport instances. @@ -40,11 +41,11 @@ func init() { func ParseImageName(imgName string) (types.ImageReference, error) { parts := strings.SplitN(imgName, ":", 2) if len(parts) != 2 { - return nil, fmt.Errorf(`Invalid image name "%s", expected colon-separated transport:reference`, imgName) + return nil, errors.Errorf(`Invalid image name "%s", expected colon-separated transport:reference`, imgName) } transport, ok := KnownTransports[parts[0]] if !ok { - return nil, fmt.Errorf(`Invalid image name "%s", unknown transport "%s"`, imgName, parts[0]) + return nil, errors.Errorf(`Invalid image name "%s", unknown transport "%s"`, imgName, parts[0]) } return transport.ParseReference(parts[1]) } diff --git a/vendor/github.com/containers/image/types/types.go b/vendor/github.com/containers/image/types/types.go index a05a7064..517388a0 100644 --- a/vendor/github.com/containers/image/types/types.go +++ b/vendor/github.com/containers/image/types/types.go @@ -1,12 +1,13 @@ package types import ( - "errors" "io" "time" + "github.com/pkg/errors" + "github.com/containers/image/docker/reference" - "github.com/docker/distribution/digest" + "github.com/opencontainers/go-digest" ) // ImageTransport is a top-level namespace for ways to to store/load an image. diff --git a/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_log.go b/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_log.go index 8477e36f..76c97566 100644 --- a/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_log.go +++ b/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_log.go @@ -11,9 +11,9 @@ import ( // Due to the way cgo works this has to be in a separate file, as devmapper.go has // definitions in the cgo block, which is incompatible with using "//export" -// DevmapperLogCallback exports the devmapper log callback for cgo. -//export DevmapperLogCallback -func DevmapperLogCallback(level C.int, file *C.char, line C.int, dmErrnoOrClass C.int, message *C.char) { +// StorageDevmapperLogCallback exports the devmapper log callback for cgo. +//export StorageDevmapperLogCallback +func StorageDevmapperLogCallback(level C.int, file *C.char, line C.int, dmErrnoOrClass C.int, message *C.char) { msg := C.GoString(message) if level < 7 { if strings.Contains(msg, "busy") { diff --git a/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper.go b/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper.go index 91fbc85b..e37e0205 100644 --- a/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper.go +++ b/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper.go @@ -8,7 +8,7 @@ package devicemapper #include // FIXME: present only for BLKGETSIZE64, maybe we can remove it? // FIXME: Can't we find a way to do the logging in pure Go? -extern void DevmapperLogCallback(int level, char *file, int line, int dm_errno_or_class, char *str); +extern void StorageDevmapperLogCallback(int level, char *file, int line, int dm_errno_or_class, char *str); static void log_cb(int level, const char *file, int line, int dm_errno_or_class, const char *f, ...) { @@ -19,7 +19,7 @@ static void log_cb(int level, const char *file, int line, int dm_errno_or_class, vsnprintf(buffer, 256, f, ap); va_end(ap); - DevmapperLogCallback(level, (char *)file, line, dm_errno_or_class, buffer); + StorageDevmapperLogCallback(level, (char *)file, line, dm_errno_or_class, buffer); } static void log_with_errno_init() diff --git a/vendor/github.com/containers/storage/storageversion/version_lib.go b/vendor/github.com/containers/storage/storageversion/version_lib.go index 761868e3..34a531a9 100644 --- a/vendor/github.com/containers/storage/storageversion/version_lib.go +++ b/vendor/github.com/containers/storage/storageversion/version_lib.go @@ -1,4 +1,4 @@ -// +build !autogen +// +build !containersstorageautogen // Package storageversion is auto-generated at build-time package storageversion diff --git a/vendor/github.com/coreos/go-systemd/daemon/sdnotify.go b/vendor/github.com/coreos/go-systemd/daemon/sdnotify.go index a6167380..ba6d41d8 100644 --- a/vendor/github.com/coreos/go-systemd/daemon/sdnotify.go +++ b/vendor/github.com/coreos/go-systemd/daemon/sdnotify.go @@ -1,3 +1,18 @@ +// Copyright 2014 Docker, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + // Code forked from Docker project package daemon @@ -7,11 +22,14 @@ import ( ) // SdNotify sends a message to the init daemon. It is common to ignore the error. +// If `unsetEnvironment` is true, the environment variable `NOTIFY_SOCKET` +// will be unconditionally unset. +// // It returns one of the following: // (false, nil) - notification not supported (i.e. NOTIFY_SOCKET is unset) // (false, err) - notification supported, but failure happened (e.g. error connecting to NOTIFY_SOCKET or while sending data) // (true, nil) - notification supported, data has been sent -func SdNotify(state string) (sent bool, err error) { +func SdNotify(unsetEnvironment bool, state string) (sent bool, err error) { socketAddr := &net.UnixAddr{ Name: os.Getenv("NOTIFY_SOCKET"), Net: "unixgram", @@ -22,6 +40,13 @@ func SdNotify(state string) (sent bool, err error) { return false, nil } + if unsetEnvironment { + err = os.Unsetenv("NOTIFY_SOCKET") + } + if err != nil { + return false, err + } + conn, err := net.DialUnix(socketAddr.Net, nil, socketAddr) // Error connecting to NOTIFY_SOCKET if err != nil { diff --git a/vendor/github.com/coreos/go-systemd/daemon/watchdog.go b/vendor/github.com/coreos/go-systemd/daemon/watchdog.go new file mode 100644 index 00000000..35a92e6e --- /dev/null +++ b/vendor/github.com/coreos/go-systemd/daemon/watchdog.go @@ -0,0 +1,72 @@ +// Copyright 2016 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package daemon + +import ( + "fmt" + "os" + "strconv" + "time" +) + +// SdWatchdogEnabled return watchdog information for a service. +// Process should send daemon.SdNotify("WATCHDOG=1") every time / 2. +// If `unsetEnvironment` is true, the environment variables `WATCHDOG_USEC` +// and `WATCHDOG_PID` will be unconditionally unset. +// +// It returns one of the following: +// (0, nil) - watchdog isn't enabled or we aren't the watched PID. +// (0, err) - an error happened (e.g. error converting time). +// (time, nil) - watchdog is enabled and we can send ping. +// time is delay before inactive service will be killed. +func SdWatchdogEnabled(unsetEnvironment bool) (time.Duration, error) { + wusec := os.Getenv("WATCHDOG_USEC") + wpid := os.Getenv("WATCHDOG_PID") + if unsetEnvironment { + wusecErr := os.Unsetenv("WATCHDOG_USEC") + wpidErr := os.Unsetenv("WATCHDOG_PID") + if wusecErr != nil { + return 0, wusecErr + } + if wpidErr != nil { + return 0, wpidErr + } + } + + if wusec == "" { + return 0, nil + } + s, err := strconv.Atoi(wusec) + if err != nil { + return 0, fmt.Errorf("error converting WATCHDOG_USEC: %s", err) + } + if s <= 0 { + return 0, fmt.Errorf("error WATCHDOG_USEC must be a positive number") + } + interval := time.Duration(s) * time.Microsecond + + if wpid == "" { + return interval, nil + } + p, err := strconv.Atoi(wpid) + if err != nil { + return 0, fmt.Errorf("error converting WATCHDOG_PID: %s", err) + } + if os.Getpid() != p { + return 0, nil + } + + return interval, nil +} diff --git a/vendor/github.com/docker/docker/pkg/stringid/stringid.go b/vendor/github.com/docker/docker/pkg/stringid/stringid.go index fa35d8ba..82f85596 100644 --- a/vendor/github.com/docker/docker/pkg/stringid/stringid.go +++ b/vendor/github.com/docker/docker/pkg/stringid/stringid.go @@ -4,6 +4,7 @@ package stringid import ( "crypto/rand" "encoding/hex" + "fmt" "io" "regexp" "strconv" @@ -14,7 +15,10 @@ import ( const shortLen = 12 -var validShortID = regexp.MustCompile("^[a-z0-9]{12}$") +var ( + validShortID = regexp.MustCompile("^[a-f0-9]{12}$") + validHex = regexp.MustCompile(`^[a-f0-9]{64}$`) +) // IsShortID determines if an arbitrary string *looks like* a short ID. func IsShortID(id string) bool { @@ -67,3 +71,11 @@ func GenerateRandomID() string { func GenerateNonCryptoID() string { return generateID(false) } + +// ValidateID checks whether an ID string is a valid image ID. +func ValidateID(id string) error { + if ok := validHex.MatchString(id); !ok { + return fmt.Errorf("image ID %q is invalid", id) + } + return nil +} diff --git a/vendor/github.com/docker/docker/pkg/stringutils/stringutils.go b/vendor/github.com/docker/docker/pkg/stringutils/stringutils.go index 8e1c812d..e17951bf 100644 --- a/vendor/github.com/docker/docker/pkg/stringutils/stringutils.go +++ b/vendor/github.com/docker/docker/pkg/stringutils/stringutils.go @@ -77,7 +77,7 @@ func quote(word string, buf *bytes.Buffer) { for i := 0; i < len(word); i++ { b := word[i] if b == '\'' { - // Replace literal ' with a close ', a \', and a open ' + // Replace literal ' with a close ', a \', and an open ' buf.WriteString("'\\''") } else { buf.WriteByte(b) diff --git a/vendor/github.com/docker/docker/pkg/truncindex/truncindex.go b/vendor/github.com/docker/docker/pkg/truncindex/truncindex.go index 02610b8b..74776e65 100644 --- a/vendor/github.com/docker/docker/pkg/truncindex/truncindex.go +++ b/vendor/github.com/docker/docker/pkg/truncindex/truncindex.go @@ -77,10 +77,7 @@ func (idx *TruncIndex) addID(id string) error { func (idx *TruncIndex) Add(id string) error { idx.Lock() defer idx.Unlock() - if err := idx.addID(id); err != nil { - return err - } - return nil + return idx.addID(id) } // Delete removes an ID from the TruncIndex. If there are multiple IDs @@ -128,8 +125,13 @@ func (idx *TruncIndex) Get(s string) (string, error) { return "", ErrNotExist } -// Iterate iterates over all stored IDs, and passes each of them to the given handler. +// Iterate iterates over all stored IDs and passes each of them to the given +// handler. Take care that the handler method does not call any public +// method on truncindex as the internal locking is not reentrant/recursive +// and will result in deadlock. func (idx *TruncIndex) Iterate(handler func(id string)) { + idx.Lock() + defer idx.Unlock() idx.trie.Visit(func(prefix patricia.Prefix, item patricia.Item) error { handler(string(prefix)) return nil diff --git a/vendor/github.com/mtrmac/gpgme/gpgme.go b/vendor/github.com/mtrmac/gpgme/gpgme.go index 5f1793ea..20aad737 100644 --- a/vendor/github.com/mtrmac/gpgme/gpgme.go +++ b/vendor/github.com/mtrmac/gpgme/gpgme.go @@ -9,6 +9,7 @@ package gpgme import "C" import ( + "fmt" "io" "os" "runtime" @@ -389,7 +390,14 @@ func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) { key := newKey() cfpr := C.CString(fingerprint) defer C.free(unsafe.Pointer(cfpr)) - return key, handleError(C.gpgme_get_key(c.ctx, cfpr, &key.k, cbool(secret))) + err := handleError(C.gpgme_get_key(c.ctx, cfpr, &key.k, cbool(secret))) + if e, ok := err.(Error); key.k == nil && ok && e.Code() == ErrorEOF { + return nil, fmt.Errorf("key %q not found", fingerprint) + } + if err != nil { + return nil, err + } + return key, nil } func (c *Context) Decrypt(ciphertext, plaintext *Data) error { diff --git a/vendor/github.com/opencontainers/go-digest/.mailmap b/vendor/github.com/opencontainers/go-digest/.mailmap new file mode 100644 index 00000000..ba611cb2 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/.mailmap @@ -0,0 +1 @@ +Stephen J Day diff --git a/vendor/github.com/opencontainers/go-digest/.pullapprove.yml b/vendor/github.com/opencontainers/go-digest/.pullapprove.yml new file mode 100644 index 00000000..45fa4b9e --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/.pullapprove.yml @@ -0,0 +1,12 @@ +approve_by_comment: true +approve_regex: '^(Approved|lgtm|LGTM|:shipit:|:star:|:\+1:|:ship:)' +reject_regex: ^Rejected +reset_on_push: true +author_approval: ignored +signed_off_by: + required: true +reviewers: + teams: + - go-digest-maintainers + name: default + required: 2 diff --git a/vendor/github.com/opencontainers/go-digest/.travis.yml b/vendor/github.com/opencontainers/go-digest/.travis.yml new file mode 100644 index 00000000..7ea4ed1d --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/.travis.yml @@ -0,0 +1,4 @@ +language: go +go: + - 1.7 + - master diff --git a/vendor/github.com/opencontainers/go-digest/CONTRIBUTING.md b/vendor/github.com/opencontainers/go-digest/CONTRIBUTING.md new file mode 100644 index 00000000..e4d962ac --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/CONTRIBUTING.md @@ -0,0 +1,72 @@ +# Contributing to Docker open source projects + +Want to hack on this project? Awesome! Here are instructions to get you started. + +This project is a part of the [Docker](https://www.docker.com) project, and follows +the same rules and principles. If you're already familiar with the way +Docker does things, you'll feel right at home. + +Otherwise, go read Docker's +[contributions guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md), +[issue triaging](https://github.com/docker/docker/blob/master/project/ISSUE-TRIAGE.md), +[review process](https://github.com/docker/docker/blob/master/project/REVIEWING.md) and +[branches and tags](https://github.com/docker/docker/blob/master/project/BRANCHES-AND-TAGS.md). + +For an in-depth description of our contribution process, visit the +contributors guide: [Understand how to contribute](https://docs.docker.com/opensource/workflow/make-a-contribution/) + +### Sign your work + +The sign-off is a simple line at the end of the explanation for the patch. Your +signature certifies that you wrote the patch or otherwise have the right to pass +it on as an open-source patch. The rules are pretty simple: if you can certify +the below (from [developercertificate.org](http://developercertificate.org/)): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +1 Letterman Drive +Suite D4700 +San Francisco, CA, 94129 + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +Then you just add a line to every git commit message: + + Signed-off-by: Joe Smith + +Use your real name (sorry, no pseudonyms or anonymous contributions.) + +If you set your `user.name` and `user.email` git configs, you can sign your +commit automatically with `git commit -s`. diff --git a/vendor/github.com/opencontainers/go-digest/LICENSE.code b/vendor/github.com/opencontainers/go-digest/LICENSE.code new file mode 100644 index 00000000..0ea3ff81 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/LICENSE.code @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2016 Docker, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/opencontainers/go-digest/LICENSE.docs b/vendor/github.com/opencontainers/go-digest/LICENSE.docs new file mode 100644 index 00000000..e26cd4fc --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/LICENSE.docs @@ -0,0 +1,425 @@ +Attribution-ShareAlike 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-ShareAlike 4.0 International Public +License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-ShareAlike 4.0 International Public License ("Public +License"). To the extent this Public License may be interpreted as a +contract, You are granted the Licensed Rights in consideration of Your +acceptance of these terms and conditions, and the Licensor grants You +such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and +conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + l. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + m. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + + including for purposes of Section 3(b); and + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public licenses. +Notwithstanding, Creative Commons may elect to apply one of its public +licenses to material it publishes and in those instances will be +considered the "Licensor." Except for the limited purpose of indicating +that material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the public +licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/vendor/github.com/opencontainers/go-digest/MAINTAINERS b/vendor/github.com/opencontainers/go-digest/MAINTAINERS new file mode 100644 index 00000000..42a29795 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/MAINTAINERS @@ -0,0 +1,9 @@ +Aaron Lehmann (@aaronlehmann) +Brandon Philips (@philips) +Brendan Burns (@brendandburns) +Derek McGowan (@dmcgowan) +Jason Bouzane (@jbouzane) +John Starks (@jstarks) +Jonathan Boulle (@jonboulle) +Stephen Day (@stevvooe) +Vincent Batts (@vbatts) diff --git a/vendor/github.com/opencontainers/go-digest/README.md b/vendor/github.com/opencontainers/go-digest/README.md new file mode 100644 index 00000000..d65261e2 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/README.md @@ -0,0 +1,104 @@ +# go-digest + +[![GoDoc](https://godoc.org/github.com/opencontainers/go-digest?status.svg)](https://godoc.org/github.com/opencontainers/go-digest) [![Go Report Card](https://goreportcard.com/badge/github.com/opencontainers/go-digest)](https://goreportcard.com/report/github.com/opencontainers/go-digest) [![Build Status](https://travis-ci.org/opencontainers/go-digest.svg?branch=master)](https://travis-ci.org/opencontainers/go-digest) + +Common digest package used across the container ecosystem. + +Please see the [godoc](https://godoc.org/github.com/opencontainers/go-digest) for more information. + +# What is a digest? + +A digest is just a hash. + +The most common use case for a digest is to create a content +identifier for use in [Content Addressable Storage](https://en.wikipedia.org/wiki/Content-addressable_storage) +systems: + +```go +id := digest.FromBytes([]byte("my content")) +``` + +In the example above, the id can be used to uniquely identify +the byte slice "my content". This allows two disparate applications +to agree on a verifiable identifier without having to trust one +another. + +An identifying digest can be verified, as follows: + +```go +if id != digest.FromBytes([]byte("my content")) { + return errors.New("the content has changed!") +} +``` + +A `Verifier` type can be used to handle cases where an `io.Reader` +makes more sense: + +```go +rd := getContent() +verifier := id.Verifier() +io.Copy(verifier, rd) + +if !verifier.Verified() { + return errors.New("the content has changed!") +} +``` + +Using [Merkle DAGs](https://en.wikipedia.org/wiki/Merkle_tree), this +can power a rich, safe, content distribution system. + +# Usage + +While the [godoc](https://godoc.org/github.com/opencontainers/go-digest) is +considered the best resource, a few important items need to be called +out when using this package. + +1. Make sure to import the hash implementations into your application + or the package will panic. You should have something like the + following in the main (or other entrypoint) of your application: + + ```go + import ( + _ "crypto/sha256" + _ "crypto/sha512" + ) + ``` + This may seem inconvenient but it allows you replace the hash + implementations with others, such as https://github.com/stevvooe/resumable. + +2. Even though `digest.Digest` may be assemable as a string, _always_ + verify your input with `digest.Parse` or use `Digest.Validate` + when accepting untrusted input. While there are measures to + avoid common problems, this will ensure you have valid digests + in the rest of your application. + +# Stability + +The Go API, at this stage, is considered stable, unless otherwise noted. + +As always, before using a package export, read the [godoc](https://godoc.org/github.com/opencontainers/go-digest). + +# Contributing + +This package is considered fairly complete. It has been in production +in thousands (millions?) of deployments and is fairly battle-hardened. +New additions will be met with skepticism. If you think there is a +missing feature, please file a bug clearly describing the problem and +the alternatives you tried before submitting a PR. + +# Reporting security issues + +The maintainers take security seriously. If you discover a security +issue, please bring it to their attention right away! + +Please DO NOT file a public issue, instead send your report privately +to security@docker.com. + +Security reports are greatly appreciated and we will publicly thank you +for it. We also like to send gifts—if you're into Docker schwag, make +sure to let us know. We currently do not offer a paid security bounty +program, but are not ruling it out in the future. + +# Copyright and license + +Copyright © 2016 Docker, Inc. All rights reserved, except as follows. Code is released under the [Apache 2.0 license](LICENSE.code). This `README.md` file and the [`CONTRIBUTING.md`](CONTRIBUTING.md) file are licensed under the Creative Commons Attribution 4.0 International License under the terms and conditions set forth in the file [`LICENSE.docs`](LICENSE.docs). You may obtain a duplicate copy of the same license, titled CC BY-SA 4.0, at http://creativecommons.org/licenses/by-sa/4.0/. diff --git a/vendor/github.com/opencontainers/go-digest/algorithm.go b/vendor/github.com/opencontainers/go-digest/algorithm.go new file mode 100644 index 00000000..a3c44801 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/algorithm.go @@ -0,0 +1,144 @@ +package digest + +import ( + "crypto" + "fmt" + "hash" + "io" +) + +// Algorithm identifies and implementation of a digester by an identifier. +// Note the that this defines both the hash algorithm used and the string +// encoding. +type Algorithm string + +// supported digest types +const ( + SHA256 Algorithm = "sha256" // sha256 with hex encoding + SHA384 Algorithm = "sha384" // sha384 with hex encoding + SHA512 Algorithm = "sha512" // sha512 with hex encoding + + // Canonical is the primary digest algorithm used with the distribution + // project. Other digests may be used but this one is the primary storage + // digest. + Canonical = SHA256 +) + +var ( + // TODO(stevvooe): Follow the pattern of the standard crypto package for + // registration of digests. Effectively, we are a registerable set and + // common symbol access. + + // algorithms maps values to hash.Hash implementations. Other algorithms + // may be available but they cannot be calculated by the digest package. + algorithms = map[Algorithm]crypto.Hash{ + SHA256: crypto.SHA256, + SHA384: crypto.SHA384, + SHA512: crypto.SHA512, + } +) + +// Available returns true if the digest type is available for use. If this +// returns false, Digester and Hash will return nil. +func (a Algorithm) Available() bool { + h, ok := algorithms[a] + if !ok { + return false + } + + // check availability of the hash, as well + return h.Available() +} + +func (a Algorithm) String() string { + return string(a) +} + +// Size returns number of bytes returned by the hash. +func (a Algorithm) Size() int { + h, ok := algorithms[a] + if !ok { + return 0 + } + return h.Size() +} + +// Set implemented to allow use of Algorithm as a command line flag. +func (a *Algorithm) Set(value string) error { + if value == "" { + *a = Canonical + } else { + // just do a type conversion, support is queried with Available. + *a = Algorithm(value) + } + + if !a.Available() { + return ErrDigestUnsupported + } + + return nil +} + +// Digester returns a new digester for the specified algorithm. If the algorithm +// does not have a digester implementation, nil will be returned. This can be +// checked by calling Available before calling Digester. +func (a Algorithm) Digester() Digester { + return &digester{ + alg: a, + hash: a.Hash(), + } +} + +// Hash returns a new hash as used by the algorithm. If not available, the +// method will panic. Check Algorithm.Available() before calling. +func (a Algorithm) Hash() hash.Hash { + if !a.Available() { + // Empty algorithm string is invalid + if a == "" { + panic(fmt.Sprintf("empty digest algorithm, validate before calling Algorithm.Hash()")) + } + + // NOTE(stevvooe): A missing hash is usually a programming error that + // must be resolved at compile time. We don't import in the digest + // package to allow users to choose their hash implementation (such as + // when using stevvooe/resumable or a hardware accelerated package). + // + // Applications that may want to resolve the hash at runtime should + // call Algorithm.Available before call Algorithm.Hash(). + panic(fmt.Sprintf("%v not available (make sure it is imported)", a)) + } + + return algorithms[a].New() +} + +// FromReader returns the digest of the reader using the algorithm. +func (a Algorithm) FromReader(rd io.Reader) (Digest, error) { + digester := a.Digester() + + if _, err := io.Copy(digester.Hash(), rd); err != nil { + return "", err + } + + return digester.Digest(), nil +} + +// FromBytes digests the input and returns a Digest. +func (a Algorithm) FromBytes(p []byte) Digest { + digester := a.Digester() + + if _, err := digester.Hash().Write(p); err != nil { + // Writes to a Hash should never fail. None of the existing + // hash implementations in the stdlib or hashes vendored + // here can return errors from Write. Having a panic in this + // condition instead of having FromBytes return an error value + // avoids unnecessary error handling paths in all callers. + panic("write to hash function returned error: " + err.Error()) + } + + return digester.Digest() +} + +// FromString digests the string input and returns a Digest. +func (a Algorithm) FromString(s string) Digest { + return a.FromBytes([]byte(s)) +} diff --git a/vendor/github.com/opencontainers/go-digest/digest.go b/vendor/github.com/opencontainers/go-digest/digest.go new file mode 100644 index 00000000..7c66c30c --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/digest.go @@ -0,0 +1,140 @@ +package digest + +import ( + "fmt" + "hash" + "io" + "regexp" + "strings" +) + +// Digest allows simple protection of hex formatted digest strings, prefixed +// by their algorithm. Strings of type Digest have some guarantee of being in +// the correct format and it provides quick access to the components of a +// digest string. +// +// The following is an example of the contents of Digest types: +// +// sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc +// +// This allows to abstract the digest behind this type and work only in those +// terms. +type Digest string + +// NewDigest returns a Digest from alg and a hash.Hash object. +func NewDigest(alg Algorithm, h hash.Hash) Digest { + return NewDigestFromBytes(alg, h.Sum(nil)) +} + +// NewDigestFromBytes returns a new digest from the byte contents of p. +// Typically, this can come from hash.Hash.Sum(...) or xxx.SumXXX(...) +// functions. This is also useful for rebuilding digests from binary +// serializations. +func NewDigestFromBytes(alg Algorithm, p []byte) Digest { + return Digest(fmt.Sprintf("%s:%x", alg, p)) +} + +// NewDigestFromHex returns a Digest from alg and a the hex encoded digest. +func NewDigestFromHex(alg, hex string) Digest { + return Digest(fmt.Sprintf("%s:%s", alg, hex)) +} + +// DigestRegexp matches valid digest types. +var DigestRegexp = regexp.MustCompile(`[a-zA-Z0-9-_+.]+:[a-fA-F0-9]+`) + +// DigestRegexpAnchored matches valid digest types, anchored to the start and end of the match. +var DigestRegexpAnchored = regexp.MustCompile(`^` + DigestRegexp.String() + `$`) + +var ( + // ErrDigestInvalidFormat returned when digest format invalid. + ErrDigestInvalidFormat = fmt.Errorf("invalid checksum digest format") + + // ErrDigestInvalidLength returned when digest has invalid length. + ErrDigestInvalidLength = fmt.Errorf("invalid checksum digest length") + + // ErrDigestUnsupported returned when the digest algorithm is unsupported. + ErrDigestUnsupported = fmt.Errorf("unsupported digest algorithm") +) + +// Parse parses s and returns the validated digest object. An error will +// be returned if the format is invalid. +func Parse(s string) (Digest, error) { + d := Digest(s) + return d, d.Validate() +} + +// FromReader consumes the content of rd until io.EOF, returning canonical digest. +func FromReader(rd io.Reader) (Digest, error) { + return Canonical.FromReader(rd) +} + +// FromBytes digests the input and returns a Digest. +func FromBytes(p []byte) Digest { + return Canonical.FromBytes(p) +} + +// FromString digests the input and returns a Digest. +func FromString(s string) Digest { + return Canonical.FromString(s) +} + +// Validate checks that the contents of d is a valid digest, returning an +// error if not. +func (d Digest) Validate() error { + s := string(d) + + i := strings.Index(s, ":") + + // validate i then run through regexp + if i < 0 || i+1 == len(s) || !DigestRegexpAnchored.MatchString(s) { + return ErrDigestInvalidFormat + } + + algorithm := Algorithm(s[:i]) + if !algorithm.Available() { + return ErrDigestUnsupported + } + + // Digests much always be hex-encoded, ensuring that their hex portion will + // always be size*2 + if algorithm.Size()*2 != len(s[i+1:]) { + return ErrDigestInvalidLength + } + + return nil +} + +// Algorithm returns the algorithm portion of the digest. This will panic if +// the underlying digest is not in a valid format. +func (d Digest) Algorithm() Algorithm { + return Algorithm(d[:d.sepIndex()]) +} + +// Verifier returns a writer object that can be used to verify a stream of +// content against the digest. If the digest is invalid, the method will panic. +func (d Digest) Verifier() Verifier { + return hashVerifier{ + hash: d.Algorithm().Hash(), + digest: d, + } +} + +// Hex returns the hex digest portion of the digest. This will panic if the +// underlying digest is not in a valid format. +func (d Digest) Hex() string { + return string(d[d.sepIndex()+1:]) +} + +func (d Digest) String() string { + return string(d) +} + +func (d Digest) sepIndex() int { + i := strings.Index(string(d), ":") + + if i < 0 { + panic(fmt.Sprintf("no ':' separator in digest %q", d)) + } + + return i +} diff --git a/vendor/github.com/opencontainers/go-digest/digester.go b/vendor/github.com/opencontainers/go-digest/digester.go new file mode 100644 index 00000000..918a3f91 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/digester.go @@ -0,0 +1,25 @@ +package digest + +import "hash" + +// Digester calculates the digest of written data. Writes should go directly +// to the return value of Hash, while calling Digest will return the current +// value of the digest. +type Digester interface { + Hash() hash.Hash // provides direct access to underlying hash instance. + Digest() Digest +} + +// digester provides a simple digester definition that embeds a hasher. +type digester struct { + alg Algorithm + hash hash.Hash +} + +func (d *digester) Hash() hash.Hash { + return d.hash +} + +func (d *digester) Digest() Digest { + return NewDigest(d.alg, d.hash) +} diff --git a/vendor/github.com/opencontainers/go-digest/doc.go b/vendor/github.com/opencontainers/go-digest/doc.go new file mode 100644 index 00000000..f64b0db3 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/doc.go @@ -0,0 +1,42 @@ +// Package digest provides a generalized type to opaquely represent message +// digests and their operations within the registry. The Digest type is +// designed to serve as a flexible identifier in a content-addressable system. +// More importantly, it provides tools and wrappers to work with +// hash.Hash-based digests with little effort. +// +// Basics +// +// The format of a digest is simply a string with two parts, dubbed the +// "algorithm" and the "digest", separated by a colon: +// +// : +// +// An example of a sha256 digest representation follows: +// +// sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc +// +// In this case, the string "sha256" is the algorithm and the hex bytes are +// the "digest". +// +// Because the Digest type is simply a string, once a valid Digest is +// obtained, comparisons are cheap, quick and simple to express with the +// standard equality operator. +// +// Verification +// +// The main benefit of using the Digest type is simple verification against a +// given digest. The Verifier interface, modeled after the stdlib hash.Hash +// interface, provides a common write sink for digest verification. After +// writing is complete, calling the Verifier.Verified method will indicate +// whether or not the stream of bytes matches the target digest. +// +// Missing Features +// +// In addition to the above, we intend to add the following features to this +// package: +// +// 1. A Digester type that supports write sink digest calculation. +// +// 2. Suspend and resume of ongoing digest calculations to support efficient digest verification in the registry. +// +package digest diff --git a/vendor/github.com/opencontainers/go-digest/verifiers.go b/vendor/github.com/opencontainers/go-digest/verifiers.go new file mode 100644 index 00000000..f1db6cda --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/verifiers.go @@ -0,0 +1,31 @@ +package digest + +import ( + "hash" + "io" +) + +// Verifier presents a general verification interface to be used with message +// digests and other byte stream verifications. Users instantiate a Verifier +// from one of the various methods, write the data under test to it then check +// the result with the Verified method. +type Verifier interface { + io.Writer + + // Verified will return true if the content written to Verifier matches + // the digest. + Verified() bool +} + +type hashVerifier struct { + digest Digest + hash hash.Hash +} + +func (hv hashVerifier) Write(p []byte) (n int, err error) { + return hv.hash.Write(p) +} + +func (hv hashVerifier) Verified() bool { + return hv.digest == NewDigest(hv.digest.Algorithm(), hv.hash) +} diff --git a/vendor/github.com/opencontainers/image-spec/specs-go/v1/config.go b/vendor/github.com/opencontainers/image-spec/specs-go/v1/config.go index d4238362..c709e2cb 100644 --- a/vendor/github.com/opencontainers/image-spec/specs-go/v1/config.go +++ b/vendor/github.com/opencontainers/image-spec/specs-go/v1/config.go @@ -19,15 +19,6 @@ type ImageConfig struct { // User defines the username or UID which the process in the container should run as. User string `json:"User,omitempty"` - // Memory defines the memory limit. - Memory int64 `json:"Memory,omitempty"` - - // MemorySwap defines the total memory usage limit (memory + swap). - MemorySwap int64 `json:"MemorySwap,omitempty"` - - // CPUShares is the CPU shares (relative weight vs. other containers). - CPUShares int64 `json:"CpuShares,omitempty"` - // ExposedPorts a set of ports to expose from a container running this image. ExposedPorts map[string]struct{} `json:"ExposedPorts,omitempty"` @@ -45,6 +36,9 @@ type ImageConfig struct { // WorkingDir sets the current working directory of the entrypoint process in the container. WorkingDir string `json:"WorkingDir,omitempty"` + + // Labels contains arbitrary metadata for the container. + Labels map[string]string `json:"labels,omitempty"` } // RootFS describes a layer content addresses diff --git a/vendor/github.com/opencontainers/image-spec/specs-go/v1/layout.go b/vendor/github.com/opencontainers/image-spec/specs-go/v1/layout.go index c44486bf..b3a9fb13 100644 --- a/vendor/github.com/opencontainers/image-spec/specs-go/v1/layout.go +++ b/vendor/github.com/opencontainers/image-spec/specs-go/v1/layout.go @@ -16,6 +16,9 @@ package v1 import "regexp" +// ImageLayoutVersion is the version of ImageLayout +const ImageLayoutVersion = "1.0.0" + // ImageLayout is the structure in the "oci-layout" file, found in the root // of an OCI Image-layout directory. type ImageLayout struct { diff --git a/vendor/github.com/opencontainers/image-spec/specs-go/version.go b/vendor/github.com/opencontainers/image-spec/specs-go/version.go index 48df9acc..5264ff3a 100644 --- a/vendor/github.com/opencontainers/image-spec/specs-go/version.go +++ b/vendor/github.com/opencontainers/image-spec/specs-go/version.go @@ -25,7 +25,7 @@ const ( VersionPatch = 0 // VersionDev indicates development branch. Releases will be empty string. - VersionDev = "-rc2-dev" + VersionDev = "-rc3-dev" ) // Version is the specification version that the package types support. diff --git a/vendor/github.com/opencontainers/runtime-tools/generate/generate.go b/vendor/github.com/opencontainers/runtime-tools/generate/generate.go index e8c36416..60d92adc 100644 --- a/vendor/github.com/opencontainers/runtime-tools/generate/generate.go +++ b/vendor/github.com/opencontainers/runtime-tools/generate/generate.go @@ -196,6 +196,16 @@ func (g *Generator) Spec() *rspec.Spec { func (g *Generator) Save(w io.Writer, exportOpts ExportOptions) (err error) { var data []byte + if g.spec.Linux != nil { + buf, err := json.Marshal(g.spec.Linux) + if err != nil { + return err + } + if string(buf) == "{}" { + g.spec.Linux = nil + } + } + if exportOpts.Seccomp { data, err = json.MarshalIndent(g.spec.Linux.Seccomp, "", "\t") } else { @@ -331,9 +341,18 @@ func (g *Generator) ClearProcessEnv() { g.spec.Process.Env = []string{} } -// AddProcessEnv adds env into g.spec.Process.Env. -func (g *Generator) AddProcessEnv(env string) { +// AddProcessEnv adds name=value into g.spec.Process.Env, or replaces an +// existing entry with the given name. +func (g *Generator) AddProcessEnv(name, value string) { g.initSpec() + + env := fmt.Sprintf("%s=%s", name, value) + for idx := range g.spec.Process.Env { + if strings.HasPrefix(g.spec.Process.Env[idx], name+"=") { + g.spec.Process.Env[idx] = env + return + } + } g.spec.Process.Env = append(g.spec.Process.Env, env) } diff --git a/vendor/github.com/pborman/uuid/README.md b/vendor/github.com/pborman/uuid/README.md index f023d47c..b0396b27 100644 --- a/vendor/github.com/pborman/uuid/README.md +++ b/vendor/github.com/pborman/uuid/README.md @@ -1,7 +1,7 @@ This project was automatically exported from code.google.com/p/go-uuid # uuid ![build status](https://travis-ci.org/pborman/uuid.svg?branch=master) -The uuid package generates and inspects UUIDs based on [RFC 412](http://tools.ietf.org/html/rfc4122) and DCE 1.1: Authentication and Security Services. +The uuid package generates and inspects UUIDs based on [RFC 4122](http://tools.ietf.org/html/rfc4122) and DCE 1.1: Authentication and Security Services. ###### Install `go get github.com/pborman/uuid` diff --git a/vendor/github.com/pborman/uuid/json.go b/vendor/github.com/pborman/uuid/json.go deleted file mode 100644 index 9dda1dfb..00000000 --- a/vendor/github.com/pborman/uuid/json.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2014 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import "errors" - -func (u UUID) MarshalJSON() ([]byte, error) { - if len(u) != 16 { - return []byte(`""`), nil - } - var js [38]byte - js[0] = '"' - encodeHex(js[1:], u) - js[37] = '"' - return js[:], nil -} - -func (u *UUID) UnmarshalJSON(data []byte) error { - if string(data) == `""` { - return nil - } - if data[0] != '"' { - return errors.New("invalid UUID format") - } - data = data[1 : len(data)-1] - uu := Parse(string(data)) - if uu == nil { - return errors.New("invalid UUID format") - } - *u = uu - return nil -} diff --git a/vendor/github.com/pborman/uuid/marshal.go b/vendor/github.com/pborman/uuid/marshal.go new file mode 100644 index 00000000..6621dd54 --- /dev/null +++ b/vendor/github.com/pborman/uuid/marshal.go @@ -0,0 +1,83 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "errors" + "fmt" +) + +// MarshalText implements encoding.TextMarshaler. +func (u UUID) MarshalText() ([]byte, error) { + if len(u) != 16 { + return nil, nil + } + var js [36]byte + encodeHex(js[:], u) + return js[:], nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (u *UUID) UnmarshalText(data []byte) error { + if len(data) == 0 { + return nil + } + id := Parse(string(data)) + if id == nil { + return errors.New("invalid UUID") + } + *u = id + return nil +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (u UUID) MarshalBinary() ([]byte, error) { + return u[:], nil +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (u *UUID) UnmarshalBinary(data []byte) error { + if len(data) == 0 { + return nil + } + if len(data) != 16 { + return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) + } + var id [16]byte + copy(id[:], data) + *u = id[:] + return nil +} + +// MarshalText implements encoding.TextMarshaler. +func (u Array) MarshalText() ([]byte, error) { + var js [36]byte + encodeHex(js[:], u[:]) + return js[:], nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (u *Array) UnmarshalText(data []byte) error { + id := Parse(string(data)) + if id == nil { + return errors.New("invalid UUID") + } + *u = id.Array() + return nil +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (u Array) MarshalBinary() ([]byte, error) { + return u[:], nil +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (u *Array) UnmarshalBinary(data []byte) error { + if len(data) != 16 { + return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) + } + copy(u[:], data) + return nil +} diff --git a/vendor/github.com/pkg/errors/.gitignore b/vendor/github.com/pkg/errors/.gitignore new file mode 100644 index 00000000..daf913b1 --- /dev/null +++ b/vendor/github.com/pkg/errors/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/pkg/errors/.travis.yml b/vendor/github.com/pkg/errors/.travis.yml new file mode 100644 index 00000000..567ccdbf --- /dev/null +++ b/vendor/github.com/pkg/errors/.travis.yml @@ -0,0 +1,11 @@ +language: go +go_import_path: github.com/pkg/errors +go: + - 1.4.3 + - 1.5.4 + - 1.6.3 + - 1.7.3 + - tip + +script: + - go test -v ./... diff --git a/vendor/github.com/pkg/errors/LICENSE b/vendor/github.com/pkg/errors/LICENSE new file mode 100644 index 00000000..835ba3e7 --- /dev/null +++ b/vendor/github.com/pkg/errors/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2015, Dave Cheney +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/pkg/errors/README.md b/vendor/github.com/pkg/errors/README.md new file mode 100644 index 00000000..273db3c9 --- /dev/null +++ b/vendor/github.com/pkg/errors/README.md @@ -0,0 +1,52 @@ +# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) + +Package errors provides simple error handling primitives. + +`go get github.com/pkg/errors` + +The traditional error handling idiom in Go is roughly akin to +```go +if err != nil { + return err +} +``` +which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error. + +## Adding context to an error + +The errors.Wrap function returns a new error that adds context to the original error. For example +```go +_, err := ioutil.ReadAll(r) +if err != nil { + return errors.Wrap(err, "read failed") +} +``` +## Retrieving the cause of an error + +Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`. +```go +type causer interface { + Cause() error +} +``` +`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example: +```go +switch err := errors.Cause(err).(type) { +case *MyError: + // handle specifically +default: + // unknown error +} +``` + +[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). + +## Contributing + +We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high. + +Before proposing a change, please discuss your change by raising an issue. + +## Licence + +BSD-2-Clause diff --git a/vendor/github.com/pkg/errors/appveyor.yml b/vendor/github.com/pkg/errors/appveyor.yml new file mode 100644 index 00000000..a932eade --- /dev/null +++ b/vendor/github.com/pkg/errors/appveyor.yml @@ -0,0 +1,32 @@ +version: build-{build}.{branch} + +clone_folder: C:\gopath\src\github.com\pkg\errors +shallow_clone: true # for startup speed + +environment: + GOPATH: C:\gopath + +platform: + - x64 + +# http://www.appveyor.com/docs/installed-software +install: + # some helpful output for debugging builds + - go version + - go env + # pre-installed MinGW at C:\MinGW is 32bit only + # but MSYS2 at C:\msys64 has mingw64 + - set PATH=C:\msys64\mingw64\bin;%PATH% + - gcc --version + - g++ --version + +build_script: + - go install -v ./... + +test_script: + - set PATH=C:\gopath\bin;%PATH% + - go test -v ./... + +#artifacts: +# - path: '%GOPATH%\bin\*.exe' +deploy: off diff --git a/vendor/github.com/pkg/errors/errors.go b/vendor/github.com/pkg/errors/errors.go new file mode 100644 index 00000000..842ee804 --- /dev/null +++ b/vendor/github.com/pkg/errors/errors.go @@ -0,0 +1,269 @@ +// Package errors provides simple error handling primitives. +// +// The traditional error handling idiom in Go is roughly akin to +// +// if err != nil { +// return err +// } +// +// which applied recursively up the call stack results in error reports +// without context or debugging information. The errors package allows +// programmers to add context to the failure path in their code in a way +// that does not destroy the original value of the error. +// +// Adding context to an error +// +// The errors.Wrap function returns a new error that adds context to the +// original error by recording a stack trace at the point Wrap is called, +// and the supplied message. For example +// +// _, err := ioutil.ReadAll(r) +// if err != nil { +// return errors.Wrap(err, "read failed") +// } +// +// If additional control is required the errors.WithStack and errors.WithMessage +// functions destructure errors.Wrap into its component operations of annotating +// an error with a stack trace and an a message, respectively. +// +// Retrieving the cause of an error +// +// Using errors.Wrap constructs a stack of errors, adding context to the +// preceding error. Depending on the nature of the error it may be necessary +// to reverse the operation of errors.Wrap to retrieve the original error +// for inspection. Any error value which implements this interface +// +// type causer interface { +// Cause() error +// } +// +// can be inspected by errors.Cause. errors.Cause will recursively retrieve +// the topmost error which does not implement causer, which is assumed to be +// the original cause. For example: +// +// switch err := errors.Cause(err).(type) { +// case *MyError: +// // handle specifically +// default: +// // unknown error +// } +// +// causer interface is not exported by this package, but is considered a part +// of stable public API. +// +// Formatted printing of errors +// +// All error values returned from this package implement fmt.Formatter and can +// be formatted by the fmt package. The following verbs are supported +// +// %s print the error. If the error has a Cause it will be +// printed recursively +// %v see %s +// %+v extended format. Each Frame of the error's StackTrace will +// be printed in detail. +// +// Retrieving the stack trace of an error or wrapper +// +// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are +// invoked. This information can be retrieved with the following interface. +// +// type stackTracer interface { +// StackTrace() errors.StackTrace +// } +// +// Where errors.StackTrace is defined as +// +// type StackTrace []Frame +// +// The Frame type represents a call site in the stack trace. Frame supports +// the fmt.Formatter interface that can be used for printing information about +// the stack trace of this error. For example: +// +// if err, ok := err.(stackTracer); ok { +// for _, f := range err.StackTrace() { +// fmt.Printf("%+s:%d", f) +// } +// } +// +// stackTracer interface is not exported by this package, but is considered a part +// of stable public API. +// +// See the documentation for Frame.Format for more details. +package errors + +import ( + "fmt" + "io" +) + +// New returns an error with the supplied message. +// New also records the stack trace at the point it was called. +func New(message string) error { + return &fundamental{ + msg: message, + stack: callers(), + } +} + +// Errorf formats according to a format specifier and returns the string +// as a value that satisfies error. +// Errorf also records the stack trace at the point it was called. +func Errorf(format string, args ...interface{}) error { + return &fundamental{ + msg: fmt.Sprintf(format, args...), + stack: callers(), + } +} + +// fundamental is an error that has a message and a stack, but no caller. +type fundamental struct { + msg string + *stack +} + +func (f *fundamental) Error() string { return f.msg } + +func (f *fundamental) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + io.WriteString(s, f.msg) + f.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, f.msg) + case 'q': + fmt.Fprintf(s, "%q", f.msg) + } +} + +// WithStack annotates err with a stack trace at the point WithStack was called. +// If err is nil, WithStack returns nil. +func WithStack(err error) error { + if err == nil { + return nil + } + return &withStack{ + err, + callers(), + } +} + +type withStack struct { + error + *stack +} + +func (w *withStack) Cause() error { return w.error } + +func (w *withStack) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v", w.Cause()) + w.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, w.Error()) + case 'q': + fmt.Fprintf(s, "%q", w.Error()) + } +} + +// Wrap returns an error annotating err with a stack trace +// at the point Wrap is called, and the supplied message. +// If err is nil, Wrap returns nil. +func Wrap(err error, message string) error { + if err == nil { + return nil + } + err = &withMessage{ + cause: err, + msg: message, + } + return &withStack{ + err, + callers(), + } +} + +// Wrapf returns an error annotating err with a stack trace +// at the point Wrapf is call, and the format specifier. +// If err is nil, Wrapf returns nil. +func Wrapf(err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + err = &withMessage{ + cause: err, + msg: fmt.Sprintf(format, args...), + } + return &withStack{ + err, + callers(), + } +} + +// WithMessage annotates err with a new message. +// If err is nil, WithMessage returns nil. +func WithMessage(err error, message string) error { + if err == nil { + return nil + } + return &withMessage{ + cause: err, + msg: message, + } +} + +type withMessage struct { + cause error + msg string +} + +func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } +func (w *withMessage) Cause() error { return w.cause } + +func (w *withMessage) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v\n", w.Cause()) + io.WriteString(s, w.msg) + return + } + fallthrough + case 's', 'q': + io.WriteString(s, w.Error()) + } +} + +// Cause returns the underlying cause of the error, if possible. +// An error value has a cause if it implements the following +// interface: +// +// type causer interface { +// Cause() error +// } +// +// If the error does not implement Cause, the original error will +// be returned. If the error is nil, nil will be returned without further +// investigation. +func Cause(err error) error { + type causer interface { + Cause() error + } + + for err != nil { + cause, ok := err.(causer) + if !ok { + break + } + err = cause.Cause() + } + return err +} diff --git a/vendor/github.com/pkg/errors/stack.go b/vendor/github.com/pkg/errors/stack.go new file mode 100644 index 00000000..6b1f2891 --- /dev/null +++ b/vendor/github.com/pkg/errors/stack.go @@ -0,0 +1,178 @@ +package errors + +import ( + "fmt" + "io" + "path" + "runtime" + "strings" +) + +// Frame represents a program counter inside a stack frame. +type Frame uintptr + +// pc returns the program counter for this frame; +// multiple frames may have the same PC value. +func (f Frame) pc() uintptr { return uintptr(f) - 1 } + +// file returns the full path to the file that contains the +// function for this Frame's pc. +func (f Frame) file() string { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return "unknown" + } + file, _ := fn.FileLine(f.pc()) + return file +} + +// line returns the line number of source code of the +// function for this Frame's pc. +func (f Frame) line() int { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return 0 + } + _, line := fn.FileLine(f.pc()) + return line +} + +// Format formats the frame according to the fmt.Formatter interface. +// +// %s source file +// %d source line +// %n function name +// %v equivalent to %s:%d +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+s path of source file relative to the compile time GOPATH +// %+v equivalent to %+s:%d +func (f Frame) Format(s fmt.State, verb rune) { + switch verb { + case 's': + switch { + case s.Flag('+'): + pc := f.pc() + fn := runtime.FuncForPC(pc) + if fn == nil { + io.WriteString(s, "unknown") + } else { + file, _ := fn.FileLine(pc) + fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file) + } + default: + io.WriteString(s, path.Base(f.file())) + } + case 'd': + fmt.Fprintf(s, "%d", f.line()) + case 'n': + name := runtime.FuncForPC(f.pc()).Name() + io.WriteString(s, funcname(name)) + case 'v': + f.Format(s, 's') + io.WriteString(s, ":") + f.Format(s, 'd') + } +} + +// StackTrace is stack of Frames from innermost (newest) to outermost (oldest). +type StackTrace []Frame + +func (st StackTrace) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case s.Flag('+'): + for _, f := range st { + fmt.Fprintf(s, "\n%+v", f) + } + case s.Flag('#'): + fmt.Fprintf(s, "%#v", []Frame(st)) + default: + fmt.Fprintf(s, "%v", []Frame(st)) + } + case 's': + fmt.Fprintf(s, "%s", []Frame(st)) + } +} + +// stack represents a stack of program counters. +type stack []uintptr + +func (s *stack) Format(st fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case st.Flag('+'): + for _, pc := range *s { + f := Frame(pc) + fmt.Fprintf(st, "\n%+v", f) + } + } + } +} + +func (s *stack) StackTrace() StackTrace { + f := make([]Frame, len(*s)) + for i := 0; i < len(f); i++ { + f[i] = Frame((*s)[i]) + } + return f +} + +func callers() *stack { + const depth = 32 + var pcs [depth]uintptr + n := runtime.Callers(3, pcs[:]) + var st stack = pcs[0:n] + return &st +} + +// funcname removes the path prefix component of a function's name reported by func.Name(). +func funcname(name string) string { + i := strings.LastIndex(name, "/") + name = name[i+1:] + i = strings.Index(name, ".") + return name[i+1:] +} + +func trimGOPATH(name, file string) string { + // Here we want to get the source file path relative to the compile time + // GOPATH. As of Go 1.6.x there is no direct way to know the compiled + // GOPATH at runtime, but we can infer the number of path segments in the + // GOPATH. We note that fn.Name() returns the function name qualified by + // the import path, which does not include the GOPATH. Thus we can trim + // segments from the beginning of the file path until the number of path + // separators remaining is one more than the number of path separators in + // the function name. For example, given: + // + // GOPATH /home/user + // file /home/user/src/pkg/sub/file.go + // fn.Name() pkg/sub.Type.Method + // + // We want to produce: + // + // pkg/sub/file.go + // + // From this we can easily see that fn.Name() has one less path separator + // than our desired output. We count separators from the end of the file + // path until it finds two more than in the function name and then move + // one character forward to preserve the initial path segment without a + // leading separator. + const sep = "/" + goal := strings.Count(name, sep) + 2 + i := len(file) + for n := 0; n < goal; n++ { + i = strings.LastIndex(file[:i], sep) + if i == -1 { + // not enough separators found, set i so that the slice expression + // below leaves file unmodified + i = -len(sep) + break + } + } + // get back to 0 or trim the leading separator + file = file[i+len(sep):] + return file +} diff --git a/vendor/github.com/rajatchopra/ocicni/LICENSE b/vendor/github.com/rajatchopra/ocicni/LICENSE new file mode 100644 index 00000000..3fd70307 --- /dev/null +++ b/vendor/github.com/rajatchopra/ocicni/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2016 Red Hat, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/rajatchopra/ocicni/noop.go b/vendor/github.com/rajatchopra/ocicni/noop.go index fb3b4dbb..95447438 100644 --- a/vendor/github.com/rajatchopra/ocicni/noop.go +++ b/vendor/github.com/rajatchopra/ocicni/noop.go @@ -1,10 +1,8 @@ - package ocicni type cniNoOp struct { } - func (noop *cniNoOp) Name() string { return "CNINoOp" } diff --git a/vendor/github.com/rajatchopra/ocicni/ocicni.go b/vendor/github.com/rajatchopra/ocicni/ocicni.go index 03f7889a..7086d0f7 100644 --- a/vendor/github.com/rajatchopra/ocicni/ocicni.go +++ b/vendor/github.com/rajatchopra/ocicni/ocicni.go @@ -1,5 +1,4 @@ - -package ocicni +package ocicni import ( "errors" @@ -22,6 +21,7 @@ type cniNetworkPlugin struct { nsenterPath string pluginDir string + cniDirs []string vendorCNIDirPrefix string } @@ -31,8 +31,8 @@ type cniNetwork struct { CNIConfig libcni.CNI } -func InitCNI(pluginDir string) (CNIPlugin, error) { - plugin := probeNetworkPluginsWithVendorCNIDirPrefix(pluginDir, "") +func InitCNI(pluginDir string, cniDirs ...string) (CNIPlugin, error) { + plugin := probeNetworkPluginsWithVendorCNIDirPrefix(pluginDir, cniDirs, "") var err error plugin.nsenterPath, err = exec.LookPath("nsenter") if err != nil { @@ -40,7 +40,7 @@ func InitCNI(pluginDir string) (CNIPlugin, error) { } // check if a default network exists, otherwise dump the CNI search and return a noop plugin - _, err = getDefaultCNINetwork(plugin.pluginDir, plugin.vendorCNIDirPrefix) + _, err = getDefaultCNINetwork(plugin.pluginDir, plugin.cniDirs, plugin.vendorCNIDirPrefix) if err != nil { glog.Warningf("Error in finding usable CNI plugin - %v", err) // create a noop plugin instead @@ -49,22 +49,23 @@ func InitCNI(pluginDir string) (CNIPlugin, error) { // sync network config from pluginDir periodically to detect network config updates go func() { - t := time.NewTimer(10*time.Second) + t := time.NewTimer(10 * time.Second) for { plugin.syncNetworkConfig() select { - case <- t.C: + case <-t.C: } } }() return plugin, nil } -func probeNetworkPluginsWithVendorCNIDirPrefix(pluginDir, vendorCNIDirPrefix string) (*cniNetworkPlugin) { +func probeNetworkPluginsWithVendorCNIDirPrefix(pluginDir string, cniDirs []string, vendorCNIDirPrefix string) *cniNetworkPlugin { plugin := &cniNetworkPlugin{ defaultNetwork: nil, - loNetwork: getLoNetwork(vendorCNIDirPrefix), + loNetwork: getLoNetwork(cniDirs, vendorCNIDirPrefix), pluginDir: pluginDir, + cniDirs: cniDirs, vendorCNIDirPrefix: vendorCNIDirPrefix, } @@ -73,10 +74,14 @@ func probeNetworkPluginsWithVendorCNIDirPrefix(pluginDir, vendorCNIDirPrefix str return plugin } -func getDefaultCNINetwork(pluginDir, vendorCNIDirPrefix string) (*cniNetwork, error) { +func getDefaultCNINetwork(pluginDir string, cniDirs []string, vendorCNIDirPrefix string) (*cniNetwork, error) { if pluginDir == "" { pluginDir = DefaultNetDir } + if len(cniDirs) == 0 { + cniDirs = []string{DefaultCNIDir} + } + files, err := libcni.ConfFiles(pluginDir) switch { case err != nil: @@ -92,11 +97,13 @@ func getDefaultCNINetwork(pluginDir, vendorCNIDirPrefix string) (*cniNetwork, er glog.Warningf("Error loading CNI config file %s: %v", confFile, err) continue } + // Search for vendor-specific plugins as well as default plugins in the CNI codebase. vendorDir := vendorCNIDir(vendorCNIDirPrefix, conf.Network.Type) cninet := &libcni.CNIConfig{ - Path: []string{DefaultCNIDir, vendorDir}, + Path: append(cniDirs, vendorDir), } + network := &cniNetwork{name: conf.Network.Name, NetworkConfig: conf, CNIConfig: cninet} return network, nil } @@ -107,7 +114,11 @@ func vendorCNIDir(prefix, pluginType string) string { return fmt.Sprintf(VendorCNIDirTemplate, prefix, pluginType) } -func getLoNetwork(vendorDirPrefix string) *cniNetwork { +func getLoNetwork(cniDirs []string, vendorDirPrefix string) *cniNetwork { + if len(cniDirs) == 0 { + cniDirs = []string{DefaultCNIDir} + } + loConfig, err := libcni.ConfFromBytes([]byte(`{ "cniVersion": "0.1.0", "name": "cni-loopback", @@ -118,8 +129,9 @@ func getLoNetwork(vendorDirPrefix string) *cniNetwork { // catch this panic(err) } + vendorDir := vendorCNIDir(vendorDirPrefix, loConfig.Network.Type) cninet := &libcni.CNIConfig{ - Path: []string{vendorCNIDir(vendorDirPrefix, loConfig.Network.Type), DefaultCNIDir}, + Path: append(cniDirs, vendorDir), } loNetwork := &cniNetwork{ name: "lo", @@ -131,7 +143,7 @@ func getLoNetwork(vendorDirPrefix string) *cniNetwork { } func (plugin *cniNetworkPlugin) syncNetworkConfig() { - network, err := getDefaultCNINetwork(plugin.pluginDir, plugin.vendorCNIDirPrefix) + network, err := getDefaultCNINetwork(plugin.pluginDir, plugin.cniDirs, plugin.vendorCNIDirPrefix) if err != nil { glog.Errorf("error updating cni config: %s", err) return diff --git a/vendor/github.com/rajatchopra/ocicni/types.go b/vendor/github.com/rajatchopra/ocicni/types.go index 505c0a7d..120f46f9 100644 --- a/vendor/github.com/rajatchopra/ocicni/types.go +++ b/vendor/github.com/rajatchopra/ocicni/types.go @@ -1,4 +1,3 @@ - package ocicni const ( diff --git a/vendor/github.com/rajatchopra/ocicni/util.go b/vendor/github.com/rajatchopra/ocicni/util.go index bc5df06a..547e9597 100644 --- a/vendor/github.com/rajatchopra/ocicni/util.go +++ b/vendor/github.com/rajatchopra/ocicni/util.go @@ -1,11 +1,10 @@ - package ocicni import ( + "fmt" + "net" "os/exec" "strings" - "net" - "fmt" ) func getContainerIP(nsenterPath, netnsPath, interfaceName, addrType string) (net.IP, error) { From c0333b102bcbdb367f31924ffa3f4437709dcd38 Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Tue, 18 Oct 2016 10:48:33 -0400 Subject: [PATCH 02/11] Integrate containers/storage Use containers/storage to store images, pod sandboxes, and containers. A pod sandbox's infrastructure container has the same ID as the pod to which it belongs, and all containers also keep track of their pod's ID. The container configuration that we build using the data in a CreateContainerRequest is stored in the container's ContainerDirectory and ContainerRunDirectory. We catch SIGTERM and SIGINT, and when we receive either, we gracefully exit the grpc loop. If we also think that there aren't any container filesystems in use, we attempt to do a clean shutdown of the storage driver. The test harness now waits for ocid to exit before attempting to delete the storage root directory. Signed-off-by: Nalin Dahyabhai --- .travis.yml | 4 + Dockerfile | 5 + README.md | 6 +- cmd/ocid/config.go | 40 +++++--- cmd/ocid/main.go | 96 +++++++++++++++---- docs/ocid.8.md | 50 ++++++---- docs/ocid.conf.5.md | 27 ++++-- oci/oci.go | 9 +- server/config.go | 75 ++++++++------- server/container_create.go | 80 ++++++++++------ server/container_list.go | 1 + server/container_remove.go | 12 ++- server/container_start.go | 3 +- server/container_status.go | 1 + server/container_stop.go | 1 + server/image_list.go | 28 +++++- server/image_pull.go | 90 ++++------------- server/image_remove.go | 20 +++- server/image_status.go | 29 +++++- server/sandbox.go | 3 +- server/sandbox_list.go | 1 + server/sandbox_remove.go | 22 +++-- server/sandbox_run.go | 110 +++++++++++++-------- server/sandbox_status.go | 1 + server/sandbox_stop.go | 3 +- server/server.go | 191 ++++++++++++++++++++++++++++++++----- test/helpers.bash | 17 +++- test/policy.json | 7 ++ utils/utils.go | 77 +-------------- 29 files changed, 637 insertions(+), 372 deletions(-) create mode 100644 test/policy.json diff --git a/.travis.yml b/.travis.yml index 811546a7..8f2a6b9d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,10 @@ sudo: required services: - docker +before_install: + - sudo apt-get -qq update + - sudo apt-get -qq install btrfs-tools libdevmapper-dev libgpgme11-dev + install: - make install.tools diff --git a/Dockerfile b/Dockerfile index 84a47ca9..48e46a7d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,6 +23,7 @@ RUN apt-get update && apt-get install -y \ btrfs-tools \ libdevmapper1.02.1 \ libdevmapper-dev \ + libgpgme11-dev \ --no-install-recommends \ && apt-get clean @@ -52,6 +53,10 @@ RUN set -x \ && cp runc /usr/local/bin/runc \ && rm -rf "$GOPATH" +# Make sure we have some policy for pulling images +RUN mkdir -p /etc/containers +COPY test/policy.json /etc/containers/policy.json + WORKDIR /go/src/github.com/kubernetes-incubator/cri-o ADD . /go/src/github.com/kubernetes-incubator/cri-o diff --git a/README.md b/README.md index 42bfce55..986e07ff 100644 --- a/README.md +++ b/README.md @@ -42,9 +42,11 @@ It is currently in active development in the Kubernetes community through the [d ### Build -`glib2-devel` and `glibc-static` packages on Fedora or ` libglib2.0-dev` on Ubuntu or equivalent is required. -In order to enable seccomp support you will need to install `libseccomp` on your platform. +`btrfs-progs-devel`, `device-mapper-devel`, `glib2-devel`, `glibc-devel`, `gpgme-devel`, `libassuan-devel`, `libgpg-error-devel`, and `pkg-config` packages on CentOS/Fedora or `btrfs-tools`, `libassuan-dev`, `libc6-dev`, `libdevmapper-dev`, `libglib2.0-dev`, `libgpg-error-dev`, `libgpgme11-dev`, and `pkg-config` on Ubuntu or equivalent is required. +In order to enable seccomp support you will need to install development files for `libseccomp` on your platform. > e.g. `libseccomp-devel` for CentOS/Fedora, or `libseccomp-dev` for Ubuntu +In order to enable apparmor support you will need to install development files for `libapparmor` on your platform. +> e.g. `libapparmor-dev` for Ubuntu ```bash $ GOPATH=/path/to/gopath diff --git a/cmd/ocid/config.go b/cmd/ocid/config.go index ec3c92f7..db1ccc15 100644 --- a/cmd/ocid/config.go +++ b/cmd/ocid/config.go @@ -12,17 +12,21 @@ var commentedConfigTemplate = template.Must(template.New("config").Parse(` # The "ocid" table contains all of the server options. [ocid] -# root is a path to the "root directory". OCID stores all of its state -# data, including container images, in this directory. +# root is a path to the "root directory". OCID stores all of its data, +# including container images, in this directory. root = "{{ .Root }}" -# sandbox_dir is the directory where ocid will store all of its sandbox -# state and other information. -sandbox_dir = "{{ .SandboxDir }}" +# run is a path to the "run directory". OCID stores all of its state +# in this directory. +runroot = "{{ .RunRoot }}" -# container_dir is the directory where ocid will store all of its -# container state and other information. -container_dir = "{{ .ContainerDir }}" +# storage_driver select which storage driver is used to manage storage +# of images and containers. +storage_driver = "{{ .Storage }}" + +# storage_option is used to pass an option to the storage driver. +storage_option = [ +{{ range $opt := .StorageOptions }}{{ printf "\t%q,\n" $opt }}{{ end }}] # The "ocid.api" table contains settings for the kubelet/gRPC # interface (which is also used by ocic). @@ -67,9 +71,23 @@ cgroup_manager = "{{ .CgroupManager }}" # management of OCI images. [ocid.image] -# pause is the path to the statically linked pause container binary, used -# as the entrypoint for infra containers. -pause = "{{ .Pause }}" +# default_transport is the prefix we try prepending to an image name if the +# image name as we receive it can't be parsed as a valid source reference +default_transport = "{{ .DefaultTransport }}" + +# pause_image is the image which we use to instantiate infra containers. +pause_image = "{{ .PauseImage }}" + +# pause_command is the command to run in a pause_image to have a container just +# sit there. If the image contains the necessary information, this value need +# not be specified. +pause_command = "{{ .PauseCommand }}" + +# signature_policy is the name of the file which decides what sort of policy we +# use when deciding whether or not to trust an image that we've pulled. +# Outside of testing situations, it is strongly advised that this be left +# unspecified so that the default system-wide policy will be used. +signature_policy = "{{ .SignaturePolicyPath }}" # The "ocid.network" table contains settings pertaining to the # management of CNI plugins. diff --git a/cmd/ocid/main.go b/cmd/ocid/main.go index 528d592a..a87cdd1d 100644 --- a/cmd/ocid/main.go +++ b/cmd/ocid/main.go @@ -4,7 +4,10 @@ import ( "fmt" "net" "os" + "os/signal" "sort" + "strings" + "syscall" "github.com/Sirupsen/logrus" "github.com/containers/storage/pkg/reexec" @@ -36,17 +39,29 @@ func mergeConfig(config *server.Config, ctx *cli.Context) error { if ctx.GlobalIsSet("conmon") { config.Conmon = ctx.GlobalString("conmon") } - if ctx.GlobalIsSet("containerdir") { - config.ContainerDir = ctx.GlobalString("containerdir") + if ctx.GlobalIsSet("pause-command") { + config.PauseCommand = ctx.GlobalString("pause-command") } - if ctx.GlobalIsSet("pause") { - config.Pause = ctx.GlobalString("pause") + if ctx.GlobalIsSet("pause-image") { + config.PauseImage = ctx.GlobalString("pause-image") + } + if ctx.GlobalIsSet("signature-policy") { + config.SignaturePolicyPath = ctx.GlobalString("signature-policy") } if ctx.GlobalIsSet("root") { config.Root = ctx.GlobalString("root") } - if ctx.GlobalIsSet("sandboxdir") { - config.SandboxDir = ctx.GlobalString("sandboxdir") + if ctx.GlobalIsSet("runroot") { + config.RunRoot = ctx.GlobalString("runroot") + } + if ctx.GlobalIsSet("storage-driver") { + config.Storage = ctx.GlobalString("storage-driver") + } + if ctx.GlobalIsSet("storage-option") { + config.StorageOptions = ctx.GlobalStringSlice("storage-option") + } + if ctx.GlobalIsSet("default-transport") { + config.DefaultTransport = ctx.GlobalString("default-transport") } if ctx.GlobalIsSet("listen") { config.Listen = ctx.GlobalString("listen") @@ -75,6 +90,26 @@ func mergeConfig(config *server.Config, ctx *cli.Context) error { return nil } +func catchShutdown(gserver *grpc.Server, sserver *server.Server, signalled *bool) { + sig := make(chan os.Signal, 10) + signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) + go func() { + for s := range sig { + switch s { + case syscall.SIGINT: + logrus.Debugf("Caught SIGINT") + case syscall.SIGTERM: + logrus.Debugf("Caught SIGTERM") + default: + continue + } + *signalled = true + gserver.GracefulStop() + return + } + }() +} + func main() { if reexec.Init() { return @@ -97,10 +132,6 @@ func main() { Name: "conmon", Usage: "path to the conmon executable", }, - cli.StringFlag{ - Name: "containerdir", - Usage: "ocid container dir", - }, cli.BoolFlag{ Name: "debug", Usage: "enable debug output for logging", @@ -120,20 +151,40 @@ func main() { Usage: "set the format used by logs ('text' (default), or 'json')", }, cli.StringFlag{ - Name: "pause", - Usage: "path to the pause executable", + Name: "pause-command", + Usage: "name of the pause command in the pause image", + }, + cli.StringFlag{ + Name: "pause-image", + Usage: "name of the pause image", + }, + cli.StringFlag{ + Name: "signature-policy", + Usage: "path to signature policy file", }, cli.StringFlag{ Name: "root", Usage: "ocid root dir", }, cli.StringFlag{ - Name: "runtime", - Usage: "OCI runtime path", + Name: "runroot", + Usage: "ocid state dir", }, cli.StringFlag{ - Name: "sandboxdir", - Usage: "ocid pod sandbox dir", + Name: "storage-driver", + Usage: "storage driver", + }, + cli.StringSliceFlag{ + Name: "storage-option", + Usage: "storage driver option", + }, + cli.StringFlag{ + Name: "default-transport", + Usage: "default transport", + }, + cli.StringFlag{ + Name: "runtime", + Usage: "OCI runtime path", }, cli.StringFlag{ Name: "seccomp-profile", @@ -236,13 +287,24 @@ func main() { logrus.Fatal(err) } + graceful := false + catchShutdown(s, service, &graceful) runtime.RegisterRuntimeServiceServer(s, service) runtime.RegisterImageServiceServer(s, service) // after the daemon is done setting up we can notify systemd api notifySystem() - if err := s.Serve(lis); err != nil { + err = s.Serve(lis) + if graceful && strings.Contains(strings.ToLower(err.Error()), "use of closed network connection") { + err = nil + } + + if err2 := service.Shutdown(); err2 != nil { + logrus.Infof("error shutting down layer storage: %v", err2) + } + + if err != nil { logrus.Fatal(err) } return nil diff --git a/docs/ocid.8.md b/docs/ocid.8.md index c0bbc353..0f007e07 100644 --- a/docs/ocid.8.md +++ b/docs/ocid.8.md @@ -8,16 +8,20 @@ ocid - Enable OCI Kubernetes Container Runtime daemon **ocid** [**--config**=[*value*]] [**--conmon**=[*value*]] -[**--containerdir**=[*value*]] [**--debug**] +[**--default-transport**=[*value*]] [**--help**|**-h**] [**--listen**=[*value*]] [**--log**=[*value*]] [**--log-format value**] -[**--pause**=[*value*]] +[**--pause-command**=[*value*]] +[**--pause-image**=[*value*]] [**--root**=[*value*]] +[**--runroot**=[*value*]] [**--runtime**=[*value*]] -[**--sandboxdir**=[*value*]] +[**--signature-policy**=[*value*]] +[**--storage-driver**=[*value*]] +[**--storage-option**=[*value*]] [**--selinux**] [**--seccomp-profile**=[*value*]] [**--apparmor-profile**=[*value*]] @@ -43,18 +47,21 @@ ocid is meant to provide an integration path between OCI conformant runtimes and # GLOBAL OPTIONS +**--apparmor_profile**="" + Name of the apparmor profile to be used as the runtime's default (default: "ocid-default") + **--config**="" path to configuration file **--conmon**="" path to the conmon executable (default: "/usr/libexec/ocid/conmon") -**--containerdir**="" - OCID container dir (default: "/var/lib/ocid/containers") - **--debug** Enable debug output for logging +**--default-transport** + A prefix to prepend to image names that can't be pulled as-is. + **--help, -h** Print usage statement @@ -67,32 +74,41 @@ ocid is meant to provide an integration path between OCI conformant runtimes and **--log-format**="" Set the format used by logs ('text' (default), or 'json') (default: "text") -**--pause**="" - Path to the pause executable (default: "/usr/libexec/ocid/pause") +**--pause-command**="" + Path to the pause executable in the pause image (default: "/pause") + +**--pause-image**="" + Image which contains the pause executable (default: "kubernetes/pause") **--root**="" - OCID root dir (default: "/var/lib/ocid") + OCID root dir (default: "/var/lib/containers") + +**--runroot**="" + OCID state dir (default: "/var/run/containers") **--runtime**="" OCI runtime path (default: "/usr/bin/runc") -**--sandboxdir**="" - OCID pod sandbox dir (default: "/var/lib/ocid/sandboxes") - **--selinux**=*true*|*false* Enable selinux support (default: false) -**--seccomp_profile**="" +**--seccomp-profile**="" Path to the seccomp json profile to be used as the runtime's default (default: "/etc/ocid/seccomp.json") -**--apparmor_profile**="" - Name of the apparmor profile to be used as the runtime's default (default: "ocid-default") +**--signature-policy**="" + Path to the signature policy json file (default: "", to use the system-wide default) + +**--storage-driver** + OCI storage driver (default: "devicemapper") + +**--storage-option** + OCI storage driver option (no default) **--cni-config-dir**="" - CNI configuration files directory (defautl: "/etc/cni/net.d/") + CNI configuration files directory (default: "/etc/cni/net.d/") **--cni-plugin-dir**="" - CNI plugin binaries directory (defautl: "/opt/cni/bin/") + CNI plugin binaries directory (default: "/opt/cni/bin/") **--version, -v** Print the version diff --git a/docs/ocid.conf.5.md b/docs/ocid.conf.5.md index 4d554640..2eec29db 100644 --- a/docs/ocid.conf.5.md +++ b/docs/ocid.conf.5.md @@ -29,15 +29,17 @@ No bare options are used. The format of TOML can be simplified to: The `ocid` table supports the following options: -**container_dir**="" - OCID container dir (default: "/var/lib/ocid/containers") - **root**="" - OCID root dir (default: "/var/lib/ocid") + OCID root dir (default: "/var/lib/containers") -**sandbox_dir**="" - OCID pod sandbox dir (default: "/var/lib/ocid/sandboxes") +**runroot**="" + OCID state dir (default: "/var/run/containers") +**storage_driver**="" + OCID storage driver (default is "devicemapper") + +**storage_option**=[] + OCID storage driver option list (no default) ## OCID.API TABLE @@ -58,6 +60,9 @@ The `ocid` table supports the following options: **selinux**=*true*|*false* Enable selinux support (default: false) +**signature_policy**="" + Path to the signature policy json file (default: "", to use the system-wide default) + **seccomp_profile**="" Path to the seccomp json profile to be used as the runtime's default (default: "/etc/ocid/seccomp.json") @@ -66,8 +71,14 @@ The `ocid` table supports the following options: ## OCID.IMAGE TABLE -**pause**="" - Path to the pause executable (default: "/usr/libexec/ocid/pause") +**default_transport** + A prefix to prepend to image names that can't be pulled as-is (default: "docker://") + +**pause_command**="" + Path to the pause executable in the pause image (default: "/pause") + +**pause_image**="" + Image which contains the pause executable (default: "kubernetes/pause") ## OCID.NETWORK TABLE diff --git a/oci/oci.go b/oci/oci.go index c155ed79..167756a3 100644 --- a/oci/oci.go +++ b/oci/oci.go @@ -34,11 +34,10 @@ const ( ) // New creates a new Runtime with options provided -func New(runtimePath string, containerDir string, conmonPath string, conmonEnv []string, cgroupManager string) (*Runtime, error) { +func New(runtimePath string, conmonPath string, conmonEnv []string, cgroupManager string) (*Runtime, error) { r := &Runtime{ name: filepath.Base(runtimePath), path: runtimePath, - containerDir: containerDir, conmonPath: conmonPath, conmonEnv: conmonEnv, cgroupManager: cgroupManager, @@ -50,7 +49,6 @@ func New(runtimePath string, containerDir string, conmonPath string, conmonEnv [ type Runtime struct { name string path string - containerDir string conmonPath string conmonEnv []string cgroupManager string @@ -76,11 +74,6 @@ func (r *Runtime) Path() string { return r.path } -// ContainerDir returns the path to the base directory for storing container configurations -func (r *Runtime) ContainerDir() string { - return r.containerDir -} - // Version returns the version of the OCI Runtime func (r *Runtime) Version() (string, error) { runtimeVersion, err := getOCIVersion(r.path, "-v") diff --git a/server/config.go b/server/config.go index b49462c0..56fcdda5 100644 --- a/server/config.go +++ b/server/config.go @@ -3,7 +3,6 @@ package server import ( "bytes" "io/ioutil" - "path/filepath" "github.com/BurntSushi/toml" "github.com/opencontainers/runc/libcontainer/selinux" @@ -11,16 +10,16 @@ import ( // Default paths if none are specified const ( - ocidRoot = "/var/lib/ocid" - conmonPath = "/usr/libexec/ocid/conmon" - pausePath = "/usr/libexec/ocid/pause" - seccompProfilePath = "/etc/ocid/seccomp.json" - cniConfigDir = "/etc/cni/net.d/" - cniBinDir = "/opt/cni/bin/" -) - -const ( + ocidRoot = "/var/lib/ocid" + ocidRunRoot = "/var/run/containers" + conmonPath = "/usr/libexec/ocid/conmon" + pauseImage = "kubernetes/pause" + pauseCommand = "/pause" + defaultTransport = "docker://" + seccompProfilePath = "/etc/ocid/seccomp.json" apparmorProfileName = "ocid-default" + cniConfigDir = "/etc/cni/net.d/" + cniBinDir = "/opt/cni/bin/" cgroupManager = "cgroupfs" ) @@ -40,17 +39,20 @@ type Config struct { // RootConfig represents the root of the "ocid" TOML config table. type RootConfig struct { - // Root is a path to the "root directory" where all information not + // Root is a path to the "root directory" where data not // explicitly handled by other options will be stored. Root string `toml:"root"` - // SandboxDir is the directory where ocid will store all of its sandbox - // state and other information. - SandboxDir string `toml:"sandbox_dir"` + // RunRoot is a path to the "run directory" where state information not + // explicitly handled by other options will be stored. + RunRoot string `toml:"runroot"` - // ContainerDir is the directory where ocid will store all of its container - // state and other information. - ContainerDir string `toml:"container_dir"` + // Storage is the name of the storage driver which handles actually + // storing the contents of containers. + Storage string `toml:"storage_driver"` + + // StorageOption is a list of storage driver specific options. + StorageOptions []string `toml:"storage_option"` // LogDir is the default log directory were all logs will go unless kubelet // tells us to put them somewhere else. @@ -98,17 +100,21 @@ type RuntimeConfig struct { // ImageConfig represents the "ocid.image" TOML config table. type ImageConfig struct { - // Pause is the path to the statically linked pause container binary, used - // as the entrypoint for infra containers. - // - // TODO(cyphar): This should be replaced with a path to an OCI image - // bundle, once the OCI image/storage code has been implemented. - Pause string `toml:"pause"` - - // ImageStore is the directory where the ocid image store will be stored. - // TODO: This is currently not really used because we don't have - // containers/storage integrated. - ImageDir string `toml:"image_dir"` + // DefaultTransport is a value we prefix to image names that fail to + // validate source references. + DefaultTransport string `toml:"default_transport"` + // PauseImage is the name of an image which we use to instantiate infra + // containers. + PauseImage string `toml:"pause_image"` + // PauseCommand is the path of the binary we run in an infra + // container that's been instantiated using PauseImage. + PauseCommand string `toml:"pause_command"` + // SignaturePolicyPath is the name of the file which decides what sort + // of policy we use when deciding whether or not to trust an image that + // we've pulled. Outside of testing situations, it is strongly advised + // that this be left unspecified so that the default system-wide policy + // will be used. + SignaturePolicyPath string `toml:"signature_policy"` } // NetworkConfig represents the "ocid.network" TOML config table @@ -191,10 +197,9 @@ func (c *Config) ToFile(path string) error { func DefaultConfig() *Config { return &Config{ RootConfig: RootConfig{ - Root: ocidRoot, - SandboxDir: filepath.Join(ocidRoot, "sandboxes"), - ContainerDir: filepath.Join(ocidRoot, "containers"), - LogDir: "/var/log/ocid/pods", + Root: ocidRoot, + RunRoot: ocidRunRoot, + LogDir: "/var/log/ocid/pods", }, APIConfig: APIConfig{ Listen: "/var/run/ocid.sock", @@ -211,8 +216,10 @@ func DefaultConfig() *Config { CgroupManager: cgroupManager, }, ImageConfig: ImageConfig{ - Pause: pausePath, - ImageDir: filepath.Join(ocidRoot, "store"), + DefaultTransport: defaultTransport, + PauseImage: pauseImage, + PauseCommand: pauseCommand, + SignaturePolicyPath: "", }, NetworkConfig: NetworkConfig{ NetworkDir: cniConfigDir, diff --git a/server/container_create.go b/server/container_create.go index bd219198..e4354607 100644 --- a/server/container_create.go +++ b/server/container_create.go @@ -4,7 +4,6 @@ import ( "encoding/json" "errors" "fmt" - "os" "path/filepath" "strings" "syscall" @@ -14,7 +13,6 @@ import ( "github.com/kubernetes-incubator/cri-o/oci" "github.com/kubernetes-incubator/cri-o/server/apparmor" "github.com/kubernetes-incubator/cri-o/server/seccomp" - "github.com/kubernetes-incubator/cri-o/utils" "github.com/opencontainers/runc/libcontainer/label" "github.com/opencontainers/runtime-tools/generate" "golang.org/x/net/context" @@ -30,6 +28,7 @@ const ( // CreateContainer creates a new container in specified PodSandbox func (s *Server) CreateContainer(ctx context.Context, req *pb.CreateContainerRequest) (res *pb.CreateContainerResponse, err error) { logrus.Debugf("CreateContainerRequest %+v", req) + s.Update() sbID := req.GetPodSandboxId() if sbID == "" { return nil, fmt.Errorf("PodSandboxId should not be empty") @@ -62,30 +61,24 @@ func (s *Server) CreateContainer(ctx context.Context, req *pb.CreateContainerReq return nil, err } - // containerDir is the dir for the container bundle. - containerDir := filepath.Join(s.runtime.ContainerDir(), containerID) defer func() { if err != nil { s.releaseContainerName(containerName) - err1 := os.RemoveAll(containerDir) - if err1 != nil { - logrus.Warnf("Failed to cleanup container directory: %v", err1) - } } }() - if _, err = os.Stat(containerDir); err == nil { - return nil, fmt.Errorf("container (%s) already exists", containerDir) - } - - if err = os.MkdirAll(containerDir, 0755); err != nil { - return nil, err - } - - container, err := s.createSandboxContainer(containerID, containerName, sb, containerDir, containerConfig) + container, err := s.createSandboxContainer(ctx, containerID, containerName, sb, req.GetSandboxConfig(), containerConfig) if err != nil { return nil, err } + defer func() { + if err != nil { + err2 := s.storage.DeleteContainer(containerID) + if err2 != nil { + logrus.Warnf("Failed to cleanup container directory: %v", err2) + } + } + }() if err = s.runtime.CreateContainer(container); err != nil { return nil, err @@ -110,23 +103,21 @@ func (s *Server) CreateContainer(ctx context.Context, req *pb.CreateContainerReq return resp, nil } -func (s *Server) createSandboxContainer(containerID string, containerName string, sb *sandbox, containerDir string, containerConfig *pb.ContainerConfig) (*oci.Container, error) { +func (s *Server) createSandboxContainer(ctx context.Context, containerID string, containerName string, sb *sandbox, SandboxConfig *pb.PodSandboxConfig, containerConfig *pb.ContainerConfig) (*oci.Container, error) { if sb == nil { return nil, errors.New("createSandboxContainer needs a sandbox") } + + // TODO: factor generating/updating the spec into something other projects can vendor + // creates a spec Generator with the default spec. specgen := generate.New() - // by default, the root path is an empty string. - // here set it to be "rootfs". - specgen.SetRootPath("rootfs") - processArgs := []string{} commands := containerConfig.GetCommand() args := containerConfig.GetArgs() if commands == nil && args == nil { - // TODO: override with image's config in #189 - processArgs = []string{"/bin/sh"} + processArgs = nil } if commands != nil { processArgs = append(processArgs, commands...) @@ -135,8 +126,6 @@ func (s *Server) createSandboxContainer(containerID string, containerName string processArgs = append(processArgs, args...) } - specgen.SetProcessArgs(processArgs) - cwd := containerConfig.GetWorkingDir() if cwd == "" { cwd = "/" @@ -357,17 +346,46 @@ func (s *Server) createSandboxContainer(containerID string, containerName string return nil, err } - if err = specgen.SaveToFile(filepath.Join(containerDir, "config.json"), generate.ExportOptions{}); err != nil { + metaname := metadata.GetName() + attempt := metadata.GetAttempt() + containerInfo, err := s.storage.CreateContainer(s.imageContext, + sb.name, sb.id, + image, "", + containerName, containerID, + metaname, + attempt, + sb.mountLabel, + nil) + if err != nil { return nil, err } - // TODO: copy the rootfs into the bundle. - // Currently, utils.CreateFakeRootfs is used to populate the rootfs. - if err = utils.CreateFakeRootfs(containerDir, image); err != nil { + mountPoint, err := s.storage.StartContainer(containerID) + if err != nil { + return nil, fmt.Errorf("failed to mount container %s(%s): %v", containerName, containerID, err) + } + + if processArgs == nil { + if containerInfo.Config != nil && len(containerInfo.Config.Config.Cmd) > 0 { + processArgs = containerInfo.Config.Config.Cmd + } else { + processArgs = []string{"/bin/sh"} + } + } + specgen.SetProcessArgs(processArgs) + + // by default, the root path is an empty string. set it now. + specgen.SetRootPath(mountPoint) + + saveOptions := generate.ExportOptions{} + if err = specgen.SaveToFile(filepath.Join(containerInfo.Dir, "config.json"), saveOptions); err != nil { + return nil, err + } + if err = specgen.SaveToFile(filepath.Join(containerInfo.RunDir, "config.json"), saveOptions); err != nil { return nil, err } - container, err := oci.NewContainer(containerID, containerName, containerDir, logPath, sb.netNs(), labels, annotations, imageSpec, metadata, sb.id, containerConfig.GetTty()) + container, err := oci.NewContainer(containerID, containerName, containerInfo.RunDir, logPath, sb.netNs(), labels, annotations, imageSpec, metadata, sb.id, containerConfig.GetTty()) if err != nil { return nil, err } diff --git a/server/container_list.go b/server/container_list.go index 776b85bc..b7a0b5e3 100644 --- a/server/container_list.go +++ b/server/container_list.go @@ -29,6 +29,7 @@ func filterContainer(c *pb.Container, filter *pb.ContainerFilter) bool { // ListContainers lists all containers by filters. func (s *Server) ListContainers(ctx context.Context, req *pb.ListContainersRequest) (*pb.ListContainersResponse, error) { logrus.Debugf("ListContainersRequest %+v", req) + s.Update() var ctrs []*pb.Container filter := req.Filter ctrList := s.state.containers.List() diff --git a/server/container_remove.go b/server/container_remove.go index 37c318ff..a4d06f17 100644 --- a/server/container_remove.go +++ b/server/container_remove.go @@ -2,8 +2,6 @@ package server import ( "fmt" - "os" - "path/filepath" "github.com/Sirupsen/logrus" "github.com/kubernetes-incubator/cri-o/oci" @@ -15,6 +13,7 @@ import ( // should be force removed. func (s *Server) RemoveContainer(ctx context.Context, req *pb.RemoveContainerRequest) (*pb.RemoveContainerResponse, error) { logrus.Debugf("RemoveContainerRequest %+v", req) + s.Update() c, err := s.getContainerFromRequest(req) if err != nil { return nil, err @@ -35,9 +34,12 @@ func (s *Server) RemoveContainer(ctx context.Context, req *pb.RemoveContainerReq return nil, fmt.Errorf("failed to delete container %s: %v", c.ID(), err) } - containerDir := filepath.Join(s.runtime.ContainerDir(), c.ID()) - if err := os.RemoveAll(containerDir); err != nil { - return nil, fmt.Errorf("failed to remove container %s directory: %v", c.ID(), err) + if err := s.storage.StopContainer(c.ID()); err != nil { + return nil, fmt.Errorf("failed to unmount container %s: %v", c.ID(), err) + } + + if err := s.storage.DeleteContainer(c.ID()); err != nil { + return nil, fmt.Errorf("failed to delete storage for container %s: %v", c.ID(), err) } s.releaseContainerName(c.Name()) diff --git a/server/container_start.go b/server/container_start.go index 5e724865..23a33b90 100644 --- a/server/container_start.go +++ b/server/container_start.go @@ -11,12 +11,13 @@ import ( // StartContainer starts the container. func (s *Server) StartContainer(ctx context.Context, req *pb.StartContainerRequest) (*pb.StartContainerResponse, error) { logrus.Debugf("StartContainerRequest %+v", req) + s.Update() c, err := s.getContainerFromRequest(req) if err != nil { return nil, err } - if err := s.runtime.StartContainer(c); err != nil { + if err = s.runtime.StartContainer(c); err != nil { return nil, fmt.Errorf("failed to start container %s: %v", c.ID(), err) } diff --git a/server/container_status.go b/server/container_status.go index fa07c89c..24ff260c 100644 --- a/server/container_status.go +++ b/server/container_status.go @@ -10,6 +10,7 @@ import ( // ContainerStatus returns status of the container. func (s *Server) ContainerStatus(ctx context.Context, req *pb.ContainerStatusRequest) (*pb.ContainerStatusResponse, error) { logrus.Debugf("ContainerStatusRequest %+v", req) + s.Update() c, err := s.getContainerFromRequest(req) if err != nil { return nil, err diff --git a/server/container_stop.go b/server/container_stop.go index 1aba8801..a6457c0e 100644 --- a/server/container_stop.go +++ b/server/container_stop.go @@ -12,6 +12,7 @@ import ( // StopContainer stops a running container with a grace period (i.e., timeout). func (s *Server) StopContainer(ctx context.Context, req *pb.StopContainerRequest) (*pb.StopContainerResponse, error) { logrus.Debugf("StopContainerRequest %+v", req) + s.Update() c, err := s.getContainerFromRequest(req) if err != nil { return nil, err diff --git a/server/image_list.go b/server/image_list.go index 964dbd74..a9cb25d4 100644 --- a/server/image_list.go +++ b/server/image_list.go @@ -8,9 +8,27 @@ import ( // ListImages lists existing images. func (s *Server) ListImages(ctx context.Context, req *pb.ListImagesRequest) (*pb.ListImagesResponse, error) { - logrus.Debugf("ListImages: %+v", req) - // TODO - // containers/storage will take care of this by looking inside /var/lib/ocid/images - // and listing images. - return &pb.ListImagesResponse{}, nil + logrus.Debugf("ListImagesRequest: %+v", req) + filter := "" + reqFilter := req.GetFilter() + if reqFilter != nil { + filterImage := reqFilter.GetImage() + if filterImage != nil { + filter = filterImage.GetImage() + } + } + results, err := s.images.ListImages(filter) + if err != nil { + return nil, err + } + response := pb.ListImagesResponse{} + for _, result := range results { + response.Images = append(response.Images, &pb.Image{ + Id: sPtr(result.ID), + RepoTags: result.Names, + Size_: result.Size, + }) + } + logrus.Debugf("ListImagesResponse: %+v", response) + return &response, nil } diff --git a/server/image_pull.go b/server/image_pull.go index 3a0aa65c..afed362c 100644 --- a/server/image_pull.go +++ b/server/image_pull.go @@ -1,86 +1,28 @@ package server import ( - "errors" - "io" - "os" - "path/filepath" - "github.com/Sirupsen/logrus" - "github.com/containers/image/directory" - "github.com/containers/image/image" - "github.com/containers/image/transports" + "github.com/containers/image/copy" "golang.org/x/net/context" pb "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" ) // PullImage pulls a image with authentication config. func (s *Server) PullImage(ctx context.Context, req *pb.PullImageRequest) (*pb.PullImageResponse, error) { - logrus.Debugf("PullImage: %+v", req) - img := req.GetImage().GetImage() - if img == "" { - return nil, errors.New("got empty imagespec name") - } - - // TODO(runcom): deal with AuthConfig in req.GetAuth() - - // TODO(mrunalp,runcom): why do we need the SandboxConfig here? - // how do we pull in a specified sandbox? - tr, err := transports.ParseImageName(img) - if err != nil { - return nil, err - } - // TODO(runcom): figure out the ImageContext story in containers/image instead of passing ("", true) - src, err := tr.NewImageSource(nil, nil) - if err != nil { - return nil, err - } - i, err := image.FromSource(src) - if err != nil { - return nil, err - } - blobs := i.LayerInfos() - config := i.ConfigInfo() - if config.Digest != "" { - blobs = append(blobs, config) - } - - if err = os.Mkdir(filepath.Join(s.config.ImageDir, tr.StringWithinTransport()), 0755); err != nil { - return nil, err - } - dir, err := directory.NewReference(filepath.Join(s.config.ImageDir, tr.StringWithinTransport())) - if err != nil { - return nil, err - } - // TODO(runcom): figure out the ImageContext story in containers/image instead of passing ("", true) - dest, err := dir.NewImageDestination(nil) - if err != nil { - return nil, err - } - // save blobs (layer + config for docker v2s2, layers only for docker v2s1 [the config is in the manifest]) - for _, b := range blobs { - // TODO(runcom,nalin): we need do-then-commit to later purge on error - var r io.ReadCloser - r, _, err = src.GetBlob(b) - if err != nil { - return nil, err - } - if _, err = dest.PutBlob(r, b); err != nil { - r.Close() - return nil, err - } - r.Close() - } - // save manifest - m, _, err := i.Manifest() - if err != nil { - return nil, err - } - if err := dest.PutManifest(m); err != nil { - return nil, err - } - + logrus.Debugf("PullImageRequest: %+v", req) + // TODO(runcom?): deal with AuthConfig in req.GetAuth() // TODO: what else do we need here? (Signatures when the story isn't just pulling from docker://) - - return &pb.PullImageResponse{}, nil + image := "" + img := req.GetImage() + if img != nil { + image = img.GetImage() + } + options := ©.Options{} + _, err := s.images.PullImage(s.imageContext, image, options) + if err != nil { + return nil, err + } + resp := &pb.PullImageResponse{} + logrus.Debugf("PullImageResponse: %+v", resp) + return resp, nil } diff --git a/server/image_remove.go b/server/image_remove.go index 21bf30ff..f68dd03f 100644 --- a/server/image_remove.go +++ b/server/image_remove.go @@ -1,6 +1,8 @@ package server import ( + "fmt" + "github.com/Sirupsen/logrus" "golang.org/x/net/context" pb "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" @@ -8,6 +10,20 @@ import ( // RemoveImage removes the image. func (s *Server) RemoveImage(ctx context.Context, req *pb.RemoveImageRequest) (*pb.RemoveImageResponse, error) { - logrus.Debugf("RemoveImage: %+v", req) - return &pb.RemoveImageResponse{}, nil + logrus.Debugf("RemoveImageRequest: %+v", req) + image := "" + img := req.GetImage() + if img != nil { + image = img.GetImage() + } + if image == "" { + return nil, fmt.Errorf("no image specified") + } + err := s.images.RemoveImage(s.imageContext, image) + if err != nil { + return nil, err + } + resp := &pb.RemoveImageResponse{} + logrus.Debugf("RemoveImageResponse: %+v", resp) + return resp, nil } diff --git a/server/image_status.go b/server/image_status.go index 4ab113d5..cf253d66 100644 --- a/server/image_status.go +++ b/server/image_status.go @@ -1,6 +1,8 @@ package server import ( + "fmt" + "github.com/Sirupsen/logrus" "golang.org/x/net/context" pb "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" @@ -8,9 +10,26 @@ import ( // ImageStatus returns the status of the image. func (s *Server) ImageStatus(ctx context.Context, req *pb.ImageStatusRequest) (*pb.ImageStatusResponse, error) { - logrus.Debugf("ImageStatus: %+v", req) - // TODO - // containers/storage will take care of this by looking inside /var/lib/ocid/images - // and getting the image status - return &pb.ImageStatusResponse{}, nil + logrus.Debugf("ImageStatusRequest: %+v", req) + image := "" + img := req.GetImage() + if img != nil { + image = img.GetImage() + } + if image == "" { + return nil, fmt.Errorf("no image specified") + } + status, err := s.images.ImageStatus(s.imageContext, image) + if err != nil { + return nil, err + } + resp := &pb.ImageStatusResponse{ + Image: &pb.Image{ + Id: &status.ID, + RepoTags: status.Names, + Size_: status.Size, + }, + } + logrus.Debugf("ImageStatusResponse: %+v", resp) + return resp, nil } diff --git a/server/sandbox.go b/server/sandbox.go index 3348ed53..732f0044 100644 --- a/server/sandbox.go +++ b/server/sandbox.go @@ -145,6 +145,7 @@ const ( podDefaultNamespace = "default" defaultShmSize = 64 * 1024 * 1024 nsRunDir = "/var/run/netns" + podInfraCommand = "/pause" ) var ( @@ -277,7 +278,7 @@ func (s *Server) getPodSandboxFromRequest(req podSandboxRequest) (*sandbox, erro sb := s.getSandbox(sandboxID) if sb == nil { - return nil, fmt.Errorf("specified sandbox not found: %s", sandboxID) + return nil, fmt.Errorf("specified pod sandbox not found: %s", sandboxID) } return sb, nil } diff --git a/server/sandbox_list.go b/server/sandbox_list.go index 3d8ae6a6..c8627018 100644 --- a/server/sandbox_list.go +++ b/server/sandbox_list.go @@ -29,6 +29,7 @@ func filterSandbox(p *pb.PodSandbox, filter *pb.PodSandboxFilter) bool { // ListPodSandbox returns a list of SandBoxes. func (s *Server) ListPodSandbox(ctx context.Context, req *pb.ListPodSandboxRequest) (*pb.ListPodSandboxResponse, error) { logrus.Debugf("ListPodSandboxRequest %+v", req) + s.Update() var pods []*pb.PodSandbox var podList []*sandbox for _, sb := range s.state.sandboxes { diff --git a/server/sandbox_remove.go b/server/sandbox_remove.go index 00f8c3b8..db7010f5 100644 --- a/server/sandbox_remove.go +++ b/server/sandbox_remove.go @@ -2,8 +2,6 @@ package server import ( "fmt" - "os" - "path/filepath" "syscall" "github.com/Sirupsen/logrus" @@ -17,6 +15,7 @@ import ( // sandbox, they should be force deleted. func (s *Server) RemovePodSandbox(ctx context.Context, req *pb.RemovePodSandboxRequest) (*pb.RemovePodSandboxResponse, error) { logrus.Debugf("RemovePodSandboxRequest %+v", req) + s.Update() sb, err := s.getPodSandboxFromRequest(req) if err != nil { if err == errSandboxIDEmpty { @@ -46,16 +45,18 @@ func (s *Server) RemovePodSandbox(ctx context.Context, req *pb.RemovePodSandboxR } if err := s.runtime.DeleteContainer(c); err != nil { - return nil, fmt.Errorf("failed to delete container %s in sandbox %s: %v", c.Name(), sb.id, err) + return nil, fmt.Errorf("failed to delete container %s in pod sandbox %s: %v", c.Name(), sb.id, err) } if c == podInfraContainer { continue } - containerDir := filepath.Join(s.runtime.ContainerDir(), c.ID()) - if err := os.RemoveAll(containerDir); err != nil { - return nil, fmt.Errorf("failed to remove container %s directory: %v", c.Name(), err) + if err := s.storage.StopContainer(c.ID()); err != nil { + return nil, fmt.Errorf("failed to delete container %s in pod sandbox %s: %v", c.Name(), sb.id, err) + } + if err := s.storage.DeleteContainer(c.ID()); err != nil { + return nil, fmt.Errorf("failed to delete container %s in pod sandbox %s: %v", c.Name(), sb.id, err) } s.releaseContainerName(c.Name()) @@ -81,10 +82,13 @@ func (s *Server) RemovePodSandbox(ctx context.Context, req *pb.RemovePodSandboxR } // Remove the files related to the sandbox - podSandboxDir := filepath.Join(s.config.SandboxDir, sb.id) - if err := os.RemoveAll(podSandboxDir); err != nil { - return nil, fmt.Errorf("failed to remove sandbox %s directory: %v", sb.id, err) + if err := s.storage.StopContainer(sb.id); err != nil { + return nil, fmt.Errorf("failed to delete sandbox container in pod sandbox %s: %v", sb.id, err) } + if err := s.storage.RemovePodSandbox(sb.id); err != nil { + return nil, fmt.Errorf("failed to remove pod sandbox %s: %v", sb.id, err) + } + s.releaseContainerName(podInfraContainer.Name()) s.removeContainer(podInfraContainer) sb.infraContainer = nil diff --git a/server/sandbox_run.go b/server/sandbox_run.go index 581a5b86..2e279b6e 100644 --- a/server/sandbox_run.go +++ b/server/sandbox_run.go @@ -9,8 +9,8 @@ import ( "syscall" "github.com/Sirupsen/logrus" + "github.com/containers/storage/storage" "github.com/kubernetes-incubator/cri-o/oci" - "github.com/kubernetes-incubator/cri-o/utils" "github.com/opencontainers/runc/libcontainer/label" "github.com/opencontainers/runtime-tools/generate" "golang.org/x/net/context" @@ -54,6 +54,10 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest if err != nil { return nil, err } + _, containerName, err := s.generateContainerIDandName(name, "infra", attempt) + if err != nil { + return nil, err + } defer func() { if err != nil { @@ -67,39 +71,51 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest defer func() { if err != nil { - if err = s.podIDIndex.Delete(id); err != nil { + if err2 := s.podIDIndex.Delete(id); err2 != nil { logrus.Warnf("couldn't delete pod id %s from idIndex", id) } } }() - podSandboxDir := filepath.Join(s.config.SandboxDir, id) - if _, err = os.Stat(podSandboxDir); err == nil { - return nil, fmt.Errorf("pod sandbox (%s) already exists", podSandboxDir) + podContainer, err := s.storage.CreatePodSandbox(s.imageContext, + name, id, + s.config.PauseImage, "", + containerName, + req.GetConfig().GetMetadata().GetName(), + req.GetConfig().GetMetadata().GetUid(), + namespace, + attempt, + nil) + if err == storage.ErrDuplicateName { + return nil, fmt.Errorf("pod sandbox with name %q already exists", name) + } + if err != nil { + return nil, fmt.Errorf("error creating pod sandbox with name %q: %v", name, err) } - defer func() { if err != nil { - if err2 := os.RemoveAll(podSandboxDir); err2 != nil { - logrus.Warnf("couldn't cleanup podSandboxDir %s: %v", podSandboxDir, err2) + if err2 := s.storage.RemovePodSandbox(id); err2 != nil { + logrus.Warnf("couldn't cleanup pod sandbox %q: %v", id, err2) } } }() - if err = os.MkdirAll(podSandboxDir, 0755); err != nil { - return nil, err - } + // TODO: factor generating/updating the spec into something other projects can vendor // creates a spec Generator with the default spec. g := generate.New() - // TODO: Make the `graph/vfs` part of this configurable once the storage - // integration has been merged. - podInfraRootfs := filepath.Join(s.config.Root, "graph/vfs/pause") // setup defaults for the pod sandbox - g.SetRootPath(filepath.Join(podInfraRootfs, "rootfs")) g.SetRootReadonly(true) - g.SetProcessArgs([]string{"/pause"}) + if s.config.PauseCommand == "" { + if podContainer.Config != nil { + g.SetProcessArgs(podContainer.Config.Config.Cmd) + } else { + g.SetProcessArgs([]string{podInfraCommand}) + } + } else { + g.SetProcessArgs([]string{s.config.PauseCommand}) + } // set hostname hostname := req.GetConfig().GetHostname() @@ -117,7 +133,7 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest dnsServers := req.GetConfig().GetDnsConfig().GetServers() dnsSearches := req.GetConfig().GetDnsConfig().GetSearches() dnsOptions := req.GetConfig().GetDnsConfig().GetOptions() - resolvPath := fmt.Sprintf("%s/resolv.conf", podSandboxDir) + resolvPath := fmt.Sprintf("%s/resolv.conf", podContainer.RunDir) err = parseDNSOptions(dnsServers, dnsSearches, dnsOptions, resolvPath) if err != nil { err1 := removeFile(resolvPath) @@ -165,7 +181,7 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest if req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().GetHostIpc() { shmPath = "/dev/shm" } else { - shmPath, err = setupShm(podSandboxDir, mountLabel) + shmPath, err = setupShm(podContainer.RunDir, mountLabel) if err != nil { return nil, err } @@ -178,7 +194,7 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest }() } - containerID, containerName, err := s.generateContainerIDandName(name, "infra", 0) + err = s.setPodSandboxMountLabel(id, mountLabel) if err != nil { return nil, err } @@ -189,14 +205,14 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest } }() - if err = s.ctrIDIndex.Add(containerID); err != nil { + if err = s.ctrIDIndex.Add(id); err != nil { return nil, err } defer func() { if err != nil { - if err = s.ctrIDIndex.Delete(containerID); err != nil { - logrus.Warnf("couldn't delete ctr id %s from idIndex", containerID) + if err2 := s.ctrIDIndex.Delete(id); err2 != nil { + logrus.Warnf("couldn't delete ctr id %s from idIndex", id) } } }() @@ -207,8 +223,9 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest g.AddAnnotation("ocid/log_path", logDir) g.AddAnnotation("ocid/name", name) g.AddAnnotation("ocid/container_type", containerTypeSandbox) + g.AddAnnotation("ocid/sandbox_id", id) g.AddAnnotation("ocid/container_name", containerName) - g.AddAnnotation("ocid/container_id", containerID) + g.AddAnnotation("ocid/container_id", id) g.AddAnnotation("ocid/shm_path", shmPath) sb := &sandbox{ @@ -246,11 +263,11 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest cgroupParent := req.GetConfig().GetLinux().GetCgroupParent() if cgroupParent != "" { if s.config.CgroupManager == "systemd" { - cgPath := sb.cgroupParent + ":" + "ocid" + ":" + containerID + cgPath := sb.cgroupParent + ":" + "ocid" + ":" + id g.SetLinuxCgroupsPath(cgPath) } else { - g.SetLinuxCgroupsPath(sb.cgroupParent + "/" + containerID) + g.SetLinuxCgroupsPath(sb.cgroupParent + "/" + id) } sb.cgroupParent = cgroupParent @@ -308,23 +325,21 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest } } - err = g.SaveToFile(filepath.Join(podSandboxDir, "config.json"), generate.ExportOptions{}) + saveOptions := generate.ExportOptions{} + mountPoint, err := s.storage.StartContainer(id) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to mount container %s in pod sandbox %s(%s): %v", containerName, sb.name, id, err) + } + g.SetRootPath(mountPoint) + err = g.SaveToFile(filepath.Join(podContainer.Dir, "config.json"), saveOptions) + if err != nil { + return nil, fmt.Errorf("failed to save template configuration for pod sandbox %s(%s): %v", sb.name, id, err) + } + if err = g.SaveToFile(filepath.Join(podContainer.RunDir, "config.json"), saveOptions); err != nil { + return nil, fmt.Errorf("failed to write runtime configuration for pod sandbox %s(%s): %v", sb.name, id, err) } - if _, err = os.Stat(podInfraRootfs); err != nil { - if os.IsNotExist(err) { - // TODO: Replace by rootfs creation API when it is ready - if err = utils.CreateInfraRootfs(podInfraRootfs, s.config.Pause); err != nil { - return nil, err - } - } else { - return nil, err - } - } - - container, err := oci.NewContainer(containerID, containerName, podSandboxDir, podSandboxDir, sb.netNs(), labels, annotations, nil, nil, id, false) + container, err := oci.NewContainer(id, containerName, podContainer.RunDir, logDir, sb.netNs(), labels, annotations, nil, nil, id, false) if err != nil { return nil, err } @@ -348,6 +363,19 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest return resp, nil } +func (s *Server) setPodSandboxMountLabel(id, mountLabel string) error { + storageMetadata, err := s.storage.GetContainerMetadata(id) + if err != nil { + return err + } + storageMetadata.SetMountLabel(mountLabel) + err = s.storage.SetContainerMetadata(id, storageMetadata) + if err != nil { + return err + } + return nil +} + func getSELinuxLabels(selinuxOptions *pb.SELinuxOption) (processLabel string, mountLabel string, err error) { processLabel = "" if selinuxOptions != nil { @@ -375,8 +403,8 @@ func getSELinuxLabels(selinuxOptions *pb.SELinuxOption) (processLabel string, mo return label.InitLabels(label.DupSecOpt(processLabel)) } -func setupShm(podSandboxDir, mountLabel string) (shmPath string, err error) { - shmPath = filepath.Join(podSandboxDir, "shm") +func setupShm(podSandboxRunDir, mountLabel string) (shmPath string, err error) { + shmPath = filepath.Join(podSandboxRunDir, "shm") if err = os.Mkdir(shmPath, 0700); err != nil { return "", err } diff --git a/server/sandbox_status.go b/server/sandbox_status.go index d3826c3a..7f087fcd 100644 --- a/server/sandbox_status.go +++ b/server/sandbox_status.go @@ -10,6 +10,7 @@ import ( // PodSandboxStatus returns the Status of the PodSandbox. func (s *Server) PodSandboxStatus(ctx context.Context, req *pb.PodSandboxStatusRequest) (*pb.PodSandboxStatusResponse, error) { logrus.Debugf("PodSandboxStatusRequest %+v", req) + s.Update() sb, err := s.getPodSandboxFromRequest(req) if err != nil { return nil, err diff --git a/server/sandbox_stop.go b/server/sandbox_stop.go index 47f570c2..fa615acd 100644 --- a/server/sandbox_stop.go +++ b/server/sandbox_stop.go @@ -14,6 +14,7 @@ import ( // sandbox, they should be force terminated. func (s *Server) StopPodSandbox(ctx context.Context, req *pb.StopPodSandboxRequest) (*pb.StopPodSandboxResponse, error) { logrus.Debugf("StopPodSandboxRequest %+v", req) + s.Update() sb, err := s.getPodSandboxFromRequest(req) if err != nil { return nil, err @@ -50,7 +51,7 @@ func (s *Server) StopPodSandbox(ctx context.Context, req *pb.StopPodSandboxReque cStatus := s.runtime.ContainerStatus(c) if cStatus.Status != oci.ContainerStateStopped { if err := s.runtime.StopContainer(c); err != nil { - return nil, fmt.Errorf("failed to stop container %s in sandbox %s: %v", c.Name(), sb.id, err) + return nil, fmt.Errorf("failed to stop container %s in pod sandbox %s: %v", c.Name(), sb.id, err) } } } diff --git a/server/server.go b/server/server.go index b2e2cc3c..1800c1f0 100644 --- a/server/server.go +++ b/server/server.go @@ -5,14 +5,16 @@ import ( "fmt" "io/ioutil" "os" - "path/filepath" "sync" "syscall" "github.com/Sirupsen/logrus" + "github.com/containers/image/types" + sstorage "github.com/containers/storage/storage" "github.com/docker/docker/pkg/registrar" "github.com/docker/docker/pkg/truncindex" "github.com/kubernetes-incubator/cri-o/oci" + "github.com/kubernetes-incubator/cri-o/pkg/storage" "github.com/kubernetes-incubator/cri-o/server/apparmor" "github.com/kubernetes-incubator/cri-o/server/seccomp" "github.com/opencontainers/runc/libcontainer/label" @@ -29,6 +31,9 @@ const ( type Server struct { config Config runtime *oci.Runtime + store sstorage.Store + images storage.ImageServer + storage storage.RuntimeServer stateLock sync.Mutex state *serverState netPlugin ocicni.CNIPlugin @@ -36,6 +41,7 @@ type Server struct { podIDIndex *truncindex.TruncIndex ctrNameIndex *registrar.Registrar ctrIDIndex *truncindex.TruncIndex + imageContext *types.SystemContext seccompEnabled bool seccompProfile seccomp.Seccomp @@ -45,7 +51,7 @@ type Server struct { } func (s *Server) loadContainer(id string) error { - config, err := ioutil.ReadFile(filepath.Join(s.runtime.ContainerDir(), id, "config.json")) + config, err := s.store.GetFromContainerDirectory(id, "config.json") if err != nil { return err } @@ -76,7 +82,10 @@ func (s *Server) loadContainer(id string) error { if v := m.Annotations["ocid/tty"]; v == "true" { tty = true } - containerPath := filepath.Join(s.runtime.ContainerDir(), id) + containerPath, err := s.store.GetContainerRunDirectory(id) + if err != nil { + return err + } var img *pb.ImageSpec image, ok := m.Annotations["ocid/image"] @@ -122,7 +131,7 @@ func configNetNsPath(spec rspec.Spec) (string, error) { } func (s *Server) loadSandbox(id string) error { - config, err := ioutil.ReadFile(filepath.Join(s.config.SandboxDir, id, "config.json")) + config, err := s.store.GetFromContainerDirectory(id, "config.json") if err != nil { return err } @@ -184,7 +193,10 @@ func (s *Server) loadSandbox(id string) error { s.addSandbox(sb) - sandboxPath := filepath.Join(s.config.SandboxDir, id) + sandboxPath, err := s.store.GetContainerRunDirectory(id) + if err != nil { + return err + } if err = label.ReserveLabel(processLabel); err != nil { return err @@ -200,7 +212,7 @@ func (s *Server) loadSandbox(id string) error { } sb.infraContainer = scontainer if err = s.runtime.UpdateStatus(scontainer); err != nil { - logrus.Warnf("error updating status for container %s: %v", scontainer.ID(), err) + logrus.Warnf("error updating status for pod sandbox infra container %s: %v", scontainer.ID(), err) } if err = s.ctrIDIndex.Add(scontainer.ID()); err != nil { return err @@ -212,31 +224,138 @@ func (s *Server) loadSandbox(id string) error { } func (s *Server) restore() { - sandboxDir, err := ioutil.ReadDir(s.config.SandboxDir) + containers, err := s.store.Containers() if err != nil && !os.IsNotExist(err) { - logrus.Warnf("could not read sandbox directory %s: %v", sandboxDir, err) + logrus.Warnf("could not read containers and sandboxes: %v", err) } - for _, v := range sandboxDir { - if !v.IsDir() { + pods := map[string]*storage.RuntimeContainerMetadata{} + podContainers := map[string]*storage.RuntimeContainerMetadata{} + for _, container := range containers { + metadata, err2 := s.storage.GetContainerMetadata(container.ID) + if err2 != nil { + logrus.Warnf("error parsing metadata for %s: %v, ignoring", container.ID, err2) continue } - if err = s.loadSandbox(v.Name()); err != nil { - logrus.Warnf("could not restore sandbox %s: %v", v.Name(), err) + if metadata.Pod { + pods[container.ID] = &metadata + } else { + podContainers[container.ID] = &metadata } } - containerDir, err := ioutil.ReadDir(s.runtime.ContainerDir()) - if err != nil && !os.IsNotExist(err) { - logrus.Warnf("could not read container directory %s: %v", s.runtime.ContainerDir(), err) - } - for _, v := range containerDir { - if !v.IsDir() { - continue + for containerID, metadata := range pods { + if err = s.loadSandbox(containerID); err != nil { + logrus.Warnf("could not restore sandbox %s container %s: %v", metadata.PodID, containerID, err) } - if err := s.loadContainer(v.Name()); err != nil { - logrus.Warnf("could not restore container %s: %v", v.Name(), err) + } + for containerID := range podContainers { + if err := s.loadContainer(containerID); err != nil { + logrus.Warnf("could not restore container %s: %v", containerID, err) + } + } +} +// Update makes changes to the server's state (lists of pods and containers) to +// reflect the list of pods and containers that are stored on disk, possibly +// having been modified by other parties +func (s *Server) Update() { + logrus.Debugf("updating sandbox and container information") + if err := s.update(); err != nil { + logrus.Errorf("error updating sandbox and container information: %v", err) + } +} + +func (s *Server) update() error { + containers, err := s.store.Containers() + if err != nil && !os.IsNotExist(err) { + logrus.Warnf("could not read containers and sandboxes: %v", err) + return err + } + newPods := map[string]*storage.RuntimeContainerMetadata{} + oldPods := map[string]string{} + removedPods := map[string]string{} + newPodContainers := map[string]*storage.RuntimeContainerMetadata{} + oldPodContainers := map[string]string{} + removedPodContainers := map[string]string{} + for _, container := range containers { + if s.hasSandbox(container.ID) { + // FIXME: do we need to reload/update any info about the sandbox? + oldPods[container.ID] = container.ID + oldPodContainers[container.ID] = container.ID + continue + } + if s.getContainer(container.ID) != nil { + // FIXME: do we need to reload/update any info about the container? + oldPodContainers[container.ID] = container.ID + continue + } + // not previously known, so figure out what it is + metadata, err2 := s.storage.GetContainerMetadata(container.ID) + if err2 != nil { + logrus.Errorf("error parsing metadata for %s: %v, ignoring", container.ID, err2) + continue + } + if metadata.Pod { + newPods[container.ID] = &metadata + } else { + newPodContainers[container.ID] = &metadata } } + s.ctrIDIndex.Iterate(func(id string) { + if _, ok := oldPodContainers[id]; !ok { + // this container's ID wasn't in the updated list -> removed + removedPodContainers[id] = id + } + }) + for removedPodContainer := range removedPodContainers { + // forget this container + c := s.getContainer(removedPodContainer) + s.releaseContainerName(c.Name()) + s.removeContainer(c) + if err = s.ctrIDIndex.Delete(c.ID()); err != nil { + return err + } + logrus.Debugf("forgetting removed pod container %s", c.ID()) + } + s.podIDIndex.Iterate(func(id string) { + if _, ok := oldPods[id]; !ok { + // this pod's ID wasn't in the updated list -> removed + removedPods[id] = id + } + }) + for removedPod := range removedPods { + // forget this pod + sb := s.getSandbox(removedPod) + podInfraContainer := sb.infraContainer + s.releaseContainerName(podInfraContainer.Name()) + s.removeContainer(podInfraContainer) + if err = s.ctrIDIndex.Delete(podInfraContainer.ID()); err != nil { + return err + } + sb.infraContainer = nil + s.releasePodName(sb.name) + s.removeSandbox(sb.id) + if err = s.podIDIndex.Delete(sb.id); err != nil { + return err + } + logrus.Debugf("forgetting removed pod %s", sb.id) + } + for sandboxID := range newPods { + // load this pod + if err = s.loadSandbox(sandboxID); err != nil { + logrus.Warnf("could not load new pod sandbox %s: %v, ignoring", sandboxID, err) + } else { + logrus.Debugf("loaded new pod sandbox %s", sandboxID, err) + } + } + for containerID := range newPodContainers { + // load this container + if err = s.loadContainer(containerID); err != nil { + logrus.Warnf("could not load new sandbox container %s: %v, ignoring", containerID, err) + } else { + logrus.Debugf("loaded new pod container %s", containerID, err) + } + } + return nil } func (s *Server) reservePodName(id, name string) (string, error) { @@ -294,17 +413,35 @@ func seccompEnabled() bool { return enabled } +// Shutdown attempts to shut down the server's storage cleanly +func (s *Server) Shutdown() error { + _, err := s.store.Shutdown(false) + return err +} + // New creates a new Server with options provided func New(config *Config) (*Server, error) { - if err := os.MkdirAll(config.ImageDir, 0755); err != nil { + store, err := sstorage.GetStore(sstorage.StoreOptions{ + RunRoot: config.RunRoot, + GraphRoot: config.Root, + GraphDriverName: config.Storage, + GraphDriverOptions: config.StorageOptions, + }) + if err != nil { return nil, err } - if err := os.MkdirAll(config.SandboxDir, 0755); err != nil { + imageService, err := storage.GetImageService(store, config.DefaultTransport) + if err != nil { return nil, err } - r, err := oci.New(config.Runtime, config.ContainerDir, config.Conmon, config.ConmonEnv, config.CgroupManager) + storageRuntimeService := storage.GetRuntimeService(imageService) + if err != nil { + return nil, err + } + + r, err := oci.New(config.Runtime, config.Conmon, config.ConmonEnv, config.CgroupManager) if err != nil { return nil, err } @@ -316,6 +453,9 @@ func New(config *Config) (*Server, error) { } s := &Server{ runtime: r, + store: store, + images: imageService, + storage: storageRuntimeService, netPlugin: netPlugin, config: *config, state: &serverState{ @@ -346,6 +486,9 @@ func New(config *Config) (*Server, error) { s.podNameIndex = registrar.NewRegistrar() s.ctrIDIndex = truncindex.NewTruncIndex([]string{}) s.ctrNameIndex = registrar.NewRegistrar() + s.imageContext = &types.SystemContext{ + SignaturePolicyPath: config.ImageConfig.SignaturePolicyPath, + } s.restore() diff --git a/test/helpers.bash b/test/helpers.bash index 8819b77c..f705b85c 100644 --- a/test/helpers.bash +++ b/test/helpers.bash @@ -59,7 +59,7 @@ PATH=$PATH:$TESTDIR # Run ocid using the binary specified by $OCID_BINARY. # This must ONLY be run on engines created with `start_ocid`. function ocid() { - "$OCID_BINARY" "$@" + "$OCID_BINARY" --listen "$OCID_SOCKET" "$@" } # Run ocic using the binary specified by $OCID_BINARY. @@ -112,7 +112,7 @@ function start_ocid() { apparmor="$APPARMOR_PROFILE" fi - "$OCID_BINARY" --conmon "$CONMON_BINARY" --pause "$PAUSE_BINARY" --listen "$OCID_SOCKET" --runtime "$RUNC_BINARY" --root "$TESTDIR/ocid" --sandboxdir "$TESTDIR/sandboxes" --containerdir "$TESTDIR/ocid/containers" --seccomp-profile "$seccomp" --apparmor-profile "$apparmor" --cni-config-dir "$OCID_CNI_CONFIG" config >$OCID_CONFIG + "$OCID_BINARY" --conmon "$CONMON_BINARY" --listen "$OCID_SOCKET" --runtime "$RUNC_BINARY" --root "$TESTDIR/ocid" --runroot "$TESTDIR/ocid-run" --seccomp-profile "$seccomp" --apparmor-profile "$apparmor" --cni-config-dir "$OCID_CNI_CONFIG" --signature-policy "$INTEGRATION_ROOT"/policy.json config >$OCID_CONFIG "$OCID_BINARY" --debug --config "$OCID_CONFIG" & OCID_PID=$! wait_until_reachable } @@ -130,6 +130,18 @@ function cleanup_ctrs() { fi } +function cleanup_images() { + run ocic image list --quiet + if [ "$status" -eq 0 ]; then + if [ "$output" != "" ]; then + printf '%s\n' "$output" | while IFS= read -r line + do + ocic image remove --id "$line" + done + fi + fi +} + function cleanup_pods() { run ocic pod list --quiet if [ "$status" -eq 0 ]; then @@ -147,6 +159,7 @@ function cleanup_pods() { function stop_ocid() { if [ "$OCID_PID" != "" ]; then kill "$OCID_PID" >/dev/null 2>&1 + wait "$OCID_PID" rm -f "$OCID_CONFIG" fi } diff --git a/test/policy.json b/test/policy.json new file mode 100644 index 00000000..bb26e57f --- /dev/null +++ b/test/policy.json @@ -0,0 +1,7 @@ +{ + "default": [ + { + "type": "insecureAcceptAnything" + } + ] +} diff --git a/utils/utils.go b/utils/utils.go index 5323d290..0db64aaf 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -4,13 +4,9 @@ import ( "bytes" "fmt" "io" - "os" "os/exec" - "path/filepath" "strings" "syscall" - - "github.com/Sirupsen/logrus" ) // ExecCmd executes a command with args and returns its output as a string along @@ -54,74 +50,7 @@ func Prctl(option int, arg2, arg3, arg4, arg5 uintptr) (err error) { return } -// CreateFakeRootfs creates a fake rootfs for test. -func CreateFakeRootfs(dir string, image string) error { - if len(image) <= 9 || image[:9] != "docker://" { - return fmt.Errorf("CreateFakeRootfs only support docker images currently") - } - - rootfs := filepath.Join(dir, "rootfs") - if err := os.MkdirAll(rootfs, 0755); err != nil { - return err - } - - // docker export $(docker create image[9:]) | tar -C rootfs -xf - - return dockerExport(image[9:], rootfs) -} - -// CreateInfraRootfs creates a rootfs similar to CreateFakeRootfs, but only -// copies a single binary from the host into the rootfs. This is all done -// without Docker, and is only used currently for the pause container which is -// required for all sandboxes. -func CreateInfraRootfs(dir string, src string) error { - rootfs := filepath.Join(dir, "rootfs") - if err := os.MkdirAll(rootfs, 0755); err != nil { - return err - } - - dest := filepath.Join(rootfs, filepath.Base(src)) - logrus.Debugf("copying infra rootfs binary: %v -> %v", src, dest) - - in, err := os.OpenFile(src, os.O_RDONLY, 0755) - if err != nil { - return err - } - defer in.Close() - - out, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0755) - if err != nil { - return err - } - defer out.Close() - - if _, err := io.Copy(out, in); err != nil { - return err - } - - return out.Sync() -} - -func dockerExport(image string, rootfs string) error { - out, err := ExecCmd("docker", "create", image) - if err != nil { - return err - } - - container := out[:strings.Index(out, "\n")] - - cmd := fmt.Sprintf("docker export %s | tar -C %s -xf -", container, rootfs) - if _, err := ExecCmd("/bin/bash", "-c", cmd); err != nil { - err1 := dockerRemove(container) - if err1 == nil { - return err - } - return fmt.Errorf("%v; %v", err, err1) - } - - return dockerRemove(container) -} - -func dockerRemove(container string) error { - _, err := ExecCmd("docker", "rm", container) - return err +// StatusToExitCode converts wait status code to an exit code +func StatusToExitCode(status int) int { + return ((status) & 0xff00) >> 8 } From 636d5d8e9aab4b9292dd0c8fc061963ddcc8d110 Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Fri, 16 Dec 2016 18:34:51 -0500 Subject: [PATCH 03/11] Add and use bin2img for creating images for tests Add tests which exercise image pulling, listing, and removal. When running tests, prepopulate the store with an image with the default infrastructure container's name, using the locally-built "pause" binary, so that tests won't have to pull it down from the network. Signed-off-by: Nalin Dahyabhai --- .gitignore | 1 + Makefile | 7 +- test/bin2img/Makefile | 6 ++ test/bin2img/bin2img.go | 225 ++++++++++++++++++++++++++++++++++++++++ test/helpers.bash | 6 ++ test/image.bats | 93 +++++++++++++++++ 6 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 test/bin2img/Makefile create mode 100644 test/bin2img/bin2img.go create mode 100644 test/image.bats diff --git a/.gitignore b/.gitignore index 20bb82a1..d8ae2bad 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ pause/pause.o ocid.conf *.orig *.rej +test/bin2img/bin2img diff --git a/Makefile b/Makefile index 30615c38..616723ac 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,9 @@ conmon: pause: make -C $@ +bin2img: + make -C test/$@ + ocid: ifndef GOPATH $(error GOPATH is not set) @@ -73,6 +76,7 @@ clean: find . -name \#\* -delete make -C conmon clean make -C pause clean + make -C test/bin2img clean ocidimage: docker build -t ${OCID_IMAGE} . @@ -86,7 +90,7 @@ integration: ocidimage localintegration: binaries ./test/test_runner.sh ${TESTFLAGS} -binaries: ocid ocic kpod conmon pause +binaries: ocid ocic kpod conmon pause bin2img MANPAGES_MD := $(wildcard docs/*.md) MANPAGES := $(MANPAGES_MD:%.md=%) @@ -180,6 +184,7 @@ install.tools: .install.gitvalidation .install.gometalinter .install.md2man go get -u github.com/cpuguy83/go-md2man .PHONY: \ + bin2img \ binaries \ clean \ conmon \ diff --git a/test/bin2img/Makefile b/test/bin2img/Makefile new file mode 100644 index 00000000..69ad14b1 --- /dev/null +++ b/test/bin2img/Makefile @@ -0,0 +1,6 @@ +bin2img: $(wildcard *.go) + go build -o $@ + +.PHONY: clean +clean: + rm -f bin2img diff --git a/test/bin2img/bin2img.go b/test/bin2img/bin2img.go new file mode 100644 index 00000000..45eba0de --- /dev/null +++ b/test/bin2img/bin2img.go @@ -0,0 +1,225 @@ +package main + +import ( + "archive/tar" + "bytes" + "encoding/json" + "io" + "os" + "runtime" + + "github.com/Sirupsen/logrus" + "github.com/containers/image/storage" + "github.com/containers/image/types" + "github.com/containers/storage/pkg/reexec" + sstorage "github.com/containers/storage/storage" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go" + "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/urfave/cli" +) + +func main() { + if reexec.Init() { + return + } + + app := cli.NewApp() + app.Name = "bin2img" + app.Usage = "barebones image builder" + app.Version = "0.0.1" + + app.Flags = []cli.Flag{ + cli.BoolFlag{ + Name: "debug", + Usage: "turn on debug logging", + }, + cli.StringFlag{ + Name: "root", + Usage: "graph root directory", + }, + cli.StringFlag{ + Name: "runroot", + Usage: "run root directory", + }, + cli.StringFlag{ + Name: "storage-driver", + Usage: "storage driver", + }, + cli.StringSliceFlag{ + Name: "storage-option", + Usage: "storage option", + }, + cli.StringFlag{ + Name: "image-name", + Usage: "set image name", + Value: "kubernetes/pause", + }, + cli.StringFlag{ + Name: "source-binary", + Usage: "source binary", + Value: "../../pause/pause", + }, + cli.StringFlag{ + Name: "image-binary", + Usage: "image binary", + Value: "/pause", + }, + } + + app.Action = func(c *cli.Context) error { + debug := c.GlobalBool("debug") + rootDir := c.GlobalString("root") + runrootDir := c.GlobalString("runroot") + storageDriver := c.GlobalString("storage-driver") + storageOptions := c.GlobalStringSlice("storage-option") + imageName := c.GlobalString("image-name") + sourceBinary := c.GlobalString("source-binary") + imageBinary := c.GlobalString("image-binary") + + if debug { + logrus.SetLevel(logrus.DebugLevel) + } else { + logrus.SetLevel(logrus.ErrorLevel) + } + if rootDir == "" && runrootDir != "" { + logrus.Errorf("must set --root and --runroot, or neither") + os.Exit(1) + } + if rootDir != "" && runrootDir == "" { + logrus.Errorf("must set --root and --runroot, or neither") + os.Exit(1) + } + storeOptions := sstorage.DefaultStoreOptions + if rootDir != "" && runrootDir != "" { + storeOptions.GraphDriverName = storageDriver + storeOptions.GraphDriverOptions = storageOptions + storeOptions.GraphRoot = rootDir + storeOptions.RunRoot = runrootDir + } + store, err := sstorage.GetStore(storeOptions) + if err != nil { + logrus.Errorf("error opening storage: %v", err) + os.Exit(1) + } + defer store.Shutdown(false) + + layerBuffer := &bytes.Buffer{} + binary, err := os.Open(sourceBinary) + if err != nil { + logrus.Errorf("error opening image binary: %v", err) + os.Exit(1) + } + binInfo, err := binary.Stat() + if err != nil { + logrus.Errorf("error statting image binary: %v", err) + os.Exit(1) + } + archive := tar.NewWriter(layerBuffer) + err = archive.WriteHeader(&tar.Header{ + Name: imageBinary, + Size: binInfo.Size(), + Mode: 0555, + ModTime: binInfo.ModTime(), + Typeflag: tar.TypeReg, + Uname: "root", + Gname: "root", + }) + if err != nil { + logrus.Errorf("error writing archive header: %v", err) + os.Exit(1) + } + _, err = io.Copy(archive, binary) + if err != nil { + logrus.Errorf("error archiving image binary: %v", err) + os.Exit(1) + } + archive.Close() + binary.Close() + layerInfo := types.BlobInfo{ + Digest: digest.Canonical.FromBytes(layerBuffer.Bytes()), + Size: int64(layerBuffer.Len()), + } + + ref, err := storage.Transport.ParseStoreReference(store, imageName) + if err != nil { + logrus.Errorf("error parsing image name: %v", err) + os.Exit(1) + } + img, err := ref.NewImageDestination(nil) + if err != nil { + logrus.Errorf("error preparing to write image: %v", err) + os.Exit(1) + } + defer img.Close() + layer, err := img.PutBlob(layerBuffer, layerInfo) + if err != nil { + logrus.Errorf("error preparing to write image: %v", err) + os.Exit(1) + } + config := &v1.Image{ + Architecture: runtime.GOARCH, + OS: runtime.GOOS, + Config: v1.ImageConfig{ + User: "root", + Entrypoint: []string{imageBinary}, + }, + RootFS: v1.RootFS{ + Type: "layers", + DiffIDs: []string{ + layer.Digest.String(), + }, + }, + } + cbytes, err := json.Marshal(config) + if err != nil { + logrus.Errorf("error encoding configuration: %v", err) + os.Exit(1) + } + configInfo := types.BlobInfo{ + Digest: digest.Canonical.FromBytes(cbytes), + Size: int64(len(cbytes)), + } + configInfo, err = img.PutBlob(bytes.NewBuffer(cbytes), configInfo) + if err != nil { + logrus.Errorf("error saving configuration: %v", err) + os.Exit(1) + } + manifest := &v1.Manifest{ + Versioned: specs.Versioned{ + SchemaVersion: 2, + MediaType: v1.MediaTypeImageManifest, + }, + Config: v1.Descriptor{ + MediaType: v1.MediaTypeImageConfig, + Digest: configInfo.Digest.String(), + Size: int64(len(cbytes)), + }, + Layers: []v1.Descriptor{{ + MediaType: v1.MediaTypeImageLayer, + Digest: layer.Digest.String(), + Size: layer.Size, + }}, + } + mbytes, err := json.Marshal(manifest) + if err != nil { + logrus.Errorf("error encoding manifest: %v", err) + os.Exit(1) + } + err = img.PutManifest(mbytes) + if err != nil { + logrus.Errorf("error saving manifest: %v", err) + os.Exit(1) + } + err = img.Commit() + if err != nil { + logrus.Errorf("error committing image: %v", err) + os.Exit(1) + } + return nil + } + + if err := app.Run(os.Args); err != nil { + logrus.Fatal(err) + } +} diff --git a/test/helpers.bash b/test/helpers.bash index f705b85c..226fef8e 100644 --- a/test/helpers.bash +++ b/test/helpers.bash @@ -36,6 +36,8 @@ APPARMOR_TEST_PROFILE_NAME=${APPARMOR_TEST_PROFILE_NAME:-apparmor-test-deny-writ BOOT_CONFIG_FILE_PATH=${BOOT_CONFIG_FILE_PATH:-/boot/config-`uname -r`} # Path of apparmor parameters file. APPARMOR_PARAMETERS_FILE_PATH=${APPARMOR_PARAMETERS_FILE_PATH:-/sys/module/apparmor/parameters/enabled} +# Path of the bin2img binary. +BIN2IMG_BINARY=${BIN2IMG_BINARY:-${OCID_ROOT}/cri-o/test/bin2img/bin2img} TESTDIR=$(mktemp -d) if [ -e /usr/sbin/selinuxenabled ] && /usr/sbin/selinuxenabled; then @@ -112,6 +114,10 @@ function start_ocid() { apparmor="$APPARMOR_PROFILE" fi + # Don't forget: bin2img and ocid have their own default drivers, so if you override either, you probably need to override both + if ! [ "$3" = "--no-pause-image" ] ; then + "$BIN2IMG_BINARY" --root "$TESTDIR/ocid" --runroot "$TESTDIR/ocid-run" --source-binary "$PAUSE_BINARY" + fi "$OCID_BINARY" --conmon "$CONMON_BINARY" --listen "$OCID_SOCKET" --runtime "$RUNC_BINARY" --root "$TESTDIR/ocid" --runroot "$TESTDIR/ocid-run" --seccomp-profile "$seccomp" --apparmor-profile "$apparmor" --cni-config-dir "$OCID_CNI_CONFIG" --signature-policy "$INTEGRATION_ROOT"/policy.json config >$OCID_CONFIG "$OCID_BINARY" --debug --config "$OCID_CONFIG" & OCID_PID=$! wait_until_reachable diff --git a/test/image.bats b/test/image.bats new file mode 100644 index 00000000..88b2ac73 --- /dev/null +++ b/test/image.bats @@ -0,0 +1,93 @@ +#!/usr/bin/env bats + +load helpers + +IMAGE=kubernetes/pause + +function teardown() { + cleanup_test +} + +@test "image pull" { + start_ocid "" "" --no-pause-image + run ocic image pull "$IMAGE" + echo "$output" + [ "$status" -eq 0 ] + cleanup_images + stop_ocid +} + +@test "image list with filter" { + start_ocid "" "" --no-pause-image + run ocic image pull "$IMAGE" + echo "$output" + [ "$status" -eq 0 ] + run ocic image list --quiet "$IMAGE" + echo "$output" + [ "$status" -eq 0 ] + printf '%s\n' "$output" | while IFS= read -r id; do + run ocic image remove --id "$id" + echo "$output" + [ "$status" -eq 0 ] + done + run ocic image list --quiet + echo "$output" + [ "$status" -eq 0 ] + printf '%s\n' "$output" | while IFS= read -r id; do + echo "$id" + status=1 + done + cleanup_images + stop_ocid +} + +@test "image list/remove" { + start_ocid "" "" --no-pause-image + run ocic image pull "$IMAGE" + echo "$output" + [ "$status" -eq 0 ] + run ocic image list --quiet + echo "$output" + [ "$status" -eq 0 ] + printf '%s\n' "$output" | while IFS= read -r id; do + run ocic image remove --id "$id" + echo "$output" + [ "$status" -eq 0 ] + done + run ocic image list --quiet + echo "$output" + [ "$status" -eq 0 ] + printf '%s\n' "$output" | while IFS= read -r id; do + echo "$id" + status=1 + done + cleanup_images + stop_ocid +} + +@test "image status/remove" { + start_ocid "" "" --no-pause-image + run ocic image pull "$IMAGE" + echo "$output" + [ "$status" -eq 0 ] + run ocic image list --quiet + echo "$output" + [ "$status" -eq 0 ] + printf '%s\n' "$output" | while IFS= read -r id; do + run ocic image status --id "$id" + echo "$output" + [ "$status" -eq 0 ] + run ocic image remove --id "$id" + echo "$output" + [ "$status" -eq 0 ] + done + run ocic image list --quiet + echo "$output" + [ "$status" -eq 0 ] + printf '%s\n' "$output" | while IFS= read -r id; do + echo "$id" + status=1 + done + cleanup_images + stop_ocid +} From 925806b8fab7e70c67be239e6c6bdf7b9d85f79f Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Tue, 10 Jan 2017 17:57:22 -0500 Subject: [PATCH 04/11] Add and use copyimg for caching images for tests Add a basic tool for copying images from one location to another, optionally adding a name if it's to local storage. Ideally we could use skopeo for this, but we don't want to build it. Use it to initially populate the test/testdata/redis-image directory, if it's not been cleaned out, with a copy of "docker://redis:latest", and to copy it in to the storage that ocid is using before we start up ocid. Signed-off-by: Nalin Dahyabhai --- .gitignore | 2 + Makefile | 8 +- test/copyimg/Makefile | 6 ++ test/copyimg/copyimg.go | 198 ++++++++++++++++++++++++++++++++++++++++ test/helpers.bash | 15 ++- 5 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 test/copyimg/Makefile create mode 100644 test/copyimg/copyimg.go diff --git a/.gitignore b/.gitignore index d8ae2bad..d073fa34 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ ocid.conf *.orig *.rej test/bin2img/bin2img +test/copyimg/copyimg +test/testdata/redis-image diff --git a/Makefile b/Makefile index 616723ac..9a37318c 100644 --- a/Makefile +++ b/Makefile @@ -47,6 +47,9 @@ pause: bin2img: make -C test/$@ +copyimg: + make -C test/$@ + ocid: ifndef GOPATH $(error GOPATH is not set) @@ -72,11 +75,13 @@ ocid.conf: ocid clean: rm -f docs/*.1 docs/*.5 docs/*.8 + rm -fr test/testdata/redis-image find . -name \*~ -delete find . -name \#\* -delete make -C conmon clean make -C pause clean make -C test/bin2img clean + make -C test/copyimg clean ocidimage: docker build -t ${OCID_IMAGE} . @@ -90,7 +95,7 @@ integration: ocidimage localintegration: binaries ./test/test_runner.sh ${TESTFLAGS} -binaries: ocid ocic kpod conmon pause bin2img +binaries: ocid ocic kpod conmon pause bin2img copyimg MANPAGES_MD := $(wildcard docs/*.md) MANPAGES := $(MANPAGES_MD:%.md=%) @@ -188,6 +193,7 @@ install.tools: .install.gitvalidation .install.gometalinter .install.md2man binaries \ clean \ conmon \ + copyimg \ default \ docs \ gofmt \ diff --git a/test/copyimg/Makefile b/test/copyimg/Makefile new file mode 100644 index 00000000..cb1d7f10 --- /dev/null +++ b/test/copyimg/Makefile @@ -0,0 +1,6 @@ +copyimg: $(wildcard *.go) + go build -o $@ + +.PHONY: clean +clean: + rm -f copyimg diff --git a/test/copyimg/copyimg.go b/test/copyimg/copyimg.go new file mode 100644 index 00000000..ba8a7849 --- /dev/null +++ b/test/copyimg/copyimg.go @@ -0,0 +1,198 @@ +package main + +import ( + "os" + + "github.com/Sirupsen/logrus" + "github.com/containers/image/copy" + "github.com/containers/image/signature" + "github.com/containers/image/storage" + "github.com/containers/image/transports" + "github.com/containers/image/types" + "github.com/containers/storage/pkg/reexec" + sstorage "github.com/containers/storage/storage" + "github.com/urfave/cli" +) + +func main() { + if reexec.Init() { + return + } + + app := cli.NewApp() + app.Name = "copyimg" + app.Usage = "barebones image copier" + app.Version = "0.0.1" + + app.Flags = []cli.Flag{ + cli.BoolFlag{ + Name: "debug", + Usage: "turn on debug logging", + }, + cli.StringFlag{ + Name: "root", + Usage: "graph root directory", + }, + cli.StringFlag{ + Name: "runroot", + Usage: "run root directory", + }, + cli.StringFlag{ + Name: "storage-driver", + Usage: "storage driver", + }, + cli.StringSliceFlag{ + Name: "storage-option", + Usage: "storage option", + }, + cli.StringFlag{ + Name: "signature-policy", + Usage: "signature policy", + }, + cli.StringFlag{ + Name: "image-name", + Usage: "set image name", + }, + cli.StringFlag{ + Name: "add-name", + Usage: "name to add to image", + }, + cli.StringFlag{ + Name: "import-from", + Usage: "import source", + }, + cli.StringFlag{ + Name: "export-to", + Usage: "export target", + }, + } + + app.Action = func(c *cli.Context) error { + var store sstorage.Store + var ref, importRef, exportRef types.ImageReference + var err error + + debug := c.GlobalBool("debug") + rootDir := c.GlobalString("root") + runrootDir := c.GlobalString("runroot") + storageDriver := c.GlobalString("storage-driver") + storageOptions := c.GlobalStringSlice("storage-option") + signaturePolicy := c.GlobalString("signature-policy") + imageName := c.GlobalString("image-name") + addName := c.GlobalString("add-name") + importFrom := c.GlobalString("import-from") + exportTo := c.GlobalString("export-to") + + if debug { + logrus.SetLevel(logrus.DebugLevel) + } else { + logrus.SetLevel(logrus.ErrorLevel) + } + + if imageName != "" { + if rootDir == "" && runrootDir != "" { + logrus.Errorf("must set --root and --runroot, or neither") + os.Exit(1) + } + if rootDir != "" && runrootDir == "" { + logrus.Errorf("must set --root and --runroot, or neither") + os.Exit(1) + } + storeOptions := sstorage.DefaultStoreOptions + if rootDir != "" && runrootDir != "" { + storeOptions.GraphDriverName = storageDriver + storeOptions.GraphDriverOptions = storageOptions + storeOptions.GraphRoot = rootDir + storeOptions.RunRoot = runrootDir + } + store, err = sstorage.GetStore(storeOptions) + if err != nil { + logrus.Errorf("error opening storage: %v", err) + os.Exit(1) + } + defer store.Shutdown(false) + + storage.Transport.SetStore(store) + ref, err = storage.Transport.ParseStoreReference(store, imageName) + if err != nil { + logrus.Errorf("error parsing image name: %v", err) + os.Exit(1) + } + } + + systemContext := types.SystemContext{ + SignaturePolicyPath: signaturePolicy, + } + policy, err := signature.DefaultPolicy(&systemContext) + if err != nil { + logrus.Errorf("error loading signature policy: %v", err) + os.Exit(1) + } + policyContext, err := signature.NewPolicyContext(policy) + if err != nil { + logrus.Errorf("error loading signature policy: %v", err) + os.Exit(1) + } + defer policyContext.Destroy() + options := ©.Options{} + + if importFrom != "" { + importRef, err = transports.ParseImageName(importFrom) + if err != nil { + logrus.Errorf("error parsing image name %v: %v", importFrom, err) + os.Exit(1) + } + } + + if exportTo != "" { + exportRef, err = transports.ParseImageName(exportTo) + if err != nil { + logrus.Errorf("error parsing image name %v: %v", exportTo, err) + os.Exit(1) + } + } + + if imageName != "" { + if importFrom != "" { + err = copy.Image(policyContext, ref, importRef, options) + if err != nil { + logrus.Errorf("error importing %s: %v", importFrom, err) + os.Exit(1) + } + } + if addName != "" { + destImage, err := storage.Transport.GetStoreImage(store, ref) + if err != nil { + logrus.Errorf("error finding image: %v", err) + os.Exit(1) + } + names := append(destImage.Names, imageName, addName) + err = store.SetNames(destImage.ID, names) + if err != nil { + logrus.Errorf("error adding name to %s: %v", imageName, err) + os.Exit(1) + } + } + if exportTo != "" { + err = copy.Image(policyContext, exportRef, ref, options) + if err != nil { + logrus.Errorf("error exporting %s: %v", exportTo, err) + os.Exit(1) + } + } + } else { + if importFrom != "" && exportTo != "" { + err = copy.Image(policyContext, exportRef, importRef, options) + if err != nil { + logrus.Errorf("error copying %s to %s: %v", importFrom, exportTo, err) + os.Exit(1) + } + } + } + return nil + } + + if err := app.Run(os.Args); err != nil { + logrus.Fatal(err) + } +} diff --git a/test/helpers.bash b/test/helpers.bash index 226fef8e..58eaee24 100644 --- a/test/helpers.bash +++ b/test/helpers.bash @@ -38,6 +38,8 @@ BOOT_CONFIG_FILE_PATH=${BOOT_CONFIG_FILE_PATH:-/boot/config-`uname -r`} APPARMOR_PARAMETERS_FILE_PATH=${APPARMOR_PARAMETERS_FILE_PATH:-/sys/module/apparmor/parameters/enabled} # Path of the bin2img binary. BIN2IMG_BINARY=${BIN2IMG_BINARY:-${OCID_ROOT}/cri-o/test/bin2img/bin2img} +# Path of the copyimg binary. +COPYIMG_BINARY=${COPYIMG_BINARY:-${OCID_ROOT}/cri-o/test/copyimg/copyimg} TESTDIR=$(mktemp -d) if [ -e /usr/sbin/selinuxenabled ] && /usr/sbin/selinuxenabled; then @@ -58,6 +60,16 @@ mkdir -p $OCID_CNI_CONFIG PATH=$PATH:$TESTDIR +# Make sure we have a copy of the redis:latest image. +if ! [ -d "$TESTDATA"/redis-image ]; then + mkdir -p "$TESTDATA"/redis-image + if ! "$COPYIMG_BINARY" --import-from=docker://redis --export-to=dir:"$TESTDATA"/redis-image --signature-policy="$INTEGRATION_ROOT"/policy.json ; then + echo "Error pulling docker://redis" + rm -fr "$TESTDATA"/redis-image + exit 1 + fi +fi + # Run ocid using the binary specified by $OCID_BINARY. # This must ONLY be run on engines created with `start_ocid`. function ocid() { @@ -114,10 +126,11 @@ function start_ocid() { apparmor="$APPARMOR_PROFILE" fi - # Don't forget: bin2img and ocid have their own default drivers, so if you override either, you probably need to override both + # Don't forget: bin2img, copyimg, and ocid have their own default drivers, so if you override any, you probably need to override them all if ! [ "$3" = "--no-pause-image" ] ; then "$BIN2IMG_BINARY" --root "$TESTDIR/ocid" --runroot "$TESTDIR/ocid-run" --source-binary "$PAUSE_BINARY" fi + "$COPYIMG_BINARY" --root "$TESTDIR/ocid" --runroot "$TESTDIR/ocid-run" --image-name=redis --import-from=dir:"$TESTDATA"/redis-image --add-name=docker://docker.io/library/redis:latest "$OCID_BINARY" --conmon "$CONMON_BINARY" --listen "$OCID_SOCKET" --runtime "$RUNC_BINARY" --root "$TESTDIR/ocid" --runroot "$TESTDIR/ocid-run" --seccomp-profile "$seccomp" --apparmor-profile "$apparmor" --cni-config-dir "$OCID_CNI_CONFIG" --signature-policy "$INTEGRATION_ROOT"/policy.json config >$OCID_CONFIG "$OCID_BINARY" --debug --config "$OCID_CONFIG" & OCID_PID=$! wait_until_reachable From dc37d36759ef06f156c3a10df5d3d873b4dec6e4 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Wed, 21 Dec 2016 21:54:15 +0100 Subject: [PATCH 05/11] server: image_status: ignore storage.ErrImageUnknown Signed-off-by: Antonio Murdaca --- server/image_status.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/image_status.go b/server/image_status.go index cf253d66..7b7c183c 100644 --- a/server/image_status.go +++ b/server/image_status.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/Sirupsen/logrus" + "github.com/containers/storage/storage" "golang.org/x/net/context" pb "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" ) @@ -21,6 +22,9 @@ func (s *Server) ImageStatus(ctx context.Context, req *pb.ImageStatusRequest) (* } status, err := s.images.ImageStatus(s.imageContext, image) if err != nil { + if err == storage.ErrImageUnknown { + return &pb.ImageStatusResponse{}, nil + } return nil, err } resp := &pb.ImageStatusResponse{ From 7bd7595b1884ed8fea549daf106df25759ef4aa1 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Wed, 21 Dec 2016 22:11:51 +0100 Subject: [PATCH 06/11] server: skip pods in bad state on disk Signed-off-by: Antonio Murdaca --- server/server.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/server.go b/server/server.go index 1800c1f0..0acf8e83 100644 --- a/server/server.go +++ b/server/server.go @@ -325,6 +325,10 @@ func (s *Server) update() error { for removedPod := range removedPods { // forget this pod sb := s.getSandbox(removedPod) + if sb == nil { + logrus.Warnf("bad state when getting pod to remove %+v", removedPod) + continue + } podInfraContainer := sb.infraContainer s.releaseContainerName(podInfraContainer.Name()) s.removeContainer(podInfraContainer) From c61a83a930bca132975a5eaa6647d8a14f1edbc1 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Thu, 22 Dec 2016 11:03:32 +0100 Subject: [PATCH 07/11] server: skip pods containers in bad state on disk Signed-off-by: Antonio Murdaca --- server/server.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/server.go b/server/server.go index 0acf8e83..244dd4d9 100644 --- a/server/server.go +++ b/server/server.go @@ -309,6 +309,10 @@ func (s *Server) update() error { for removedPodContainer := range removedPodContainers { // forget this container c := s.getContainer(removedPodContainer) + if c == nil { + logrus.Warnf("bad state when getting container removed %+v", removedPodContainer) + continue + } s.releaseContainerName(c.Name()) s.removeContainer(c) if err = s.ctrIDIndex.Delete(c.ID()); err != nil { From 437459bd64da28cb645fc1febf118745eee55ae9 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Thu, 22 Dec 2016 11:11:14 +0100 Subject: [PATCH 08/11] server: do not add ctrs with bad state when restoring Signed-off-by: Antonio Murdaca --- server/server.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/server.go b/server/server.go index 244dd4d9..9f82726a 100644 --- a/server/server.go +++ b/server/server.go @@ -107,6 +107,7 @@ func (s *Server) loadContainer(id string) error { s.addContainer(ctr) if err = s.runtime.UpdateStatus(ctr); err != nil { logrus.Warnf("error updating status for container %s: %v", ctr.ID(), err) + return nil } if err = s.ctrIDIndex.Add(id); err != nil { return err @@ -213,6 +214,7 @@ func (s *Server) loadSandbox(id string) error { sb.infraContainer = scontainer if err = s.runtime.UpdateStatus(scontainer); err != nil { logrus.Warnf("error updating status for pod sandbox infra container %s: %v", scontainer.ID(), err) + return nil } if err = s.ctrIDIndex.Add(scontainer.ID()); err != nil { return err From 749d24fbabf4944d83d15c349ca5a61a4b6c2bf8 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Thu, 22 Dec 2016 11:26:19 +0100 Subject: [PATCH 09/11] server: cleanup on failed restore Signed-off-by: Antonio Murdaca --- server/server.go | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/server/server.go b/server/server.go index 9f82726a..b47697ee 100644 --- a/server/server.go +++ b/server/server.go @@ -68,14 +68,20 @@ func (s *Server) loadContainer(id string) error { if err != nil { return err } + + defer func() { + if err != nil { + s.releaseContainerName(name) + } + }() + var metadata pb.ContainerMetadata if err = json.Unmarshal([]byte(m.Annotations["ocid/metadata"]), &metadata); err != nil { return err } sb := s.getSandbox(m.Annotations["ocid/sandbox_id"]) if sb == nil { - logrus.Warnf("could not get sandbox with id %s, skipping", m.Annotations["ocid/sandbox_id"]) - return nil + return fmt.Errorf("could not get sandbox with id %s, skipping", m.Annotations["ocid/sandbox_id"]) } var tty bool @@ -104,11 +110,10 @@ func (s *Server) loadContainer(id string) error { if err != nil { return err } - s.addContainer(ctr) if err = s.runtime.UpdateStatus(ctr); err != nil { - logrus.Warnf("error updating status for container %s: %v", ctr.ID(), err) - return nil + return fmt.Errorf("error updating status for container %s: %v", ctr.ID(), err) } + s.addContainer(ctr) if err = s.ctrIDIndex.Add(id); err != nil { return err } @@ -149,6 +154,11 @@ func (s *Server) loadSandbox(id string) error { if err != nil { return err } + defer func() { + if err != nil { + s.releasePodName(name) + } + }() var metadata pb.PodSandboxMetadata if err = json.Unmarshal([]byte(m.Annotations["ocid/metadata"]), &metadata); err != nil { return err @@ -194,28 +204,37 @@ func (s *Server) loadSandbox(id string) error { s.addSandbox(sb) + defer func() { + if err != nil { + s.removeSandbox(sb.id) + } + }() + sandboxPath, err := s.store.GetContainerRunDirectory(id) if err != nil { return err } - if err = label.ReserveLabel(processLabel); err != nil { - return err - } - cname, err := s.reserveContainerName(m.Annotations["ocid/container_id"], m.Annotations["ocid/container_name"]) if err != nil { return err } + defer func() { + if err != nil { + s.releaseContainerName(cname) + } + }() scontainer, err := oci.NewContainer(m.Annotations["ocid/container_id"], cname, sandboxPath, sandboxPath, sb.netNs(), labels, annotations, nil, nil, id, false) if err != nil { return err } - sb.infraContainer = scontainer if err = s.runtime.UpdateStatus(scontainer); err != nil { - logrus.Warnf("error updating status for pod sandbox infra container %s: %v", scontainer.ID(), err) - return nil + return fmt.Errorf("error updating status for pod sandbox infra container %s: %v", scontainer.ID(), err) } + if err = label.ReserveLabel(processLabel); err != nil { + return err + } + sb.infraContainer = scontainer if err = s.ctrIDIndex.Add(scontainer.ID()); err != nil { return err } From aeea6565813c35a457d74efd47f5d586d82f38ea Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Mon, 16 Jan 2017 13:19:44 -0500 Subject: [PATCH 10/11] Limit implicit image pulling to the pause image The CRI doesn't expect us to implicitly pull an image if it isn't already present before we're asked to use it to create a container, and the tests no longer depend on us doing so, either. Limit the logic which attempts to pull an image, if it isn't present, to only pulling the configured "pause" image, since our use of that image for running pod sandboxes is an implementation detail that our clients can't be expected to know or care about. Include the name of the image that we didn't pull in the error we return when we don't pull one. Signed-off-by: Nalin Dahyabhai --- pkg/storage/runtime.go | 25 ++++++++++++++++--------- server/server.go | 2 +- test/helpers.bash | 5 +++++ 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/pkg/storage/runtime.go b/pkg/storage/runtime.go index 5347bd48..08c451f5 100644 --- a/pkg/storage/runtime.go +++ b/pkg/storage/runtime.go @@ -3,6 +3,7 @@ package storage import ( "encoding/json" "errors" + "fmt" "time" "github.com/Sirupsen/logrus" @@ -38,7 +39,8 @@ var ( ) type runtimeService struct { - image ImageServer + image ImageServer + pauseImage string } // ContainerInfo wraps a subset of information about a container: its ID and @@ -176,12 +178,7 @@ func (r *runtimeService) createContainerOrPodSandbox(systemContext *types.System } img, err = istorage.Transport.GetStoreImage(r.image.GetStore(), ref) } - if err != nil && err != storage.ErrImageUnknown { - return ContainerInfo{}, err - } - - // Pull the image down if we don't already have it. - if err == storage.ErrImageUnknown { + if img == nil && err == storage.ErrImageUnknown && imageName == r.pauseImage { image := imageID if imageName != "" { image = imageName @@ -200,6 +197,15 @@ func (r *runtimeService) createContainerOrPodSandbox(systemContext *types.System } logrus.Debugf("successfully pulled image %q", image) } + if img == nil && err == storage.ErrImageUnknown { + if imageID == "" { + return ContainerInfo{}, fmt.Errorf("image %q not present in image store", imageName) + } + if imageName == "" { + return ContainerInfo{}, fmt.Errorf("image with ID %q not present in image store", imageID) + } + return ContainerInfo{}, fmt.Errorf("image %q with ID %q not present in image store", imageName, imageID) + } // Pull out a copy of the image's configuration. image, err := ref.NewImage(systemContext) @@ -449,8 +455,9 @@ func (r *runtimeService) GetRunDir(id string) (string, error) { // GetRuntimeService returns a RuntimeServer that uses the passed-in image // service to pull and manage images, and its store to manage containers based // on those images. -func GetRuntimeService(image ImageServer) RuntimeServer { +func GetRuntimeService(image ImageServer, pauseImage string) RuntimeServer { return &runtimeService{ - image: image, + image: image, + pauseImage: pauseImage, } } diff --git a/server/server.go b/server/server.go index b47697ee..979b3ea3 100644 --- a/server/server.go +++ b/server/server.go @@ -465,7 +465,7 @@ func New(config *Config) (*Server, error) { return nil, err } - storageRuntimeService := storage.GetRuntimeService(imageService) + storageRuntimeService := storage.GetRuntimeService(imageService, config.PauseImage) if err != nil { return nil, err } diff --git a/test/helpers.bash b/test/helpers.bash index 58eaee24..f0e2f8e2 100644 --- a/test/helpers.bash +++ b/test/helpers.bash @@ -134,6 +134,11 @@ function start_ocid() { "$OCID_BINARY" --conmon "$CONMON_BINARY" --listen "$OCID_SOCKET" --runtime "$RUNC_BINARY" --root "$TESTDIR/ocid" --runroot "$TESTDIR/ocid-run" --seccomp-profile "$seccomp" --apparmor-profile "$apparmor" --cni-config-dir "$OCID_CNI_CONFIG" --signature-policy "$INTEGRATION_ROOT"/policy.json config >$OCID_CONFIG "$OCID_BINARY" --debug --config "$OCID_CONFIG" & OCID_PID=$! wait_until_reachable + + run ocic image status --id=redis + if [ "$status" -ne 0 ] ; then + ocic image pull docker://redis:latest + fi } function cleanup_ctrs() { From 0e3ff61350d316d7820b02e371fecefb070613db Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Tue, 17 Jan 2017 12:21:32 +0100 Subject: [PATCH 11/11] server: fix ImagePullResponse Signed-off-by: Antonio Murdaca --- server/image_pull.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/image_pull.go b/server/image_pull.go index afed362c..5448a4a5 100644 --- a/server/image_pull.go +++ b/server/image_pull.go @@ -22,7 +22,9 @@ func (s *Server) PullImage(ctx context.Context, req *pb.PullImageRequest) (*pb.P if err != nil { return nil, err } - resp := &pb.PullImageResponse{} + resp := &pb.PullImageResponse{ + ImageRef: &image, + } logrus.Debugf("PullImageResponse: %+v", resp) return resp, nil }