Fix ECR auth
This commit is contained in:
1
examples/tests/ecr/.dockerignore
Normal file
1
examples/tests/ecr/.dockerignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*
|
||||||
36
examples/tests/ecr/Pulumi.yaml
Normal file
36
examples/tests/ecr/Pulumi.yaml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
name: ecr
|
||||||
|
description: Push to AWS ECR with caching
|
||||||
|
outputs:
|
||||||
|
ref: ${my-image.ref}
|
||||||
|
resources:
|
||||||
|
ecr-repository:
|
||||||
|
type: aws:ecr:Repository
|
||||||
|
properties:
|
||||||
|
forceDelete: true
|
||||||
|
my-image:
|
||||||
|
type: dockerbuild:Image
|
||||||
|
properties:
|
||||||
|
tags:
|
||||||
|
- ${ecr-repository.repositoryUrl}:tag-name
|
||||||
|
push: true
|
||||||
|
context:
|
||||||
|
location: .
|
||||||
|
dockerfile:
|
||||||
|
inline: FROM alpine
|
||||||
|
cacheFrom:
|
||||||
|
- registry:
|
||||||
|
ref: ${ecr-repository.repositoryUrl}:cache
|
||||||
|
cacheTo:
|
||||||
|
- registry:
|
||||||
|
ref: ${ecr-repository.repositoryUrl}:cache
|
||||||
|
imageManifest: true
|
||||||
|
ociMediaTypes: true
|
||||||
|
registries:
|
||||||
|
- username: ${auth-token.userName}
|
||||||
|
password: ${auth-token.password}
|
||||||
|
address: ${ecr-repository.repositoryUrl}
|
||||||
|
runtime: yaml
|
||||||
|
variables:
|
||||||
|
auth-token:
|
||||||
|
fn::aws:ecr:getAuthorizationToken:
|
||||||
|
registryId: ${ecr-repository.registryId}
|
||||||
@@ -25,3 +25,18 @@ func TestYAMLExample(t *testing.T) {
|
|||||||
|
|
||||||
integration.ProgramTest(t, &test)
|
integration.ProgramTest(t, &test)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestECR(t *testing.T) {
|
||||||
|
if os.Getenv("AWS_SESSION_TOKEN") == "" {
|
||||||
|
t.Skip("Missing AWS credentials")
|
||||||
|
}
|
||||||
|
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
test := integration.ProgramTestOptions{
|
||||||
|
Dir: path.Join(cwd, "tests/ecr"),
|
||||||
|
}
|
||||||
|
|
||||||
|
integration.ProgramTest(t, &test)
|
||||||
|
}
|
||||||
|
|||||||
4
go.mod
4
go.mod
@@ -23,9 +23,9 @@ require (
|
|||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/spf13/afero v1.11.0
|
github.com/spf13/afero v1.11.0
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
github.com/theupdateframework/notary v0.7.0
|
|
||||||
github.com/tonistiigi/fsutil v0.0.0-20240301111122-7525a1af2bb5
|
github.com/tonistiigi/fsutil v0.0.0-20240301111122-7525a1af2bb5
|
||||||
go.uber.org/mock v0.3.0
|
go.uber.org/mock v0.3.0
|
||||||
|
golang.org/x/crypto v0.21.0
|
||||||
google.golang.org/protobuf v1.33.0
|
google.golang.org/protobuf v1.33.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
@@ -392,6 +392,7 @@ require (
|
|||||||
github.com/tdakkota/asciicheck v0.2.0 // indirect
|
github.com/tdakkota/asciicheck v0.2.0 // indirect
|
||||||
github.com/tetafro/godot v1.4.16 // indirect
|
github.com/tetafro/godot v1.4.16 // indirect
|
||||||
github.com/texttheater/golang-levenshtein v1.0.1 // indirect
|
github.com/texttheater/golang-levenshtein v1.0.1 // indirect
|
||||||
|
github.com/theupdateframework/notary v0.7.0 // indirect
|
||||||
github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 // indirect
|
github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 // indirect
|
||||||
github.com/timonwong/loggercheck v0.9.4 // indirect
|
github.com/timonwong/loggercheck v0.9.4 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||||
@@ -446,7 +447,6 @@ require (
|
|||||||
go.uber.org/zap v1.26.0 // indirect
|
go.uber.org/zap v1.26.0 // indirect
|
||||||
gocloud.dev v0.36.0 // indirect
|
gocloud.dev v0.36.0 // indirect
|
||||||
gocloud.dev/secrets/hashivault v0.28.0 // indirect
|
gocloud.dev/secrets/hashivault v0.28.0 // indirect
|
||||||
golang.org/x/crypto v0.21.0 // indirect
|
|
||||||
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect
|
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect
|
||||||
golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f // indirect
|
golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f // indirect
|
||||||
golang.org/x/mod v0.16.0 // indirect
|
golang.org/x/mod v0.16.0 // indirect
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import (
|
|||||||
"github.com/docker/buildx/commands"
|
"github.com/docker/buildx/commands"
|
||||||
"github.com/docker/cli/cli-plugins/manager"
|
"github.com/docker/cli/cli-plugins/manager"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
|
"github.com/docker/cli/cli/config/credentials"
|
||||||
cfgtypes "github.com/docker/cli/cli/config/types"
|
cfgtypes "github.com/docker/cli/cli/config/types"
|
||||||
"github.com/docker/cli/cli/streams"
|
"github.com/docker/cli/cli/streams"
|
||||||
"github.com/moby/buildkit/client"
|
"github.com/moby/buildkit/client"
|
||||||
@@ -66,7 +67,8 @@ type Cli interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// wrap creates a new cli client with auth configs layered on top of our host's
|
// wrap creates a new cli client with auth configs layered on top of our host's
|
||||||
// auth.
|
// auth. Repeated auth for the same host will take precedence over earlier
|
||||||
|
// credentials.
|
||||||
func wrap(host *host, registries ...Registry) (*cli, error) {
|
func wrap(host *host, registries ...Registry) (*cli, error) {
|
||||||
// We need to create a new DockerCLI instance because we don't want the
|
// We need to create a new DockerCLI instance because we don't want the
|
||||||
// auth changes we make to the ConfigFile to leak to the host.
|
// auth changes we make to the ConfigFile to leak to the host.
|
||||||
@@ -81,10 +83,17 @@ func wrap(host *host, registries ...Registry) (*cli, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, r := range registries {
|
for _, r := range registries {
|
||||||
// HostNewName takes care of DockerHub's special-casing for us.
|
// Special handling for legacy DockerHub domains. The OCI-compliant
|
||||||
h := config.HostNewName(r.Address)
|
// registry is registry-1.docker.io but this is stored in config under the
|
||||||
auths[h.CredHost] = cfgtypes.AuthConfig{
|
// legacy name.
|
||||||
ServerAddress: h.Hostname,
|
// https://github.com/docker/cli/issues/3793#issuecomment-1269051403
|
||||||
|
key := credentials.ConvertToHostname(r.Address)
|
||||||
|
if key == "registry-1.docker.io" || key == "index.docker.io" || key == "docker.io" {
|
||||||
|
key = "https://index.docker.io/v1/"
|
||||||
|
}
|
||||||
|
|
||||||
|
auths[key] = cfgtypes.AuthConfig{
|
||||||
|
ServerAddress: r.Address,
|
||||||
Username: r.Username,
|
Username: r.Username,
|
||||||
Password: r.Password,
|
Password: r.Password,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -338,16 +338,20 @@ func (is *ImageState) Annotate(a infer.Annotator) {
|
|||||||
|
|
||||||
// client produces a CLI client with scoped to this resource and layered on top
|
// client produces a CLI client with scoped to this resource and layered on top
|
||||||
// of any host-level credentials.
|
// of any host-level credentials.
|
||||||
func (i *Image) client(ctx provider.Context, state ImageState, args ImageArgs) (Client, error) {
|
func (i *Image) client(pctx provider.Context, state ImageState, args ImageArgs) (Client, error) {
|
||||||
cfg := infer.GetConfig[Config](ctx)
|
ctx := context.Context(pctx)
|
||||||
|
|
||||||
|
cfg := infer.GetConfig[Config](pctx)
|
||||||
|
|
||||||
if cli, ok := ctx.Value(_mockClientKey).(Client); ok {
|
if cli, ok := ctx.Value(_mockClientKey).(Client); ok {
|
||||||
return cli, nil
|
return cli, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Layer auth from args, state, and the provider in that order.
|
// We prefer auth from args, the provider, and state in that order. We
|
||||||
auths := cfg.Registries
|
// build a slice in reverse order because wrap() will overwrite earlier
|
||||||
auths = append(auths, state.Registries...)
|
// entries with later ones.
|
||||||
|
auths := state.Registries
|
||||||
|
auths = append(auths, cfg.Registries...)
|
||||||
auths = append(auths, args.Registries...)
|
auths = append(auths, args.Registries...)
|
||||||
|
|
||||||
return wrap(cfg.host, auths...)
|
return wrap(cfg.host, auths...)
|
||||||
|
|||||||
@@ -298,8 +298,12 @@ func (i *Index) client(
|
|||||||
return cli, nil
|
return cli, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
auths := cfg.Registries
|
// We prefer auth from args, the provider, and state in that order. We
|
||||||
auths = append(auths, state.Registry, args.Registry)
|
// build a slice in reverse order because wrap() will overwrite earlier
|
||||||
|
// entries with later ones.
|
||||||
|
auths := []Registry{state.Registry}
|
||||||
|
auths = append(auths, cfg.Registries...)
|
||||||
|
auths = append(auths, args.Registry)
|
||||||
|
|
||||||
return wrap(cfg.host, auths...)
|
return wrap(cfg.host, auths...)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user