Skip to content

Commit

Permalink
buildx.Image MVP
Browse files Browse the repository at this point in the history
  • Loading branch information
blampe committed Jan 18, 2024
1 parent 2eaa243 commit e3f225b
Show file tree
Hide file tree
Showing 14 changed files with 711 additions and 29 deletions.
21 changes: 21 additions & 0 deletions provider/cmd/pulumi-resource-docker/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2038,6 +2038,9 @@
"docker:buildx/image:Image": {
"description": "A Docker image built using Buildkit",
"properties": {
"architecture": {
"type": "string"
},
"context": {
"type": "array",
"items": {
Expand All @@ -2057,6 +2060,24 @@
"description": "\nName of the Dockerfile to use (default: \"$PATH/Dockerfile\").",
"default": "Dockerfile"
},
"os": {
"type": "string"
},
"repoDigests": {
"type": "array",
"items": {
"type": "string"
}
},
"repoTags": {
"type": "array",
"items": {
"type": "string"
}
},
"size": {
"type": "integer"
},
"tags": {
"type": "array",
"items": {
Expand Down
3 changes: 2 additions & 1 deletion provider/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ require (
github.com/ettle/strcase v0.1.1 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/fvbommel/sortorder v1.0.1 // indirect
github.com/fvbommel/sortorder v1.1.0 // indirect
github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.5.0 // indirect
Expand Down Expand Up @@ -216,6 +216,7 @@ require (
github.com/moby/locker v1.0.1 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/spdystream v0.2.0 // indirect
github.com/moby/sys/mountinfo v0.6.2 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/sys/signal v0.7.0 // indirect
github.com/moby/sys/symlink v0.2.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions provider/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1275,8 +1275,8 @@ github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA=
github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE=
github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw=
github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 h1:Uc+IZ7gYqAf/rSGFplbWBSHaGolEQlNLgMgSE3ccnIQ=
github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813/go.mod h1:P+oSoE9yhSRvsmYyZsshflcR6ePWYLql6UU1amW13IM=
Expand Down
29 changes: 27 additions & 2 deletions provider/internal/buildx.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package internal

import (
"context"
"fmt"

"github.com/docker/cli/cli/flags"
provider "github.com/pulumi/pulumi-go-provider"
"github.com/pulumi/pulumi-go-provider/infer"
"github.com/pulumi/pulumi/pkg/v3/codegen/schema"
Expand All @@ -13,17 +15,40 @@ import (
// Config configures the buildx provider.
type Config struct {
Host string `pulumi:"host,optional"`

client Client
}

var (
_ infer.CustomConfigure = (*Config)(nil)
_ infer.Annotated = (infer.Annotated)((*Config)(nil))
)

// _mockClientKey is used by tests to inject a mock Docker client.
var _mockClientKey struct{}

// Annotate provides user-facing descriptions and defaults for Config's fields.
func (c *Config) Annotate(a infer.Annotator) {
a.Describe(&c.Host, "The build daemon's address.")
a.SetDefault(&c.Host, "", "DOCKER_HOST")
}

// Configure validates and processes user-provided configuration values.
func (c *Config) Configure(_ provider.Context) error {
return nil
func (c *Config) Configure(ctx provider.Context) error {
if client, ok := ctx.Value(_mockClientKey).(Client); ok {
c.client = client
return nil // Client has already been injected, nothing to do.
}

client, err := newDockerClient()
if err != nil {
return fmt.Errorf("getting client: %w", err)
}
if err = client.Initialize(flags.NewClientOptions()); err != nil {
return fmt.Errorf("initializing client: %w", err)
}
c.client = client
return err
}

// NewBuildxProvider returns a new buildx provider.
Expand Down
17 changes: 13 additions & 4 deletions provider/internal/buildx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import (
provider "github.com/pulumi/pulumi-go-provider"
"github.com/pulumi/pulumi-go-provider/infer"
"github.com/pulumi/pulumi-go-provider/integration"
mwcontext "github.com/pulumi/pulumi-go-provider/middleware/context"
"github.com/stretchr/testify/assert"
)

func TestConfigure(t *testing.T) {
s := newServer()
s := newServer(nil)

err := s.Configure(
provider.ConfigureRequest{},
Expand Down Expand Up @@ -43,7 +44,15 @@ func (annotator) Describe(_ any, _ string) {}
func (annotator) SetDefault(_ any, _ any, _ ...string) {}
func (annotator) SetToken(_, _ string) {}

func newServer() integration.Server {
provider := NewBuildxProvider()
return integration.NewServer("docker", semver.Version{Major: 4}, provider)
func newServer(client Client) integration.Server {
p := NewBuildxProvider()

// Inject a mock client if provided.
if client != nil {
p = mwcontext.Wrap(p, func(ctx provider.Context) provider.Context {
return provider.CtxWithValue(ctx, _mockClientKey, client)
})
}

return integration.NewServer("docker", semver.Version{Major: 4}, p)
}
83 changes: 83 additions & 0 deletions provider/internal/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//go:generate go run go.uber.org/mock/mockgen -package mock -source client.go -destination mock/client.go
package internal

import (
"context"
"fmt"
"os"

cbuild "github.com/docker/buildx/controller/build"
controllerapi "github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/util/progress"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/flags"
"github.com/docker/docker/api/types"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/util/progress/progressui"
)

// Client handles all our Docker API calls.
type Client interface {
Build(ctx context.Context, opts controllerapi.BuildOptions) (*client.SolveResponse, error)
BuildKitEnabled() (bool, error)
Initialize(opts *flags.ClientOptions, ops ...command.InitializeOpt) error
Inspect(ctx context.Context, id string) (types.ImageInspect, error)
Delete(ctx context.Context, id string) ([]types.ImageDeleteResponseItem, error)
}

type docker struct {
cli *command.DockerCli
}

func newDockerClient() (Client, error) {
cli, err := command.NewDockerCli(
command.WithCombinedStreams(os.Stdout),
)
if err != nil {
return nil, err
}

err = cli.Initialize(flags.NewClientOptions())
return &docker{cli: cli}, err
}

// Initialize the docker client.
func (d *docker) Initialize(opts *flags.ClientOptions, ops ...command.InitializeOpt) error {
return d.cli.Initialize(opts, ops...)
}

// Build performs a buildkit build.
func (d *docker) Build(
ctx context.Context,
opts controllerapi.BuildOptions,
) (*client.SolveResponse, error) {
printer, err := progress.NewPrinter(ctx, os.Stdout, progressui.PlainMode)
if err != nil {
return nil, fmt.Errorf("creating printer: %w", err)
}
solve, res, err := cbuild.RunBuild(ctx, d.cli, opts, d.cli.In(), printer, true)
if res != nil {
res.Done()
}
return solve, err
}

// BuildKitEnabled returns true if the client supports buildkit.
func (d *docker) BuildKitEnabled() (bool, error) {
return d.cli.BuildKitEnabled()
}

// Inspect inspects an image.
//
// TODO: Inspect the manifest instead?
func (d *docker) Inspect(ctx context.Context, id string) (types.ImageInspect, error) {
inspect, _, err := d.cli.Client().ImageInspectWithRaw(ctx, id)
return inspect, err
}

// Delete deletes an image with the given ID.
func (d *docker) Delete(ctx context.Context, id string) ([]types.ImageDeleteResponseItem, error) {
return d.cli.Client().ImageRemove(ctx, id, types.ImageRemoveOptions{
Force: true, // Needed in case the image has multiple tags.
})
}
Loading

0 comments on commit e3f225b

Please sign in to comment.