github.com/pdmccormick/importable-docker-buildx@v0.0.0-20240426161518-e47091289030/util/imagetools/inspect.go (about) 1 package imagetools 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/base64" 7 "encoding/json" 8 "io" 9 "net/http" 10 11 "github.com/containerd/containerd/remotes" 12 "github.com/containerd/containerd/remotes/docker" 13 "github.com/containerd/log" 14 "github.com/distribution/reference" 15 "github.com/docker/buildx/util/resolver" 16 clitypes "github.com/docker/cli/cli/config/types" 17 "github.com/moby/buildkit/util/contentutil" 18 "github.com/moby/buildkit/util/tracing" 19 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 20 "github.com/sirupsen/logrus" 21 ) 22 23 type Auth interface { 24 GetAuthConfig(registryHostname string) (clitypes.AuthConfig, error) 25 } 26 27 type Opt struct { 28 Auth Auth 29 RegistryConfig map[string]resolver.RegistryConfig 30 } 31 32 type Resolver struct { 33 auth docker.Authorizer 34 hosts docker.RegistryHosts 35 buffer contentutil.Buffer 36 } 37 38 func New(opt Opt) *Resolver { 39 return &Resolver{ 40 auth: docker.NewDockerAuthorizer(docker.WithAuthCreds(toCredentialsFunc(opt.Auth)), docker.WithAuthClient(http.DefaultClient)), 41 hosts: resolver.NewRegistryConfig(opt.RegistryConfig), 42 buffer: contentutil.NewBuffer(), 43 } 44 } 45 46 func (r *Resolver) resolver() remotes.Resolver { 47 return docker.NewResolver(docker.ResolverOptions{ 48 Hosts: func(domain string) ([]docker.RegistryHost, error) { 49 res, err := r.hosts(domain) 50 if err != nil { 51 return nil, err 52 } 53 for i := range res { 54 res[i].Authorizer = r.auth 55 res[i].Client = tracing.DefaultClient 56 } 57 return res, nil 58 }, 59 }) 60 } 61 62 func (r *Resolver) Resolve(ctx context.Context, in string) (string, ocispec.Descriptor, error) { 63 // discard containerd logger to avoid printing unnecessary info during image reference resolution. 64 // https://github.com/containerd/containerd/blob/1a88cf5242445657258e0c744def5017d7cfb492/remotes/docker/resolver.go#L288 65 logger := logrus.New() 66 logger.Out = io.Discard 67 ctx = log.WithLogger(ctx, logrus.NewEntry(logger)) 68 69 ref, err := parseRef(in) 70 if err != nil { 71 return "", ocispec.Descriptor{}, err 72 } 73 74 in, desc, err := r.resolver().Resolve(ctx, ref.String()) 75 if err != nil { 76 return "", ocispec.Descriptor{}, err 77 } 78 79 return in, desc, nil 80 } 81 82 func (r *Resolver) Get(ctx context.Context, in string) ([]byte, ocispec.Descriptor, error) { 83 in, desc, err := r.Resolve(ctx, in) 84 if err != nil { 85 return nil, ocispec.Descriptor{}, err 86 } 87 88 dt, err := r.GetDescriptor(ctx, in, desc) 89 if err != nil { 90 return nil, ocispec.Descriptor{}, err 91 } 92 return dt, desc, nil 93 } 94 95 func (r *Resolver) GetDescriptor(ctx context.Context, in string, desc ocispec.Descriptor) ([]byte, error) { 96 fetcher, err := r.resolver().Fetcher(ctx, in) 97 if err != nil { 98 return nil, err 99 } 100 101 rc, err := fetcher.Fetch(ctx, desc) 102 if err != nil { 103 return nil, err 104 } 105 106 buf := &bytes.Buffer{} 107 _, err = io.Copy(buf, rc) 108 rc.Close() 109 if err != nil { 110 return nil, err 111 } 112 113 return buf.Bytes(), nil 114 } 115 116 func parseRef(s string) (reference.Named, error) { 117 ref, err := reference.ParseNormalizedNamed(s) 118 if err != nil { 119 return nil, err 120 } 121 ref = reference.TagNameOnly(ref) 122 return ref, nil 123 } 124 125 func toCredentialsFunc(a Auth) func(string) (string, string, error) { 126 return func(host string) (string, string, error) { 127 if host == "registry-1.docker.io" { 128 host = "https://index.docker.io/v1/" 129 } 130 ac, err := a.GetAuthConfig(host) 131 if err != nil { 132 return "", "", err 133 } 134 if ac.IdentityToken != "" { 135 return "", ac.IdentityToken, nil 136 } 137 return ac.Username, ac.Password, nil 138 } 139 } 140 141 func RegistryAuthForRef(ref string, a Auth) (string, error) { 142 if a == nil { 143 return "", nil 144 } 145 r, err := parseRef(ref) 146 if err != nil { 147 return "", err 148 } 149 host := reference.Domain(r) 150 if host == "docker.io" { 151 host = "https://index.docker.io/v1/" 152 } 153 ac, err := a.GetAuthConfig(host) 154 if err != nil { 155 return "", err 156 } 157 buf, err := json.Marshal(ac) 158 if err != nil { 159 return "", err 160 } 161 return base64.URLEncoding.EncodeToString(buf), nil 162 }