diff --git a/.github/workflows/delivery-docker.yml b/.github/workflows/delivery-docker.yml index 62fe7d95d..e25120892 100644 --- a/.github/workflows/delivery-docker.yml +++ b/.github/workflows/delivery-docker.yml @@ -61,7 +61,7 @@ jobs: password: ${{ secrets.DOCKER_PASSWORD }} - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - - uses: buildpacks/github-actions/setup-tools@v5.9.1 + - uses: buildpacks/github-actions/setup-tools@v5.9.2 - name: Buildx Build/Publish run: | docker buildx build . \ diff --git a/internal/commands/builder_create.go b/internal/commands/builder_create.go index 0f0146d92..0f1897f16 100644 --- a/internal/commands/builder_create.go +++ b/internal/commands/builder_create.go @@ -27,6 +27,7 @@ type BuilderCreateFlags struct { Flatten []string Targets []string Label map[string]string + DockerHost string } // CreateBuilder creates a builder image, based on a builder config @@ -131,6 +132,7 @@ Creating a custom builder allows you to control what buildpacks are used and wha Labels: flags.Label, Targets: multiArchCfg.Targets(), TempDirectory: tempDir, + DockerHost: flags.DockerHost, }); err != nil { return err } @@ -156,6 +158,12 @@ Creating a custom builder allows you to control what buildpacks are used and wha - To specify the distribution version: '--target "linux/arm/v6:ubuntu@14.04"' - To specify multiple distribution versions: '--target "linux/arm/v6:ubuntu@14.04" --target "linux/arm/v6:ubuntu@16.04"' `) + cmd.Flags().StringVar(&flags.DockerHost, "docker-host", "", + `Address to docker daemon that will be exposed to the build container. +If not set (or set to empty string) the standard socket location will be used. +Special value 'inherit' may be used in which case DOCKER_HOST environment variable will be used. +This option may set DOCKER_HOST environment variable for the build container if needed. +`) AddHelpFlag(cmd, "create") return cmd diff --git a/internal/commands/builder_create_test.go b/internal/commands/builder_create_test.go index db8baeda0..022ac869c 100644 --- a/internal/commands/builder_create_test.go +++ b/internal/commands/builder_create_test.go @@ -462,6 +462,34 @@ func testCreateCommand(t *testing.T, when spec.G, it spec.S) { }) }) + when("--docker-host", func() { + it.Before(func() { + h.AssertNil(t, os.WriteFile(builderConfigPath, []byte(validConfig), 0666)) + }) + + it("passes docker host to CreateBuilder", func() { + mockClient.EXPECT().CreateBuilder(gomock.Any(), EqCreateBuilderOptionsDockerHost("unix:///var/run/docker.sock")).Return(nil) + + command.SetArgs([]string{ + "some/builder", + "--config", builderConfigPath, + "--docker-host", "unix:///var/run/docker.sock", + }) + h.AssertNil(t, command.Execute()) + }) + + it("passes inherit value to CreateBuilder", func() { + mockClient.EXPECT().CreateBuilder(gomock.Any(), EqCreateBuilderOptionsDockerHost("inherit")).Return(nil) + + command.SetArgs([]string{ + "some/builder", + "--config", builderConfigPath, + "--docker-host", "inherit", + }) + h.AssertNil(t, command.Execute()) + }) + }) + when("multi-platform builder is expected to be created", func() { when("builder config has no targets defined", func() { it.Before(func() { @@ -570,6 +598,15 @@ func EqCreateBuilderOptionsTargets(targets []dist.Target) gomock.Matcher { } } +func EqCreateBuilderOptionsDockerHost(host string) gomock.Matcher { + return createbuilderOptionsMatcher{ + description: fmt.Sprintf("DockerHost=%s", host), + equals: func(o client.CreateBuilderOptions) bool { + return o.DockerHost == host + }, + } +} + type createbuilderOptionsMatcher struct { equals func(options client.CreateBuilderOptions) bool description string diff --git a/internal/commands/buildpack_package.go b/internal/commands/buildpack_package.go index f8c11a749..0e3d89874 100644 --- a/internal/commands/buildpack_package.go +++ b/internal/commands/buildpack_package.go @@ -31,6 +31,7 @@ type BuildpackPackageFlags struct { Publish bool Flatten bool AppendImageNameSuffix bool + DockerHost string } // BuildpackPackager packages buildpacks @@ -148,6 +149,7 @@ func BuildpackPackage(logger logging.Logger, cfg config.Config, packager Buildpa FlattenExclude: flags.FlattenExclude, Labels: flags.Label, Targets: multiArchCfg.Targets(), + DockerHost: flags.DockerHost, }); err != nil { return err } @@ -183,6 +185,10 @@ Targets should be in the format '[os][/arch][/variant]:[distroname@osversion@ano - To specify the distribution version: '--target "linux/arm/v6:ubuntu@14.04"' - To specify multiple distribution versions: '--target "linux/arm/v6:ubuntu@14.04" --target "linux/arm/v6:ubuntu@16.04"' `) + cmd.Flags().StringVar(&flags.DockerHost, "docker-host", "", + `Address to docker daemon that will be exposed to the build container. +If not set (or set to empty string) the standard socket location will be used. +Special value 'inherit' may be used in which case the value from DOCKER_HOST environment variable will be used.`) if !cfg.Experimental { cmd.Flags().MarkHidden("flatten") cmd.Flags().MarkHidden("flatten-exclude") diff --git a/internal/commands/extension_package.go b/internal/commands/extension_package.go index 15b9a82d0..990df3a95 100644 --- a/internal/commands/extension_package.go +++ b/internal/commands/extension_package.go @@ -25,6 +25,7 @@ type ExtensionPackageFlags struct { Publish bool Policy string Path string + DockerHost string } // ExtensionPackager packages extensions @@ -121,6 +122,7 @@ func ExtensionPackage(logger logging.Logger, cfg config.Config, packager Extensi Publish: flags.Publish, PullPolicy: pullPolicy, Targets: multiArchCfg.Targets(), + DockerHost: flags.DockerHost, }); err != nil { return err } @@ -152,6 +154,10 @@ Targets should be in the format '[os][/arch][/variant]:[distroname@osversion@ano - To specify the distribution version: '--target "linux/arm/v6:ubuntu@14.04"' - To specify multiple distribution versions: '--target "linux/arm/v6:ubuntu@14.04" --target "linux/arm/v6:ubuntu@16.04"' `) + cmd.Flags().StringVar(&flags.DockerHost, "docker-host", "", + `Address to docker daemon that will be exposed to the build container. +If not set (or set to empty string) the standard socket location will be used. +Special value 'inherit' may be used in which case the value from DOCKER_HOST environment variable will be used.`) AddHelpFlag(cmd, "package") return cmd } diff --git a/pkg/client/create_builder.go b/pkg/client/create_builder.go index 1015c8f61..4e713d7b5 100644 --- a/pkg/client/create_builder.go +++ b/pkg/client/create_builder.go @@ -10,14 +10,15 @@ import ( "sort" "strings" - "github.com/buildpacks/pack/internal/name" - "github.com/Masterminds/semver" "github.com/buildpacks/imgutil" + "github.com/docker/docker/client" "github.com/pkg/errors" "golang.org/x/text/cases" "golang.org/x/text/language" + "github.com/buildpacks/pack/internal/name" + pubbldr "github.com/buildpacks/pack/builder" "github.com/buildpacks/pack/internal/builder" "github.com/buildpacks/pack/internal/paths" @@ -67,11 +68,38 @@ type CreateBuilderOptions struct { // Temporary directory to use for downloading lifecycle images. TempDirectory string + + // Address of docker daemon exposed to build container + // e.g. tcp://example.com:1234, unix:///run/user/1000/podman/podman.sock + DockerHost string } // CreateBuilder creates and saves a builder image to a registry with the provided options. // If any configuration is invalid, it will error and exit without creating any images. func (c *Client) CreateBuilder(ctx context.Context, opts CreateBuilderOptions) error { + if opts.DockerHost != "" { + host := opts.DockerHost + if host == "inherit" { + host = OS.Getenv("DOCKER_HOST") + } + if host != "" { + OS.Setenv("DOCKER_HOST", host) + newDocker, err := client.NewClientWithOpts( + client.FromEnv, + client.WithVersion(DockerAPIVersion), + ) + if err != nil { + return errors.Wrapf(err, "initializing docker client with host %s", host) + } + c.docker = newDocker + c.imageFetcher = image.NewFetcher(c.logger, c.docker, image.WithRegistryMirrors(c.registryMirrors), image.WithKeychain(c.keychain)) + c.imageFactory = &imageFactory{ + dockerClient: c.docker, + keychain: c.keychain, + } + } + } + targets, err := c.processBuilderCreateTargets(ctx, opts) if err != nil { return err diff --git a/pkg/client/package_buildpack.go b/pkg/client/package_buildpack.go index fbf544bd8..d271837ef 100644 --- a/pkg/client/package_buildpack.go +++ b/pkg/client/package_buildpack.go @@ -3,12 +3,14 @@ package client import ( "context" "fmt" + "os" "path/filepath" - "github.com/buildpacks/pack/internal/name" - + "github.com/docker/docker/client" "github.com/pkg/errors" + "github.com/buildpacks/pack/internal/name" + pubbldpkg "github.com/buildpacks/pack/buildpackage" "github.com/buildpacks/pack/internal/layer" "github.com/buildpacks/pack/internal/paths" @@ -71,10 +73,32 @@ type PackageBuildpackOptions struct { // Target platforms to build packages for Targets []dist.Target + + // Address to docker daemon that will be used for image operations + DockerHost string } // PackageBuildpack packages buildpack(s) into either an image or file. func (c *Client) PackageBuildpack(ctx context.Context, opts PackageBuildpackOptions) error { + if opts.DockerHost != "" { + host := opts.DockerHost + if host == "inherit" { + host = os.Getenv("DOCKER_HOST") + } + if host != "" { + os.Setenv("DOCKER_HOST", host) + newDocker, err := client.NewClientWithOpts( + client.FromEnv, + client.WithVersion(DockerAPIVersion), + ) + if err != nil { + return errors.Wrapf(err, "initializing docker client with host %s", host) + } + c.docker = newDocker + c.imageFetcher = image.NewFetcher(c.logger, c.docker, image.WithRegistryMirrors(c.registryMirrors), image.WithKeychain(c.keychain)) + } + } + if opts.Format == "" { opts.Format = FormatImage }