108 lines
3.0 KiB
Go
108 lines
3.0 KiB
Go
package docker
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/containers/image/docker/reference"
|
|
"github.com/containers/image/image"
|
|
"github.com/containers/image/types"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// Image is a Docker-specific implementation of types.ImageCloser with a few extra methods
|
|
// which are specific to Docker.
|
|
type Image struct {
|
|
types.ImageCloser
|
|
src *dockerImageSource
|
|
}
|
|
|
|
// newImage returns a new Image interface type after setting up
|
|
// a client to the registry hosting the given image.
|
|
// The caller must call .Close() on the returned Image.
|
|
func newImage(ctx context.Context, sys *types.SystemContext, ref dockerReference) (types.ImageCloser, error) {
|
|
s, err := newImageSource(ctx, sys, ref)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
img, err := image.FromSource(ctx, sys, s)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &Image{ImageCloser: img, src: s}, nil
|
|
}
|
|
|
|
// SourceRefFullName returns a fully expanded name for the repository this image is in.
|
|
func (i *Image) SourceRefFullName() string {
|
|
return i.src.ref.ref.Name()
|
|
}
|
|
|
|
// GetRepositoryTags list all tags available in the repository. The tag
|
|
// provided inside the ImageReference will be ignored. (This is a
|
|
// backward-compatible shim method which calls the module-level
|
|
// GetRepositoryTags)
|
|
func (i *Image) GetRepositoryTags(ctx context.Context) ([]string, error) {
|
|
return GetRepositoryTags(ctx, i.src.c.sys, i.src.ref)
|
|
}
|
|
|
|
// GetRepositoryTags list all tags available in the repository. The tag
|
|
// provided inside the ImageReference will be ignored.
|
|
func GetRepositoryTags(ctx context.Context, sys *types.SystemContext, ref types.ImageReference) ([]string, error) {
|
|
dr, ok := ref.(dockerReference)
|
|
if !ok {
|
|
return nil, errors.Errorf("ref must be a dockerReference")
|
|
}
|
|
|
|
path := fmt.Sprintf(tagsPath, reference.Path(dr.ref))
|
|
client, err := newDockerClientFromRef(sys, dr, false, "pull")
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to create client")
|
|
}
|
|
|
|
tags := make([]string, 0)
|
|
|
|
for {
|
|
res, err := client.makeRequest(ctx, "GET", path, nil, nil, v2Auth, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer res.Body.Close()
|
|
if res.StatusCode != http.StatusOK {
|
|
// print url also
|
|
return nil, errors.Errorf("Invalid status code returned when fetching tags list %d (%s)", res.StatusCode, http.StatusText(res.StatusCode))
|
|
}
|
|
|
|
var tagsHolder struct {
|
|
Tags []string
|
|
}
|
|
if err = json.NewDecoder(res.Body).Decode(&tagsHolder); err != nil {
|
|
return nil, err
|
|
}
|
|
tags = append(tags, tagsHolder.Tags...)
|
|
|
|
link := res.Header.Get("Link")
|
|
if link == "" {
|
|
break
|
|
}
|
|
|
|
linkURLStr := strings.Trim(strings.Split(link, ";")[0], "<>")
|
|
linkURL, err := url.Parse(linkURLStr)
|
|
if err != nil {
|
|
return tags, err
|
|
}
|
|
|
|
// can be relative or absolute, but we only want the path (and I
|
|
// guess we're in trouble if it forwards to a new place...)
|
|
path = linkURL.Path
|
|
if linkURL.RawQuery != "" {
|
|
path += "?"
|
|
path += linkURL.RawQuery
|
|
}
|
|
}
|
|
return tags, nil
|
|
}
|