blob: 9a9cc84c89fd1a749f608003fa2fc50afc0b6abf [file] [log] [blame]
Serge Bazanskibe538db2020-11-12 00:22:42 +01001// Copyright 2017 The kubecfg authors
2//
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16package utils
17
18import (
19 "bytes"
20 "context"
21 "fmt"
22
23 "github.com/genuinetools/reg/registry"
24 "github.com/genuinetools/reg/repoutils"
25)
26
27const defaultRegistry = "registry-1.docker.io"
28
29// ImageName represents the parts of a docker image name
30type ImageName struct {
31 // eg: "myregistryhost:5000/fedora/httpd:version1.0"
32 Registry string // "myregistryhost:5000"
33 Repository string // "fedora"
34 Name string // "httpd"
35 Tag string // "version1.0"
36 Digest string
37}
38
39// String implements the Stringer interface
40func (n ImageName) String() string {
41 buf := bytes.Buffer{}
42 if n.Registry != "" {
43 buf.WriteString(n.Registry)
44 buf.WriteString("/")
45 }
46 if n.Repository != "" {
47 buf.WriteString(n.Repository)
48 buf.WriteString("/")
49 }
50 buf.WriteString(n.Name)
51 if n.Digest != "" {
52 buf.WriteString("@")
53 buf.WriteString(n.Digest)
54 } else {
55 buf.WriteString(":")
56 buf.WriteString(n.Tag)
57 }
58 return buf.String()
59}
60
61// RegistryRepoName returns the "repository" as used in the registry URL
62func (n ImageName) RegistryRepoName() string {
63 repo := n.Repository
64 if repo == "" {
65 repo = "library"
66 }
67 return fmt.Sprintf("%s/%s", repo, n.Name)
68}
69
70// RegistryURL returns the deduced base URL of the registry for this image
71func (n ImageName) RegistryURL() string {
72 reg := n.Registry
73 if reg == "" {
74 reg = defaultRegistry
75 }
76 return fmt.Sprintf("https://%s", reg)
77}
78
79// ParseImageName parses a docker image into an ImageName struct.
80func ParseImageName(image string) (ImageName, error) {
81 ret := ImageName{}
82
83 img, err := registry.ParseImage(image)
84 if err != nil {
85 return ret, err
86 }
87
88 ret.Registry = img.Domain
89 ret.Name = img.Path
90 ret.Digest = img.Digest.String()
91 ret.Tag = img.Tag
92
93 return ret, nil
94}
95
96// Resolver is able to resolve docker image names into more specific forms
97type Resolver interface {
98 Resolve(image *ImageName) error
99}
100
101// NewIdentityResolver returns a resolver that does only trivial
102// :latest canonicalisation
103func NewIdentityResolver() Resolver {
104 return identityResolver{}
105}
106
107type identityResolver struct{}
108
109func (r identityResolver) Resolve(image *ImageName) error {
110 return nil
111}
112
113// NewRegistryResolver returns a resolver that looks up a docker
114// registry to resolve digests
115func NewRegistryResolver(opt registry.Opt) Resolver {
116 return &registryResolver{
117 opt: opt,
118 cache: make(map[string]string),
119 }
120}
121
122type registryResolver struct {
123 opt registry.Opt
124 cache map[string]string
125}
126
127func (r *registryResolver) Resolve(n *ImageName) error {
128 // TODO: get context from caller.
129 ctx := context.Background()
130
131 if n.Digest != "" {
132 // Already has explicit digest
133 return nil
134 }
135
136 if digest, ok := r.cache[n.String()]; ok {
137 n.Digest = digest
138 return nil
139 }
140
141 img, err := registry.ParseImage(n.String())
142 if err != nil {
143 return fmt.Errorf("unable to parse image name: %v", err)
144 }
145
146 auth, err := repoutils.GetAuthConfig("", "", img.Domain)
147 if err != nil {
148 return fmt.Errorf("unable to get auth config for registry: %v", err)
149 }
150
151 c, err := registry.New(ctx, auth, r.opt)
152 if err != nil {
153 return fmt.Errorf("unable to create registry client: %v", err)
154 }
155
156 digest, err := c.Digest(ctx, img)
157 if err != nil {
158 return fmt.Errorf("unable to get digest from the registry: %v", err)
159 }
160
161 n.Digest = digest.String()
162 r.cache[n.String()] = n.Digest
163
164 return nil
165}