Skip to content

Commit

Permalink
fix: distinguish image name and ref
Browse files Browse the repository at this point in the history
after introduce the registry-mirror feature. the image name
and the ref is not the same. using image name for user-interface
operation.

Signed-off-by: zhuangqh <[email protected]>
  • Loading branch information
zhuangqh authored and fuweid committed Jun 14, 2019
1 parent 513161f commit 3197fcf
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 84 deletions.
28 changes: 8 additions & 20 deletions ctrd/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ func (c *Client) PushImage(ctx context.Context, ref string, authConfig *types.Au

pushTracker := docker.NewInMemoryTracker()

resolver, err := c.getResolver(authConfig, ref, docker.ResolverOptions{
resolver, _, err := c.getResolver(ctx, authConfig, ref, []string{ref}, docker.ResolverOptions{
Tracker: pushTracker,
})
if err != nil {
Expand Down Expand Up @@ -251,33 +251,21 @@ func (c *Client) PushImage(ctx context.Context, ref string, authConfig *types.Au
return nil
}

// ResolveImage attempts to resolve the image reference into a name and descriptor.
func (c *Client) ResolveImage(ctx context.Context, ref string, authConfig *types.AuthConfig) (name string, desc ocispec.Descriptor, err error) {
resolver, err := c.getResolver(authConfig, ref, docker.ResolverOptions{})
if err != nil {
return "", ocispec.Descriptor{}, err
}

name, desc, err = resolver.Resolve(ctx, ref)
if err != nil {
err = errors.Wrapf(err, "failed to resolve reference %q", ref)
}
return
}

// FetchImage fetches image content from the remote repository.
func (c *Client) FetchImage(ctx context.Context, ref string, authConfig *types.AuthConfig, stream *jsonstream.JSONStream) (containerd.Image, error) {
func (c *Client) FetchImage(ctx context.Context, name string, refs []string, authConfig *types.AuthConfig, stream *jsonstream.JSONStream) (containerd.Image, error) {
wrapperCli, err := c.Get(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get a containerd grpc client: %v", err)
}

resolver, err := c.getResolver(authConfig, ref, docker.ResolverOptions{})
resolver, availableRef, err := c.getResolver(ctx, authConfig, name, refs, docker.ResolverOptions{})
if err != nil {
return nil, err
}

ongoing := newJobs(ref)
logrus.Infof("pulling image name %v reference %v", name, availableRef)

ongoing := newJobs(name)

options := []containerd.RemoteOpt{
containerd.WithSchema1Conversion,
Expand All @@ -302,11 +290,11 @@ func (c *Client) FetchImage(ctx context.Context, ref string, authConfig *types.A
}
close(wait)

logrus.Infof("fetch progress exited, ref: %s.", ref)
logrus.Infof("fetch progress exited, ref: %s.", availableRef)
}()

// start to pull image.
img, err := c.fetchImage(ctx, wrapperCli, ref, options)
img, err := c.fetchImage(ctx, wrapperCli, availableRef, options)

// cancel fetch progress before handle error.
cancelProgress()
Expand Down
5 changes: 1 addition & 4 deletions ctrd/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"github.com/containerd/containerd/mount"
"github.com/containerd/containerd/snapshots"
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)

// APIClient defines common methods of containerd api client
Expand Down Expand Up @@ -79,9 +78,7 @@ type ImageAPIClient interface {
// ListImages returns the list of containerd.Image filtered by the given conditions.
ListImages(ctx context.Context, filter ...string) ([]containerd.Image, error)
// FetchImage fetchs image content by the given reference.
FetchImage(ctx context.Context, ref string, authConfig *types.AuthConfig, stream *jsonstream.JSONStream) (containerd.Image, error)
// ResolveImage attempts to resolve the image reference into a name and descriptor.
ResolveImage(ctx context.Context, ref string, authConfig *types.AuthConfig) (name string, desc ocispec.Descriptor, err error)
FetchImage(ctx context.Context, name string, refs []string, authConfig *types.AuthConfig, stream *jsonstream.JSONStream) (containerd.Image, error)
// RemoveImage removes the image by the given reference.
RemoveImage(ctx context.Context, ref string) error
// ImportImage creates a set of images by tarstream.
Expand Down
154 changes: 120 additions & 34 deletions ctrd/utils.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package ctrd

import (
"context"
"crypto/tls"
"fmt"
"net"
"net/http"
"net/url"
Expand All @@ -10,12 +12,14 @@ import (

"github.com/alibaba/pouch/apis/types"
"github.com/alibaba/pouch/pkg/errtypes"
"github.com/alibaba/pouch/pkg/reference"

"github.com/containerd/containerd"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/remotes"
"github.com/containerd/containerd/remotes/docker"
"github.com/containerd/containerd/runtime/linux/runctypes"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -48,46 +52,128 @@ func (c *Client) isInsecureDomain(ref string) bool {
return false
}

func (c *Client) getResolver(authConfig *types.AuthConfig, ref string, resolverOpt docker.ResolverOptions) (remotes.Resolver, error) {
var (
username = ""
secret = ""
insecure = c.isInsecureDomain(ref)
)
// resolverWrapper wrap a image resolver
// do reference <-> name translation before each operation.
type resolverWrapper struct {
refToName map[string]string
resolver remotes.Resolver
}

// Resolve attempts to resolve the reference into a name and descriptor.
// translate the reference to a name which may be a short name like 'library/ubuntu'.
func (r *resolverWrapper) Resolve(ctx context.Context, ref string) (name string, desc ocispec.Descriptor, err error) {
newRef, desc, err := r.resolver.Resolve(ctx, ref)
if err != nil {
return "", ocispec.Descriptor{}, err
}

if name, ok := r.refToName[newRef]; ok {
return name, desc, nil
}

return newRef, desc, nil
}

// Fetcher returns a new fetcher for the provided reference.
// All content fetched from the returned fetcher will be
// from the namespace referred to by ref.
func (r *resolverWrapper) Fetcher(ctx context.Context, name string) (remotes.Fetcher, error) {
ref := name
for rf, n := range r.refToName {
if name == n {
ref = rf
break
}
}
return r.resolver.Fetcher(ctx, ref)
}

// Pusher returns a new pusher for the provided reference
func (r *resolverWrapper) Pusher(ctx context.Context, name string) (remotes.Pusher, error) {
ref := name
for rf, n := range r.refToName {
if name == n {
ref = rf
break
}
}
return r.resolver.Pusher(ctx, ref)
}

func newImageResolver(refToName map[string]string, resolverOpt docker.ResolverOptions) remotes.Resolver {
return &resolverWrapper{
refToName: refToName,
resolver: docker.NewResolver(resolverOpt),
}
}

// getResolver try to resolve ref in the reference list, return the resolver and the first available ref.
func (c *Client) getResolver(ctx context.Context, authConfig *types.AuthConfig, name string, refs []string, resolverOpt docker.ResolverOptions) (remotes.Resolver, string, error) {
username, secret := "", ""
if authConfig != nil {
username = authConfig.Username
secret = authConfig.Password
}

tr := &http.Transport{
Proxy: proxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: insecure,
},
ExpectContinueTimeout: 5 * time.Second,
}

options := docker.ResolverOptions{
Tracker: resolverOpt.Tracker,
PlainHTTP: insecure,
Credentials: func(host string) (string, string, error) {
// Only one host
return username, secret, nil
},
Client: &http.Client{
Transport: tr,
},
}
return docker.NewResolver(options), nil
var (
availableRef string
opt docker.ResolverOptions
)

for _, ref := range refs {
namedRef, err := reference.Parse(ref)
if err != nil {
logrus.Warnf("failed to parse image reference when trying to resolve image %s, raw reference is %s: %v", ref, name, err)
continue
}
namedRef = reference.TrimTagForDigest(reference.WithDefaultTagIfMissing(namedRef))

insecure := c.isInsecureDomain(ref)
tr := &http.Transport{
Proxy: proxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: insecure,
},
ExpectContinueTimeout: 5 * time.Second,
}

opt = docker.ResolverOptions{
Tracker: resolverOpt.Tracker,
PlainHTTP: insecure,
Credentials: func(host string) (string, string, error) {
// Only one host
return username, secret, nil
},
Client: &http.Client{
Transport: tr,
},
}

resolver := docker.NewResolver(opt)

if _, _, err := resolver.Resolve(ctx, namedRef.String()); err == nil {
availableRef = namedRef.String()
break
}
}

if availableRef == "" {
return nil, "", fmt.Errorf("there is no available image reference after trying %+q", refs)
}

refToName := map[string]string{
availableRef: name,
}

return newImageResolver(refToName, opt), availableRef, nil
}

// GetWeightDevice Convert weight device from []*types.WeightDevice to []specs.LinuxWeightDevice
Expand Down
33 changes: 7 additions & 26 deletions daemon/mgr/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ func (mgr *ImageManager) LookupImageReferences(ref string) []string {

// PullImage pulls images from specified registry.
func (mgr *ImageManager) PullImage(ctx context.Context, ref string, authConfig *types.AuthConfig, out io.Writer) error {
namedRef, err := reference.Parse(ref)
if err != nil {
return err
}

pctx, cancel := context.WithCancel(ctx)
stream := jsonstream.New(out, nil)

Expand All @@ -209,34 +214,10 @@ func (mgr *ImageManager) PullImage(ctx context.Context, ref string, authConfig *
closeStream()
}

var (
namedRef reference.Named
err error
)

// find an available image which could be resolved.
fullRefs := mgr.LookupImageReferences(ref)
for _, newRef := range fullRefs {
namedRef, err = reference.Parse(newRef)
if err != nil {
logrus.Warnf("failed to parse image reference when trying to pull image %s, raw reference is %s: %v", newRef, ref, err)
continue
}
namedRef = reference.TrimTagForDigest(reference.WithDefaultTagIfMissing(namedRef))
_, _, err = mgr.client.ResolveImage(pctx, namedRef.String(), authConfig)

// got available one
if err == nil {
break
}
}

if err != nil {
writeStream(err)
return err
}
namedRef = reference.TrimTagForDigest(reference.WithDefaultTagIfMissing(namedRef))

img, err := mgr.client.FetchImage(pctx, namedRef.String(), authConfig, stream)
img, err := mgr.client.FetchImage(pctx, namedRef.String(), fullRefs, authConfig, stream)
if err != nil {
writeStream(err)
return err
Expand Down

0 comments on commit 3197fcf

Please sign in to comment.