mirror of
https://github.com/containers/skopeo.git
synced 2025-06-29 08:07:37 +00:00
Fix uploading layer blobs to Docker registry
Implement a client to the chunked API, instead of the nonexistent
one-shot API (per
2a4deee441
).
Adds a FIXME to DELETE the pending upload on failure; the uploads are
supposed to time out so this is not immediately critical.
Fixes #64 .
This commit is contained in:
parent
e66541f7d0
commit
fc761ed74f
@ -29,7 +29,7 @@ const (
|
|||||||
tagsURL = "%s/tags/list"
|
tagsURL = "%s/tags/list"
|
||||||
manifestURL = "%s/manifests/%s"
|
manifestURL = "%s/manifests/%s"
|
||||||
blobsURL = "%s/blobs/%s"
|
blobsURL = "%s/blobs/%s"
|
||||||
blobUploadURL = "%s/blobs/uploads/?digest=%s"
|
blobUploadURL = "%s/blobs/uploads/"
|
||||||
)
|
)
|
||||||
|
|
||||||
// dockerClient is configuration for dealing with a single Docker registry.
|
// dockerClient is configuration for dealing with a single Docker registry.
|
||||||
@ -78,6 +78,8 @@ func newDockerClient(refHostname, certPath string, tlsVerify bool) (*dockerClien
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// makeRequest creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client.
|
||||||
|
// url is NOT an absolute URL, but a path relative to the /v2/ top-level API path. The host name and schema is taken from the client or autodetected.
|
||||||
func (c *dockerClient) makeRequest(method, url string, headers map[string][]string, stream io.Reader) (*http.Response, error) {
|
func (c *dockerClient) makeRequest(method, url string, headers map[string][]string, stream io.Reader) (*http.Response, error) {
|
||||||
if c.scheme == "" {
|
if c.scheme == "" {
|
||||||
pr, err := c.ping()
|
pr, err := c.ping()
|
||||||
@ -89,6 +91,12 @@ func (c *dockerClient) makeRequest(method, url string, headers map[string][]stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
url = fmt.Sprintf(baseURL, c.scheme, c.registry) + url
|
url = fmt.Sprintf(baseURL, c.scheme, c.registry) + url
|
||||||
|
return c.makeRequestToResolvedURL(method, url, headers, stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeRequestToResolvedURL creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client.
|
||||||
|
// makeRequest should generally be preferred.
|
||||||
|
func (c *dockerClient) makeRequestToResolvedURL(method, url string, headers map[string][]string, stream io.Reader) (*http.Response, error) {
|
||||||
req, err := http.NewRequest(method, url, stream)
|
req, err := http.NewRequest(method, url, stream)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -86,19 +86,38 @@ func (d *dockerImageDestination) PutLayer(digest string, stream io.Reader) error
|
|||||||
logrus.Debugf("... failed, status %d", res.StatusCode)
|
logrus.Debugf("... failed, status %d", res.StatusCode)
|
||||||
|
|
||||||
// FIXME? Chunked upload, progress reporting, etc.
|
// FIXME? Chunked upload, progress reporting, etc.
|
||||||
uploadURL := fmt.Sprintf(blobUploadURL, d.ref.RemoteName(), digest)
|
uploadURL := fmt.Sprintf(blobUploadURL, d.ref.RemoteName())
|
||||||
logrus.Debugf("Uploading %s", uploadURL)
|
logrus.Debugf("Uploading %s", uploadURL)
|
||||||
// FIXME: Set Content-Length?
|
res, err = d.c.makeRequest("POST", uploadURL, nil, nil)
|
||||||
res, err = d.c.makeRequest("POST", uploadURL, map[string][]string{"Content-Type": {"application/octet-stream"}}, stream)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
if res.StatusCode != http.StatusAccepted {
|
||||||
|
logrus.Debugf("Error initiating layer upload, response %#v", *res)
|
||||||
|
return fmt.Errorf("Error initiating layer upload to %s, status %d", uploadURL, res.StatusCode)
|
||||||
|
}
|
||||||
|
uploadLocation, err := res.Location()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error determining upload URL: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: DELETE uploadLocation on failure
|
||||||
|
|
||||||
|
locationQuery := uploadLocation.Query()
|
||||||
|
locationQuery.Set("digest", digest)
|
||||||
|
uploadLocation.RawQuery = locationQuery.Encode()
|
||||||
|
res, err = d.c.makeRequestToResolvedURL("PUT", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, stream)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != http.StatusCreated {
|
if res.StatusCode != http.StatusCreated {
|
||||||
logrus.Debugf("Error uploading, status %d", res.StatusCode)
|
logrus.Debugf("Error uploading layer, response %#v", *res)
|
||||||
return fmt.Errorf("Error uploading to %s, status %d", uploadURL, res.StatusCode)
|
return fmt.Errorf("Error uploading layer to %s, status %d", uploadLocation, res.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logrus.Debugf("Upload of layer %s complete", digest)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user