From 3980ac5894e837eaf303d06ded628daf27a3a229 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Tue, 13 Sep 2016 10:12:39 +0200 Subject: [PATCH] vendor containers/image#81 Signed-off-by: Antonio Murdaca --- integration/copy_test.go | 56 +++++++------- integration/fixtures/policy.json | 18 ++--- .../containers/image/openshift/openshift.go | 33 +++------ .../image/openshift/openshift_transport.go | 73 +++++-------------- .../image-spec/specs-go/v1/mediatype.go | 14 ++-- 5 files changed, 76 insertions(+), 118 deletions(-) diff --git a/integration/copy_test.go b/integration/copy_test.go index bd4c3289..dc23bb21 100644 --- a/integration/copy_test.go +++ b/integration/copy_test.go @@ -98,9 +98,9 @@ func (s *CopySuite) TestCopySimple(c *check.C) { // "pull": docker: → dir: assertSkopeoSucceeds(c, "", "copy", "docker://estesp/busybox:latest", "dir:"+dir1) // "push": dir: → atomic: - assertSkopeoSucceeds(c, "", "--debug", "copy", "dir:"+dir1, "atomic:myns/unsigned:unsigned") + assertSkopeoSucceeds(c, "", "--debug", "copy", "dir:"+dir1, "atomic:localhost:5000/myns/unsigned:unsigned") // The result of pushing and pulling is an unmodified image. - assertSkopeoSucceeds(c, "", "copy", "atomic:myns/unsigned:unsigned", "dir:"+dir2) + assertSkopeoSucceeds(c, "", "copy", "atomic:localhost:5000/myns/unsigned:unsigned", "dir:"+dir2) out := combinedOutputOfCommand(c, "diff", "-urN", dir1, dir2) c.Assert(out, check.Equals, "") @@ -127,10 +127,10 @@ func (s *CopySuite) TestCopyStreaming(c *check.C) { // FIXME: It would be nice to use one of the local Docker registries instead of neeeding an Internet connection. // streaming: docker: → atomic: - assertSkopeoSucceeds(c, "", "--debug", "copy", "docker://estesp/busybox:amd64", "atomic:myns/unsigned:streaming") + assertSkopeoSucceeds(c, "", "--debug", "copy", "docker://estesp/busybox:amd64", "atomic:localhost:5000/myns/unsigned:streaming") // Compare (copies of) the original and the copy: assertSkopeoSucceeds(c, "", "copy", "docker://estesp/busybox:amd64", "dir:"+dir1) - assertSkopeoSucceeds(c, "", "copy", "atomic:myns/unsigned:streaming", "dir:"+dir2) + assertSkopeoSucceeds(c, "", "copy", "atomic:localhost:5000/myns/unsigned:streaming", "dir:"+dir2) // The manifests will have different JWS signatures; so, compare the manifests by digests, which // strips the signatures, and remove them, comparing the rest file by file. digests := []string{} @@ -170,34 +170,34 @@ func (s *CopySuite) TestCopySignatures(c *check.C) { // type: signedBy // Sign the images - assertSkopeoSucceeds(c, "", "copy", "--sign-by", "personal@example.com", "docker://busybox:1.23", "atomic:myns/personal:personal") - assertSkopeoSucceeds(c, "", "copy", "--sign-by", "official@example.com", "docker://busybox:1.23.2", "atomic:myns/official:official") + assertSkopeoSucceeds(c, "", "copy", "--sign-by", "personal@example.com", "docker://busybox:1.23", "atomic:localhost:5000/myns/personal:personal") + assertSkopeoSucceeds(c, "", "copy", "--sign-by", "official@example.com", "docker://busybox:1.23.2", "atomic:localhost:5000/myns/official:official") // Verify that we can pull them - assertSkopeoSucceeds(c, "", "--policy", policy, "copy", "atomic:myns/personal:personal", dirDest) - assertSkopeoSucceeds(c, "", "--policy", policy, "copy", "atomic:myns/official:official", dirDest) + assertSkopeoSucceeds(c, "", "--policy", policy, "copy", "atomic:localhost:5000/myns/personal:personal", dirDest) + assertSkopeoSucceeds(c, "", "--policy", policy, "copy", "atomic:localhost:5000/myns/official:official", dirDest) // Verify that mis-signed images are rejected - assertSkopeoSucceeds(c, "", "copy", "atomic:myns/personal:personal", "atomic:myns/official:attack") - assertSkopeoSucceeds(c, "", "copy", "atomic:myns/official:official", "atomic:myns/personal:attack") + assertSkopeoSucceeds(c, "", "copy", "atomic:localhost:5000/myns/personal:personal", "atomic:localhost:5000/myns/official:attack") + assertSkopeoSucceeds(c, "", "copy", "atomic:localhost:5000/myns/official:official", "atomic:localhost:5000/myns/personal:attack") assertSkopeoFails(c, ".*Source image rejected: Invalid GPG signature.*", - "--policy", policy, "copy", "atomic:myns/personal:attack", dirDest) + "--policy", policy, "copy", "atomic:localhost:5000/myns/personal:attack", dirDest) assertSkopeoFails(c, ".*Source image rejected: Invalid GPG signature.*", - "--policy", policy, "copy", "atomic:myns/official:attack", dirDest) + "--policy", policy, "copy", "atomic:localhost:5000/myns/official:attack", dirDest) // Verify that signed identity is verified. - assertSkopeoSucceeds(c, "", "copy", "atomic:myns/official:official", "atomic:myns/naming:test1") - assertSkopeoFails(c, ".*Source image rejected: Signature for identity localhost:8443/myns/official:official is not accepted.*", - "--policy", policy, "copy", "atomic:myns/naming:test1", dirDest) + assertSkopeoSucceeds(c, "", "copy", "atomic:localhost:5000/myns/official:official", "atomic:localhost:5000/myns/naming:test1") + assertSkopeoFails(c, ".*Source image rejected: Signature for identity localhost:5000/myns/official:official is not accepted.*", + "--policy", policy, "copy", "atomic:localhost:5000/myns/naming:test1", dirDest) // signedIdentity works - assertSkopeoSucceeds(c, "", "copy", "atomic:myns/official:official", "atomic:myns/naming:naming") - assertSkopeoSucceeds(c, "", "--policy", policy, "copy", "atomic:myns/naming:naming", dirDest) + assertSkopeoSucceeds(c, "", "copy", "atomic:localhost:5000/myns/official:official", "atomic:localhost:5000/myns/naming:naming") + assertSkopeoSucceeds(c, "", "--policy", policy, "copy", "atomic:localhost:5000/myns/naming:naming", dirDest) // Verify that cosigning requirements are enforced - assertSkopeoSucceeds(c, "", "copy", "atomic:myns/official:official", "atomic:myns/cosigned:cosigned") + assertSkopeoSucceeds(c, "", "copy", "atomic:localhost:5000/myns/official:official", "atomic:localhost:5000/myns/cosigned:cosigned") assertSkopeoFails(c, ".*Source image rejected: Invalid GPG signature.*", - "--policy", policy, "copy", "atomic:myns/cosigned:cosigned", dirDest) + "--policy", policy, "copy", "atomic:localhost:5000/myns/cosigned:cosigned", dirDest) - assertSkopeoSucceeds(c, "", "copy", "--sign-by", "personal@example.com", "atomic:myns/official:official", "atomic:myns/cosigned:cosigned") - assertSkopeoSucceeds(c, "", "--policy", policy, "copy", "atomic:myns/cosigned:cosigned", dirDest) + assertSkopeoSucceeds(c, "", "copy", "--sign-by", "personal@example.com", "atomic:localhost:5000/myns/official:official", "atomic:localhost:5000/myns/cosigned:cosigned") + assertSkopeoSucceeds(c, "", "--policy", policy, "copy", "atomic:localhost:5000/myns/cosigned:cosigned", dirDest) } // --policy copy for dir: sources @@ -224,10 +224,10 @@ func (s *CopySuite) TestCopyDirSignatures(c *check.C) { // Sign the images. By coping fom a topDirDest/dirN, also test that non-/restricted paths // use the dir:"" default of insecureAcceptAnything. // (For signing, we must push to atomic: to get a Docker identity to use in the signature.) - assertSkopeoSucceeds(c, "", "--policy", policy, "copy", "--sign-by", "personal@example.com", topDirDest+"/dir1", "atomic:myns/personal:dirstaging") - assertSkopeoSucceeds(c, "", "--policy", policy, "copy", "--sign-by", "official@example.com", topDirDest+"/dir2", "atomic:myns/official:dirstaging") - assertSkopeoSucceeds(c, "", "copy", "atomic:myns/personal:dirstaging", topDirDest+"/restricted/personal") - assertSkopeoSucceeds(c, "", "copy", "atomic:myns/official:dirstaging", topDirDest+"/restricted/official") + assertSkopeoSucceeds(c, "", "--policy", policy, "copy", "--sign-by", "personal@example.com", topDirDest+"/dir1", "atomic:localhost:5000/myns/personal:dirstaging") + assertSkopeoSucceeds(c, "", "--policy", policy, "copy", "--sign-by", "official@example.com", topDirDest+"/dir2", "atomic:localhost:5000/myns/official:dirstaging") + assertSkopeoSucceeds(c, "", "copy", "atomic:localhost:5000/myns/personal:dirstaging", topDirDest+"/restricted/personal") + assertSkopeoSucceeds(c, "", "copy", "atomic:localhost:5000/myns/official:dirstaging", topDirDest+"/restricted/official") // type: signedBy, with a signedIdentity override (necessary because dir: identities can't be signed) // Verify that correct images are accepted @@ -237,8 +237,8 @@ func (s *CopySuite) TestCopyDirSignatures(c *check.C) { "--policy", policy, "copy", topDirDest+"/restricted/personal", topDirDest+"/dest") // Verify that the signed identity is verified. - assertSkopeoSucceeds(c, "", "--policy", policy, "copy", "--sign-by", "official@example.com", topDirDest+"/dir1", "atomic:myns/personal:dirstaging2") - assertSkopeoSucceeds(c, "", "copy", "atomic:myns/personal:dirstaging2", topDirDest+"/restricted/badidentity") - assertSkopeoFails(c, ".*Source image rejected: .*Signature for identity localhost:8443/myns/personal:dirstaging2 is not accepted.*", + assertSkopeoSucceeds(c, "", "--policy", policy, "copy", "--sign-by", "official@example.com", topDirDest+"/dir1", "atomic:localhost:5000/myns/personal:dirstaging2") + assertSkopeoSucceeds(c, "", "copy", "atomic:localhost:5000/myns/personal:dirstaging2", topDirDest+"/restricted/badidentity") + assertSkopeoFails(c, ".*Source image rejected: .*Signature for identity localhost:5000/myns/personal:dirstaging2 is not accepted.*", "--policy", policy, "copy", topDirDest+"/restricted/badidentity", topDirDest+"/dest") } diff --git a/integration/fixtures/policy.json b/integration/fixtures/policy.json index 3326d07b..c1b34fb6 100644 --- a/integration/fixtures/policy.json +++ b/integration/fixtures/policy.json @@ -20,7 +20,7 @@ "keyPath": "@keydir@/official-pubkey.gpg", "signedIdentity": { "type": "exactRepository", - "dockerRepository": "localhost:8443/myns/official" + "dockerRepository": "localhost:5000/myns/official" } } ], @@ -31,46 +31,46 @@ ] }, "atomic": { - "localhost:8443/myns/personal": [ + "localhost:5000/myns/personal": [ { "type": "signedBy", "keyType": "GPGKeys", "keyPath": "@keydir@/personal-pubkey.gpg" } ], - "localhost:8443/myns/official": [ + "localhost:5000/myns/official": [ { "type": "signedBy", "keyType": "GPGKeys", "keyPath": "@keydir@/official-pubkey.gpg" } ], - "localhost:8443/myns/naming:test1": [ + "localhost:5000/myns/naming:test1": [ { "type": "signedBy", "keyType": "GPGKeys", "keyPath": "@keydir@/official-pubkey.gpg" } ], - "localhost:8443/myns/naming:naming": [ + "localhost:5000/myns/naming:naming": [ { "type": "signedBy", "keyType": "GPGKeys", "keyPath": "@keydir@/official-pubkey.gpg", "signedIdentity": { "type": "exactRepository", - "dockerRepository": "localhost:8443/myns/official" + "dockerRepository": "localhost:5000/myns/official" } } ], - "localhost:8443/myns/cosigned:cosigned": [ + "localhost:5000/myns/cosigned:cosigned": [ { "type": "signedBy", "keyType": "GPGKeys", "keyPath": "@keydir@/official-pubkey.gpg", "signedIdentity": { "type": "exactRepository", - "dockerRepository": "localhost:8443/myns/official" + "dockerRepository": "localhost:5000/myns/official" } }, { @@ -81,4 +81,4 @@ ] } } -} \ No newline at end of file +} diff --git a/vendor/github.com/containers/image/openshift/openshift.go b/vendor/github.com/containers/image/openshift/openshift.go index 686c78a9..ecd41306 100644 --- a/vendor/github.com/containers/image/openshift/openshift.go +++ b/vendor/github.com/containers/image/openshift/openshift.go @@ -9,6 +9,7 @@ import ( "io" "io/ioutil" "net/http" + "net/url" "strings" "time" @@ -21,7 +22,8 @@ import ( // openshiftClient is configuration for dealing with a single image stream, for reading or writing. type openshiftClient struct { - ref openshiftReference + ref openshiftReference + baseURL *url.URL // Values from Kubernetes configuration httpClient *http.Client bearerToken string // "" if not used @@ -51,9 +53,7 @@ func newOpenshiftClient(ref openshiftReference) (*openshiftClient, error) { return nil, err } logrus.Debugf("URL: %#v", *baseURL) - if *baseURL != *ref.baseURL { - return nil, fmt.Errorf("Unexpected baseURL mismatch: default %#v, reference %#v", *baseURL, *ref.baseURL) - } + if httpClient == nil { httpClient = http.DefaultClient } @@ -61,6 +61,7 @@ func newOpenshiftClient(ref openshiftReference) (*openshiftClient, error) { return &openshiftClient{ ref: ref, + baseURL: baseURL, httpClient: httpClient, bearerToken: restConfig.BearerToken, username: restConfig.Username, @@ -70,7 +71,7 @@ func newOpenshiftClient(ref openshiftReference) (*openshiftClient, error) { // doRequest performs a correctly authenticated request to a specified path, and returns response body or an error object. func (c *openshiftClient) doRequest(method, path string, requestBody []byte) ([]byte, error) { - url := *c.ref.baseURL + url := *c.baseURL url.Path = path var requestBodyReader io.Reader if requestBody != nil { @@ -153,19 +154,7 @@ func (c *openshiftClient) convertDockerImageReference(ref string) (string, error if len(parts) != 2 { return "", fmt.Errorf("Invalid format of docker reference %s: missing '/'", ref) } - // Sanity check that the reference is at least plausibly similar, i.e. uses the hard-coded port we expect. - if !strings.HasSuffix(parts[0], ":5000") { - return "", fmt.Errorf("Invalid format of docker reference %s: expecting port 5000", ref) - } - return c.dockerRegistryHostPart() + "/" + parts[1], nil -} - -// dockerRegistryHostPart returns the host:port of the embedded Docker Registry API endpoint -// FIXME: There seems to be no way to discover the correct:host port using the API, so hard-code our knowledge -// about how the OpenShift Atomic Registry is configured, per examples/atomic-registry/run.sh: -// -p OPENSHIFT_OAUTH_PROVIDER_URL=https://${INSTALL_HOST}:8443,COCKPIT_KUBE_URL=https://${INSTALL_HOST},REGISTRY_HOST=${INSTALL_HOST}:5000 -func (c *openshiftClient) dockerRegistryHostPart() string { - return strings.SplitN(c.ref.baseURL.Host, ":", 2)[0] + ":5000" + return c.ref.dockerReference.Hostname() + "/" + parts[1], nil } type openshiftImageSource struct { @@ -261,7 +250,7 @@ func (s *openshiftImageSource) ensureImageIsResolved() error { } var te *tagEvent for _, tag := range is.Status.Tags { - if tag.Tag != s.client.ref.tag { + if tag.Tag != s.client.ref.dockerReference.Tag() { continue } if len(tag.Items) > 0 { @@ -308,7 +297,7 @@ func newImageDestination(ctx *types.SystemContext, ref openshiftReference) (type // FIXME: Should this always use a digest, not a tag? Uploading to Docker by tag requires the tag _inside_ the manifest to match, // i.e. a single signed image cannot be available under multiple tags. But with types.ImageDestination, we don't know // the manifest digest at this point. - dockerRefString := fmt.Sprintf("//%s/%s/%s:%s", client.dockerRegistryHostPart(), client.ref.namespace, client.ref.stream, client.ref.tag) + dockerRefString := fmt.Sprintf("//%s/%s/%s:%s", client.ref.dockerReference.Hostname(), client.ref.namespace, client.ref.stream, client.ref.dockerReference.Tag()) dockerRef, err := docker.ParseReference(dockerRefString) if err != nil { return nil, err @@ -370,7 +359,7 @@ func (d *openshiftImageDestination) PutManifest(m []byte) error { } d.imageStreamImageName = manifestDigest // FIXME: We can't do what respositorymiddleware.go does because we don't know the internal address. Does any of this matter? - dockerImageReference := fmt.Sprintf("%s/%s/%s@%s", d.client.dockerRegistryHostPart(), d.client.ref.namespace, d.client.ref.stream, manifestDigest) + dockerImageReference := fmt.Sprintf("%s/%s/%s@%s", d.client.ref.dockerReference.Hostname(), d.client.ref.namespace, d.client.ref.stream, manifestDigest) ism := imageStreamMapping{ typeMeta: typeMeta{ Kind: "ImageStreamMapping", @@ -387,7 +376,7 @@ func (d *openshiftImageDestination) PutManifest(m []byte) error { DockerImageReference: dockerImageReference, DockerImageManifest: string(m), }, - Tag: d.client.ref.tag, + Tag: d.client.ref.dockerReference.Tag(), } body, err := json.Marshal(ism) if err != nil { diff --git a/vendor/github.com/containers/image/openshift/openshift_transport.go b/vendor/github.com/containers/image/openshift/openshift_transport.go index 240d05ac..8e53b1f9 100644 --- a/vendor/github.com/containers/image/openshift/openshift_transport.go +++ b/vendor/github.com/containers/image/openshift/openshift_transport.go @@ -3,10 +3,9 @@ package openshift import ( "errors" "fmt" - "net/url" "regexp" + "strings" - "github.com/Sirupsen/logrus" "github.com/containers/image/docker/policyconfiguration" "github.com/containers/image/types" "github.com/docker/docker/reference" @@ -44,67 +43,33 @@ func (t openshiftTransport) ValidatePolicyConfigurationScope(scope string) error // openshiftReference is an ImageReference for OpenShift images. type openshiftReference struct { - baseURL *url.URL - namespace string - stream string - tag string - dockerReference reference.Named // Computed from the above in advance, so that later references can not fail. + dockerReference reference.NamedTagged + namespace string // Computed from dockerReference in advance. + stream string // Computed from dockerReference in advance. } -// FIXME: Is imageName like this a good way to refer to OpenShift images? -// Keep this in sync with scopeRegexp! -var imageNameRegexp = regexp.MustCompile("^([^:/]*)/([^:/]*):([^:/]*)$") - // ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an OpenShift ImageReference. -func ParseReference(reference string) (types.ImageReference, error) { - // Overall, this is modelled on openshift/origin/pkg/cmd/util/clientcmd.New().ClientConfig() and openshift/origin/pkg/client. - cmdConfig := defaultClientConfig() - logrus.Debugf("cmdConfig: %#v", cmdConfig) - restConfig, err := cmdConfig.ClientConfig() +func ParseReference(ref string) (types.ImageReference, error) { + r, err := reference.ParseNamed(ref) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to parse image reference %q, %v", ref, err) } - // REMOVED: SetOpenShiftDefaults (values are not overridable in config files, so hard-coded these defaults.) - logrus.Debugf("restConfig: %#v", restConfig) - baseURL, _, err := restClientFor(restConfig) - if err != nil { - return nil, err + tagged, ok := r.(reference.NamedTagged) + if !ok { + return nil, fmt.Errorf("invalid image reference %s, %#v", ref, r) } - logrus.Debugf("URL: %#v", *baseURL) - - m := imageNameRegexp.FindStringSubmatch(reference) - if m == nil || len(m) != 4 { - return nil, fmt.Errorf("Invalid image reference %s, %#v", reference, m) - } - - return NewReference(baseURL, m[1], m[2], m[3]) + return NewReference(tagged) } -// NewReference returns an OpenShift reference for a base URL, namespace, stream and tag. -func NewReference(baseURL *url.URL, namespace, stream, tag string) (types.ImageReference, error) { - // Precompute also dockerReference so that later references can not fail. - // - // This discards ref.baseURL.Path, which is unexpected for a “base URL”; - // but openshiftClient.doRequest actually completely overrides url.Path - // (and defaultServerURL rejects non-trivial Path values), so it is OK for - // us to ignore it as well. - // - // FIXME: This is, strictly speaking, a namespace conflict with images placed in a Docker registry running on the same host. - // Do we need to do something else, perhaps disambiguate (port number?) or namespace Docker and OpenShift separately? - dockerRef, err := reference.WithName(fmt.Sprintf("%s/%s/%s", baseURL.Host, namespace, stream)) - if err != nil { - return nil, err +// NewReference returns an OpenShift reference for a reference.NamedTagged +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()) } - dockerRef, err = reference.WithTag(dockerRef, tag) - if err != nil { - return nil, err - } - return openshiftReference{ - baseURL: baseURL, - namespace: namespace, - stream: stream, - tag: tag, + namespace: r[0], + stream: r[1], dockerReference: dockerRef, }, nil } @@ -119,7 +84,7 @@ func (ref openshiftReference) Transport() types.ImageTransport { // e.g. default attribute values omitted by the user may be filled in in the return value, or vice versa. // WARNING: Do not use the return value in the UI to describe an image, it does not contain the Transport().Name() prefix. func (ref openshiftReference) StringWithinTransport() string { - return fmt.Sprintf("%s/%s:%s", ref.namespace, ref.stream, ref.tag) + return ref.dockerReference.String() } // DockerReference returns a Docker reference associated with this reference diff --git a/vendor/github.com/opencontainers/image-spec/specs-go/v1/mediatype.go b/vendor/github.com/opencontainers/image-spec/specs-go/v1/mediatype.go index 1cd5450c..90199dd3 100644 --- a/vendor/github.com/opencontainers/image-spec/specs-go/v1/mediatype.go +++ b/vendor/github.com/opencontainers/image-spec/specs-go/v1/mediatype.go @@ -15,18 +15,22 @@ package v1 const ( - // MediaTypeDescriptor specifies the mediaType for a content descriptor. + // MediaTypeDescriptor specifies the media type for a content descriptor. MediaTypeDescriptor = "application/vnd.oci.descriptor.v1+json" - // MediaTypeImageManifest specifies the mediaType for an image manifest. + // MediaTypeImageManifest specifies the media type for an image manifest. MediaTypeImageManifest = "application/vnd.oci.image.manifest.v1+json" - // MediaTypeImageManifestList specifies the mediaType for an image manifest list. + // MediaTypeImageManifestList specifies the media type for an image manifest list. MediaTypeImageManifestList = "application/vnd.oci.image.manifest.list.v1+json" - // MediaTypeImageLayer is the mediaType used for layers referenced by the manifest. + // MediaTypeImageLayer is the media type used for layers referenced by the manifest. MediaTypeImageLayer = "application/vnd.oci.image.layer.tar+gzip" - // MediaTypeImageConfig specifies the mediaType for the image configuration. + // MediaTypeImageLayerNonDistributable is the media type for layers referenced by + // the manifest but with distribution restrictions. + MediaTypeImageLayerNonDistributable = "application/vnd.oci.image.layer.nondistributable.tar+gzip" + + // MediaTypeImageConfig specifies the media type for the image configuration. MediaTypeImageConfig = "application/vnd.oci.image.config.v1+json" )