mirror of
https://github.com/containers/skopeo.git
synced 2025-04-29 03:33:25 +00:00
Add an ImageSource implementation to docker.go
The ImageSource type does not provide all of the functionality of docker.go, but we will be able to reuse the ImageSource parts in an OpenShift client. This is only a restructuring, does not change behavior.
This commit is contained in:
parent
a4aedae063
commit
e169c311d3
167
docker.go
167
docker.go
@ -51,16 +51,9 @@ func (e errFetchManifest) Error() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type dockerImage struct {
|
type dockerImage struct {
|
||||||
ref reference.Named
|
src *dockerImageSource
|
||||||
tag string
|
digest string
|
||||||
digest string
|
rawManifest []byte
|
||||||
registry string
|
|
||||||
username string
|
|
||||||
password string
|
|
||||||
WWWAuthenticate string
|
|
||||||
scheme string
|
|
||||||
rawManifest []byte
|
|
||||||
transport *http.Transport
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *dockerImage) RawManifest(version string) ([]byte, error) {
|
func (i *dockerImage) RawManifest(version string) ([]byte, error) {
|
||||||
@ -85,7 +78,7 @@ func (i *dockerImage) Manifest() (types.ImageManifest, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
imgManifest, err := makeImageManifest(i.ref.FullName(), ms1, i.digest, tags)
|
imgManifest, err := makeImageManifest(i.src.ref.FullName(), ms1, i.digest, tags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -93,8 +86,9 @@ func (i *dockerImage) Manifest() (types.ImageManifest, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *dockerImage) getTags() ([]string, error) {
|
func (i *dockerImage) getTags() ([]string, error) {
|
||||||
url := fmt.Sprintf(tagsURL, i.scheme, i.registry, i.ref.RemoteName())
|
// FIXME? Breaking the abstraction.
|
||||||
res, err := i.makeRequest("GET", url, i.WWWAuthenticate != "", nil)
|
url := fmt.Sprintf(tagsURL, i.src.scheme, i.src.registry, i.src.ref.RemoteName())
|
||||||
|
res, err := i.src.makeRequest("GET", url, i.src.WWWAuthenticate != "", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -189,7 +183,61 @@ func sanitize(s string) string {
|
|||||||
return strings.Replace(s, "/", "-", -1)
|
return strings.Replace(s, "/", "-", -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *dockerImage) makeRequest(method, url string, auth bool, headers map[string]string) (*http.Response, error) {
|
type dockerImageSource struct {
|
||||||
|
ref reference.Named
|
||||||
|
tag string
|
||||||
|
registry string
|
||||||
|
username string
|
||||||
|
password string
|
||||||
|
WWWAuthenticate string // Obtained by s.ping()
|
||||||
|
scheme string // Obtained by s.ping()
|
||||||
|
transport *http.Transport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dockerImageSource) GetManifest() (manifest []byte, unverifiedCanonicalDigest string, err error) {
|
||||||
|
pr, err := s.ping()
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
s.WWWAuthenticate = pr.WWWAuthenticate
|
||||||
|
s.scheme = pr.scheme
|
||||||
|
url := fmt.Sprintf(manifestURL, s.scheme, s.registry, s.ref.RemoteName(), s.tag)
|
||||||
|
// 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
|
||||||
|
res, err := s.makeRequest("GET", url, pr.needsAuth(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
manblob, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
return nil, "", errFetchManifest{res.StatusCode, manblob}
|
||||||
|
}
|
||||||
|
return manblob, res.Header.Get("Docker-Content-Digest"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dockerImageSource) GetLayer(digest string) (io.ReadCloser, error) {
|
||||||
|
url := fmt.Sprintf(blobsURL, s.scheme, s.registry, s.ref.RemoteName(), digest)
|
||||||
|
logrus.Infof("Downloading %s", url)
|
||||||
|
res, err := s.makeRequest("GET", url, s.WWWAuthenticate != "", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
// print url also
|
||||||
|
return nil, fmt.Errorf("Invalid status code returned when fetching blob %d", res.StatusCode)
|
||||||
|
}
|
||||||
|
return res.Body, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dockerImageSource) GetSignatures() ([][]byte, error) {
|
||||||
|
return [][]byte{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dockerImageSource) makeRequest(method, url string, auth bool, headers map[string]string) (*http.Response, error) {
|
||||||
req, err := http.NewRequest("GET", url, nil)
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -199,13 +247,13 @@ func (i *dockerImage) makeRequest(method, url string, auth bool, headers map[str
|
|||||||
req.Header.Add(n, h)
|
req.Header.Add(n, h)
|
||||||
}
|
}
|
||||||
if auth {
|
if auth {
|
||||||
if err := i.setupRequestAuth(req); err != nil {
|
if err := s.setupRequestAuth(req); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
if i.transport != nil {
|
if s.transport != nil {
|
||||||
client.Transport = i.transport
|
client.Transport = s.transport
|
||||||
}
|
}
|
||||||
res, err := client.Do(req)
|
res, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -214,19 +262,19 @@ func (i *dockerImage) makeRequest(method, url string, auth bool, headers map[str
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *dockerImage) setupRequestAuth(req *http.Request) error {
|
func (s *dockerImageSource) setupRequestAuth(req *http.Request) error {
|
||||||
tokens := strings.SplitN(strings.TrimSpace(i.WWWAuthenticate), " ", 2)
|
tokens := strings.SplitN(strings.TrimSpace(s.WWWAuthenticate), " ", 2)
|
||||||
if len(tokens) != 2 {
|
if len(tokens) != 2 {
|
||||||
return fmt.Errorf("expected 2 tokens in WWW-Authenticate: %d, %s", len(tokens), i.WWWAuthenticate)
|
return fmt.Errorf("expected 2 tokens in WWW-Authenticate: %d, %s", len(tokens), s.WWWAuthenticate)
|
||||||
}
|
}
|
||||||
switch tokens[0] {
|
switch tokens[0] {
|
||||||
case "Basic":
|
case "Basic":
|
||||||
req.SetBasicAuth(i.username, i.password)
|
req.SetBasicAuth(s.username, s.password)
|
||||||
return nil
|
return nil
|
||||||
case "Bearer":
|
case "Bearer":
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
if i.transport != nil {
|
if s.transport != nil {
|
||||||
client.Transport = i.transport
|
client.Transport = s.transport
|
||||||
}
|
}
|
||||||
res, err := client.Do(req)
|
res, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -263,7 +311,7 @@ func (i *dockerImage) setupRequestAuth(req *http.Request) error {
|
|||||||
if scope == "" {
|
if scope == "" {
|
||||||
return fmt.Errorf("missing scope in bearer auth challenge")
|
return fmt.Errorf("missing scope in bearer auth challenge")
|
||||||
}
|
}
|
||||||
token, err := i.getBearerToken(realm, service, scope)
|
token, err := s.getBearerToken(realm, service, scope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -274,7 +322,7 @@ func (i *dockerImage) setupRequestAuth(req *http.Request) error {
|
|||||||
// support docker bearer with authconfig's Auth string? see docker2aci
|
// support docker bearer with authconfig's Auth string? see docker2aci
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *dockerImage) getBearerToken(realm, service, scope string) (string, error) {
|
func (s *dockerImageSource) getBearerToken(realm, service, scope string) (string, error) {
|
||||||
authReq, err := http.NewRequest("GET", realm, nil)
|
authReq, err := http.NewRequest("GET", realm, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -285,8 +333,8 @@ func (i *dockerImage) getBearerToken(realm, service, scope string) (string, erro
|
|||||||
getParams.Add("scope", scope)
|
getParams.Add("scope", scope)
|
||||||
}
|
}
|
||||||
authReq.URL.RawQuery = getParams.Encode()
|
authReq.URL.RawQuery = getParams.Encode()
|
||||||
if i.username != "" && i.password != "" {
|
if s.username != "" && s.password != "" {
|
||||||
authReq.SetBasicAuth(i.username, i.password)
|
authReq.SetBasicAuth(s.username, s.password)
|
||||||
}
|
}
|
||||||
// insecure for now to contact the external token service
|
// insecure for now to contact the external token service
|
||||||
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
|
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
|
||||||
@ -328,29 +376,12 @@ func (i *dockerImage) retrieveRawManifest() error {
|
|||||||
if i.rawManifest != nil {
|
if i.rawManifest != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
pr, err := i.ping()
|
manblob, unverifiedCanonicalDigest, err := i.src.GetManifest()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
i.WWWAuthenticate = pr.WWWAuthenticate
|
|
||||||
i.scheme = pr.scheme
|
|
||||||
url := fmt.Sprintf(manifestURL, i.scheme, i.registry, i.ref.RemoteName(), i.tag)
|
|
||||||
// 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
|
|
||||||
res, err := i.makeRequest("GET", url, pr.needsAuth(), nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer res.Body.Close()
|
|
||||||
manblob, err := ioutil.ReadAll(res.Body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if res.StatusCode != http.StatusOK {
|
|
||||||
return errFetchManifest{res.StatusCode, manblob}
|
|
||||||
}
|
|
||||||
i.rawManifest = manblob
|
i.rawManifest = manblob
|
||||||
i.digest = res.Header.Get("Docker-Content-Digest")
|
i.digest = unverifiedCanonicalDigest
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,31 +429,25 @@ func (i *dockerImage) Layers(layers ...string) error {
|
|||||||
if !strings.HasPrefix(l, "sha256:") {
|
if !strings.HasPrefix(l, "sha256:") {
|
||||||
l = "sha256:" + l
|
l = "sha256:" + l
|
||||||
}
|
}
|
||||||
url := fmt.Sprintf(blobsURL, i.scheme, i.registry, i.ref.RemoteName(), l)
|
if err := i.getLayer(l, tmpDir); err != nil {
|
||||||
if err := i.getLayer(l, url, tmpDir); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *dockerImage) getLayer(l, url, tmpDir string) error {
|
func (i *dockerImage) getLayer(digest, tmpDir string) error {
|
||||||
logrus.Infof("Downloading %s", url)
|
stream, err := i.src.GetLayer(digest)
|
||||||
res, err := i.makeRequest("GET", url, i.WWWAuthenticate != "", nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer stream.Close()
|
||||||
if res.StatusCode != http.StatusOK {
|
layerPath := path.Join(tmpDir, strings.Replace(digest, "sha256:", "", -1)+".tar")
|
||||||
// print url also
|
|
||||||
return fmt.Errorf("Invalid status code returned when fetching blob %d", res.StatusCode)
|
|
||||||
}
|
|
||||||
layerPath := path.Join(tmpDir, strings.Replace(l, "sha256:", "", -1)+".tar")
|
|
||||||
layerFile, err := os.Create(layerPath)
|
layerFile, err := os.Create(layerPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err := io.Copy(layerFile, res.Body); err != nil {
|
if _, err := io.Copy(layerFile, stream); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := layerFile.Sync(); err != nil {
|
if err := layerFile.Sync(); err != nil {
|
||||||
@ -431,7 +456,8 @@ func (i *dockerImage) getLayer(l, url, tmpDir string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseDockerImage(img, certPath string, tlsVerify bool) (types.Image, error) {
|
// newDockerImageSource is the same as NewDockerImageSource, only it returns the more specific *dockerImageSource type.
|
||||||
|
func newDockerImageSource(img, certPath string, tlsVerify bool) (*dockerImageSource, error) {
|
||||||
ref, err := reference.ParseNamed(img)
|
ref, err := reference.ParseNamed(img)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -472,7 +498,7 @@ func parseDockerImage(img, certPath string, tlsVerify bool) (types.Image, error)
|
|||||||
TLSClientConfig: tlsc,
|
TLSClientConfig: tlsc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &dockerImage{
|
return &dockerImageSource{
|
||||||
ref: ref,
|
ref: ref,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
registry: registry,
|
registry: registry,
|
||||||
@ -482,6 +508,19 @@ func parseDockerImage(img, certPath string, tlsVerify bool) (types.Image, error)
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewDockerImageSource creates a new ImageSource for the specified image and connection specification.
|
||||||
|
func NewDockerImageSource(img, certPath string, tlsVerify bool) (types.ImageSource, error) {
|
||||||
|
return newDockerImageSource(img, certPath, tlsVerify)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDockerImage(img, certPath string, tlsVerify bool) (types.Image, error) {
|
||||||
|
s, err := newDockerImageSource(img, certPath, tlsVerify)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &dockerImage{src: s}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func getDefaultConfigDir(confPath string) string {
|
func getDefaultConfigDir(confPath string) string {
|
||||||
return filepath.Join(homedir.Get(), confPath)
|
return filepath.Join(homedir.Get(), confPath)
|
||||||
}
|
}
|
||||||
@ -576,13 +615,13 @@ func (pr *pingResponse) needsAuth() bool {
|
|||||||
return pr.WWWAuthenticate != ""
|
return pr.WWWAuthenticate != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *dockerImage) ping() (*pingResponse, error) {
|
func (s *dockerImageSource) ping() (*pingResponse, error) {
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
if i.transport != nil {
|
if s.transport != nil {
|
||||||
client.Transport = i.transport
|
client.Transport = s.transport
|
||||||
}
|
}
|
||||||
ping := func(scheme string) (*pingResponse, error) {
|
ping := func(scheme string) (*pingResponse, error) {
|
||||||
url := fmt.Sprintf(baseURL, scheme, i.registry)
|
url := fmt.Sprintf(baseURL, scheme, s.registry)
|
||||||
resp, err := client.Get(url)
|
resp, err := client.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
Loading…
Reference in New Issue
Block a user