diff --git a/copy/copy.go b/copy/copy.go index a8afcc9ee1..13d35e7083 100644 --- a/copy/copy.go +++ b/copy/copy.go @@ -3,6 +3,7 @@ package copy import ( "bytes" "compress/gzip" + "context" "fmt" "io" "io/ioutil" @@ -171,7 +172,7 @@ func Image(policyContext *signature.PolicyContext, destRef, srcRef types.ImageRe sigs = [][]byte{} } else { writeReport("Getting image source signatures\n") - s, err := src.Signatures() + s, err := src.Signatures(context.TODO()) if err != nil { return errors.Wrap(err, "Error reading signatures") } diff --git a/copy/manifest_test.go b/copy/manifest_test.go index eda492dce9..ed636c5e6d 100644 --- a/copy/manifest_test.go +++ b/copy/manifest_test.go @@ -1,6 +1,7 @@ package copy import ( + "context" "errors" "fmt" "testing" @@ -43,7 +44,7 @@ func (f fakeImageSource) Manifest() ([]byte, string, error) { } return nil, string(f), nil } -func (f fakeImageSource) Signatures() ([][]byte, error) { +func (f fakeImageSource) Signatures(context.Context) ([][]byte, error) { panic("Unexpected call to a mock function") } func (f fakeImageSource) ConfigInfo() types.BlobInfo { diff --git a/directory/directory_src.go b/directory/directory_src.go index d432fb72ed..fddc1c522c 100644 --- a/directory/directory_src.go +++ b/directory/directory_src.go @@ -1,6 +1,7 @@ package directory import ( + "context" "io" "io/ioutil" "os" @@ -59,7 +60,7 @@ func (s *dirImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, err return r, fi.Size(), nil } -func (s *dirImageSource) GetSignatures() ([][]byte, error) { +func (s *dirImageSource) GetSignatures(ctx context.Context) ([][]byte, error) { signatures := [][]byte{} for i := 0; ; i++ { signature, err := ioutil.ReadFile(s.ref.signaturePath(i)) diff --git a/directory/directory_test.go b/directory/directory_test.go index 2693311c63..86ff004d0e 100644 --- a/directory/directory_test.go +++ b/directory/directory_test.go @@ -2,6 +2,7 @@ package directory import ( "bytes" + "context" "io/ioutil" "os" "testing" @@ -145,7 +146,7 @@ func TestGetPutSignatures(t *testing.T) { src, err := ref.NewImageSource(nil, nil) require.NoError(t, err) defer src.Close() - sigs, err := src.GetSignatures() + sigs, err := src.GetSignatures(context.Background()) assert.NoError(t, err) assert.Equal(t, signatures, sigs) } diff --git a/docker/docker_client.go b/docker/docker_client.go index 1343946215..ca442828b9 100644 --- a/docker/docker_client.go +++ b/docker/docker_client.go @@ -1,6 +1,7 @@ package docker import ( + "context" "crypto/tls" "encoding/base64" "encoding/json" @@ -254,24 +255,25 @@ func newDockerClient(ctx *types.SystemContext, ref dockerReference, write bool, // makeRequest creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client. // The host name and schema is taken from the client or autodetected, and the path is relative to it, i.e. the path usually starts with /v2/. -func (c *dockerClient) makeRequest(method, path string, headers map[string][]string, stream io.Reader) (*http.Response, error) { - if err := c.detectProperties(); err != nil { +func (c *dockerClient) makeRequest(ctx context.Context, method, path string, headers map[string][]string, stream io.Reader) (*http.Response, error) { + if err := c.detectProperties(ctx); err != nil { return nil, err } url := fmt.Sprintf("%s://%s%s", c.scheme, c.registry, path) - return c.makeRequestToResolvedURL(method, url, headers, stream, -1, true) + return c.makeRequestToResolvedURL(ctx, method, url, headers, stream, -1, true) } // makeRequestToResolvedURL creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client. // streamLen, if not -1, specifies the length of the data expected on stream. // makeRequest should generally be preferred. // TODO(runcom): too many arguments here, use a struct -func (c *dockerClient) makeRequestToResolvedURL(method, url string, headers map[string][]string, stream io.Reader, streamLen int64, sendAuth bool) (*http.Response, error) { +func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method, url string, headers map[string][]string, stream io.Reader, streamLen int64, sendAuth bool) (*http.Response, error) { req, err := http.NewRequest(method, url, stream) if err != nil { return nil, err } + req = req.WithContext(ctx) if streamLen != -1 { // Do not blindly overwrite if streamLen == -1, http.NewRequest above can figure out the length of bytes.Reader and similar objects without us having to compute it. req.ContentLength = streamLen } @@ -323,7 +325,7 @@ func (c *dockerClient) setupRequestAuth(req *http.Request) error { } service, _ := challenge.Parameters["service"] // Will be "" if not present scope := fmt.Sprintf("repository:%s:%s", c.scope.remoteName, c.scope.actions) - token, err := c.getBearerToken(realm, service, scope) + token, err := c.getBearerToken(req.Context(), realm, service, scope) if err != nil { return err } @@ -340,11 +342,12 @@ func (c *dockerClient) setupRequestAuth(req *http.Request) error { return nil } -func (c *dockerClient) getBearerToken(realm, service, scope string) (*bearerToken, error) { +func (c *dockerClient) getBearerToken(ctx context.Context, realm, service, scope string) (*bearerToken, error) { authReq, err := http.NewRequest("GET", realm, nil) if err != nil { return nil, err } + authReq = authReq.WithContext(ctx) getParams := authReq.URL.Query() if service != "" { getParams.Add("service", service) @@ -447,14 +450,14 @@ func getAuth(ctx *types.SystemContext, registry string) (string, string, error) // detectProperties detects various properties of the registry. // See the dockerClient documentation for members which are affected by this. -func (c *dockerClient) detectProperties() error { +func (c *dockerClient) detectProperties(ctx context.Context) error { if c.scheme != "" { return nil } ping := func(scheme string) error { url := fmt.Sprintf(resolvedPingV2URL, scheme, c.registry) - resp, err := c.makeRequestToResolvedURL("GET", url, nil, nil, -1, true) + resp, err := c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, true) logrus.Debugf("Ping %s err %#v", url, err) if err != nil { return err @@ -481,7 +484,7 @@ func (c *dockerClient) detectProperties() error { // best effort to understand if we're talking to a V1 registry pingV1 := func(scheme string) bool { url := fmt.Sprintf(resolvedPingV1URL, scheme, c.registry) - resp, err := c.makeRequestToResolvedURL("GET", url, nil, nil, -1, true) + resp, err := c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, true) logrus.Debugf("Ping %s err %#v", url, err) if err != nil { return false @@ -506,9 +509,9 @@ func (c *dockerClient) detectProperties() error { // getExtensionsSignatures returns signatures from the X-Registry-Supports-Signatures API extension, // using the original data structures. -func (c *dockerClient) getExtensionsSignatures(ref dockerReference, manifestDigest digest.Digest) (*extensionSignatureList, error) { +func (c *dockerClient) getExtensionsSignatures(ctx context.Context, ref dockerReference, manifestDigest digest.Digest) (*extensionSignatureList, error) { path := fmt.Sprintf(extensionsSignaturePath, reference.Path(ref.ref), manifestDigest) - res, err := c.makeRequest("GET", path, nil, nil) + res, err := c.makeRequest(ctx, "GET", path, nil, nil) if err != nil { return nil, err } diff --git a/docker/docker_image.go b/docker/docker_image.go index 3bc2bebabd..992d920354 100644 --- a/docker/docker_image.go +++ b/docker/docker_image.go @@ -1,6 +1,7 @@ package docker import ( + "context" "encoding/json" "fmt" "net/http" @@ -41,7 +42,8 @@ func (i *Image) SourceRefFullName() string { // GetRepositoryTags list all tags available in the repository. Note that this has no connection with the tag(s) used for this specific image, if any. func (i *Image) GetRepositoryTags() ([]string, error) { path := fmt.Sprintf(tagsPath, reference.Path(i.src.ref.ref)) - res, err := i.src.c.makeRequest("GET", path, nil, nil) + // FIXME: Pass the context.Context + res, err := i.src.c.makeRequest(context.TODO(), "GET", path, nil, nil) if err != nil { return nil, err } diff --git a/docker/docker_image_dest.go b/docker/docker_image_dest.go index a3a4ca395b..427e358a02 100644 --- a/docker/docker_image_dest.go +++ b/docker/docker_image_dest.go @@ -2,6 +2,7 @@ package docker import ( "bytes" + "context" "crypto/rand" "encoding/json" "fmt" @@ -75,7 +76,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 { - if err := d.c.detectProperties(); err != nil { + if err := d.c.detectProperties(context.TODO()); err != nil { return err } switch { @@ -132,7 +133,7 @@ func (d *dockerImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobI // FIXME? Chunked upload, progress reporting, etc. uploadPath := fmt.Sprintf(blobUploadPath, reference.Path(d.ref.ref)) logrus.Debugf("Uploading %s", uploadPath) - res, err := d.c.makeRequest("POST", uploadPath, nil, nil) + res, err := d.c.makeRequest(context.TODO(), "POST", uploadPath, nil, nil) if err != nil { return types.BlobInfo{}, err } @@ -149,7 +150,7 @@ func (d *dockerImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobI 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) + res, err = d.c.makeRequestToResolvedURL(context.TODO(), "PATCH", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, tee, inputInfo.Size, true) if err != nil { logrus.Debugf("Error uploading layer chunked, response %#v", res) return types.BlobInfo{}, err @@ -168,7 +169,7 @@ func (d *dockerImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobI // TODO: check inputInfo.Digest == computedDigest https://github.com/containers/image/pull/70#discussion_r77646717 locationQuery.Set("digest", computedDigest.String()) uploadLocation.RawQuery = locationQuery.Encode() - res, err = d.c.makeRequestToResolvedURL("PUT", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, nil, -1, true) + res, err = d.c.makeRequestToResolvedURL(context.TODO(), "PUT", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, nil, -1, true) if err != nil { return types.BlobInfo{}, err } @@ -193,7 +194,7 @@ func (d *dockerImageDestination) HasBlob(info types.BlobInfo) (bool, int64, erro checkPath := fmt.Sprintf(blobsPath, reference.Path(d.ref.ref), info.Digest.String()) logrus.Debugf("Checking %s", checkPath) - res, err := d.c.makeRequest("HEAD", checkPath, nil, nil) + res, err := d.c.makeRequest(context.TODO(), "HEAD", checkPath, nil, nil) if err != nil { return false, -1, err } @@ -239,7 +240,7 @@ func (d *dockerImageDestination) PutManifest(m []byte) error { if mimeType != "" { headers["Content-Type"] = []string{mimeType} } - res, err := d.c.makeRequest("PUT", path, headers, bytes.NewReader(m)) + res, err := d.c.makeRequest(context.TODO(), "PUT", path, headers, bytes.NewReader(m)) if err != nil { return err } @@ -275,7 +276,7 @@ func (d *dockerImageDestination) PutSignatures(signatures [][]byte) error { if len(signatures) == 0 { return nil } - if err := d.c.detectProperties(); err != nil { + if err := d.c.detectProperties(context.TODO()); err != nil { return err } switch { @@ -396,7 +397,7 @@ func (d *dockerImageDestination) putSignaturesToAPIExtension(signatures [][]byte // always adds signatures. Eventually we should also allow removing signatures, // but the X-Registry-Supports-Signatures API extension does not support that yet. - existingSignatures, err := d.c.getExtensionsSignatures(d.ref, d.manifestDigest) + existingSignatures, err := d.c.getExtensionsSignatures(context.TODO(), d.ref, d.manifestDigest) if err != nil { return err } @@ -438,7 +439,7 @@ sigExists: } path := fmt.Sprintf(extensionsSignaturePath, reference.Path(d.ref.ref), d.manifestDigest.String()) - res, err := d.c.makeRequest("PUT", path, nil, bytes.NewReader(body)) + res, err := d.c.makeRequest(context.TODO(), "PUT", path, nil, bytes.NewReader(body)) if err != nil { return err } diff --git a/docker/docker_image_src.go b/docker/docker_image_src.go index cb48086963..d6edb50c04 100644 --- a/docker/docker_image_src.go +++ b/docker/docker_image_src.go @@ -1,6 +1,7 @@ package docker import ( + "context" "fmt" "io" "io/ioutil" @@ -85,18 +86,18 @@ func simplifyContentType(contentType string) string { // GetManifest returns the image's manifest along with its MIME type (which may be empty when it can't be determined but the manifest is available). // It may use a remote (= slow) service. func (s *dockerImageSource) GetManifest() ([]byte, string, error) { - err := s.ensureManifestIsLoaded() + err := s.ensureManifestIsLoaded(context.TODO()) if err != nil { return nil, "", err } return s.cachedManifest, s.cachedManifestMIMEType, nil } -func (s *dockerImageSource) fetchManifest(tagOrDigest string) ([]byte, string, error) { +func (s *dockerImageSource) fetchManifest(ctx context.Context, tagOrDigest string) ([]byte, string, error) { path := fmt.Sprintf(manifestPath, reference.Path(s.ref.ref), tagOrDigest) headers := make(map[string][]string) headers["Accept"] = s.requestedManifestMIMETypes - res, err := s.c.makeRequest("GET", path, headers, nil) + res, err := s.c.makeRequest(ctx, "GET", path, headers, nil) if err != nil { return nil, "", err } @@ -114,7 +115,7 @@ func (s *dockerImageSource) fetchManifest(tagOrDigest string) ([]byte, string, e // GetTargetManifest returns an image's manifest given a digest. // This is mainly used to retrieve a single image's manifest out of a manifest list. func (s *dockerImageSource) GetTargetManifest(digest digest.Digest) ([]byte, string, error) { - return s.fetchManifest(digest.String()) + return s.fetchManifest(context.TODO(), digest.String()) } // ensureManifestIsLoaded sets s.cachedManifest and s.cachedManifestMIMEType @@ -124,7 +125,7 @@ func (s *dockerImageSource) GetTargetManifest(digest digest.Digest) ([]byte, str // we need to ensure that the digest of the manifest returned by GetManifest // and used by GetSignatures are consistent, otherwise we would get spurious // signature verification failures when pulling while a tag is being updated. -func (s *dockerImageSource) ensureManifestIsLoaded() error { +func (s *dockerImageSource) ensureManifestIsLoaded(ctx context.Context) error { if s.cachedManifest != nil { return nil } @@ -134,7 +135,7 @@ func (s *dockerImageSource) ensureManifestIsLoaded() error { return err } - manblob, mt, err := s.fetchManifest(reference) + manblob, mt, err := s.fetchManifest(ctx, reference) if err != nil { return err } @@ -150,7 +151,7 @@ func (s *dockerImageSource) getExternalBlob(urls []string) (io.ReadCloser, int64 err error ) for _, url := range urls { - resp, err = s.c.makeRequestToResolvedURL("GET", url, nil, nil, -1, false) + resp, err = s.c.makeRequestToResolvedURL(context.TODO(), "GET", url, nil, nil, -1, false) if err == nil { if resp.StatusCode != http.StatusOK { err = errors.Errorf("error fetching external blob from %q: %d", url, resp.StatusCode) @@ -181,7 +182,7 @@ func (s *dockerImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, path := fmt.Sprintf(blobsPath, reference.Path(s.ref.ref), info.Digest.String()) logrus.Debugf("Downloading %s", path) - res, err := s.c.makeRequest("GET", path, nil, nil) + res, err := s.c.makeRequest(context.TODO(), "GET", path, nil, nil) if err != nil { return nil, 0, err } @@ -192,29 +193,29 @@ func (s *dockerImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, return res.Body, getBlobSize(res), nil } -func (s *dockerImageSource) GetSignatures() ([][]byte, error) { - if err := s.c.detectProperties(); err != nil { +func (s *dockerImageSource) GetSignatures(ctx context.Context) ([][]byte, error) { + if err := s.c.detectProperties(ctx); err != nil { return nil, err } switch { case s.c.signatureBase != nil: - return s.getSignaturesFromLookaside() + return s.getSignaturesFromLookaside(ctx) case s.c.supportsSignatures: - return s.getSignaturesFromAPIExtension() + return s.getSignaturesFromAPIExtension(ctx) default: return [][]byte{}, nil } } // manifestDigest returns a digest of the manifest, either from the supplied reference or from a fetched manifest. -func (s *dockerImageSource) manifestDigest() (digest.Digest, error) { +func (s *dockerImageSource) manifestDigest(ctx context.Context) (digest.Digest, error) { if digested, ok := s.ref.ref.(reference.Digested); ok { d := digested.Digest() if d.Algorithm() == digest.Canonical { return d, nil } } - if err := s.ensureManifestIsLoaded(); err != nil { + if err := s.ensureManifestIsLoaded(ctx); err != nil { return "", err } return manifest.Digest(s.cachedManifest) @@ -222,8 +223,8 @@ func (s *dockerImageSource) manifestDigest() (digest.Digest, error) { // getSignaturesFromLookaside implements GetSignatures() from the lookaside location configured in s.c.signatureBase, // which is not nil. -func (s *dockerImageSource) getSignaturesFromLookaside() ([][]byte, error) { - manifestDigest, err := s.manifestDigest() +func (s *dockerImageSource) getSignaturesFromLookaside(ctx context.Context) ([][]byte, error) { + manifestDigest, err := s.manifestDigest(ctx) if err != nil { return nil, err } @@ -235,7 +236,7 @@ func (s *dockerImageSource) getSignaturesFromLookaside() ([][]byte, error) { if url == nil { return nil, errors.Errorf("Internal error: signatureStorageURL with non-nil base returned nil") } - signature, missing, err := s.getOneSignature(url) + signature, missing, err := s.getOneSignature(ctx, url) if err != nil { return nil, err } @@ -250,7 +251,7 @@ func (s *dockerImageSource) getSignaturesFromLookaside() ([][]byte, error) { // getOneSignature downloads one signature from url. // If it successfully determines that the signature does not exist, returns with missing set to true and error set to nil. // NOTE: Keep this in sync with docs/signature-protocols.md! -func (s *dockerImageSource) getOneSignature(url *url.URL) (signature []byte, missing bool, err error) { +func (s *dockerImageSource) getOneSignature(ctx context.Context, url *url.URL) (signature []byte, missing bool, err error) { switch url.Scheme { case "file": logrus.Debugf("Reading %s", url.Path) @@ -265,7 +266,12 @@ func (s *dockerImageSource) getOneSignature(url *url.URL) (signature []byte, mis case "http", "https": logrus.Debugf("GET %s", url) - res, err := s.c.client.Get(url.String()) + req, err := http.NewRequest("GET", url.String(), nil) + if err != nil { + return nil, false, err + } + req = req.WithContext(ctx) + res, err := s.c.client.Do(req) if err != nil { return nil, false, err } @@ -287,13 +293,13 @@ func (s *dockerImageSource) getOneSignature(url *url.URL) (signature []byte, mis } // getSignaturesFromAPIExtension implements GetSignatures() using the X-Registry-Supports-Signatures API extension. -func (s *dockerImageSource) getSignaturesFromAPIExtension() ([][]byte, error) { - manifestDigest, err := s.manifestDigest() +func (s *dockerImageSource) getSignaturesFromAPIExtension(ctx context.Context) ([][]byte, error) { + manifestDigest, err := s.manifestDigest(ctx) if err != nil { return nil, err } - parsedBody, err := s.c.getExtensionsSignatures(s.ref, manifestDigest) + parsedBody, err := s.c.getExtensionsSignatures(ctx, s.ref, manifestDigest) if err != nil { return nil, err } @@ -324,7 +330,7 @@ func deleteImage(ctx *types.SystemContext, ref dockerReference) error { return err } getPath := fmt.Sprintf(manifestPath, reference.Path(ref.ref), refTail) - get, err := c.makeRequest("GET", getPath, headers, nil) + get, err := c.makeRequest(context.TODO(), "GET", getPath, headers, nil) if err != nil { return err } @@ -346,7 +352,7 @@ func deleteImage(ctx *types.SystemContext, ref dockerReference) error { // When retrieving the digest from a registry >= 2.3 use the following header: // "Accept": "application/vnd.docker.distribution.manifest.v2+json" - delete, err := c.makeRequest("DELETE", deletePath, headers, nil) + delete, err := c.makeRequest(context.TODO(), "DELETE", deletePath, headers, nil) if err != nil { return err } diff --git a/docker/tarfile/src.go b/docker/tarfile/src.go index a526f3ef28..f77cb713c4 100644 --- a/docker/tarfile/src.go +++ b/docker/tarfile/src.go @@ -3,6 +3,7 @@ package tarfile import ( "archive/tar" "bytes" + "context" "encoding/json" "io" "io/ioutil" @@ -354,6 +355,6 @@ func (s *Source) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, error) { } // GetSignatures returns the image's signatures. It may use a remote (= slow) service. -func (s *Source) GetSignatures() ([][]byte, error) { +func (s *Source) GetSignatures(ctx context.Context) ([][]byte, error) { return [][]byte{}, nil } diff --git a/image/docker_schema2_test.go b/image/docker_schema2_test.go index 7e16ce31b9..d80a018844 100644 --- a/image/docker_schema2_test.go +++ b/image/docker_schema2_test.go @@ -2,6 +2,7 @@ package image import ( "bytes" + "context" "encoding/json" "io" "io/ioutil" @@ -37,7 +38,7 @@ func (f unusedImageSource) GetTargetManifest(digest digest.Digest) ([]byte, stri func (f unusedImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, error) { panic("Unexpected call to a mock function") } -func (f unusedImageSource) GetSignatures() ([][]byte, error) { +func (f unusedImageSource) GetSignatures(context.Context) ([][]byte, error) { panic("Unexpected call to a mock function") } diff --git a/image/memory.go b/image/memory.go index 304274d142..62995f6188 100644 --- a/image/memory.go +++ b/image/memory.go @@ -1,6 +1,8 @@ package image import ( + "context" + "github.com/pkg/errors" "github.com/containers/image/types" @@ -54,7 +56,7 @@ func (i *memoryImage) Manifest() ([]byte, string, error) { } // Signatures is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need. -func (i *memoryImage) Signatures() ([][]byte, error) { +func (i *memoryImage) Signatures(ctx context.Context) ([][]byte, error) { // Modifying an image invalidates signatures; a caller asking the updated image for signatures // is probably confused. return nil, errors.New("Internal error: Image.Signatures() is not supported for images modified in memory") diff --git a/image/unparsed.go b/image/unparsed.go index 446652e79f..483cfd04f1 100644 --- a/image/unparsed.go +++ b/image/unparsed.go @@ -1,6 +1,8 @@ package image import ( + "context" + "github.com/containers/image/docker/reference" "github.com/containers/image/manifest" "github.com/containers/image/types" @@ -71,9 +73,9 @@ func (i *UnparsedImage) Manifest() ([]byte, string, error) { } // Signatures is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need. -func (i *UnparsedImage) Signatures() ([][]byte, error) { +func (i *UnparsedImage) Signatures(ctx context.Context) ([][]byte, error) { if i.cachedSignatures == nil { - sigs, err := i.src.GetSignatures() + sigs, err := i.src.GetSignatures(ctx) if err != nil { return nil, err } diff --git a/oci/layout/oci_src.go b/oci/layout/oci_src.go index 42556eea43..99b9f2083a 100644 --- a/oci/layout/oci_src.go +++ b/oci/layout/oci_src.go @@ -1,6 +1,7 @@ package layout import ( + "context" "io" "io/ioutil" "os" @@ -85,6 +86,6 @@ func (s *ociImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, err return r, fi.Size(), nil } -func (s *ociImageSource) GetSignatures() ([][]byte, error) { +func (s *ociImageSource) GetSignatures(context.Context) ([][]byte, error) { return [][]byte{}, nil } diff --git a/openshift/openshift.go b/openshift/openshift.go index 88659212ba..6050f7bfca 100644 --- a/openshift/openshift.go +++ b/openshift/openshift.go @@ -2,6 +2,7 @@ package openshift import ( "bytes" + "context" "crypto/rand" "encoding/json" "fmt" @@ -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) { +func (c *openshiftClient) doRequest(ctx context.Context, method, path string, requestBody []byte) ([]byte, error) { url := *c.baseURL url.Path = path var requestBodyReader io.Reader @@ -82,6 +83,7 @@ func (c *openshiftClient) doRequest(method, path string, requestBody []byte) ([] if err != nil { return nil, err } + req = req.WithContext(ctx) if len(c.bearerToken) != 0 { req.Header.Set("Authorization", "Bearer "+c.bearerToken) @@ -132,10 +134,10 @@ func (c *openshiftClient) doRequest(method, path string, requestBody []byte) ([] } // getImage loads the specified image object. -func (c *openshiftClient) getImage(imageStreamImageName string) (*image, error) { +func (c *openshiftClient) getImage(ctx context.Context, imageStreamImageName string) (*image, error) { // FIXME: validate components per validation.IsValidPathSegmentName? path := fmt.Sprintf("/oapi/v1/namespaces/%s/imagestreamimages/%s@%s", c.ref.namespace, c.ref.stream, imageStreamImageName) - body, err := c.doRequest("GET", path, nil) + body, err := c.doRequest(ctx, "GET", path, nil) if err != nil { return nil, err } @@ -203,7 +205,7 @@ func (s *openshiftImageSource) Close() error { } func (s *openshiftImageSource) GetTargetManifest(digest digest.Digest) ([]byte, string, error) { - if err := s.ensureImageIsResolved(); err != nil { + if err := s.ensureImageIsResolved(context.TODO()); err != nil { return nil, "", err } return s.docker.GetTargetManifest(digest) @@ -212,7 +214,7 @@ func (s *openshiftImageSource) GetTargetManifest(digest digest.Digest) ([]byte, // GetManifest returns the image's manifest along with its MIME type (which may be empty when it can't be determined but the manifest is available). // It may use a remote (= slow) service. func (s *openshiftImageSource) GetManifest() ([]byte, string, error) { - if err := s.ensureImageIsResolved(); err != nil { + if err := s.ensureImageIsResolved(context.TODO()); err != nil { return nil, "", err } return s.docker.GetManifest() @@ -220,18 +222,18 @@ func (s *openshiftImageSource) GetManifest() ([]byte, string, error) { // GetBlob returns a stream for the specified blob, and the blob’s size (or -1 if unknown). func (s *openshiftImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, error) { - if err := s.ensureImageIsResolved(); err != nil { + if err := s.ensureImageIsResolved(context.TODO()); err != nil { return nil, 0, err } return s.docker.GetBlob(info) } -func (s *openshiftImageSource) GetSignatures() ([][]byte, error) { - if err := s.ensureImageIsResolved(); err != nil { +func (s *openshiftImageSource) GetSignatures(ctx context.Context) ([][]byte, error) { + if err := s.ensureImageIsResolved(ctx); err != nil { return nil, err } - image, err := s.client.getImage(s.imageStreamImageName) + image, err := s.client.getImage(ctx, s.imageStreamImageName) if err != nil { return nil, err } @@ -245,14 +247,14 @@ func (s *openshiftImageSource) GetSignatures() ([][]byte, error) { } // ensureImageIsResolved sets up s.docker and s.imageStreamImageName -func (s *openshiftImageSource) ensureImageIsResolved() error { +func (s *openshiftImageSource) ensureImageIsResolved(ctx context.Context) error { if s.docker != nil { return nil } // FIXME: validate components per validation.IsValidPathSegmentName? path := fmt.Sprintf("/oapi/v1/namespaces/%s/imagestreams/%s", s.client.ref.namespace, s.client.ref.stream) - body, err := s.client.doRequest("GET", path, nil) + body, err := s.client.doRequest(ctx, "GET", path, nil) if err != nil { return err } @@ -410,7 +412,7 @@ func (d *openshiftImageDestination) PutSignatures(signatures [][]byte) error { return nil // No need to even read the old state. } - image, err := d.client.getImage(d.imageStreamImageName) + image, err := d.client.getImage(context.TODO(), d.imageStreamImageName) if err != nil { return err } @@ -451,7 +453,7 @@ sigExists: Content: newSig, } body, err := json.Marshal(sig) - _, err = d.client.doRequest("POST", "/oapi/v1/imagesignatures", body) + _, err = d.client.doRequest(context.TODO(), "POST", "/oapi/v1/imagesignatures", body) if err != nil { return err } diff --git a/signature/policy_eval.go b/signature/policy_eval.go index ba1fcc2a86..408510cdb6 100644 --- a/signature/policy_eval.go +++ b/signature/policy_eval.go @@ -6,6 +6,8 @@ package signature import ( + "context" + "github.com/Sirupsen/logrus" "github.com/containers/image/types" "github.com/pkg/errors" @@ -188,7 +190,8 @@ func (pc *PolicyContext) GetSignaturesWithAcceptedAuthor(image types.UnparsedIma reqs := pc.requirementsForImageRef(image.Reference()) // FIXME: rename Signatures to UnverifiedSignatures - unverifiedSignatures, err := image.Signatures() + // FIXME: pass context.Context + unverifiedSignatures, err := image.Signatures(context.TODO()) if err != nil { return nil, err } diff --git a/signature/policy_eval_signedby.go b/signature/policy_eval_signedby.go index 285b863007..56665124c0 100644 --- a/signature/policy_eval_signedby.go +++ b/signature/policy_eval_signedby.go @@ -3,6 +3,7 @@ package signature import ( + "context" "fmt" "io/ioutil" "strings" @@ -90,7 +91,8 @@ func (pr *prSignedBy) isSignatureAuthorAccepted(image types.UnparsedImage, sig [ } func (pr *prSignedBy) isRunningImageAllowed(image types.UnparsedImage) (bool, error) { - sigs, err := image.Signatures() + // FIXME: pass context.Context + sigs, err := image.Signatures(context.TODO()) if err != nil { return false, err } diff --git a/signature/policy_reference_match_test.go b/signature/policy_reference_match_test.go index da934f3d56..2ddd173081 100644 --- a/signature/policy_reference_match_test.go +++ b/signature/policy_reference_match_test.go @@ -1,6 +1,7 @@ package signature import ( + "context" "fmt" "testing" @@ -63,7 +64,7 @@ func (ref refImageMock) Close() error { func (ref refImageMock) Manifest() ([]byte, string, error) { panic("unexpected call to a mock function") } -func (ref refImageMock) Signatures() ([][]byte, error) { +func (ref refImageMock) Signatures(context.Context) ([][]byte, error) { panic("unexpected call to a mock function") } @@ -328,7 +329,7 @@ func (ref forbiddenImageMock) Close() error { func (ref forbiddenImageMock) Manifest() ([]byte, string, error) { panic("unexpected call to a mock function") } -func (ref forbiddenImageMock) Signatures() ([][]byte, error) { +func (ref forbiddenImageMock) Signatures(context.Context) ([][]byte, error) { panic("unexpected call to a mock function") } diff --git a/storage/storage_image.go b/storage/storage_image.go index 8b708bdf94..fbecdf8c9b 100644 --- a/storage/storage_image.go +++ b/storage/storage_image.go @@ -2,6 +2,7 @@ package storage import ( "bytes" + "context" "encoding/json" "io" "io/ioutil" @@ -537,7 +538,7 @@ func (s *storageImageSource) GetTargetManifest(digest ddigest.Digest) (manifestB return nil, "", ErrNoManifestLists } -func (s *storageImageSource) GetSignatures() (signatures [][]byte, err error) { +func (s *storageImageSource) GetSignatures(ctx context.Context) (signatures [][]byte, err error) { var offset int signature, err := s.imageRef.transport.store.ImageBigData(s.ID, "signatures") if err != nil { diff --git a/storage/storage_test.go b/storage/storage_test.go index 74b9b37870..2a6e22024d 100644 --- a/storage/storage_test.go +++ b/storage/storage_test.go @@ -3,6 +3,7 @@ package storage import ( "archive/tar" "bytes" + "context" "crypto/rand" "crypto/sha256" "flag" @@ -454,7 +455,7 @@ func TestWriteRead(t *testing.T) { if err == nil { t.Fatalf("GetTargetManifest(%q) is supposed to fail", ref.StringWithinTransport()) } - sigs, err := src.GetSignatures() + sigs, err := src.GetSignatures(context.Background()) if err != nil { t.Fatalf("GetSignatures(%q) returned error %v", ref.StringWithinTransport(), err) } diff --git a/types/types.go b/types/types.go index 0788904c9c..6bcd392f13 100644 --- a/types/types.go +++ b/types/types.go @@ -1,6 +1,7 @@ package types import ( + "context" "io" "time" @@ -121,7 +122,7 @@ type ImageSource interface { // The Digest field in BlobInfo is guaranteed to be provided; Size may be -1. GetBlob(BlobInfo) (io.ReadCloser, int64, error) // GetSignatures returns the image's signatures. It may use a remote (= slow) service. - GetSignatures() ([][]byte, error) + GetSignatures(context.Context) ([][]byte, error) } // ImageDestination is a service, possibly remote (= slow), to store components of a single image. @@ -204,7 +205,7 @@ type UnparsedImage interface { // Manifest is like ImageSource.GetManifest, but the result is cached; it is OK to call this however often you need. Manifest() ([]byte, string, error) // Signatures is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need. - Signatures() ([][]byte, error) + Signatures(ctx context.Context) ([][]byte, error) } // Image is the primary API for inspecting properties of images.