Skip to content

Commit

Permalink
internal fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
blampe committed Jan 22, 2024
1 parent cfba635 commit 9c79852
Show file tree
Hide file tree
Showing 28 changed files with 1,584 additions and 86 deletions.
83 changes: 80 additions & 3 deletions provider/cmd/pulumi-resource-docker/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,51 @@
"types": {
"docker:buildx/image:Manifest": {
"properties": {
"digest": {
"type": "string"
},
"platform": {
"$ref": "#/types/docker:buildx/image:Platform"
},
"ref": {
"type": "string",
"description": "The manifest's ref"
},
"size": {
"type": "integer"
},
"urls": {
"type": "array",
"items": {
"type": "string"
}
}
},
"type": "object",
"required": [
"ref"
"digest",
"platform",
"ref",
"size",
"urls"
]
},
"docker:buildx/image:ProviderRegistryAuth": {
"docker:buildx/image:Platform": {
"properties": {
"architecture": {
"type": "string"
},
"os": {
"type": "string"
}
},
"type": "object",
"required": [
"os",
"architecture"
]
},
"docker:buildx/image:RegistryAuth": {
"properties": {
"address": {
"type": "string",
Expand Down Expand Up @@ -2071,7 +2105,21 @@
"docker:buildx/image:Image": {
"description": "A Docker image built using Buildkit",
"properties": {
"buildArgs": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "\nAn optional map of named build-time argument variables to set during\nthe Docker build. This flag allows you to pass build-time variables that\ncan be accessed like environment variables inside the RUN\ninstruction."
},
"cacheFrom": {
"type": "array",
"items": {
"type": "string"
},
"description": "\nExternal cache sources (e.g., \"user/app:cache\", \"type=local,src=path/to/dir\")"
},
"cacheTo": {
"type": "array",
"items": {
"type": "string"
Expand Down Expand Up @@ -2111,6 +2159,13 @@
"type": "boolean",
"description": "\nAlways attempt to pull all referenced images"
},
"registries": {
"type": "array",
"items": {
"$ref": "#/types/docker:buildx/image:RegistryAuth"
},
"description": "\nLogins for registry outputs"
},
"tags": {
"type": "array",
"items": {
Expand All @@ -2120,10 +2175,25 @@
}
},
"required": [
"tags"
"tags",
"manifests"
],
"inputProperties": {
"buildArgs": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "\nAn optional map of named build-time argument variables to set during\nthe Docker build. This flag allows you to pass build-time variables that\ncan be accessed like environment variables inside the RUN\ninstruction."
},
"cacheFrom": {
"type": "array",
"items": {
"type": "string"
},
"description": "\nExternal cache sources (e.g., \"user/app:cache\", \"type=local,src=path/to/dir\")"
},
"cacheTo": {
"type": "array",
"items": {
"type": "string"
Expand Down Expand Up @@ -2157,6 +2227,13 @@
"type": "boolean",
"description": "\nAlways attempt to pull all referenced images"
},
"registries": {
"type": "array",
"items": {
"$ref": "#/types/docker:buildx/image:RegistryAuth"
},
"description": "\nLogins for registry outputs"
},
"tags": {
"type": "array",
"items": {
Expand Down
6 changes: 3 additions & 3 deletions provider/internal/buildx.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ import (
var (
_ infer.CustomConfigure = (*Config)(nil)
_ infer.Annotated = (infer.Annotated)((*Config)(nil))
_ infer.Annotated = (infer.Annotated)((*properties.ProviderRegistryAuth)(nil))
_ infer.Annotated = (infer.Annotated)((*properties.RegistryAuth)(nil))
)

// Config configures the buildx provider.
type Config struct {
Host string `pulumi:"host,optional"`
RegistryAuth []properties.ProviderRegistryAuth `pulumi:"registryAuth,optional"`
Host string `pulumi:"host,optional"`
RegistryAuth []properties.RegistryAuth `pulumi:"registryAuth,optional"`

client Client
}
Expand Down
15 changes: 12 additions & 3 deletions provider/internal/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (

// Client handles all our Docker API calls.
type Client interface {
Auth(ctx context.Context, creds properties.ProviderRegistryAuth) error
Auth(ctx context.Context, creds properties.RegistryAuth) error
Build(ctx context.Context, opts controllerapi.BuildOptions) (*client.SolveResponse, error)
BuildKitEnabled() (bool, error)
Inspect(ctx context.Context, id string) ([]manifesttypes.ImageManifest, error)
Expand Down Expand Up @@ -52,7 +52,7 @@ func newDockerClient() (*docker, error) {
return &docker{cli: cli}, err
}

func (d *docker) Auth(ctx context.Context, creds properties.ProviderRegistryAuth) error {
func (d *docker) Auth(_ context.Context, creds properties.RegistryAuth) error {
cfg := d.cli.ConfigFile()

// Special handling for legacy DockerHub domains. The OCI-compliant
Expand All @@ -69,7 +69,16 @@ func (d *docker) Auth(ctx context.Context, creds properties.ProviderRegistryAuth
Password: creds.Password,
}

err := cfg.GetCredentialsStore(creds.Address).Store(auth)
// Workaround for https://github.com/docker/docker-credential-helpers/issues/37.
all, err := cfg.GetAllCredentials()
if err != nil {
return fmt.Errorf("getting credentials: %w", err)
}
if _, ok := all[creds.Address]; ok {
return nil // Already logged in.
}

err = cfg.GetCredentialsStore(creds.Address).Store(auth)
if err != nil {
return fmt.Errorf("storing auth: %w", err)
}
Expand Down
17 changes: 15 additions & 2 deletions provider/internal/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@ func TestAuth(t *testing.T) {
user = u
}
password := os.Getenv("DOCKER_HUB_PASSWORD")
host := "pulumi.com" // Fake host -- we don't actually hit it.

err = d.Auth(context.Background(), properties.ProviderRegistryAuth{
Address: "docker.io",
t.Cleanup(func() {
_ = d.cli.ConfigFile().GetCredentialsStore(host).Erase(host)
})

err = d.Auth(context.Background(), properties.RegistryAuth{
Address: host,
Username: user,
Password: password,
})
Expand All @@ -41,6 +46,14 @@ func TestBuild(t *testing.T) {
assert.NoError(t, err)
}

func TestBuildkitEnabled(t *testing.T) {
d, err := newDockerClient()
require.NoError(t, err)
ok, err := d.BuildKitEnabled()
assert.NoError(t, err)
assert.True(t, ok)
}

func TestInspect(t *testing.T) {
d, err := newDockerClient()
require.NoError(t, err)
Expand Down
75 changes: 59 additions & 16 deletions provider/internal/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
_ "github.com/docker/buildx/driver/kubernetes"
_ "github.com/docker/buildx/util/buildflags"

"github.com/distribution/reference"
controllerapi "github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/util/buildflags"
"github.com/docker/buildx/util/platformutil"
Expand Down Expand Up @@ -45,18 +46,26 @@ func (i *Image) Annotate(a infer.Annotator) {

// ImageArgs instantiates a new Image.
type ImageArgs struct {
CacheFrom []string `pulumi:"cacheFrom,optional"`
CacheTo []string `pulumi:"cacheTo,optional"`
Context string `pulumi:"context,optional"`
Exports []string `pulumi:"exports,optional"`
File string `pulumi:"file,optional"`
Platforms []string `pulumi:"platforms,optional"`
Pull bool `pulumi:"pull,optional"`
Tags []string `pulumi:"tags"`
BuildArgs map[string]string `pulumi:"buildArgs,optional"`
CacheFrom []string `pulumi:"cacheFrom,optional"`
CacheTo []string `pulumi:"cacheTo,optional"`
Context string `pulumi:"context,optional"`
Exports []string `pulumi:"exports,optional"`
File string `pulumi:"file,optional"`
Platforms []string `pulumi:"platforms,optional"`
Pull bool `pulumi:"pull,optional"`
Registries []properties.RegistryAuth `pulumi:"registries,optional"`
Tags []string `pulumi:"tags"`
}

// Annotate describes inputs to the Image resource.
func (ia *ImageArgs) Annotate(a infer.Annotator) {
a.Describe(&ia.BuildArgs, dedent.String(`
An optional map of named build-time argument variables to set during
the Docker build. This flag allows you to pass build-time variables that
can be accessed like environment variables inside the RUN
instruction.`,
))
a.Describe(&ia.CacheFrom, dedent.String(`
External cache sources (e.g., "user/app:cache", "type=local,src=path/to/dir")`,
))
Expand Down Expand Up @@ -85,6 +94,9 @@ func (ia *ImageArgs) Annotate(a infer.Annotator) {
Name and optionally a tag (format: "name:tag"). If outputting to a
registry, the name should include the fully qualified registry address.`,
))
a.Describe(&ia.Registries, dedent.String(`
Logins for registry outputs`,
))

a.SetDefault(&ia.File, "Dockerfile")
}
Expand All @@ -93,17 +105,18 @@ func (ia *ImageArgs) Annotate(a infer.Annotator) {
type ImageState struct {
ImageArgs

Manifests []properties.Manifest `pulumi:"manifests,optional" provider:"output"`
Manifests []properties.Manifest `pulumi:"manifests" provider:"output"`
}

// Annotate describes outputs of the Image resource.
func (is *ImageState) Annotate(a infer.Annotator) {
is.ImageArgs.Annotate(a)
}

// Check validates ImageArgs and sets defaults.
// Check validates ImageArgs, sets defaults, and ensures our client is
// authenticated.
func (*Image) Check(
_ provider.Context,
ctx provider.Context,
_ string,
_ resource.PropertyMap,
news resource.PropertyMap,
Expand Down Expand Up @@ -131,6 +144,16 @@ func (*Image) Check(
}
}

// Check is called before every operation except Read, so this ensures
// we're authenticated in almost all cases.
cfg := infer.GetConfig[Config](ctx)
for _, reg := range args.Registries {
if err = cfg.client.Auth(ctx, reg); err != nil {
failures = append(failures,
provider.CheckFailure{Property: "registries", Reason: fmt.Sprintf("unable to authenticate: %s", err.Error())})
}
}

return args, failures, err
}

Expand Down Expand Up @@ -168,7 +191,14 @@ func (ia *ImageArgs) toBuildOptions() (controllerapi.BuildOptions, error) {
multierr = errors.Join(multierr, newCheckFailure("cacheTo", err))
}

for _, t := range ia.Tags {
if _, err := reference.Parse(t); err != nil {
multierr = errors.Join(multierr, newCheckFailure("tags", err))
}
}

opts := controllerapi.BuildOptions{
BuildArgs: ia.BuildArgs,
CacheFrom: cacheFrom,
CacheTo: cacheTo,
ContextPath: ia.Context,
Expand Down Expand Up @@ -247,13 +277,19 @@ func (*Image) Read(
ImageState, // normalized state
error,
) {
cfg := infer.GetConfig[Config](ctx)

opts, err := input.toBuildOptions()
if err != nil {
return id, input, state, err
}

// Ensure we're authenticated.
cfg := infer.GetConfig[Config](ctx)
for _, reg := range input.Registries {
if err = cfg.client.Auth(ctx, reg); err != nil {
return id, input, state, err
}
}

manifests := []properties.Manifest{}
for _, export := range opts.Exports {
switch export.GetType() {
Expand All @@ -263,11 +299,11 @@ func (*Image) Read(
continue
}
for _, tag := range input.Tags {
inspected, err := cfg.client.Inspect(ctx, tag)
infos, err := cfg.client.Inspect(ctx, tag)
if err != nil {
continue
}
for _, m := range inspected {
for _, m := range infos {
if m.Descriptor.Platform != nil && m.Descriptor.Platform.Architecture == "unknown" {
// Ignore cache manifests.
continue
Expand All @@ -277,7 +313,14 @@ func (*Image) Read(
continue
}
manifests = append(manifests, properties.Manifest{
Ref: m.Ref.String(),
Digest: m.Descriptor.Digest.String(),
Platform: properties.Platform{
OS: m.Descriptor.Platform.OS,
Architecture: m.Descriptor.Platform.Architecture,
},
Ref: m.Ref.String(),
Size: m.Descriptor.Size,
URLs: m.Descriptor.URLs,
})
}
}
Expand Down
Loading

0 comments on commit 9c79852

Please sign in to comment.