Vendor after merging in mtrmac/image:docker-lookaside

This commit is contained in:
Miloslav Trmač
2016-09-12 21:11:44 +02:00
parent d1d1d6533e
commit f46da343e2
6 changed files with 437 additions and 25 deletions

View File

@@ -6,6 +6,8 @@ import (
"io/ioutil"
"mime"
"net/http"
"net/url"
"os"
"strconv"
"github.com/Sirupsen/logrus"
@@ -26,6 +28,9 @@ type dockerImageSource struct {
ref dockerReference
requestedManifestMIMETypes []string
c *dockerClient
// State
cachedManifest []byte // nil if not loaded yet
cachedManifestMIMEType string // Only valid if cachedManifest != nil
}
// newImageSource creates a new ImageSource for the specified image reference,
@@ -33,7 +38,7 @@ type dockerImageSource struct {
// nil requestedManifestMIMETypes means manifest.DefaultRequestedManifestMIMETypes.
// The caller must call .Close() on the returned ImageSource.
func newImageSource(ctx *types.SystemContext, ref dockerReference, requestedManifestMIMETypes []string) (*dockerImageSource, error) {
c, err := newDockerClient(ctx, ref.ref.Hostname())
c, err := newDockerClient(ctx, ref, false)
if err != nil {
return nil, err
}
@@ -71,10 +76,29 @@ func simplifyContentType(contentType string) string {
}
func (s *dockerImageSource) GetManifest() ([]byte, string, error) {
reference, err := s.ref.tagOrDigest()
err := s.ensureManifestIsLoaded()
if err != nil {
return nil, "", err
}
return s.cachedManifest, s.cachedManifestMIMEType, nil
}
// ensureManifestIsLoaded sets s.cachedManifest and s.cachedManifestMIMEType
//
// ImageSource implementations are not required or expected to do any caching,
// but because our signatures are “attached” to the manifest digest,
// 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 {
if s.cachedManifest != nil {
return nil
}
reference, err := s.ref.tagOrDigest()
if err != nil {
return err
}
url := fmt.Sprintf(manifestURL, s.ref.ref.RemoteName(), reference)
// TODO(runcom) set manifest version header! schema1 for now - then schema2 etc etc and v1
// TODO(runcom) NO, switch on the resulter manifest like Docker is doing
@@ -82,18 +106,20 @@ func (s *dockerImageSource) GetManifest() ([]byte, string, error) {
headers["Accept"] = s.requestedManifestMIMETypes
res, err := s.c.makeRequest("GET", url, headers, nil)
if err != nil {
return nil, "", err
return err
}
defer res.Body.Close()
manblob, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, "", err
return err
}
if res.StatusCode != http.StatusOK {
return nil, "", errFetchManifest{res.StatusCode, manblob}
return errFetchManifest{res.StatusCode, manblob}
}
// We might validate manblob against the Docker-Content-Digest header here to protect against transport errors.
return manblob, simplifyContentType(res.Header.Get("Content-Type")), nil
s.cachedManifest = manblob
s.cachedManifestMIMEType = simplifyContentType(res.Header.Get("Content-Type"))
return nil
}
// GetBlob returns a stream for the specified blob, and the blobs size (or -1 if unknown).
@@ -116,12 +142,77 @@ func (s *dockerImageSource) GetBlob(digest string) (io.ReadCloser, int64, error)
}
func (s *dockerImageSource) GetSignatures() ([][]byte, error) {
return [][]byte{}, nil
if s.c.signatureBase == nil { // Skip dealing with the manifest digest if not necessary.
return [][]byte{}, nil
}
if err := s.ensureManifestIsLoaded(); err != nil {
return nil, err
}
manifestDigest, err := manifest.Digest(s.cachedManifest)
if err != nil {
return nil, err
}
signatures := [][]byte{}
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")
}
signature, missing, err := s.getOneSignature(url)
if err != nil {
return nil, err
}
if missing {
break
}
signatures = append(signatures, signature)
}
return signatures, nil
}
// 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.
func (s *dockerImageSource) getOneSignature(url *url.URL) (signature []byte, missing bool, err error) {
switch url.Scheme {
case "file":
logrus.Debugf("Reading %s", url.Path)
sig, err := ioutil.ReadFile(url.Path)
if err != nil {
if os.IsNotExist(err) {
return nil, true, nil
}
return nil, false, err
}
return sig, false, nil
case "http", "https":
logrus.Debugf("GET %s", url)
res, err := s.c.client.Get(url.String())
if err != nil {
return nil, false, err
}
defer res.Body.Close()
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)
}
sig, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, false, err
}
return sig, false, nil
default:
return nil, false, fmt.Errorf("Unsupported scheme when reading signature from %s", url.String())
}
}
// deleteImage deletes the named image from the registry, if supported.
func deleteImage(ctx *types.SystemContext, ref dockerReference) error {
c, err := newDockerClient(ctx, ref.ref.Hostname())
c, err := newDockerClient(ctx, ref, true)
if err != nil {
return err
}
@@ -141,7 +232,7 @@ func deleteImage(ctx *types.SystemContext, ref dockerReference) error {
return err
}
defer get.Body.Close()
body, err := ioutil.ReadAll(get.Body)
manifestBody, err := ioutil.ReadAll(get.Body)
if err != nil {
return err
}
@@ -150,7 +241,7 @@ func deleteImage(ctx *types.SystemContext, ref dockerReference) error {
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)
default:
return fmt.Errorf("Failed to delete %v: %s (%v)", ref.ref, string(body), get.Status)
return fmt.Errorf("Failed to delete %v: %s (%v)", ref.ref, manifestBody, get.Status)
}
digest := get.Header.Get("Docker-Content-Digest")
@@ -164,7 +255,7 @@ func deleteImage(ctx *types.SystemContext, ref dockerReference) error {
}
defer delete.Body.Close()
body, err = ioutil.ReadAll(delete.Body)
body, err := ioutil.ReadAll(delete.Body)
if err != nil {
return err
}
@@ -172,5 +263,26 @@ func deleteImage(ctx *types.SystemContext, ref dockerReference) error {
return fmt.Errorf("Failed to delete %v: %s (%v)", deleteURL, string(body), delete.Status)
}
if c.signatureBase != nil {
manifestDigest, err := manifest.Digest(manifestBody)
if err != nil {
return err
}
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")
}
missing, err := c.deleteOneSignature(url)
if err != nil {
return err
}
if missing {
break
}
}
}
return nil
}