Skip to content

Commit f714904

Browse files
authored
feat(cli): Add ability to pull images from private registry (crossplane#6227)
Add method to get docker configfile, and use it to retrieve credentials to pull images from private registries Signed-off-by: Chuan-Yen Chiang <[email protected]>
1 parent c97099b commit f714904

File tree

6 files changed

+155
-43
lines changed

6 files changed

+155
-43
lines changed

apis/apiextensions/fn/proto/v1/run_function.pb.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apis/apiextensions/fn/proto/v1beta1/zz_generated_run_function.pb.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/crank/render/runtime_docker.go

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,18 @@ import (
2121
"fmt"
2222
"io"
2323
"net"
24+
"os"
2425

26+
"github.com/distribution/reference"
27+
"github.com/docker/cli/cli/config"
28+
"github.com/docker/cli/cli/config/configfile"
2529
"github.com/docker/docker/api/types/container"
2630
"github.com/docker/docker/api/types/filters"
2731
typesimage "github.com/docker/docker/api/types/image"
32+
registrytypes "github.com/docker/docker/api/types/registry"
2833
"github.com/docker/docker/client"
2934
"github.com/docker/docker/errdefs"
35+
"github.com/docker/docker/registry"
3036
"github.com/docker/go-connections/nat"
3137

3238
"github.com/crossplane/crossplane-runtime/pkg/errors"
@@ -106,6 +112,9 @@ type RuntimeDocker struct {
106112
// Cleanup controls how the containers are handled after rendering.
107113
Cleanup DockerCleanup
108114

115+
// ConfigFile contains information like credentials for each registry, default to ~/.docker/config.json
116+
ConfigFile *configfile.ConfigFile
117+
109118
// PullPolicy controls how the runtime image is pulled.
110119
PullPolicy DockerPullPolicy
111120

@@ -152,13 +161,19 @@ func GetRuntimeDocker(fn pkgv1.Function, log logging.Logger) (*RuntimeDocker, er
152161
if err != nil {
153162
return nil, errors.Wrapf(err, "cannot get pull policy for Function %q", fn.GetName())
154163
}
164+
165+
// Initial ConfigFile
166+
configFile := config.LoadDefaultConfigFile(os.Stderr)
167+
155168
r := &RuntimeDocker{
156169
Image: fn.Spec.Package,
157170
Name: "",
158171
Cleanup: cleanup,
172+
ConfigFile: configFile,
159173
PullPolicy: pullPolicy,
160174
log: log,
161175
}
176+
162177
if i := fn.GetAnnotations()[AnnotationKeyRuntimeDockerImage]; i != "" {
163178
r.Image = i
164179
}
@@ -244,9 +259,16 @@ func (r *RuntimeDocker) createContainer(ctx context.Context, cli *client.Client)
244259
PortBindings: bind,
245260
}
246261

262+
options, err := r.getPullOptions()
263+
if err != nil {
264+
// We can continue to pull an image if we don't have the PullOptions with RegistryAuth
265+
// as long as the image is from a public registry. Therefore, we log the error message and continue.
266+
r.log.Info("Cannot get pull options", "image", r.Image, "err", err)
267+
}
268+
247269
if r.PullPolicy == AnnotationValueRuntimeDockerPullPolicyAlways {
248270
r.log.Debug("Pulling image with pullPolicy: Always", "image", r.Image)
249-
err = PullImage(ctx, cli, r.Image)
271+
err = PullImage(ctx, cli, r.Image, options)
250272
if err != nil {
251273
return "", "", errors.Wrapf(err, "cannot pull Docker image %q", r.Image)
252274
}
@@ -262,7 +284,7 @@ func (r *RuntimeDocker) createContainer(ctx context.Context, cli *client.Client)
262284

263285
// The image was not found, but we're allowed to pull it.
264286
r.log.Debug("Image not found, pulling", "image", r.Image)
265-
err = PullImage(ctx, cli, r.Image)
287+
err = PullImage(ctx, cli, r.Image, options)
266288
if err != nil {
267289
return "", "", errors.Wrapf(err, "cannot pull Docker image %q", r.Image)
268290
}
@@ -278,6 +300,37 @@ func (r *RuntimeDocker) createContainer(ctx context.Context, cli *client.Client)
278300
return rsp.ID, containerAddr, errors.Wrap(err, "cannot start Docker container")
279301
}
280302

303+
func (r *RuntimeDocker) getPullOptions() (typesimage.PullOptions, error) {
304+
// Resolve auth token by looking into config file
305+
named, err := reference.ParseNormalizedNamed(r.Image)
306+
if err != nil {
307+
return typesimage.PullOptions{}, errors.Wrapf(err, "Image is not a valid reference %s", r.Image)
308+
}
309+
310+
repoInfo, err := registry.ParseRepositoryInfo(named)
311+
if err != nil {
312+
return typesimage.PullOptions{}, errors.Wrapf(err, "Cannot parse repository info: %s", named.String())
313+
}
314+
315+
configKey := repoInfo.Index.Name
316+
if repoInfo.Index.Official {
317+
configKey = registry.IndexServer
318+
}
319+
authConfig, err := r.ConfigFile.GetAuthConfig(configKey)
320+
if err != nil {
321+
return typesimage.PullOptions{}, errors.Wrapf(err, "Cannot get auth config info with configKey: %s", configKey)
322+
}
323+
324+
encodedAuth, err := registrytypes.EncodeAuthConfig(registrytypes.AuthConfig(authConfig))
325+
if err != nil {
326+
return typesimage.PullOptions{}, errors.Wrapf(err, "Cannot encode auth config with configKey: %s", configKey)
327+
}
328+
329+
return typesimage.PullOptions{
330+
RegistryAuth: encodedAuth,
331+
}, nil
332+
}
333+
281334
// Start a Function as a Docker container.
282335
func (r *RuntimeDocker) Start(ctx context.Context) (RuntimeContext, error) {
283336
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
@@ -327,8 +380,8 @@ type pullClient interface {
327380

328381
// PullImage pulls the supplied image using the supplied client. It blocks until
329382
// the image has either finished pulling or hit an error.
330-
func PullImage(ctx context.Context, p pullClient, image string) error {
331-
out, err := p.ImagePull(ctx, image, typesimage.PullOptions{})
383+
func PullImage(ctx context.Context, p pullClient, image string, options typesimage.PullOptions) error {
384+
out, err := p.ImagePull(ctx, image, options)
332385
if err != nil {
333386
return err
334387
}

cmd/crank/render/runtime_docker_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ package render
1919
import (
2020
"context"
2121
"io"
22+
"os"
2223
"testing"
2324

25+
"github.com/docker/cli/cli/config"
2426
"github.com/docker/docker/api/types/image"
2527
"github.com/google/go-cmp/cmp"
2628
"github.com/google/go-cmp/cmp/cmpopts"
@@ -76,6 +78,7 @@ func TestGetRuntimeDocker(t *testing.T) {
7678
want: want{
7779
rd: &RuntimeDocker{
7880
Image: "test-image-from-annotation",
81+
ConfigFile: config.LoadDefaultConfigFile(os.Stderr),
7982
Cleanup: AnnotationValueRuntimeDockerCleanupOrphan,
8083
PullPolicy: AnnotationValueRuntimeDockerPullPolicyAlways,
8184
},
@@ -102,6 +105,7 @@ func TestGetRuntimeDocker(t *testing.T) {
102105
want: want{
103106
rd: &RuntimeDocker{
104107
Image: "test-image-from-annotation",
108+
ConfigFile: config.LoadDefaultConfigFile(os.Stderr),
105109
Cleanup: AnnotationValueRuntimeDockerCleanupOrphan,
106110
Name: "test-container-name-function",
107111
PullPolicy: AnnotationValueRuntimeDockerPullPolicyIfNotPresent,
@@ -125,6 +129,7 @@ func TestGetRuntimeDocker(t *testing.T) {
125129
want: want{
126130
rd: &RuntimeDocker{
127131
Image: "test-package",
132+
ConfigFile: config.LoadDefaultConfigFile(os.Stderr),
128133
Cleanup: AnnotationValueRuntimeDockerCleanupRemove,
129134
PullPolicy: AnnotationValueRuntimeDockerPullPolicyIfNotPresent,
130135
},
@@ -189,6 +194,7 @@ func TestGetRuntimeDocker(t *testing.T) {
189194
want: want{
190195
rd: &RuntimeDocker{
191196
Image: "test-package",
197+
ConfigFile: config.LoadDefaultConfigFile(os.Stderr),
192198
Cleanup: AnnotationValueRuntimeDockerCleanupStop,
193199
PullPolicy: AnnotationValueRuntimeDockerPullPolicyIfNotPresent,
194200
},

go.mod

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ require (
88
github.com/Masterminds/semver v1.5.0
99
github.com/alecthomas/kong v0.9.0
1010
github.com/crossplane/crossplane-runtime v1.19.0-rc.0.0.20241105071456-19d95a69cc03
11+
github.com/distribution/reference v0.5.0
1112
github.com/docker/docker v27.1.1+incompatible
1213
github.com/docker/go-connections v0.5.0
1314
github.com/emicklei/dot v1.6.2
@@ -26,9 +27,9 @@ require (
2627
github.com/spf13/afero v1.11.0
2728
github.com/upbound/up-sdk-go v0.1.1-0.20240122203953-2d00664aab8e
2829
golang.org/x/sync v0.10.0
29-
google.golang.org/grpc v1.67.1
30+
google.golang.org/grpc v1.68.0
3031
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0
31-
google.golang.org/protobuf v1.35.1
32+
google.golang.org/protobuf v1.35.2
3233
k8s.io/api v0.31.2
3334
k8s.io/apiextensions-apiserver v0.31.2
3435
k8s.io/apimachinery v0.31.2
@@ -63,7 +64,8 @@ require (
6364
github.com/cyphar/filepath-securejoin v0.2.5 // indirect
6465
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect
6566
github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect
66-
github.com/distribution/reference v0.5.0 // indirect
67+
github.com/docker/go-metrics v0.0.1 // indirect
68+
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
6769
github.com/dustin/go-humanize v1.0.1 // indirect
6870
github.com/emirpasic/gods v1.18.1 // indirect
6971
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
@@ -87,9 +89,9 @@ require (
8789
github.com/google/certificate-transparency-go v1.2.1 // indirect
8890
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
8991
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
92+
github.com/gorilla/mux v1.8.1 // indirect
9093
github.com/gorilla/websocket v1.5.1 // indirect
9194
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
92-
github.com/grpc-ecosystem/grpc-gateway/v2 v2.21.0 // indirect
9395
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
9496
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 // indirect
9597
github.com/hashicorp/go-sockaddr v1.0.6 // indirect
@@ -139,11 +141,14 @@ require (
139141
github.com/xanzy/ssh-agent v0.3.3 // indirect
140142
github.com/xlab/treeprint v1.2.0 // indirect
141143
go.mongodb.org/mongo-driver v1.14.0 // indirect
144+
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
142145
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
143146
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.26.0 // indirect
147+
go.opentelemetry.io/otel/sdk v1.33.0 // indirect
148+
go.opentelemetry.io/proto/otlp v1.4.0 // indirect
144149
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
145150
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect
146-
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect
151+
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect
147152
gopkg.in/ini.v1 v1.67.0 // indirect
148153
gopkg.in/warnings.v0 v0.1.2 // indirect
149154
k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 // indirect
@@ -187,7 +192,7 @@ require (
187192
github.com/dave/jennifer v1.6.0 // indirect
188193
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
189194
github.com/dimchansky/utfbom v1.1.1 // indirect
190-
github.com/docker/cli v24.0.7+incompatible // indirect
195+
github.com/docker/cli v27.4.1+incompatible
191196
github.com/docker/distribution v2.8.3+incompatible // indirect
192197
github.com/docker/docker-credential-helpers v0.8.2
193198
github.com/docker/go-units v0.5.0 // indirect
@@ -235,22 +240,22 @@ require (
235240
github.com/spf13/pflag v1.0.5 // indirect
236241
github.com/vbatts/tar-split v0.11.5 // indirect
237242
github.com/vladimirvivien/gexe v0.3.0 // indirect
238-
go.opentelemetry.io/otel v1.28.0 // indirect
239-
go.opentelemetry.io/otel/metric v1.28.0 // indirect
240-
go.opentelemetry.io/otel/trace v1.28.0 // indirect
243+
go.opentelemetry.io/otel v1.33.0 // indirect
244+
go.opentelemetry.io/otel/metric v1.33.0 // indirect
245+
go.opentelemetry.io/otel/trace v1.33.0 // indirect
241246
go.uber.org/multierr v1.11.0 // indirect
242247
go.uber.org/zap v1.27.0 // indirect
243248
golang.org/x/crypto v0.31.0 // indirect
244249
golang.org/x/mod v0.21.0 // indirect
245250
golang.org/x/net v0.33.0
246-
golang.org/x/oauth2 v0.22.0 // indirect
251+
golang.org/x/oauth2 v0.24.0 // indirect
247252
golang.org/x/sys v0.28.0 // indirect
248253
golang.org/x/term v0.27.0
249254
golang.org/x/text v0.21.0 // indirect
250255
golang.org/x/time v0.6.0 // indirect
251256
golang.org/x/tools v0.26.0 // indirect
252257
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
253-
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect
258+
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect
254259
gopkg.in/inf.v0 v0.9.1 // indirect
255260
gopkg.in/yaml.v2 v2.4.0 // indirect
256261
gopkg.in/yaml.v3 v3.0.1 // indirect

0 commit comments

Comments
 (0)