Compare commits

...

65 Commits

Author SHA1 Message Date
Tõnis Tiigi
9e17bc7a4c Merge pull request #3127 from sarahsanders-docker/docs-buildx-history
docs: add descriptions and examples for buildx history commands
2025-05-05 15:00:01 -07:00
Tonis Tiigi
e1e8f5c68d docs: updated reference docs generation
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-05-05 14:32:03 -07:00
sarahsanders-docker
6ed39b2618 fix examples and headings
Signed-off-by: sarahsanders-docker <sarah.sanders@docker.com>
2025-05-05 14:32:03 -07:00
sarahsanders-docker
03019049e8 addressed feedback
Signed-off-by: sarahsanders-docker <sarah.sanders@docker.com>
2025-05-05 14:32:03 -07:00
sarahsanders-docker
23ce21c341 feedback + updated examples + added links for h3 headings
Signed-off-by: sarahsanders-docker <sarah.sanders@docker.com>
2025-05-05 14:32:03 -07:00
sarahsanders-docker
4dac5295a1 Add descriptions and examples for buildx history commands
Signed-off-by: sarahsanders-docker <sarah.sanders@docker.com>
2025-05-05 14:32:03 -07:00
Tõnis Tiigi
9a48aca461 Merge pull request #3136 from ctalledo/reworked-fix-for-moby-45458
Output correct image ID when using Docker with the containerd-snapshotter
2025-05-01 16:59:42 -07:00
Cesar Talledo
679407862f Output correct image ID when using Docker with the containerd-snapshotter.
Prior to this change, the following command emits the wrong image ID when buildx
uses the "docker-container" driver and Docker is configured with the
containerd-snapshotter.

$ docker buildx build --load --iidfile=img.txt

$ docker run --rm "$(cat img.txt)" echo hello
docker: Error response from daemon: No such image: sha256:4ac37e81e00f242010e42f3251094e47de6100e01d25e9bd0feac6b8906976df.
See 'docker run --help'.

The problem is that buildx is outputing the incorrect image ID in this scenario
(it's outputing the container image config digest, instead of the container
image digest used by the containerd-snapshotter).

This commit fixes this. See https://github.com/moby/moby/issues/45458.

Signed-off-by: Cesar Talledo <cesar.talledo@docker.com>
2025-05-01 16:33:22 -07:00
Tõnis Tiigi
674cfff1a4 Merge pull request #3165 from tonistiigi/fix-openbsd-ci
attempt openbsd fix
2025-05-01 11:29:09 -07:00
Tonis Tiigi
19a241f4ed attempt openbsd fix
7.5 packages seem to be removed from main mirrors. Couldn't find
a popular 7.6/7.7 image in vagrant cloud.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-05-01 11:02:38 -07:00
Tõnis Tiigi
7da31076ae Merge pull request #3164 from jsternberg/controller-errdefs-proto-removal
controller: remove controller/errdefs protobuf files
2025-05-01 10:40:41 -07:00
Jonathan A. Sternberg
384f0565f5 controller: remove controller/errdefs protobuf files
Remove the protobuf files associated with controller/errdefs.

This doesn't completely remove the type as the monitor still uses it as
a signal to start the monitor.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-05-01 12:14:36 -05:00
Tõnis Tiigi
6df3a09284 Merge pull request #3126 from jsternberg/controller-removal
controller: remove controller grpc service
2025-04-30 18:20:51 -07:00
Tõnis Tiigi
e7be640d9b Merge pull request #3155 from crazy-max/fix-bin-image
ci: fix bin-image job
2025-04-30 17:53:20 -07:00
Jonathan A. Sternberg
2f1be25b8f controller: remove controller grpc service
Remove the controller grpc service along with associated code related to
sessions or remote controllers.

Data types that are still used with complicated dependency chains have
been kept in the same package for a future refactor.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-04-30 13:46:58 -05:00
CrazyMax
a40edbb47b ci: fix bin-image job
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-04-24 14:40:43 +02:00
Tõnis Tiigi
2eaea647d8 Merge pull request #3146 from fiam/alberto/propagate-otel-trace
chore(dockerutil): propagate OTEL context to Docker daemon
2025-04-23 09:45:17 -07:00
Alberto Garcia Hierro
f3a3d9c26b chore(dockerutil): propagate OTEL context to Docker daemon
This allows to correlate operations triggered by a build (e.g.
a client-side pull) with the build that generated them.

Signed-off-by: Alberto Garcia Hierro <damaso.hierro@docker.com>
2025-04-22 20:29:30 +01:00
Tõnis Tiigi
9ba3f77219 Merge pull request #3143 from crazy-max/ci-fix-vagrant
ci: fix vagrant build
2025-04-22 12:17:29 -07:00
Tõnis Tiigi
2799ed6dd8 Merge pull request #3142 from thaJeztah/bump_docker_28.1.1
vendor: github.com/docker/docker, docker/cli v28.1.1, containerd v2.0.5
2025-04-22 11:37:27 -07:00
CrazyMax
719a41a4c3 Merge pull request #3135 from docker/dependabot/github_actions/softprops/action-gh-release-2.2.2
build(deps): bump softprops/action-gh-release from 2.2.1 to 2.2.2
2025-04-22 14:03:03 +02:00
CrazyMax
a9807be458 ci: fix vagrant build
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-04-22 13:47:03 +02:00
CrazyMax
7a7be2ffa1 Merge pull request #3141 from ndeloof/path.IsAbs
use filepath.IsAbs to support windows paths
2025-04-22 13:42:30 +02:00
Sebastiaan van Stijn
ab533b0cb4 vendor: github.com/docker/cli v28.1.1
no changes in vendored code

diff:  https://github.com/docker/cli/compare/v28.1.0...v28.1.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-04-22 12:30:00 +02:00
Sebastiaan van Stijn
0855cab1bd vendor: github.com/docker/docker v28.1.1
diff:  https://github.com/docker/docker/compare/v28.1.0...v28.1.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-04-22 12:29:21 +02:00
Sebastiaan van Stijn
735555ff7b vendor: github.com/containerd/containerd v2.0.5
full diff: https://github.com/containerd/containerd/compare/v2.0.4...v2.0.5

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-04-22 12:28:37 +02:00
Sebastiaan van Stijn
67ccbd06f6 vendor: golang.org/x/oauth2 v0.29.0
notable changes

- fixes CVE-2025-22868
- oauth2.go: use a more straightforward return value
- oauth2: Deep copy context client in NewClient
- jws: improve fix for CVE-2025-22868

full diff: https://github.com/golang/oauth2/compare/v0.23.0...v0.29.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-04-22 12:26:36 +02:00
Nicolas De Loof
c370f90b73 use filepath.IsAbs to support windows paths
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-04-22 11:37:53 +02:00
CrazyMax
9730a20f6b Merge pull request #3133 from tonistiigi/build-defers-fix
build: make sure defers always run in the end of the build
2025-04-22 09:39:05 +02:00
dependabot[bot]
2e93ac32bc build(deps): bump softprops/action-gh-release from 2.2.1 to 2.2.2
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.2.1 to 2.2.2.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](c95fe14893...da05d55257)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: 2.2.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-21 18:55:17 +00:00
Tonis Tiigi
19c22136b4 build: make sure defers always run in the end of the build
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-04-21 09:28:44 -07:00
Tõnis Tiigi
bad5063577 Merge pull request #3107 from thaJeztah/bump_engine
vendor: github.com/docker/docker, github.com/docker/cli v28.1.0
2025-04-18 17:05:35 -07:00
Sebastiaan van Stijn
286c018f84 vendor: github.com/docker/cli v28.1.0
full diff: https://github.com/docker/cli/compare/v28.0.4...v28.1.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-04-18 15:54:36 -07:00
Sebastiaan van Stijn
ac970c03e7 vendor: github.com/docker/docker v28.1.0
full diff: https://github.com/docker/docker/compare/v28.0.4...v28.1.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-04-18 15:54:08 -07:00
Sebastiaan van Stijn
5398c33937 vendor: github.com/mattn/go-runewidth v0.0.16
adds support for Unicode 15.1.0

full diff: https://github.com/mattn/go-runewidth/compare/v0.0.15...v0.0.16

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-04-18 15:54:06 -07:00
Tõnis Tiigi
1365652a74 Merge pull request #3113 from crazy-max/update-hcl
vendor: update hcl dependencies
2025-04-18 15:51:53 -07:00
Tõnis Tiigi
a4f0a21468 Merge pull request #3125 from thaJeztah/dockerfile_update_engine
Dockerfile: update to docker v28.1.0
2025-04-18 15:51:03 -07:00
CrazyMax
d55616b22c Merge pull request #3130 from crazy-max/fix-pr-assign
ci: update pr-assign-author
2025-04-18 13:52:27 +02:00
CrazyMax
113606a24c ci: update pr-assign-author
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-04-18 13:38:59 +02:00
CrazyMax
cd38da0244 Merge pull request #3123 from thaJeztah/update_spdy
vendor: github.com/moby/spdystream v0.5.0 (indirect)
2025-04-17 16:25:49 +02:00
Sebastiaan van Stijn
cc6547c51d Dockerfile: update to docker v28.1.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-04-17 16:17:55 +02:00
Sebastiaan van Stijn
26f2e002c6 vendor: github.com/moby/spdystream v0.5.0 (indirect)
This is an indirect dependency, but I recalled it fixed some leaking
goroutines, so it may be worth considering updating.

full diff: https://github.com/moby/spdystream/compare/v0.4.0...v0.5.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-04-17 14:26:25 +02:00
CrazyMax
372feb38ff Merge pull request #3120 from crazy-max/ci-pr-assign
ci: assign author on pull request
2025-04-16 15:39:06 +02:00
CrazyMax
b08d576ec0 Merge pull request #3119 from thaJeztah/bump_archive
vendor: github.com/moby/go-archive v0.1.0
2025-04-16 15:14:07 +02:00
CrazyMax
0034cdbffc ci: assign author on pull request
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-04-16 15:06:01 +02:00
CrazyMax
a9666e7df1 Merge pull request #3118 from crazy-max/dockerfile-buildkit-0.21.0
dockerfile: update buildkit to 0.21.0
2025-04-16 14:37:09 +02:00
CrazyMax
b7e77af256 dockerfile: update buildkit to 0.21.0
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-04-16 14:14:35 +02:00
CrazyMax
d72ff8f88c Merge pull request #2842 from thaJeztah/test_registry_v3
Dockerfile: update to registry v3.0.0
2025-04-16 14:14:00 +02:00
Sebastiaan van Stijn
d75c650792 vendor: github.com/moby/go-archive v0.1.0
full diff: https://github.com/moby/go-archive/compare/21f3f3385ab7...v0.1.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-04-16 13:49:57 +02:00
Tõnis Tiigi
8c74109330 Merge pull request #3115 from crazy-max/buildkit-v0.21.0
vendor: github.com/moby/buildkit v0.21.0
2025-04-15 09:30:51 -07:00
CrazyMax
9f102b5c34 vendor: github.com/moby/buildkit v0.21.0
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-04-15 16:54:08 +02:00
CrazyMax
b4b2dc9664 Merge pull request #3114 from tonistiigi/bake-variadic-fix
bake: fix variadic_params inconsistency for user functions
2025-04-15 15:48:49 +02:00
Tonis Tiigi
2e81e301ae bake: fix variadic_params inconsistency for user functions
There was inconsistency between variables used for function
definitions in HCL and JSON format. Updated JSON to match HCL,
fixed documentation and removed the unused code from userfunc
pkg (based on HCL upstream) to avoid confusion.

Theoretically we could add some temporary backwards compatibility
for the JSON format but I think it is unlikely that someone uses
JSON format for this and also defined variadic parameters.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-04-14 10:56:20 -07:00
CrazyMax
fb4417e14d vendor: update hcl dependencies
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-04-14 12:55:59 +02:00
CrazyMax
eb74b483bd Merge pull request #3110 from crazy-max/buildkit-0.21.0-rc2
vendor: github.com/moby/buildkit v0.21.0-rc2
2025-04-11 19:44:05 +02:00
CrazyMax
db194abdc8 vendor: github.com/moby/buildkit v0.21.0-rc2
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-04-11 19:28:20 +02:00
CrazyMax
86eb3be1c4 Merge pull request #3103 from thaJeztah/use_atomicwriter
migrate to use github.com/moby/sys/atomicwriter
2025-04-11 12:05:00 +02:00
CrazyMax
a05a166f81 Merge pull request #3106 from crazy-max/inline-result
build: print frontend inline message
2025-04-11 12:04:47 +02:00
CrazyMax
cfc9d3a8c9 Merge pull request #3105 from glours/bump-compose-go-v2.6.0
bump compose-go to version v2.6.0
2025-04-11 10:57:53 +02:00
CrazyMax
5bac0b1197 build: print frontend inline message
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-04-11 09:45:25 +02:00
Guillaume Lours
0b4e624aaa bump compose-go to version v2.6.0
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-04-10 18:04:00 +02:00
Sebastiaan van Stijn
b7b5a3a1cc migrate to use github.com/moby/sys/atomicwriter
The github.com/docker/docker/pkg/atomicwriter package was moved
to a separate module.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-04-10 12:13:01 +02:00
CrazyMax
f8de3c3bdc Merge pull request #3095 from thaJeztah/migrate_archive
migrate to github.com/moby/go-archive module
2025-04-10 10:31:55 +02:00
Sebastiaan van Stijn
fa0c3e3786 migrate to github.com/moby/go-archive module
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-04-10 09:16:43 +02:00
Sebastiaan van Stijn
df6d36af35 Dockerfile: update to registry v3.0.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-04-08 14:20:58 +02:00
469 changed files with 3425 additions and 69319 deletions

View File

@@ -260,6 +260,9 @@ jobs:
- freebsd
- netbsd
- openbsd
env:
# https://github.com/hashicorp/vagrant/issues/13652
VAGRANT_DISABLE_STRICT_DEPENDENCY_ENFORCEMENT: 1
steps:
-
name: Prepare
@@ -420,6 +423,9 @@ jobs:
haskell: true
large-packages: true
swap-storage: true
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up QEMU
uses: docker/setup-qemu-action@v3
@@ -453,9 +459,10 @@ jobs:
name: Build and push image
uses: docker/bake-action@v6
with:
source: .
files: |
./docker-bake.hcl
cwd://${{ steps.meta.outputs.bake-file }}
${{ steps.meta.outputs.bake-file }}
targets: image-cross
push: ${{ github.event_name != 'pull_request' }}
sbom: true
@@ -528,7 +535,7 @@ jobs:
-
name: GitHub Release
if: startsWith(github.ref, 'refs/tags/v')
uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2.2.1
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:

17
.github/workflows/pr-assign-author.yml vendored Normal file
View File

@@ -0,0 +1,17 @@
name: pr-assign-author
permissions:
contents: read
on:
pull_request_target:
types:
- opened
- reopened
jobs:
run:
uses: crazy-max/.github/.github/workflows/pr-assign-author.yml@c27924b5b93ccfe6dcc0d7b22e779ef3c05f9a92
permissions:
contents: read
pull-requests: write

View File

@@ -5,13 +5,13 @@ ARG ALPINE_VERSION=3.21
ARG XX_VERSION=1.6.1
# for testing
ARG DOCKER_VERSION=28.0.0
ARG DOCKER_VERSION=28.1.0
ARG DOCKER_VERSION_ALT_27=27.5.1
ARG DOCKER_VERSION_ALT_26=26.1.3
ARG DOCKER_CLI_VERSION=${DOCKER_VERSION}
ARG GOTESTSUM_VERSION=v1.12.0
ARG REGISTRY_VERSION=2.8.3
ARG BUILDKIT_VERSION=v0.20.2
ARG REGISTRY_VERSION=3.0.0
ARG BUILDKIT_VERSION=v0.21.0
ARG UNDOCK_VERSION=0.9.0
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx

View File

@@ -1360,8 +1360,8 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
if strings.HasPrefix(bi.ContextPath, "cwd://") {
bi.ContextPath = path.Clean(strings.TrimPrefix(bi.ContextPath, "cwd://"))
}
if !build.IsRemoteURL(bi.ContextPath) && bi.ContextState == nil && !path.IsAbs(bi.DockerfilePath) {
bi.DockerfilePath = path.Join(bi.ContextPath, bi.DockerfilePath)
if !build.IsRemoteURL(bi.ContextPath) && bi.ContextState == nil && !filepath.IsAbs(bi.DockerfilePath) {
bi.DockerfilePath = filepath.Join(bi.ContextPath, bi.DockerfilePath)
}
for k, v := range bi.NamedContexts {
if strings.HasPrefix(v.Path, "cwd://") {

View File

@@ -692,7 +692,7 @@ func TestHCLContextCwdPrefix(t *testing.T) {
require.Contains(t, m, "app")
assert.Equal(t, "test", *m["app"].Dockerfile)
assert.Equal(t, "foo", *m["app"].Context)
assert.Equal(t, "foo/test", bo["app"].Inputs.DockerfilePath)
assert.Equal(t, filepath.Clean("foo/test"), bo["app"].Inputs.DockerfilePath)
assert.Equal(t, "foo", bo["app"].Inputs.ContextPath)
}

View File

@@ -42,7 +42,7 @@ type variableValidation struct {
type functionDef struct {
Name string `json:"-" hcl:"name,label"`
Params *hcl.Attribute `json:"params,omitempty" hcl:"params"`
Variadic *hcl.Attribute `json:"variadic_param,omitempty" hcl:"variadic_params"`
Variadic *hcl.Attribute `json:"variadic_params,omitempty" hcl:"variadic_params"`
Result *hcl.Attribute `json:"result,omitempty" hcl:"result"`
}

View File

@@ -205,15 +205,6 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
return nil, err
}
defers := make([]func(), 0, 2)
defer func() {
if err != nil {
for _, f := range defers {
f()
}
}
}()
reqForNodes := make(map[string][]*reqForNode)
eg, ctx := errgroup.WithContext(ctx)
@@ -243,11 +234,11 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
if err != nil {
return nil, err
}
defer release()
if err := saveLocalState(so, k, opt, np.Node(), cfg); err != nil {
return nil, err
}
addGitAttrs(so)
defers = append(defers, release)
reqn = append(reqn, &reqForNode{
resolvedNode: np,
so: so,
@@ -534,7 +525,6 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
}
}
}
node := dp.Node().Driver
if node.IsMobyDriver() {
for _, e := range so.Exports {
@@ -570,6 +560,14 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
}
}
}
// if prefer-image-digest is set in the solver options, remove the image
// config digest from the exporter's response
for _, e := range so.Exports {
if e.Attrs["prefer-image-digest"] == "true" {
delete(rr.ExporterResponse, exptypes.ExporterImageConfigDigestKey)
break
}
}
return nil
})
}

View File

@@ -237,6 +237,11 @@ func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt *O
opt.Exports[i].Output = func(_ map[string]string) (io.WriteCloser, error) {
return w, nil
}
// if docker is using the containerd snapshotter, prefer to export the image digest
// (rather than the image config digest). See https://github.com/moby/moby/issues/45458.
if features[dockerutil.OCIImporter] {
opt.Exports[i].Attrs["prefer-image-digest"] = "true"
}
}
} else if !nodeDriver.Features(ctx)[driver.DockerExporter] {
return nil, nil, notSupported(driver.DockerExporter, nodeDriver, "https://docs.docker.com/go/build-exporters/")

View File

@@ -7,7 +7,6 @@ import (
"path/filepath"
"github.com/docker/buildx/commands"
controllererrors "github.com/docker/buildx/controller/errdefs"
"github.com/docker/buildx/util/desktop"
"github.com/docker/buildx/version"
"github.com/docker/cli/cli"
@@ -112,11 +111,6 @@ func main() {
var ebr *desktop.ErrorWithBuildRef
if errors.As(err, &ebr) {
ebr.Print(cmd.Err())
} else {
var be *controllererrors.BuildError
if errors.As(err, &be) {
be.PrintBuildDetails(cmd.Err())
}
}
os.Exit(1)

View File

@@ -424,6 +424,14 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
fmt.Fprintln(dockerCli.Out(), string(dt))
}
for _, name := range names {
if sp, ok := resp[name]; ok {
if v, ok := sp.ExporterResponse["frontend.result.inlinemessage"]; ok {
fmt.Fprintf(dockerCli.Out(), "\n# %s\n%s\n", name, v)
}
}
}
if exitCode != 0 {
os.Exit(exitCode)
}

View File

@@ -42,7 +42,6 @@ import (
"github.com/docker/cli/cli/command"
dockeropts "github.com/docker/cli/opts"
"github.com/docker/docker/api/types/versions"
"github.com/docker/docker/pkg/atomicwriter"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/exporter/containerimage/exptypes"
"github.com/moby/buildkit/frontend/subrequests"
@@ -53,6 +52,7 @@ import (
solverpb "github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/grpcerrors"
"github.com/moby/buildkit/util/progress/progressui"
"github.com/moby/sys/atomicwriter"
"github.com/morikuni/aec"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -104,12 +104,10 @@ type buildOptions struct {
exportPush bool
exportLoad bool
control.ControlOptions
invokeConfig *invokeConfig
}
func (o *buildOptions) toControllerOptions() (*controllerapi.BuildOptions, error) {
func (o *buildOptions) toControllerOptions() (*cbuild.Options, error) {
var err error
buildArgs, err := listToMap(o.buildArgs, true)
@@ -122,7 +120,7 @@ func (o *buildOptions) toControllerOptions() (*controllerapi.BuildOptions, error
return nil, err
}
opts := controllerapi.BuildOptions{
opts := cbuild.Options{
Allow: o.allow,
Annotations: o.annotations,
BuildArgs: buildArgs,
@@ -404,6 +402,10 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
os.Exit(exitcode)
}
}
if v, ok := resp.ExporterResponse["frontend.result.inlinemessage"]; ok {
fmt.Fprintf(dockerCli.Out(), "\n%s\n", v)
return nil
}
return nil
}
@@ -416,7 +418,7 @@ func getImageID(resp map[string]string) string {
return dgst
}
func runBasicBuild(ctx context.Context, dockerCli command.Cli, opts *controllerapi.BuildOptions, printer *progress.Printer) (*client.SolveResponse, *build.Inputs, error) {
func runBasicBuild(ctx context.Context, dockerCli command.Cli, opts *cbuild.Options, printer *progress.Printer) (*client.SolveResponse, *build.Inputs, error) {
resp, res, dfmap, err := cbuild.RunBuild(ctx, dockerCli, opts, dockerCli.In(), printer, false)
if res != nil {
res.Done()
@@ -424,15 +426,12 @@ func runBasicBuild(ctx context.Context, dockerCli command.Cli, opts *controllera
return resp, dfmap, err
}
func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *controllerapi.BuildOptions, options buildOptions, printer *progress.Printer) (*client.SolveResponse, *build.Inputs, error) {
func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *cbuild.Options, options buildOptions, printer *progress.Printer) (*client.SolveResponse, *build.Inputs, error) {
if options.invokeConfig != nil && (options.dockerfileName == "-" || options.contextPath == "-") {
// stdin must be usable for monitor
return nil, nil, errors.Errorf("Dockerfile or context from stdin is not supported with invoke")
}
c, err := controller.NewController(ctx, options.ControlOptions, dockerCli, printer)
if err != nil {
return nil, nil, err
}
c := controller.NewController(ctx, dockerCli)
defer func() {
if err := c.Close(); err != nil {
logrus.Warnf("failed to close server connection %v", err)
@@ -441,7 +440,7 @@ func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *contro
// NOTE: buildx server has the current working directory different from the client
// so we need to resolve paths to abosolute ones in the client.
opts, err = controllerapi.ResolveOptionPaths(opts)
opts, err := cbuild.ResolveOptionPaths(opts)
if err != nil {
return nil, nil, err
}
@@ -467,11 +466,10 @@ func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *contro
})
}
ref, resp, inputs, err = c.Build(ctx, opts, pr, printer)
resp, inputs, err = c.Build(ctx, opts, pr, printer)
if err != nil {
var be *controllererrors.BuildError
if errors.As(err, &be) {
ref = be.SessionID
retErr = err
// We can proceed to monitor
} else {
@@ -511,8 +509,8 @@ func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *contro
resp, retErr = monitorBuildResult.Resp, monitorBuildResult.Err
}
} else {
if err := c.Disconnect(ctx, ref); err != nil {
logrus.Warnf("disconnect error: %v", err)
if err := c.Close(); err != nil {
logrus.Warnf("close error: %v", err)
}
}
@@ -649,14 +647,6 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions, debugConfig *debug.D
flags.StringVar(&options.sbom, "sbom", "", `Shorthand for "--attest=type=sbom"`)
flags.StringVar(&options.provenance, "provenance", "", `Shorthand for "--attest=type=provenance"`)
if confutil.IsExperimental() {
// TODO: move this to debug command if needed
flags.StringVar(&options.Root, "root", "", "Specify root directory of server to connect")
flags.BoolVar(&options.Detach, "detach", false, "Detach buildx server (supported only on linux)")
flags.StringVar(&options.ServerConfig, "server-config", "", "Specify buildx server config file (used only when launching new server)")
cobrautil.MarkFlagsExperimental(flags, "root", "detach", "server-config")
}
flags.StringVar(&options.callFunc, "call", "build", `Set method for evaluating build ("check", "outline", "targets")`)
flags.VarPF(callAlias(&options.callFunc, "check"), "check", "", `Shorthand for "--call=check"`)
flags.Lookup("check").NoOptDefVal = "true"
@@ -1013,12 +1003,12 @@ func (cfg *invokeConfig) needsDebug(retErr error) bool {
}
}
func (cfg *invokeConfig) runDebug(ctx context.Context, ref string, options *controllerapi.BuildOptions, c control.BuildxController, stdin io.ReadCloser, stdout io.WriteCloser, stderr console.File, progress *progress.Printer) (*monitor.MonitorBuildResult, error) {
func (cfg *invokeConfig) runDebug(ctx context.Context, ref string, options *cbuild.Options, c control.BuildxController, stdin io.ReadCloser, stdout io.WriteCloser, stderr console.File, progress *progress.Printer) (*monitor.MonitorBuildResult, error) {
con := console.Current()
if err := con.SetRaw(); err != nil {
// TODO: run disconnect in build command (on error case)
if err := c.Disconnect(ctx, ref); err != nil {
logrus.Warnf("disconnect error: %v", err)
if err := c.Close(); err != nil {
logrus.Warnf("close error: %v", err)
}
return nil, errors.Errorf("failed to configure terminal: %v", err)
}

View File

@@ -3,11 +3,9 @@ package debug
import (
"context"
"os"
"runtime"
"github.com/containerd/console"
"github.com/docker/buildx/controller"
"github.com/docker/buildx/controller/control"
controllerapi "github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/monitor"
"github.com/docker/buildx/util/cobrautil"
@@ -35,7 +33,6 @@ type DebuggableCmd interface {
}
func RootCmd(dockerCli command.Cli, children ...DebuggableCmd) *cobra.Command {
var controlOptions control.ControlOptions
var progressMode string
var options DebugConfig
@@ -50,10 +47,7 @@ func RootCmd(dockerCli command.Cli, children ...DebuggableCmd) *cobra.Command {
}
ctx := context.TODO()
c, err := controller.NewController(ctx, controlOptions, dockerCli, printer)
if err != nil {
return err
}
c := controller.NewController(ctx, dockerCli)
defer func() {
if err := c.Close(); err != nil {
logrus.Warnf("failed to close server connection %v", err)
@@ -76,13 +70,9 @@ func RootCmd(dockerCli command.Cli, children ...DebuggableCmd) *cobra.Command {
flags := cmd.Flags()
flags.StringVar(&options.InvokeFlag, "invoke", "", "Launch a monitor with executing specified command")
flags.StringVar(&options.OnFlag, "on", "error", "When to launch the monitor ([always, error])")
flags.StringVar(&controlOptions.Root, "root", "", "Specify root directory of server to connect for the monitor")
flags.BoolVar(&controlOptions.Detach, "detach", runtime.GOOS == "linux", "Detach buildx server for the monitor (supported only on linux)")
flags.StringVar(&controlOptions.ServerConfig, "server-config", "", "Specify buildx server config file for the monitor (used only when launching new server)")
flags.StringVar(&progressMode, "progress", "auto", `Set type of progress output ("auto", "plain", "tty", "rawjson") for the monitor. Use plain to show container output`)
cobrautil.MarkFlagsExperimental(flags, "invoke", "on", "root", "detach", "server-config")
cobrautil.MarkFlagsExperimental(flags, "invoke", "on")
for _, c := range children {
cmd.AddCommand(c.NewDebugger(&options))

View File

@@ -7,7 +7,6 @@ import (
debugcmd "github.com/docker/buildx/commands/debug"
historycmd "github.com/docker/buildx/commands/history"
imagetoolscmd "github.com/docker/buildx/commands/imagetools"
"github.com/docker/buildx/controller/remote"
"github.com/docker/buildx/util/cobrautil/completion"
"github.com/docker/buildx/util/confutil"
"github.com/docker/buildx/util/logutil"
@@ -124,7 +123,6 @@ func addCommands(cmd *cobra.Command, opts *rootOptions, dockerCli command.Cli) {
cmd.AddCommand(debugcmd.RootCmd(dockerCli,
newDebuggableBuild(dockerCli, opts),
))
remote.AddControllerCommands(cmd, dockerCli)
}
cmd.RegisterFlagCompletionFunc( //nolint:errcheck

View File

@@ -34,7 +34,7 @@ const defaultTargetName = "default"
// NOTE: When an error happens during the build and this function acquires the debuggable *build.ResultHandle,
// this function returns it in addition to the error (i.e. it does "return nil, res, err"). The caller can
// inspect the result and debug the cause of that error.
func RunBuild(ctx context.Context, dockerCli command.Cli, in *controllerapi.BuildOptions, inStream io.Reader, progress progress.Writer, generateResult bool) (*client.SolveResponse, *build.ResultHandle, *build.Inputs, error) {
func RunBuild(ctx context.Context, dockerCli command.Cli, in *Options, inStream io.Reader, progress progress.Writer, generateResult bool) (*client.SolveResponse, *build.ResultHandle, *build.Inputs, error) {
if in.NoCache && len(in.NoCacheFilter) > 0 {
return nil, nil, nil, errors.Errorf("--no-cache and --no-cache-filter cannot currently be used together")
}

View File

@@ -1,15 +1,52 @@
package pb
package build
import (
"path/filepath"
"strings"
"github.com/docker/buildx/controller/pb"
sourcepolicy "github.com/moby/buildkit/sourcepolicy/pb"
"github.com/moby/buildkit/util/gitutil"
)
type Options struct {
ContextPath string
DockerfileName string
CallFunc *pb.CallFunc
NamedContexts map[string]string
Allow []string
Attests []*pb.Attest
BuildArgs map[string]string
CacheFrom []*pb.CacheOptionsEntry
CacheTo []*pb.CacheOptionsEntry
CgroupParent string
Exports []*pb.ExportEntry
ExtraHosts []string
Labels map[string]string
NetworkMode string
NoCacheFilter []string
Platforms []string
Secrets []*pb.Secret
ShmSize int64
SSH []*pb.SSH
Tags []string
Target string
Ulimits *pb.UlimitOpt
Builder string
NoCache bool
Pull bool
ExportPush bool
ExportLoad bool
SourcePolicy *sourcepolicy.Policy
Ref string
GroupRef string
Annotations []string
ProvenanceResponseMode string
}
// ResolveOptionPaths resolves all paths contained in BuildOptions
// and replaces them to absolute paths.
func ResolveOptionPaths(options *BuildOptions) (_ *BuildOptions, err error) {
func ResolveOptionPaths(options *Options) (_ *Options, err error) {
localContext := false
if options.ContextPath != "" && options.ContextPath != "-" {
if !isRemoteURL(options.ContextPath) {
@@ -56,7 +93,7 @@ func ResolveOptionPaths(options *BuildOptions) (_ *BuildOptions, err error) {
}
options.NamedContexts = contexts
var cacheFrom []*CacheOptionsEntry
var cacheFrom []*pb.CacheOptionsEntry
for _, co := range options.CacheFrom {
switch co.Type {
case "local":
@@ -87,7 +124,7 @@ func ResolveOptionPaths(options *BuildOptions) (_ *BuildOptions, err error) {
}
options.CacheFrom = cacheFrom
var cacheTo []*CacheOptionsEntry
var cacheTo []*pb.CacheOptionsEntry
for _, co := range options.CacheTo {
switch co.Type {
case "local":
@@ -117,7 +154,7 @@ func ResolveOptionPaths(options *BuildOptions) (_ *BuildOptions, err error) {
}
}
options.CacheTo = cacheTo
var exports []*ExportEntry
var exports []*pb.ExportEntry
for _, e := range options.Exports {
if e.Destination != "" && e.Destination != "-" {
e.Destination, err = filepath.Abs(e.Destination)
@@ -129,7 +166,7 @@ func ResolveOptionPaths(options *BuildOptions) (_ *BuildOptions, err error) {
}
options.Exports = exports
var secrets []*Secret
var secrets []*pb.Secret
for _, s := range options.Secrets {
if s.FilePath != "" {
s.FilePath, err = filepath.Abs(s.FilePath)
@@ -141,7 +178,7 @@ func ResolveOptionPaths(options *BuildOptions) (_ *BuildOptions, err error) {
}
options.Secrets = secrets
var ssh []*SSH
var ssh []*pb.SSH
for _, s := range options.SSH {
var ps []string
for _, pt := range s.Paths {

View File

@@ -1,12 +1,12 @@
package pb
package build
import (
"os"
"path/filepath"
"testing"
"github.com/docker/buildx/controller/pb"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
)
func TestResolvePaths(t *testing.T) {
@@ -16,59 +16,59 @@ func TestResolvePaths(t *testing.T) {
require.NoError(t, os.Chdir(tmpwd))
tests := []struct {
name string
options *BuildOptions
want *BuildOptions
options *Options
want *Options
}{
{
name: "contextpath",
options: &BuildOptions{ContextPath: "test"},
want: &BuildOptions{ContextPath: filepath.Join(tmpwd, "test")},
options: &Options{ContextPath: "test"},
want: &Options{ContextPath: filepath.Join(tmpwd, "test")},
},
{
name: "contextpath-cwd",
options: &BuildOptions{ContextPath: "."},
want: &BuildOptions{ContextPath: tmpwd},
options: &Options{ContextPath: "."},
want: &Options{ContextPath: tmpwd},
},
{
name: "contextpath-dash",
options: &BuildOptions{ContextPath: "-"},
want: &BuildOptions{ContextPath: "-"},
options: &Options{ContextPath: "-"},
want: &Options{ContextPath: "-"},
},
{
name: "contextpath-ssh",
options: &BuildOptions{ContextPath: "git@github.com:docker/buildx.git"},
want: &BuildOptions{ContextPath: "git@github.com:docker/buildx.git"},
options: &Options{ContextPath: "git@github.com:docker/buildx.git"},
want: &Options{ContextPath: "git@github.com:docker/buildx.git"},
},
{
name: "dockerfilename",
options: &BuildOptions{DockerfileName: "test", ContextPath: "."},
want: &BuildOptions{DockerfileName: filepath.Join(tmpwd, "test"), ContextPath: tmpwd},
options: &Options{DockerfileName: "test", ContextPath: "."},
want: &Options{DockerfileName: filepath.Join(tmpwd, "test"), ContextPath: tmpwd},
},
{
name: "dockerfilename-dash",
options: &BuildOptions{DockerfileName: "-", ContextPath: "."},
want: &BuildOptions{DockerfileName: "-", ContextPath: tmpwd},
options: &Options{DockerfileName: "-", ContextPath: "."},
want: &Options{DockerfileName: "-", ContextPath: tmpwd},
},
{
name: "dockerfilename-remote",
options: &BuildOptions{DockerfileName: "test", ContextPath: "git@github.com:docker/buildx.git"},
want: &BuildOptions{DockerfileName: "test", ContextPath: "git@github.com:docker/buildx.git"},
options: &Options{DockerfileName: "test", ContextPath: "git@github.com:docker/buildx.git"},
want: &Options{DockerfileName: "test", ContextPath: "git@github.com:docker/buildx.git"},
},
{
name: "contexts",
options: &BuildOptions{NamedContexts: map[string]string{
options: &Options{NamedContexts: map[string]string{
"a": "test1", "b": "test2",
"alpine": "docker-image://alpine@sha256:0123456789", "project": "https://github.com/myuser/project.git",
}},
want: &BuildOptions{NamedContexts: map[string]string{
want: &Options{NamedContexts: map[string]string{
"a": filepath.Join(tmpwd, "test1"), "b": filepath.Join(tmpwd, "test2"),
"alpine": "docker-image://alpine@sha256:0123456789", "project": "https://github.com/myuser/project.git",
}},
},
{
name: "cache-from",
options: &BuildOptions{
CacheFrom: []*CacheOptionsEntry{
options: &Options{
CacheFrom: []*pb.CacheOptionsEntry{
{
Type: "local",
Attrs: map[string]string{"src": "test"},
@@ -79,8 +79,8 @@ func TestResolvePaths(t *testing.T) {
},
},
},
want: &BuildOptions{
CacheFrom: []*CacheOptionsEntry{
want: &Options{
CacheFrom: []*pb.CacheOptionsEntry{
{
Type: "local",
Attrs: map[string]string{"src": filepath.Join(tmpwd, "test")},
@@ -94,8 +94,8 @@ func TestResolvePaths(t *testing.T) {
},
{
name: "cache-to",
options: &BuildOptions{
CacheTo: []*CacheOptionsEntry{
options: &Options{
CacheTo: []*pb.CacheOptionsEntry{
{
Type: "local",
Attrs: map[string]string{"dest": "test"},
@@ -106,8 +106,8 @@ func TestResolvePaths(t *testing.T) {
},
},
},
want: &BuildOptions{
CacheTo: []*CacheOptionsEntry{
want: &Options{
CacheTo: []*pb.CacheOptionsEntry{
{
Type: "local",
Attrs: map[string]string{"dest": filepath.Join(tmpwd, "test")},
@@ -121,8 +121,8 @@ func TestResolvePaths(t *testing.T) {
},
{
name: "exports",
options: &BuildOptions{
Exports: []*ExportEntry{
options: &Options{
Exports: []*pb.ExportEntry{
{
Type: "local",
Destination: "-",
@@ -149,8 +149,8 @@ func TestResolvePaths(t *testing.T) {
},
},
},
want: &BuildOptions{
Exports: []*ExportEntry{
want: &Options{
Exports: []*pb.ExportEntry{
{
Type: "local",
Destination: "-",
@@ -180,8 +180,8 @@ func TestResolvePaths(t *testing.T) {
},
{
name: "secrets",
options: &BuildOptions{
Secrets: []*Secret{
options: &Options{
Secrets: []*pb.Secret{
{
FilePath: "test1",
},
@@ -195,8 +195,8 @@ func TestResolvePaths(t *testing.T) {
},
},
},
want: &BuildOptions{
Secrets: []*Secret{
want: &Options{
Secrets: []*pb.Secret{
{
FilePath: filepath.Join(tmpwd, "test1"),
},
@@ -213,8 +213,8 @@ func TestResolvePaths(t *testing.T) {
},
{
name: "ssh",
options: &BuildOptions{
SSH: []*SSH{
options: &Options{
SSH: []*pb.SSH{
{
ID: "default",
Paths: []string{"test1", "test2"},
@@ -225,8 +225,8 @@ func TestResolvePaths(t *testing.T) {
},
},
},
want: &BuildOptions{
SSH: []*SSH{
want: &Options{
SSH: []*pb.SSH{
{
ID: "default",
Paths: []string{filepath.Join(tmpwd, "test1"), filepath.Join(tmpwd, "test2")},
@@ -244,9 +244,7 @@ func TestResolvePaths(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
got, err := ResolveOptionPaths(tt.options)
require.NoError(t, err)
if !proto.Equal(tt.want, got) {
t.Fatalf("expected %#v, got %#v", tt.want, got)
}
require.Equal(t, tt.want, got)
})
}
}

View File

@@ -5,29 +5,22 @@ import (
"io"
"github.com/docker/buildx/build"
cbuild "github.com/docker/buildx/controller/build"
controllerapi "github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/controller/processes"
"github.com/docker/buildx/util/progress"
"github.com/moby/buildkit/client"
)
type BuildxController interface {
Build(ctx context.Context, options *controllerapi.BuildOptions, in io.ReadCloser, progress progress.Writer) (ref string, resp *client.SolveResponse, inputs *build.Inputs, err error)
Build(ctx context.Context, options *cbuild.Options, in io.ReadCloser, progress progress.Writer) (resp *client.SolveResponse, inputs *build.Inputs, err error)
// Invoke starts an IO session into the specified process.
// If pid doesn't match to any running processes, it starts a new process with the specified config.
// If there is no container running or InvokeConfig.Rollback is specified, the process will start in a newly created container.
// NOTE: If needed, in the future, we can split this API into three APIs (NewContainer, NewProcess and Attach).
Invoke(ctx context.Context, ref, pid string, options *controllerapi.InvokeConfig, ioIn io.ReadCloser, ioOut io.WriteCloser, ioErr io.WriteCloser) error
Kill(ctx context.Context) error
Invoke(ctx context.Context, pid string, options *controllerapi.InvokeConfig, ioIn io.ReadCloser, ioOut io.WriteCloser, ioErr io.WriteCloser) error
Close() error
List(ctx context.Context) (refs []string, _ error)
Disconnect(ctx context.Context, ref string) error
ListProcesses(ctx context.Context, ref string) (infos []*controllerapi.ProcessInfo, retErr error)
DisconnectProcess(ctx context.Context, ref, pid string) error
Inspect(ctx context.Context, ref string) (*controllerapi.InspectResponse, error)
}
type ControlOptions struct {
ServerConfig string
Root string
Detach bool
ListProcesses(ctx context.Context) (infos []*processes.ProcessInfo, retErr error)
DisconnectProcess(ctx context.Context, pid string) error
Inspect(ctx context.Context) *cbuild.Options
}

View File

@@ -2,35 +2,12 @@ package controller
import (
"context"
"fmt"
"github.com/docker/buildx/controller/control"
"github.com/docker/buildx/controller/local"
"github.com/docker/buildx/controller/remote"
"github.com/docker/buildx/util/progress"
"github.com/docker/cli/cli/command"
"github.com/pkg/errors"
)
func NewController(ctx context.Context, opts control.ControlOptions, dockerCli command.Cli, pw progress.Writer) (control.BuildxController, error) {
var name string
if opts.Detach {
name = "remote"
} else {
name = "local"
}
var c control.BuildxController
err := progress.Wrap(fmt.Sprintf("[internal] connecting to %s controller", name), pw.Write, func(l progress.SubLogger) (err error) {
if opts.Detach {
c, err = remote.NewRemoteBuildxController(ctx, dockerCli, opts, l)
} else {
c = local.NewLocalBuildxController(ctx, dockerCli, l)
}
return err
})
if err != nil {
return nil, errors.Wrap(err, "failed to start buildx controller")
}
return c, nil
func NewController(ctx context.Context, dockerCli command.Cli) control.BuildxController {
return local.NewLocalBuildxController(ctx, dockerCli)
}

View File

@@ -1,48 +1,20 @@
package errdefs
import (
"io"
"github.com/containerd/typeurl/v2"
"github.com/docker/buildx/util/desktop"
"github.com/moby/buildkit/util/grpcerrors"
)
func init() {
typeurl.Register((*Build)(nil), "github.com/docker/buildx", "errdefs.Build+json")
}
type BuildError struct {
*Build
error
err error
}
func (e *BuildError) Unwrap() error {
return e.error
return e.err
}
func (e *BuildError) ToProto() grpcerrors.TypedErrorProto {
return e.Build
func (e *BuildError) Error() string {
return e.err.Error()
}
func (e *BuildError) PrintBuildDetails(w io.Writer) error {
if e.Ref == "" {
return nil
}
ebr := &desktop.ErrorWithBuildRef{
Ref: e.Ref,
Err: e.error,
}
return ebr.Print(w)
}
func WrapBuild(err error, sessionID string, ref string) error {
func WrapBuild(err error) error {
if err == nil {
return nil
}
return &BuildError{Build: &Build{SessionID: sessionID, Ref: ref}, error: err}
}
func (b *Build) WrapError(err error) error {
return &BuildError{error: err, Build: b}
return &BuildError{err: err}
}

View File

@@ -1,157 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.34.1
// protoc v3.11.4
// source: github.com/docker/buildx/controller/errdefs/errdefs.proto
package errdefs
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Build struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
SessionID string `protobuf:"bytes,1,opt,name=SessionID,proto3" json:"SessionID,omitempty"`
Ref string `protobuf:"bytes,2,opt,name=Ref,proto3" json:"Ref,omitempty"`
}
func (x *Build) Reset() {
*x = Build{}
if protoimpl.UnsafeEnabled {
mi := &file_github_com_docker_buildx_controller_errdefs_errdefs_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Build) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Build) ProtoMessage() {}
func (x *Build) ProtoReflect() protoreflect.Message {
mi := &file_github_com_docker_buildx_controller_errdefs_errdefs_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Build.ProtoReflect.Descriptor instead.
func (*Build) Descriptor() ([]byte, []int) {
return file_github_com_docker_buildx_controller_errdefs_errdefs_proto_rawDescGZIP(), []int{0}
}
func (x *Build) GetSessionID() string {
if x != nil {
return x.SessionID
}
return ""
}
func (x *Build) GetRef() string {
if x != nil {
return x.Ref
}
return ""
}
var File_github_com_docker_buildx_controller_errdefs_errdefs_proto protoreflect.FileDescriptor
var file_github_com_docker_buildx_controller_errdefs_errdefs_proto_rawDesc = []byte{
0x0a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x6f, 0x63,
0x6b, 0x65, 0x72, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x78, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72,
0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x65, 0x72, 0x72, 0x64, 0x65, 0x66, 0x73, 0x2f, 0x65, 0x72,
0x72, 0x64, 0x65, 0x66, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x64, 0x6f, 0x63,
0x6b, 0x65, 0x72, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x78, 0x2e, 0x65, 0x72, 0x72, 0x64, 0x65,
0x66, 0x73, 0x22, 0x37, 0x0a, 0x05, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x53,
0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x10, 0x0a, 0x03, 0x52, 0x65, 0x66,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x52, 0x65, 0x66, 0x42, 0x2d, 0x5a, 0x2b, 0x67,
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72,
0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x78, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
0x65, 0x72, 0x2f, 0x65, 0x72, 0x72, 0x64, 0x65, 0x66, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
}
var (
file_github_com_docker_buildx_controller_errdefs_errdefs_proto_rawDescOnce sync.Once
file_github_com_docker_buildx_controller_errdefs_errdefs_proto_rawDescData = file_github_com_docker_buildx_controller_errdefs_errdefs_proto_rawDesc
)
func file_github_com_docker_buildx_controller_errdefs_errdefs_proto_rawDescGZIP() []byte {
file_github_com_docker_buildx_controller_errdefs_errdefs_proto_rawDescOnce.Do(func() {
file_github_com_docker_buildx_controller_errdefs_errdefs_proto_rawDescData = protoimpl.X.CompressGZIP(file_github_com_docker_buildx_controller_errdefs_errdefs_proto_rawDescData)
})
return file_github_com_docker_buildx_controller_errdefs_errdefs_proto_rawDescData
}
var file_github_com_docker_buildx_controller_errdefs_errdefs_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_github_com_docker_buildx_controller_errdefs_errdefs_proto_goTypes = []interface{}{
(*Build)(nil), // 0: docker.buildx.errdefs.Build
}
var file_github_com_docker_buildx_controller_errdefs_errdefs_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_github_com_docker_buildx_controller_errdefs_errdefs_proto_init() }
func file_github_com_docker_buildx_controller_errdefs_errdefs_proto_init() {
if File_github_com_docker_buildx_controller_errdefs_errdefs_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_github_com_docker_buildx_controller_errdefs_errdefs_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Build); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_github_com_docker_buildx_controller_errdefs_errdefs_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_github_com_docker_buildx_controller_errdefs_errdefs_proto_goTypes,
DependencyIndexes: file_github_com_docker_buildx_controller_errdefs_errdefs_proto_depIdxs,
MessageInfos: file_github_com_docker_buildx_controller_errdefs_errdefs_proto_msgTypes,
}.Build()
File_github_com_docker_buildx_controller_errdefs_errdefs_proto = out.File
file_github_com_docker_buildx_controller_errdefs_errdefs_proto_rawDesc = nil
file_github_com_docker_buildx_controller_errdefs_errdefs_proto_goTypes = nil
file_github_com_docker_buildx_controller_errdefs_errdefs_proto_depIdxs = nil
}

View File

@@ -1,10 +0,0 @@
syntax = "proto3";
package docker.buildx.errdefs;
option go_package = "github.com/docker/buildx/controller/errdefs";
message Build {
string SessionID = 1;
string Ref = 2;
}

View File

@@ -1,241 +0,0 @@
// Code generated by protoc-gen-go-vtproto. DO NOT EDIT.
// protoc-gen-go-vtproto version: v0.6.1-0.20240319094008-0393e58bdf10
// source: github.com/docker/buildx/controller/errdefs/errdefs.proto
package errdefs
import (
fmt "fmt"
protohelpers "github.com/planetscale/vtprotobuf/protohelpers"
proto "google.golang.org/protobuf/proto"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
io "io"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
func (m *Build) CloneVT() *Build {
if m == nil {
return (*Build)(nil)
}
r := new(Build)
r.SessionID = m.SessionID
r.Ref = m.Ref
if len(m.unknownFields) > 0 {
r.unknownFields = make([]byte, len(m.unknownFields))
copy(r.unknownFields, m.unknownFields)
}
return r
}
func (m *Build) CloneMessageVT() proto.Message {
return m.CloneVT()
}
func (this *Build) EqualVT(that *Build) bool {
if this == that {
return true
} else if this == nil || that == nil {
return false
}
if this.SessionID != that.SessionID {
return false
}
if this.Ref != that.Ref {
return false
}
return string(this.unknownFields) == string(that.unknownFields)
}
func (this *Build) EqualMessageVT(thatMsg proto.Message) bool {
that, ok := thatMsg.(*Build)
if !ok {
return false
}
return this.EqualVT(that)
}
func (m *Build) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
}
size := m.SizeVT()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBufferVT(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Build) MarshalToVT(dAtA []byte) (int, error) {
size := m.SizeVT()
return m.MarshalToSizedBufferVT(dAtA[:size])
}
func (m *Build) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
if m == nil {
return 0, nil
}
i := len(dAtA)
_ = i
var l int
_ = l
if m.unknownFields != nil {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if len(m.Ref) > 0 {
i -= len(m.Ref)
copy(dAtA[i:], m.Ref)
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Ref)))
i--
dAtA[i] = 0x12
}
if len(m.SessionID) > 0 {
i -= len(m.SessionID)
copy(dAtA[i:], m.SessionID)
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SessionID)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *Build) SizeVT() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.SessionID)
if l > 0 {
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
l = len(m.Ref)
if l > 0 {
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
n += len(m.unknownFields)
return n
}
func (m *Build) UnmarshalVT(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: Build: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: Build: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field SessionID", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return protohelpers.ErrInvalidLength
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return protohelpers.ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.SessionID = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Ref", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return protohelpers.ErrInvalidLength
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return protohelpers.ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Ref = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return protohelpers.ErrInvalidLength
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}

View File

@@ -11,7 +11,6 @@ import (
controllererrors "github.com/docker/buildx/controller/errdefs"
controllerapi "github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/controller/processes"
"github.com/docker/buildx/util/desktop"
"github.com/docker/buildx/util/ioset"
"github.com/docker/buildx/util/progress"
"github.com/docker/cli/cli/command"
@@ -19,10 +18,9 @@ import (
"github.com/pkg/errors"
)
func NewLocalBuildxController(ctx context.Context, dockerCli command.Cli, logger progress.SubLogger) control.BuildxController {
func NewLocalBuildxController(ctx context.Context, dockerCli command.Cli) control.BuildxController {
return &localController{
dockerCli: dockerCli,
sessionID: "local",
processes: processes.NewManager(),
}
}
@@ -31,21 +29,20 @@ type buildConfig struct {
// TODO: these two structs should be merged
// Discussion: https://github.com/docker/buildx/pull/1640#discussion_r1113279719
resultCtx *build.ResultHandle
buildOptions *controllerapi.BuildOptions
buildOptions *cbuild.Options
}
type localController struct {
dockerCli command.Cli
sessionID string
buildConfig buildConfig
processes *processes.Manager
buildOnGoing atomic.Bool
}
func (b *localController) Build(ctx context.Context, options *controllerapi.BuildOptions, in io.ReadCloser, progress progress.Writer) (string, *client.SolveResponse, *build.Inputs, error) {
func (b *localController) Build(ctx context.Context, options *cbuild.Options, in io.ReadCloser, progress progress.Writer) (*client.SolveResponse, *build.Inputs, error) {
if !b.buildOnGoing.CompareAndSwap(false, true) {
return "", nil, nil, errors.New("build ongoing")
return nil, nil, errors.New("build ongoing")
}
defer b.buildOnGoing.Store(false)
@@ -57,31 +54,20 @@ func (b *localController) Build(ctx context.Context, options *controllerapi.Buil
buildOptions: options,
}
if buildErr != nil {
var ref string
var ebr *desktop.ErrorWithBuildRef
if errors.As(buildErr, &ebr) {
ref = ebr.Ref
}
buildErr = controllererrors.WrapBuild(buildErr, b.sessionID, ref)
buildErr = controllererrors.WrapBuild(buildErr)
}
}
if buildErr != nil {
return "", nil, nil, buildErr
return nil, nil, buildErr
}
return b.sessionID, resp, dockerfileMappings, nil
return resp, dockerfileMappings, nil
}
func (b *localController) ListProcesses(ctx context.Context, sessionID string) (infos []*controllerapi.ProcessInfo, retErr error) {
if sessionID != b.sessionID {
return nil, errors.Errorf("unknown session ID %q", sessionID)
}
func (b *localController) ListProcesses(ctx context.Context) (infos []*processes.ProcessInfo, retErr error) {
return b.processes.ListProcesses(), nil
}
func (b *localController) DisconnectProcess(ctx context.Context, sessionID, pid string) error {
if sessionID != b.sessionID {
return errors.Errorf("unknown session ID %q", sessionID)
}
func (b *localController) DisconnectProcess(ctx context.Context, pid string) error {
return b.processes.DeleteProcess(pid)
}
@@ -89,11 +75,7 @@ func (b *localController) cancelRunningProcesses() {
b.processes.CancelRunningProcesses()
}
func (b *localController) Invoke(ctx context.Context, sessionID string, pid string, cfg *controllerapi.InvokeConfig, ioIn io.ReadCloser, ioOut io.WriteCloser, ioErr io.WriteCloser) error {
if sessionID != b.sessionID {
return errors.Errorf("unknown session ID %q", sessionID)
}
func (b *localController) Invoke(ctx context.Context, pid string, cfg *controllerapi.InvokeConfig, ioIn io.ReadCloser, ioOut io.WriteCloser, ioErr io.WriteCloser) error {
proc, ok := b.processes.Get(pid)
if !ok {
// Start a new process.
@@ -121,11 +103,6 @@ func (b *localController) Invoke(ctx context.Context, sessionID string, pid stri
}
}
func (b *localController) Kill(context.Context) error {
b.Close()
return nil
}
func (b *localController) Close() error {
b.cancelRunningProcesses()
if b.buildConfig.resultCtx != nil {
@@ -135,18 +112,6 @@ func (b *localController) Close() error {
return nil
}
func (b *localController) List(ctx context.Context) (res []string, _ error) {
return []string{b.sessionID}, nil
}
func (b *localController) Disconnect(ctx context.Context, key string) error {
b.Close()
return nil
}
func (b *localController) Inspect(ctx context.Context, sessionID string) (*controllerapi.InspectResponse, error) {
if sessionID != b.sessionID {
return nil, errors.Errorf("unknown session ID %q", sessionID)
}
return &controllerapi.InspectResponse{Options: b.buildConfig.buildOptions}, nil
func (b *localController) Inspect(ctx context.Context) *cbuild.Options {
return b.buildConfig.buildOptions
}

View File

@@ -1,5 +1,11 @@
package pb
type Attest struct {
Type string
Disabled bool
Attrs string
}
func CreateAttestations(attests []*Attest) map[string]*string {
result := map[string]*string{}
for _, attest := range attests {

View File

@@ -6,6 +6,11 @@ import (
"github.com/moby/buildkit/client"
)
type CacheOptionsEntry struct {
Type string
Attrs map[string]string
}
func CreateCaches(entries []*CacheOptionsEntry) []client.CacheOptionsEntry {
var outs []client.CacheOptionsEntry
if len(entries) == 0 {

File diff suppressed because it is too large Load Diff

View File

@@ -1,250 +0,0 @@
syntax = "proto3";
package buildx.controller.v1;
import "github.com/moby/buildkit/api/services/control/control.proto";
import "github.com/moby/buildkit/sourcepolicy/pb/policy.proto";
option go_package = "github.com/docker/buildx/controller/pb";
service Controller {
rpc Build(BuildRequest) returns (BuildResponse);
rpc Inspect(InspectRequest) returns (InspectResponse);
rpc Status(StatusRequest) returns (stream StatusResponse);
rpc Input(stream InputMessage) returns (InputResponse);
rpc Invoke(stream Message) returns (stream Message);
rpc List(ListRequest) returns (ListResponse);
rpc Disconnect(DisconnectRequest) returns (DisconnectResponse);
rpc Info(InfoRequest) returns (InfoResponse);
rpc ListProcesses(ListProcessesRequest) returns (ListProcessesResponse);
rpc DisconnectProcess(DisconnectProcessRequest) returns (DisconnectProcessResponse);
}
message ListProcessesRequest {
string SessionID = 1;
}
message ListProcessesResponse {
repeated ProcessInfo Infos = 1;
}
message ProcessInfo {
string ProcessID = 1;
InvokeConfig InvokeConfig = 2;
}
message DisconnectProcessRequest {
string SessionID = 1;
string ProcessID = 2;
}
message DisconnectProcessResponse {
}
message BuildRequest {
string SessionID = 1;
BuildOptions Options = 2;
}
message BuildOptions {
string ContextPath = 1;
string DockerfileName = 2;
CallFunc CallFunc = 3;
map<string, string> NamedContexts = 4;
repeated string Allow = 5;
repeated Attest Attests = 6;
map<string, string> BuildArgs = 7;
repeated CacheOptionsEntry CacheFrom = 8;
repeated CacheOptionsEntry CacheTo = 9;
string CgroupParent = 10;
repeated ExportEntry Exports = 11;
repeated string ExtraHosts = 12;
map<string, string> Labels = 13;
string NetworkMode = 14;
repeated string NoCacheFilter = 15;
repeated string Platforms = 16;
repeated Secret Secrets = 17;
int64 ShmSize = 18;
repeated SSH SSH = 19;
repeated string Tags = 20;
string Target = 21;
UlimitOpt Ulimits = 22;
string Builder = 23;
bool NoCache = 24;
bool Pull = 25;
bool ExportPush = 26;
bool ExportLoad = 27;
moby.buildkit.v1.sourcepolicy.Policy SourcePolicy = 28;
string Ref = 29;
string GroupRef = 30;
repeated string Annotations = 31;
string ProvenanceResponseMode = 32;
}
message ExportEntry {
string Type = 1;
map<string, string> Attrs = 2;
string Destination = 3;
}
message CacheOptionsEntry {
string Type = 1;
map<string, string> Attrs = 2;
}
message Attest {
string Type = 1;
bool Disabled = 2;
string Attrs = 3;
}
message SSH {
string ID = 1;
repeated string Paths = 2;
}
message Secret {
string ID = 1;
string FilePath = 2;
string Env = 3;
}
message CallFunc {
string Name = 1;
string Format = 2;
bool IgnoreStatus = 3;
}
message InspectRequest {
string SessionID = 1;
}
message InspectResponse {
BuildOptions Options = 1;
}
message UlimitOpt {
map<string, Ulimit> values = 1;
}
message Ulimit {
string Name = 1;
int64 Hard = 2;
int64 Soft = 3;
}
message BuildResponse {
map<string, string> ExporterResponse = 1;
}
message DisconnectRequest {
string SessionID = 1;
}
message DisconnectResponse {}
message ListRequest {
string SessionID = 1;
}
message ListResponse {
repeated string keys = 1;
}
message InputMessage {
oneof Input {
InputInitMessage Init = 1;
DataMessage Data = 2;
}
}
message InputInitMessage {
string SessionID = 1;
}
message DataMessage {
bool EOF = 1; // true if eof was reached
bytes Data = 2; // should be chunked smaller than 4MB:
// https://pkg.go.dev/google.golang.org/grpc#MaxRecvMsgSize
}
message InputResponse {}
message Message {
oneof Input {
InitMessage Init = 1;
// FdMessage used from client to server for input (stdin) and
// from server to client for output (stdout, stderr)
FdMessage File = 2;
// ResizeMessage used from client to server for terminal resize events
ResizeMessage Resize = 3;
// SignalMessage is used from client to server to send signal events
SignalMessage Signal = 4;
}
}
message InitMessage {
string SessionID = 1;
// If ProcessID already exists in the server, it tries to connect to it
// instead of invoking the new one. In this case, InvokeConfig will be ignored.
string ProcessID = 2;
InvokeConfig InvokeConfig = 3;
}
message InvokeConfig {
repeated string Entrypoint = 1;
repeated string Cmd = 2;
bool NoCmd = 11; // Do not set cmd but use the image's default
repeated string Env = 3;
string User = 4;
bool NoUser = 5; // Do not set user but use the image's default
string Cwd = 6;
bool NoCwd = 7; // Do not set cwd but use the image's default
bool Tty = 8;
bool Rollback = 9; // Kill all process in the container and recreate it.
bool Initial = 10; // Run container from the initial state of that stage (supported only on the failed step)
}
message FdMessage {
uint32 Fd = 1; // what fd the data was from
bool EOF = 2; // true if eof was reached
bytes Data = 3; // should be chunked smaller than 4MB:
// https://pkg.go.dev/google.golang.org/grpc#MaxRecvMsgSize
}
message ResizeMessage {
uint32 Rows = 1;
uint32 Cols = 2;
}
message SignalMessage {
// we only send name (ie HUP, INT) because the int values
// are platform dependent.
string Name = 1;
}
message StatusRequest {
string SessionID = 1;
}
message StatusResponse {
repeated moby.buildkit.v1.Vertex vertexes = 1;
repeated moby.buildkit.v1.VertexStatus statuses = 2;
repeated moby.buildkit.v1.VertexLog logs = 3;
repeated moby.buildkit.v1.VertexWarning warnings = 4;
}
message InfoRequest {}
message InfoResponse {
BuildxVersion buildxVersion = 1;
}
message BuildxVersion {
string package = 1;
string version = 2;
string revision = 3;
}

View File

@@ -1,452 +0,0 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v3.11.4
// source: github.com/docker/buildx/controller/pb/controller.proto
package pb
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
Controller_Build_FullMethodName = "/buildx.controller.v1.Controller/Build"
Controller_Inspect_FullMethodName = "/buildx.controller.v1.Controller/Inspect"
Controller_Status_FullMethodName = "/buildx.controller.v1.Controller/Status"
Controller_Input_FullMethodName = "/buildx.controller.v1.Controller/Input"
Controller_Invoke_FullMethodName = "/buildx.controller.v1.Controller/Invoke"
Controller_List_FullMethodName = "/buildx.controller.v1.Controller/List"
Controller_Disconnect_FullMethodName = "/buildx.controller.v1.Controller/Disconnect"
Controller_Info_FullMethodName = "/buildx.controller.v1.Controller/Info"
Controller_ListProcesses_FullMethodName = "/buildx.controller.v1.Controller/ListProcesses"
Controller_DisconnectProcess_FullMethodName = "/buildx.controller.v1.Controller/DisconnectProcess"
)
// ControllerClient is the client API for Controller service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type ControllerClient interface {
Build(ctx context.Context, in *BuildRequest, opts ...grpc.CallOption) (*BuildResponse, error)
Inspect(ctx context.Context, in *InspectRequest, opts ...grpc.CallOption) (*InspectResponse, error)
Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[StatusResponse], error)
Input(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[InputMessage, InputResponse], error)
Invoke(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[Message, Message], error)
List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error)
Disconnect(ctx context.Context, in *DisconnectRequest, opts ...grpc.CallOption) (*DisconnectResponse, error)
Info(ctx context.Context, in *InfoRequest, opts ...grpc.CallOption) (*InfoResponse, error)
ListProcesses(ctx context.Context, in *ListProcessesRequest, opts ...grpc.CallOption) (*ListProcessesResponse, error)
DisconnectProcess(ctx context.Context, in *DisconnectProcessRequest, opts ...grpc.CallOption) (*DisconnectProcessResponse, error)
}
type controllerClient struct {
cc grpc.ClientConnInterface
}
func NewControllerClient(cc grpc.ClientConnInterface) ControllerClient {
return &controllerClient{cc}
}
func (c *controllerClient) Build(ctx context.Context, in *BuildRequest, opts ...grpc.CallOption) (*BuildResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(BuildResponse)
err := c.cc.Invoke(ctx, Controller_Build_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *controllerClient) Inspect(ctx context.Context, in *InspectRequest, opts ...grpc.CallOption) (*InspectResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(InspectResponse)
err := c.cc.Invoke(ctx, Controller_Inspect_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *controllerClient) Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[StatusResponse], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &Controller_ServiceDesc.Streams[0], Controller_Status_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
x := &grpc.GenericClientStream[StatusRequest, StatusResponse]{ClientStream: stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type Controller_StatusClient = grpc.ServerStreamingClient[StatusResponse]
func (c *controllerClient) Input(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[InputMessage, InputResponse], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &Controller_ServiceDesc.Streams[1], Controller_Input_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
x := &grpc.GenericClientStream[InputMessage, InputResponse]{ClientStream: stream}
return x, nil
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type Controller_InputClient = grpc.ClientStreamingClient[InputMessage, InputResponse]
func (c *controllerClient) Invoke(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[Message, Message], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &Controller_ServiceDesc.Streams[2], Controller_Invoke_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
x := &grpc.GenericClientStream[Message, Message]{ClientStream: stream}
return x, nil
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type Controller_InvokeClient = grpc.BidiStreamingClient[Message, Message]
func (c *controllerClient) List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListResponse)
err := c.cc.Invoke(ctx, Controller_List_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *controllerClient) Disconnect(ctx context.Context, in *DisconnectRequest, opts ...grpc.CallOption) (*DisconnectResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DisconnectResponse)
err := c.cc.Invoke(ctx, Controller_Disconnect_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *controllerClient) Info(ctx context.Context, in *InfoRequest, opts ...grpc.CallOption) (*InfoResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(InfoResponse)
err := c.cc.Invoke(ctx, Controller_Info_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *controllerClient) ListProcesses(ctx context.Context, in *ListProcessesRequest, opts ...grpc.CallOption) (*ListProcessesResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListProcessesResponse)
err := c.cc.Invoke(ctx, Controller_ListProcesses_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *controllerClient) DisconnectProcess(ctx context.Context, in *DisconnectProcessRequest, opts ...grpc.CallOption) (*DisconnectProcessResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DisconnectProcessResponse)
err := c.cc.Invoke(ctx, Controller_DisconnectProcess_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// ControllerServer is the server API for Controller service.
// All implementations should embed UnimplementedControllerServer
// for forward compatibility.
type ControllerServer interface {
Build(context.Context, *BuildRequest) (*BuildResponse, error)
Inspect(context.Context, *InspectRequest) (*InspectResponse, error)
Status(*StatusRequest, grpc.ServerStreamingServer[StatusResponse]) error
Input(grpc.ClientStreamingServer[InputMessage, InputResponse]) error
Invoke(grpc.BidiStreamingServer[Message, Message]) error
List(context.Context, *ListRequest) (*ListResponse, error)
Disconnect(context.Context, *DisconnectRequest) (*DisconnectResponse, error)
Info(context.Context, *InfoRequest) (*InfoResponse, error)
ListProcesses(context.Context, *ListProcessesRequest) (*ListProcessesResponse, error)
DisconnectProcess(context.Context, *DisconnectProcessRequest) (*DisconnectProcessResponse, error)
}
// UnimplementedControllerServer should be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedControllerServer struct{}
func (UnimplementedControllerServer) Build(context.Context, *BuildRequest) (*BuildResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Build not implemented")
}
func (UnimplementedControllerServer) Inspect(context.Context, *InspectRequest) (*InspectResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Inspect not implemented")
}
func (UnimplementedControllerServer) Status(*StatusRequest, grpc.ServerStreamingServer[StatusResponse]) error {
return status.Errorf(codes.Unimplemented, "method Status not implemented")
}
func (UnimplementedControllerServer) Input(grpc.ClientStreamingServer[InputMessage, InputResponse]) error {
return status.Errorf(codes.Unimplemented, "method Input not implemented")
}
func (UnimplementedControllerServer) Invoke(grpc.BidiStreamingServer[Message, Message]) error {
return status.Errorf(codes.Unimplemented, "method Invoke not implemented")
}
func (UnimplementedControllerServer) List(context.Context, *ListRequest) (*ListResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method List not implemented")
}
func (UnimplementedControllerServer) Disconnect(context.Context, *DisconnectRequest) (*DisconnectResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Disconnect not implemented")
}
func (UnimplementedControllerServer) Info(context.Context, *InfoRequest) (*InfoResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Info not implemented")
}
func (UnimplementedControllerServer) ListProcesses(context.Context, *ListProcessesRequest) (*ListProcessesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListProcesses not implemented")
}
func (UnimplementedControllerServer) DisconnectProcess(context.Context, *DisconnectProcessRequest) (*DisconnectProcessResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DisconnectProcess not implemented")
}
func (UnimplementedControllerServer) testEmbeddedByValue() {}
// UnsafeControllerServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to ControllerServer will
// result in compilation errors.
type UnsafeControllerServer interface {
mustEmbedUnimplementedControllerServer()
}
func RegisterControllerServer(s grpc.ServiceRegistrar, srv ControllerServer) {
// If the following call pancis, it indicates UnimplementedControllerServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Controller_ServiceDesc, srv)
}
func _Controller_Build_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(BuildRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ControllerServer).Build(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Controller_Build_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ControllerServer).Build(ctx, req.(*BuildRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Controller_Inspect_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(InspectRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ControllerServer).Inspect(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Controller_Inspect_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ControllerServer).Inspect(ctx, req.(*InspectRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Controller_Status_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(StatusRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(ControllerServer).Status(m, &grpc.GenericServerStream[StatusRequest, StatusResponse]{ServerStream: stream})
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type Controller_StatusServer = grpc.ServerStreamingServer[StatusResponse]
func _Controller_Input_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(ControllerServer).Input(&grpc.GenericServerStream[InputMessage, InputResponse]{ServerStream: stream})
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type Controller_InputServer = grpc.ClientStreamingServer[InputMessage, InputResponse]
func _Controller_Invoke_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(ControllerServer).Invoke(&grpc.GenericServerStream[Message, Message]{ServerStream: stream})
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type Controller_InvokeServer = grpc.BidiStreamingServer[Message, Message]
func _Controller_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ControllerServer).List(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Controller_List_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ControllerServer).List(ctx, req.(*ListRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Controller_Disconnect_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DisconnectRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ControllerServer).Disconnect(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Controller_Disconnect_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ControllerServer).Disconnect(ctx, req.(*DisconnectRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Controller_Info_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(InfoRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ControllerServer).Info(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Controller_Info_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ControllerServer).Info(ctx, req.(*InfoRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Controller_ListProcesses_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListProcessesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ControllerServer).ListProcesses(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Controller_ListProcesses_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ControllerServer).ListProcesses(ctx, req.(*ListProcessesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Controller_DisconnectProcess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DisconnectProcessRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ControllerServer).DisconnectProcess(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Controller_DisconnectProcess_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ControllerServer).DisconnectProcess(ctx, req.(*DisconnectProcessRequest))
}
return interceptor(ctx, in, info, handler)
}
// Controller_ServiceDesc is the grpc.ServiceDesc for Controller service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Controller_ServiceDesc = grpc.ServiceDesc{
ServiceName: "buildx.controller.v1.Controller",
HandlerType: (*ControllerServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Build",
Handler: _Controller_Build_Handler,
},
{
MethodName: "Inspect",
Handler: _Controller_Inspect_Handler,
},
{
MethodName: "List",
Handler: _Controller_List_Handler,
},
{
MethodName: "Disconnect",
Handler: _Controller_Disconnect_Handler,
},
{
MethodName: "Info",
Handler: _Controller_Info_Handler,
},
{
MethodName: "ListProcesses",
Handler: _Controller_ListProcesses_Handler,
},
{
MethodName: "DisconnectProcess",
Handler: _Controller_DisconnectProcess_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "Status",
Handler: _Controller_Status_Handler,
ServerStreams: true,
},
{
StreamName: "Input",
Handler: _Controller_Input_Handler,
ClientStreams: true,
},
{
StreamName: "Invoke",
Handler: _Controller_Invoke_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Metadata: "github.com/docker/buildx/controller/pb/controller.proto",
}

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,12 @@ import (
"github.com/pkg/errors"
)
type ExportEntry struct {
Type string
Attrs map[string]string
Destination string
}
func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, []string, error) {
var outs []client.ExportEntry
var localPaths []string

40
controller/pb/invoke.go Normal file
View File

@@ -0,0 +1,40 @@
package pb
import (
"fmt"
"strings"
)
type CallFunc struct {
Name string
Format string
IgnoreStatus bool
}
func (x *CallFunc) String() string {
var elems []string
if x.Name != "" {
elems = append(elems, fmt.Sprintf("Name:%q", x.Name))
}
if x.Format != "" {
elems = append(elems, fmt.Sprintf("Format:%q", x.Format))
}
if x.IgnoreStatus {
elems = append(elems, fmt.Sprintf("IgnoreStatus:%v", x.IgnoreStatus))
}
return strings.Join(elems, " ")
}
type InvokeConfig struct {
Entrypoint []string
Cmd []string
NoCmd bool
Env []string
User string
NoUser bool
Cwd string
NoCwd bool
Tty bool
Rollback bool
Initial bool
}

View File

@@ -1,162 +0,0 @@
package pb
import (
"time"
"github.com/docker/buildx/util/progress"
control "github.com/moby/buildkit/api/services/control"
"github.com/moby/buildkit/client"
"github.com/opencontainers/go-digest"
"google.golang.org/protobuf/types/known/timestamppb"
)
type writer struct {
ch chan<- *StatusResponse
}
func NewProgressWriter(ch chan<- *StatusResponse) progress.Writer {
return &writer{ch: ch}
}
func (w *writer) Write(status *client.SolveStatus) {
w.ch <- ToControlStatus(status)
}
func (w *writer) WriteBuildRef(target string, ref string) {}
func (w *writer) ValidateLogSource(digest.Digest, any) bool {
return true
}
func (w *writer) ClearLogSource(any) {}
func ToControlStatus(s *client.SolveStatus) *StatusResponse {
resp := StatusResponse{}
for _, v := range s.Vertexes {
resp.Vertexes = append(resp.Vertexes, &control.Vertex{
Digest: string(v.Digest),
Inputs: digestSliceToPB(v.Inputs),
Name: v.Name,
Started: timestampToPB(v.Started),
Completed: timestampToPB(v.Completed),
Error: v.Error,
Cached: v.Cached,
ProgressGroup: v.ProgressGroup,
})
}
for _, v := range s.Statuses {
resp.Statuses = append(resp.Statuses, &control.VertexStatus{
ID: v.ID,
Vertex: string(v.Vertex),
Name: v.Name,
Total: v.Total,
Current: v.Current,
Timestamp: timestamppb.New(v.Timestamp),
Started: timestampToPB(v.Started),
Completed: timestampToPB(v.Completed),
})
}
for _, v := range s.Logs {
resp.Logs = append(resp.Logs, &control.VertexLog{
Vertex: string(v.Vertex),
Stream: int64(v.Stream),
Msg: v.Data,
Timestamp: timestamppb.New(v.Timestamp),
})
}
for _, v := range s.Warnings {
resp.Warnings = append(resp.Warnings, &control.VertexWarning{
Vertex: string(v.Vertex),
Level: int64(v.Level),
Short: v.Short,
Detail: v.Detail,
Url: v.URL,
Info: v.SourceInfo,
Ranges: v.Range,
})
}
return &resp
}
func FromControlStatus(resp *StatusResponse) *client.SolveStatus {
s := client.SolveStatus{}
for _, v := range resp.Vertexes {
s.Vertexes = append(s.Vertexes, &client.Vertex{
Digest: digest.Digest(v.Digest),
Inputs: digestSliceFromPB(v.Inputs),
Name: v.Name,
Started: timestampFromPB(v.Started),
Completed: timestampFromPB(v.Completed),
Error: v.Error,
Cached: v.Cached,
ProgressGroup: v.ProgressGroup,
})
}
for _, v := range resp.Statuses {
s.Statuses = append(s.Statuses, &client.VertexStatus{
ID: v.ID,
Vertex: digest.Digest(v.Vertex),
Name: v.Name,
Total: v.Total,
Current: v.Current,
Timestamp: v.Timestamp.AsTime(),
Started: timestampFromPB(v.Started),
Completed: timestampFromPB(v.Completed),
})
}
for _, v := range resp.Logs {
s.Logs = append(s.Logs, &client.VertexLog{
Vertex: digest.Digest(v.Vertex),
Stream: int(v.Stream),
Data: v.Msg,
Timestamp: v.Timestamp.AsTime(),
})
}
for _, v := range resp.Warnings {
s.Warnings = append(s.Warnings, &client.VertexWarning{
Vertex: digest.Digest(v.Vertex),
Level: int(v.Level),
Short: v.Short,
Detail: v.Detail,
URL: v.Url,
SourceInfo: v.Info,
Range: v.Ranges,
})
}
return &s
}
func timestampFromPB(ts *timestamppb.Timestamp) *time.Time {
if ts == nil {
return nil
}
t := ts.AsTime()
if t.IsZero() {
return nil
}
return &t
}
func timestampToPB(ts *time.Time) *timestamppb.Timestamp {
if ts == nil {
return nil
}
return timestamppb.New(*ts)
}
func digestSliceFromPB(elems []string) []digest.Digest {
clone := make([]digest.Digest, len(elems))
for i, e := range elems {
clone[i] = digest.Digest(e)
}
return clone
}
func digestSliceToPB(elems []digest.Digest) []string {
clone := make([]string, len(elems))
for i, e := range elems {
clone[i] = string(e)
}
return clone
}

View File

@@ -5,6 +5,12 @@ import (
"github.com/moby/buildkit/session/secrets/secretsprovider"
)
type Secret struct {
ID string
FilePath string
Env string
}
func CreateSecrets(secrets []*Secret) (session.Attachable, error) {
fs := make([]secretsprovider.Source, 0, len(secrets))
for _, secret := range secrets {

View File

@@ -7,6 +7,11 @@ import (
"github.com/moby/buildkit/session/sshforward/sshprovider"
)
type SSH struct {
ID string
Paths []string
}
func CreateSSH(ssh []*SSH) (session.Attachable, error) {
configs := make([]sshprovider.AgentConfig, 0, len(ssh))
for _, ssh := range ssh {

11
controller/pb/ulimit.go Normal file
View File

@@ -0,0 +1,11 @@
package pb
type UlimitOpt struct {
Values map[string]*Ulimit
}
type Ulimit struct {
Name string
Hard int64
Soft int64
}

View File

@@ -73,9 +73,9 @@ func (m *Manager) CancelRunningProcesses() {
}
// ListProcesses lists all running processes.
func (m *Manager) ListProcesses() (res []*pb.ProcessInfo) {
func (m *Manager) ListProcesses() (res []*ProcessInfo) {
m.processes.Range(func(key, value any) bool {
res = append(res, &pb.ProcessInfo{
res = append(res, &ProcessInfo{
ProcessID: key.(string),
InvokeConfig: value.(*Process).invokeConfig,
})
@@ -154,3 +154,8 @@ func (m *Manager) StartProcess(pid string, resultCtx *build.ResultHandle, cfg *p
return p, nil
}
type ProcessInfo struct {
ProcessID string
InvokeConfig *pb.InvokeConfig
}

View File

@@ -1,243 +0,0 @@
package remote
import (
"context"
"io"
"sync"
"time"
"github.com/containerd/containerd/v2/defaults"
"github.com/containerd/containerd/v2/pkg/dialer"
"github.com/docker/buildx/build"
"github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/util/progress"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/util/grpcerrors"
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/backoff"
"google.golang.org/grpc/credentials/insecure"
)
func NewClient(ctx context.Context, addr string) (*Client, error) {
backoffConfig := backoff.DefaultConfig
backoffConfig.MaxDelay = 3 * time.Second
connParams := grpc.ConnectParams{
Backoff: backoffConfig,
}
gopts := []grpc.DialOption{
//nolint:staticcheck // ignore SA1019: WithBlock is deprecated and does not work with NewClient.
grpc.WithBlock(),
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithConnectParams(connParams),
grpc.WithContextDialer(dialer.ContextDialer),
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize)),
grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize)),
grpc.WithUnaryInterceptor(grpcerrors.UnaryClientInterceptor),
grpc.WithStreamInterceptor(grpcerrors.StreamClientInterceptor),
}
//nolint:staticcheck // ignore SA1019: Recommended NewClient has different behavior from DialContext.
conn, err := grpc.DialContext(ctx, dialer.DialAddress(addr), gopts...)
if err != nil {
return nil, err
}
return &Client{conn: conn}, nil
}
type Client struct {
conn *grpc.ClientConn
closeOnce sync.Once
}
func (c *Client) Close() (err error) {
c.closeOnce.Do(func() {
err = c.conn.Close()
})
return
}
func (c *Client) Version(ctx context.Context) (string, string, string, error) {
res, err := c.client().Info(ctx, &pb.InfoRequest{})
if err != nil {
return "", "", "", err
}
v := res.BuildxVersion
return v.Package, v.Version, v.Revision, nil
}
func (c *Client) List(ctx context.Context) (keys []string, retErr error) {
res, err := c.client().List(ctx, &pb.ListRequest{})
if err != nil {
return nil, err
}
return res.Keys, nil
}
func (c *Client) Disconnect(ctx context.Context, sessionID string) error {
if sessionID == "" {
return nil
}
_, err := c.client().Disconnect(ctx, &pb.DisconnectRequest{SessionID: sessionID})
return err
}
func (c *Client) ListProcesses(ctx context.Context, sessionID string) (infos []*pb.ProcessInfo, retErr error) {
res, err := c.client().ListProcesses(ctx, &pb.ListProcessesRequest{SessionID: sessionID})
if err != nil {
return nil, err
}
return res.Infos, nil
}
func (c *Client) DisconnectProcess(ctx context.Context, sessionID, pid string) error {
_, err := c.client().DisconnectProcess(ctx, &pb.DisconnectProcessRequest{SessionID: sessionID, ProcessID: pid})
return err
}
func (c *Client) Invoke(ctx context.Context, sessionID string, pid string, invokeConfig *pb.InvokeConfig, in io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error {
if sessionID == "" || pid == "" {
return errors.New("build session ID must be specified")
}
stream, err := c.client().Invoke(ctx)
if err != nil {
return err
}
return attachIO(ctx, stream, &pb.InitMessage{SessionID: sessionID, ProcessID: pid, InvokeConfig: invokeConfig}, ioAttachConfig{
stdin: in,
stdout: stdout,
stderr: stderr,
// TODO: Signal, Resize
})
}
func (c *Client) Inspect(ctx context.Context, sessionID string) (*pb.InspectResponse, error) {
return c.client().Inspect(ctx, &pb.InspectRequest{SessionID: sessionID})
}
func (c *Client) Build(ctx context.Context, options *pb.BuildOptions, in io.ReadCloser, progress progress.Writer) (string, *client.SolveResponse, *build.Inputs, error) {
ref := identity.NewID()
statusChan := make(chan *client.SolveStatus)
eg, egCtx := errgroup.WithContext(ctx)
var resp *client.SolveResponse
eg.Go(func() error {
defer close(statusChan)
var err error
resp, err = c.build(egCtx, ref, options, in, statusChan)
return err
})
eg.Go(func() error {
for s := range statusChan {
st := s
progress.Write(st)
}
return nil
})
return ref, resp, nil, eg.Wait()
}
func (c *Client) build(ctx context.Context, sessionID string, options *pb.BuildOptions, in io.ReadCloser, statusChan chan *client.SolveStatus) (*client.SolveResponse, error) {
eg, egCtx := errgroup.WithContext(ctx)
done := make(chan struct{})
var resp *client.SolveResponse
eg.Go(func() error {
defer close(done)
pbResp, err := c.client().Build(egCtx, &pb.BuildRequest{
SessionID: sessionID,
Options: options,
})
if err != nil {
return err
}
resp = &client.SolveResponse{
ExporterResponse: pbResp.ExporterResponse,
}
return nil
})
eg.Go(func() error {
stream, err := c.client().Status(egCtx, &pb.StatusRequest{
SessionID: sessionID,
})
if err != nil {
return err
}
for {
resp, err := stream.Recv()
if err != nil {
if err == io.EOF {
return nil
}
return errors.Wrap(err, "failed to receive status")
}
statusChan <- pb.FromControlStatus(resp)
}
})
if in != nil {
eg.Go(func() error {
stream, err := c.client().Input(egCtx)
if err != nil {
return err
}
if err := stream.Send(&pb.InputMessage{
Input: &pb.InputMessage_Init{
Init: &pb.InputInitMessage{
SessionID: sessionID,
},
},
}); err != nil {
return errors.Wrap(err, "failed to init input")
}
inReader, inWriter := io.Pipe()
eg2, _ := errgroup.WithContext(ctx)
eg2.Go(func() error {
<-done
return inWriter.Close()
})
go func() {
// do not wait for read completion but return here and let the caller send EOF
// this allows us to return on ctx.Done() without being blocked by this reader.
io.Copy(inWriter, in)
inWriter.Close()
}()
eg2.Go(func() error {
for {
buf := make([]byte, 32*1024)
n, err := inReader.Read(buf)
if err != nil {
if err == io.EOF {
break // break loop and send EOF
}
return err
} else if n > 0 {
if err := stream.Send(&pb.InputMessage{
Input: &pb.InputMessage_Data{
Data: &pb.DataMessage{
Data: buf[:n],
},
},
}); err != nil {
return err
}
}
}
return stream.Send(&pb.InputMessage{
Input: &pb.InputMessage_Data{
Data: &pb.DataMessage{
EOF: true,
},
},
})
})
return eg2.Wait()
})
}
return resp, eg.Wait()
}
func (c *Client) client() pb.ControllerClient {
return pb.NewControllerClient(c.conn)
}

View File

@@ -1,335 +0,0 @@
//go:build linux
package remote
import (
"context"
"fmt"
"io"
"net"
"os"
"os/exec"
"os/signal"
"path/filepath"
"strconv"
"syscall"
"time"
"github.com/containerd/log"
"github.com/docker/buildx/build"
cbuild "github.com/docker/buildx/controller/build"
"github.com/docker/buildx/controller/control"
controllerapi "github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/util/confutil"
"github.com/docker/buildx/util/progress"
"github.com/docker/buildx/version"
"github.com/docker/cli/cli/command"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/util/grpcerrors"
"github.com/pelletier/go-toml"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"google.golang.org/grpc"
)
const (
serveCommandName = "_INTERNAL_SERVE"
)
var (
defaultLogFilename = fmt.Sprintf("buildx.%s.log", version.Revision)
defaultSocketFilename = fmt.Sprintf("buildx.%s.sock", version.Revision)
defaultPIDFilename = fmt.Sprintf("buildx.%s.pid", version.Revision)
)
type serverConfig struct {
// Specify buildx server root
Root string `toml:"root"`
// LogLevel sets the logging level [trace, debug, info, warn, error, fatal, panic]
LogLevel string `toml:"log_level"`
// Specify file to output buildx server log
LogFile string `toml:"log_file"`
}
func NewRemoteBuildxController(ctx context.Context, dockerCli command.Cli, opts control.ControlOptions, logger progress.SubLogger) (control.BuildxController, error) {
rootDir := opts.Root
if rootDir == "" {
rootDir = rootDataDir(dockerCli)
}
serverRoot := filepath.Join(rootDir, "shared")
// connect to buildx server if it is already running
ctx2, cancel := context.WithCancelCause(ctx)
ctx2, _ = context.WithTimeoutCause(ctx2, 1*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet,lostcancel // no need to manually cancel this context as we already rely on parent
c, err := newBuildxClientAndCheck(ctx2, filepath.Join(serverRoot, defaultSocketFilename))
cancel(errors.WithStack(context.Canceled))
if err != nil {
if !errors.Is(err, context.DeadlineExceeded) {
return nil, errors.Wrap(err, "cannot connect to the buildx server")
}
} else {
return &buildxController{c, serverRoot}, nil
}
// start buildx server via subcommand
err = logger.Wrap("no buildx server found; launching...", func() error {
launchFlags := []string{}
if opts.ServerConfig != "" {
launchFlags = append(launchFlags, "--config", opts.ServerConfig)
}
logFile, err := getLogFilePath(dockerCli, opts.ServerConfig)
if err != nil {
return err
}
wait, err := launch(ctx, logFile, append([]string{serveCommandName}, launchFlags...)...)
if err != nil {
return err
}
go wait()
// wait for buildx server to be ready
ctx2, cancel = context.WithCancelCause(ctx)
ctx2, _ = context.WithTimeoutCause(ctx2, 10*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet,lostcancel // no need to manually cancel this context as we already rely on parent
c, err = newBuildxClientAndCheck(ctx2, filepath.Join(serverRoot, defaultSocketFilename))
cancel(errors.WithStack(context.Canceled))
if err != nil {
return errors.Wrap(err, "cannot connect to the buildx server")
}
return nil
})
if err != nil {
return nil, err
}
return &buildxController{c, serverRoot}, nil
}
func AddControllerCommands(cmd *cobra.Command, dockerCli command.Cli) {
cmd.AddCommand(
serveCmd(dockerCli),
)
}
func serveCmd(dockerCli command.Cli) *cobra.Command {
var serverConfigPath string
cmd := &cobra.Command{
Use: fmt.Sprintf("%s [OPTIONS]", serveCommandName),
Hidden: true,
RunE: func(cmd *cobra.Command, args []string) error {
// Parse config
config, err := getConfig(dockerCli, serverConfigPath)
if err != nil {
return err
}
if config.LogLevel == "" {
logrus.SetLevel(logrus.InfoLevel)
} else {
lvl, err := logrus.ParseLevel(config.LogLevel)
if err != nil {
return errors.Wrap(err, "failed to prepare logger")
}
logrus.SetLevel(lvl)
}
logrus.SetFormatter(&logrus.JSONFormatter{
TimestampFormat: log.RFC3339NanoFixed,
})
root, err := prepareRootDir(dockerCli, config)
if err != nil {
return err
}
pidF := filepath.Join(root, defaultPIDFilename)
if err := os.WriteFile(pidF, fmt.Appendf(nil, "%d", os.Getpid()), 0600); err != nil {
return err
}
defer func() {
if err := os.Remove(pidF); err != nil {
logrus.Errorf("failed to clean up info file %q: %v", pidF, err)
}
}()
// prepare server
b := NewServer(func(ctx context.Context, options *controllerapi.BuildOptions, stdin io.Reader, progress progress.Writer) (*client.SolveResponse, *build.ResultHandle, *build.Inputs, error) {
return cbuild.RunBuild(ctx, dockerCli, options, stdin, progress, true)
})
defer b.Close()
// serve server
addr := filepath.Join(root, defaultSocketFilename)
if err := os.Remove(addr); err != nil && !os.IsNotExist(err) { // avoid EADDRINUSE
return err
}
defer func() {
if err := os.Remove(addr); err != nil {
logrus.Errorf("failed to clean up socket %q: %v", addr, err)
}
}()
logrus.Infof("starting server at %q", addr)
l, err := net.Listen("unix", addr)
if err != nil {
return err
}
rpc := grpc.NewServer(
grpc.UnaryInterceptor(grpcerrors.UnaryServerInterceptor),
grpc.StreamInterceptor(grpcerrors.StreamServerInterceptor),
)
controllerapi.RegisterControllerServer(rpc, b)
doneCh := make(chan struct{})
errCh := make(chan error, 1)
go func() {
defer close(doneCh)
if err := rpc.Serve(l); err != nil {
errCh <- errors.Wrapf(err, "error on serving via socket %q", addr)
}
}()
var s os.Signal
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT)
signal.Notify(sigCh, syscall.SIGTERM)
select {
case err := <-errCh:
logrus.Errorf("got error %s, exiting", err)
return err
case s = <-sigCh:
logrus.Infof("got signal %s, exiting", s)
return nil
case <-doneCh:
logrus.Infof("rpc server done, exiting")
return nil
}
},
}
flags := cmd.Flags()
flags.StringVar(&serverConfigPath, "config", "", "Specify buildx server config file")
return cmd
}
func getLogFilePath(dockerCli command.Cli, configPath string) (string, error) {
config, err := getConfig(dockerCli, configPath)
if err != nil {
return "", err
}
if config.LogFile == "" {
root, err := prepareRootDir(dockerCli, config)
if err != nil {
return "", err
}
return filepath.Join(root, defaultLogFilename), nil
}
return config.LogFile, nil
}
func getConfig(dockerCli command.Cli, configPath string) (*serverConfig, error) {
var defaultConfigPath bool
if configPath == "" {
defaultRoot := rootDataDir(dockerCli)
configPath = filepath.Join(defaultRoot, "config.toml")
defaultConfigPath = true
}
var config serverConfig
tree, err := toml.LoadFile(configPath)
if err != nil && !(os.IsNotExist(err) && defaultConfigPath) {
return nil, errors.Wrapf(err, "failed to read config %q", configPath)
} else if err == nil {
if err := tree.Unmarshal(&config); err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal config %q", configPath)
}
}
return &config, nil
}
func prepareRootDir(dockerCli command.Cli, config *serverConfig) (string, error) {
rootDir := config.Root
if rootDir == "" {
rootDir = rootDataDir(dockerCli)
}
if rootDir == "" {
return "", errors.New("buildx root dir must be determined")
}
if err := os.MkdirAll(rootDir, 0700); err != nil {
return "", err
}
serverRoot := filepath.Join(rootDir, "shared")
if err := os.MkdirAll(serverRoot, 0700); err != nil {
return "", err
}
return serverRoot, nil
}
func rootDataDir(dockerCli command.Cli) string {
return filepath.Join(confutil.NewConfig(dockerCli).Dir(), "controller")
}
func newBuildxClientAndCheck(ctx context.Context, addr string) (*Client, error) {
c, err := NewClient(ctx, addr)
if err != nil {
return nil, err
}
p, v, r, err := c.Version(ctx)
if err != nil {
return nil, err
}
logrus.Debugf("connected to server (\"%v %v %v\")", p, v, r)
if !(p == version.Package && v == version.Version && r == version.Revision) {
return nil, errors.Errorf("version mismatch (client: \"%v %v %v\", server: \"%v %v %v\")", version.Package, version.Version, version.Revision, p, v, r)
}
return c, nil
}
type buildxController struct {
*Client
serverRoot string
}
func (c *buildxController) Kill(ctx context.Context) error {
pidB, err := os.ReadFile(filepath.Join(c.serverRoot, defaultPIDFilename))
if err != nil {
return err
}
pid, err := strconv.ParseInt(string(pidB), 10, 64)
if err != nil {
return err
}
if pid <= 0 {
return errors.New("no PID is recorded for buildx server")
}
p, err := os.FindProcess(int(pid))
if err != nil {
return err
}
if err := p.Signal(syscall.SIGINT); err != nil {
return err
}
// TODO: Should we send SIGKILL if process doesn't finish?
return nil
}
func launch(ctx context.Context, logFile string, args ...string) (func() error, error) {
// set absolute path of binary, since we set the working directory to the root
pathname, err := os.Executable()
if err != nil {
return nil, err
}
bCmd := exec.CommandContext(ctx, pathname, args...)
if logFile != "" {
f, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return nil, err
}
defer f.Close()
bCmd.Stdout = f
bCmd.Stderr = f
}
bCmd.Stdin = nil
bCmd.Dir = "/"
bCmd.SysProcAttr = &syscall.SysProcAttr{
Setsid: true,
}
if err := bCmd.Start(); err != nil {
return nil, err
}
return bCmd.Wait, nil
}

View File

@@ -1,19 +0,0 @@
//go:build !linux
package remote
import (
"context"
"github.com/docker/buildx/controller/control"
"github.com/docker/buildx/util/progress"
"github.com/docker/cli/cli/command"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
func NewRemoteBuildxController(ctx context.Context, dockerCli command.Cli, opts control.ControlOptions, logger progress.SubLogger) (control.BuildxController, error) {
return nil, errors.New("remote buildx unsupported")
}
func AddControllerCommands(cmd *cobra.Command, dockerCli command.Cli) {}

View File

@@ -1,430 +0,0 @@
package remote
import (
"context"
"io"
"syscall"
"time"
"github.com/docker/buildx/controller/pb"
"github.com/moby/sys/signal"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sync/errgroup"
)
type msgStream interface {
Send(*pb.Message) error
Recv() (*pb.Message, error)
}
type ioServerConfig struct {
stdin io.WriteCloser
stdout, stderr io.ReadCloser
// signalFn is a callback function called when a signal is reached to the client.
signalFn func(context.Context, syscall.Signal) error
// resizeFn is a callback function called when a resize event is reached to the client.
resizeFn func(context.Context, winSize) error
}
func serveIO(attachCtx context.Context, srv msgStream, initFn func(*pb.InitMessage) error, ioConfig *ioServerConfig) (err error) {
stdin, stdout, stderr := ioConfig.stdin, ioConfig.stdout, ioConfig.stderr
stream := &debugStream{srv, "server=" + time.Now().String()}
eg, ctx := errgroup.WithContext(attachCtx)
done := make(chan struct{})
msg, err := receive(ctx, stream)
if err != nil {
return err
}
init := msg.GetInit()
if init == nil {
return errors.Errorf("unexpected message: %T; wanted init", msg.GetInput())
}
sessionID := init.SessionID
if sessionID == "" {
return errors.New("no session ID is provided")
}
if err := initFn(init); err != nil {
return errors.Wrap(err, "failed to initialize IO server")
}
if stdout != nil {
stdoutReader, stdoutWriter := io.Pipe()
eg.Go(func() error {
<-done
return stdoutWriter.Close()
})
go func() {
// do not wait for read completion but return here and let the caller send EOF
// this allows us to return on ctx.Done() without being blocked by this reader.
io.Copy(stdoutWriter, stdout)
stdoutWriter.Close()
}()
eg.Go(func() error {
defer stdoutReader.Close()
return copyToStream(1, stream, stdoutReader)
})
}
if stderr != nil {
stderrReader, stderrWriter := io.Pipe()
eg.Go(func() error {
<-done
return stderrWriter.Close()
})
go func() {
// do not wait for read completion but return here and let the caller send EOF
// this allows us to return on ctx.Done() without being blocked by this reader.
io.Copy(stderrWriter, stderr)
stderrWriter.Close()
}()
eg.Go(func() error {
defer stderrReader.Close()
return copyToStream(2, stream, stderrReader)
})
}
msgCh := make(chan *pb.Message)
eg.Go(func() error {
defer close(msgCh)
for {
msg, err := receive(ctx, stream)
if err != nil {
return err
}
select {
case msgCh <- msg:
case <-done:
return nil
case <-ctx.Done():
return nil
}
}
})
eg.Go(func() error {
defer close(done)
for {
var msg *pb.Message
select {
case msg = <-msgCh:
case <-ctx.Done():
return nil
}
if msg == nil {
return nil
}
if file := msg.GetFile(); file != nil {
if file.Fd != 0 {
return errors.Errorf("unexpected fd: %v", file.Fd)
}
if stdin == nil {
continue // no stdin destination is specified so ignore the data
}
if len(file.Data) > 0 {
_, err := stdin.Write(file.Data)
if err != nil {
return err
}
}
if file.EOF {
stdin.Close()
}
} else if resize := msg.GetResize(); resize != nil {
if ioConfig.resizeFn != nil {
ioConfig.resizeFn(ctx, winSize{
cols: resize.Cols,
rows: resize.Rows,
})
}
} else if sig := msg.GetSignal(); sig != nil {
if ioConfig.signalFn != nil {
syscallSignal, ok := signal.SignalMap[sig.Name]
if !ok {
continue
}
ioConfig.signalFn(ctx, syscallSignal)
}
} else {
return errors.Errorf("unexpected message: %T", msg.GetInput())
}
}
})
return eg.Wait()
}
type ioAttachConfig struct {
stdin io.ReadCloser
stdout, stderr io.WriteCloser
signal <-chan syscall.Signal
resize <-chan winSize
}
type winSize struct {
rows uint32
cols uint32
}
func attachIO(ctx context.Context, stream msgStream, initMessage *pb.InitMessage, cfg ioAttachConfig) (retErr error) {
eg, ctx := errgroup.WithContext(ctx)
done := make(chan struct{})
if err := stream.Send(&pb.Message{
Input: &pb.Message_Init{
Init: initMessage,
},
}); err != nil {
return errors.Wrap(err, "failed to init")
}
if cfg.stdin != nil {
stdinReader, stdinWriter := io.Pipe()
eg.Go(func() error {
<-done
return stdinWriter.Close()
})
go func() {
// do not wait for read completion but return here and let the caller send EOF
// this allows us to return on ctx.Done() without being blocked by this reader.
io.Copy(stdinWriter, cfg.stdin)
stdinWriter.Close()
}()
eg.Go(func() error {
defer stdinReader.Close()
return copyToStream(0, stream, stdinReader)
})
}
if cfg.signal != nil {
eg.Go(func() error {
names := signalNames()
for {
var sig syscall.Signal
select {
case sig = <-cfg.signal:
case <-done:
return nil
case <-ctx.Done():
return nil
}
name := names[sig]
if name == "" {
continue
}
if err := stream.Send(&pb.Message{
Input: &pb.Message_Signal{
Signal: &pb.SignalMessage{
Name: name,
},
},
}); err != nil {
return errors.Wrap(err, "failed to send signal")
}
}
})
}
if cfg.resize != nil {
eg.Go(func() error {
for {
var win winSize
select {
case win = <-cfg.resize:
case <-done:
return nil
case <-ctx.Done():
return nil
}
if err := stream.Send(&pb.Message{
Input: &pb.Message_Resize{
Resize: &pb.ResizeMessage{
Rows: win.rows,
Cols: win.cols,
},
},
}); err != nil {
return errors.Wrap(err, "failed to send resize")
}
}
})
}
msgCh := make(chan *pb.Message)
eg.Go(func() error {
defer close(msgCh)
for {
msg, err := receive(ctx, stream)
if err != nil {
return err
}
select {
case msgCh <- msg:
case <-done:
return nil
case <-ctx.Done():
return nil
}
}
})
eg.Go(func() error {
eofs := make(map[uint32]struct{})
defer close(done)
for {
var msg *pb.Message
select {
case msg = <-msgCh:
case <-ctx.Done():
return nil
}
if msg == nil {
return nil
}
if file := msg.GetFile(); file != nil {
if _, ok := eofs[file.Fd]; ok {
continue
}
var out io.WriteCloser
switch file.Fd {
case 1:
out = cfg.stdout
case 2:
out = cfg.stderr
default:
return errors.Errorf("unsupported fd %d", file.Fd)
}
if out == nil {
logrus.Warnf("attachIO: no writer for fd %d", file.Fd)
continue
}
if len(file.Data) > 0 {
if _, err := out.Write(file.Data); err != nil {
return err
}
}
if file.EOF {
eofs[file.Fd] = struct{}{}
}
} else {
return errors.Errorf("unexpected message: %T", msg.GetInput())
}
}
})
return eg.Wait()
}
func receive(ctx context.Context, stream msgStream) (*pb.Message, error) {
msgCh := make(chan *pb.Message)
errCh := make(chan error)
go func() {
msg, err := stream.Recv()
if err != nil {
if errors.Is(err, io.EOF) {
return
}
errCh <- err
return
}
msgCh <- msg
}()
select {
case msg := <-msgCh:
return msg, nil
case err := <-errCh:
return nil, err
case <-ctx.Done():
return nil, context.Cause(ctx)
}
}
func copyToStream(fd uint32, snd msgStream, r io.Reader) error {
for {
buf := make([]byte, 32*1024)
n, err := r.Read(buf)
if err != nil {
if err == io.EOF {
break // break loop and send EOF
}
return err
} else if n > 0 {
if err := snd.Send(&pb.Message{
Input: &pb.Message_File{
File: &pb.FdMessage{
Fd: fd,
Data: buf[:n],
},
},
}); err != nil {
return err
}
}
}
return snd.Send(&pb.Message{
Input: &pb.Message_File{
File: &pb.FdMessage{
Fd: fd,
EOF: true,
},
},
})
}
func signalNames() map[syscall.Signal]string {
m := make(map[syscall.Signal]string, len(signal.SignalMap))
for name, value := range signal.SignalMap {
m[value] = name
}
return m
}
type debugStream struct {
msgStream
prefix string
}
func (s *debugStream) Send(msg *pb.Message) error {
switch m := msg.GetInput().(type) {
case *pb.Message_File:
if m.File.EOF {
logrus.Debugf("|---> File Message (sender:%v) fd=%d, EOF", s.prefix, m.File.Fd)
} else {
logrus.Debugf("|---> File Message (sender:%v) fd=%d, %d bytes", s.prefix, m.File.Fd, len(m.File.Data))
}
case *pb.Message_Resize:
logrus.Debugf("|---> Resize Message (sender:%v): %+v", s.prefix, m.Resize)
case *pb.Message_Signal:
logrus.Debugf("|---> Signal Message (sender:%v): %s", s.prefix, m.Signal.Name)
}
return s.msgStream.Send(msg)
}
func (s *debugStream) Recv() (*pb.Message, error) {
msg, err := s.msgStream.Recv()
if err != nil {
return nil, err
}
switch m := msg.GetInput().(type) {
case *pb.Message_File:
if m.File.EOF {
logrus.Debugf("|<--- File Message (receiver:%v) fd=%d, EOF", s.prefix, m.File.Fd)
} else {
logrus.Debugf("|<--- File Message (receiver:%v) fd=%d, %d bytes", s.prefix, m.File.Fd, len(m.File.Data))
}
case *pb.Message_Resize:
logrus.Debugf("|<--- Resize Message (receiver:%v): %+v", s.prefix, m.Resize)
case *pb.Message_Signal:
logrus.Debugf("|<--- Signal Message (receiver:%v): %s", s.prefix, m.Signal.Name)
}
return msg, nil
}

View File

@@ -1,445 +0,0 @@
package remote
import (
"context"
"io"
"sync"
"sync/atomic"
"time"
"github.com/docker/buildx/build"
controllererrors "github.com/docker/buildx/controller/errdefs"
"github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/controller/processes"
"github.com/docker/buildx/util/desktop"
"github.com/docker/buildx/util/ioset"
"github.com/docker/buildx/util/progress"
"github.com/docker/buildx/version"
"github.com/moby/buildkit/client"
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"
)
type BuildFunc func(ctx context.Context, options *pb.BuildOptions, stdin io.Reader, progress progress.Writer) (resp *client.SolveResponse, res *build.ResultHandle, inp *build.Inputs, err error)
func NewServer(buildFunc BuildFunc) *Server {
return &Server{
buildFunc: buildFunc,
}
}
type Server struct {
buildFunc BuildFunc
session map[string]*session
sessionMu sync.Mutex
}
type session struct {
buildOnGoing atomic.Bool
statusChan chan *pb.StatusResponse
cancelBuild func(error)
buildOptions *pb.BuildOptions
inputPipe *io.PipeWriter
result *build.ResultHandle
processes *processes.Manager
}
func (s *session) cancelRunningProcesses() {
s.processes.CancelRunningProcesses()
}
func (m *Server) ListProcesses(ctx context.Context, req *pb.ListProcessesRequest) (res *pb.ListProcessesResponse, err error) {
m.sessionMu.Lock()
defer m.sessionMu.Unlock()
s, ok := m.session[req.SessionID]
if !ok {
return nil, errors.Errorf("unknown session ID %q", req.SessionID)
}
res = new(pb.ListProcessesResponse)
res.Infos = append(res.Infos, s.processes.ListProcesses()...)
return res, nil
}
func (m *Server) DisconnectProcess(ctx context.Context, req *pb.DisconnectProcessRequest) (res *pb.DisconnectProcessResponse, err error) {
m.sessionMu.Lock()
defer m.sessionMu.Unlock()
s, ok := m.session[req.SessionID]
if !ok {
return nil, errors.Errorf("unknown session ID %q", req.SessionID)
}
return res, s.processes.DeleteProcess(req.ProcessID)
}
func (m *Server) Info(ctx context.Context, req *pb.InfoRequest) (res *pb.InfoResponse, err error) {
return &pb.InfoResponse{
BuildxVersion: &pb.BuildxVersion{
Package: version.Package,
Version: version.Version,
Revision: version.Revision,
},
}, nil
}
func (m *Server) List(ctx context.Context, req *pb.ListRequest) (res *pb.ListResponse, err error) {
keys := make(map[string]struct{})
m.sessionMu.Lock()
for k := range m.session {
keys[k] = struct{}{}
}
m.sessionMu.Unlock()
var keysL []string
for k := range keys {
keysL = append(keysL, k)
}
return &pb.ListResponse{
Keys: keysL,
}, nil
}
func (m *Server) Disconnect(ctx context.Context, req *pb.DisconnectRequest) (res *pb.DisconnectResponse, err error) {
sessionID := req.SessionID
if sessionID == "" {
return nil, errors.New("disconnect: empty session ID")
}
m.sessionMu.Lock()
if s, ok := m.session[sessionID]; ok {
if s.cancelBuild != nil {
s.cancelBuild(errors.WithStack(context.Canceled))
}
s.cancelRunningProcesses()
if s.result != nil {
s.result.Done()
}
}
delete(m.session, sessionID)
m.sessionMu.Unlock()
return &pb.DisconnectResponse{}, nil
}
func (m *Server) Close() error {
m.sessionMu.Lock()
for k := range m.session {
if s, ok := m.session[k]; ok {
if s.cancelBuild != nil {
s.cancelBuild(errors.WithStack(context.Canceled))
}
s.cancelRunningProcesses()
}
}
m.sessionMu.Unlock()
return nil
}
func (m *Server) Inspect(ctx context.Context, req *pb.InspectRequest) (*pb.InspectResponse, error) {
sessionID := req.SessionID
if sessionID == "" {
return nil, errors.New("inspect: empty session ID")
}
var bo *pb.BuildOptions
m.sessionMu.Lock()
if s, ok := m.session[sessionID]; ok {
bo = s.buildOptions
} else {
m.sessionMu.Unlock()
return nil, errors.Errorf("inspect: unknown key %v", sessionID)
}
m.sessionMu.Unlock()
return &pb.InspectResponse{Options: bo}, nil
}
func (m *Server) Build(ctx context.Context, req *pb.BuildRequest) (*pb.BuildResponse, error) {
sessionID := req.SessionID
if sessionID == "" {
return nil, errors.New("build: empty session ID")
}
// Prepare status channel and session
m.sessionMu.Lock()
if m.session == nil {
m.session = make(map[string]*session)
}
s, ok := m.session[sessionID]
if ok {
if !s.buildOnGoing.CompareAndSwap(false, true) {
m.sessionMu.Unlock()
return &pb.BuildResponse{}, errors.New("build ongoing")
}
s.cancelRunningProcesses()
s.result = nil
} else {
s = &session{}
s.buildOnGoing.Store(true)
}
s.processes = processes.NewManager()
statusChan := make(chan *pb.StatusResponse)
s.statusChan = statusChan
inR, inW := io.Pipe()
defer inR.Close()
s.inputPipe = inW
m.session[sessionID] = s
m.sessionMu.Unlock()
defer func() {
close(statusChan)
m.sessionMu.Lock()
s, ok := m.session[sessionID]
if ok {
s.statusChan = nil
s.buildOnGoing.Store(false)
}
m.sessionMu.Unlock()
}()
pw := pb.NewProgressWriter(statusChan)
// Build the specified request
ctx, cancel := context.WithCancelCause(ctx)
defer func() { cancel(errors.WithStack(context.Canceled)) }()
resp, res, _, buildErr := m.buildFunc(ctx, req.Options, inR, pw)
m.sessionMu.Lock()
if s, ok := m.session[sessionID]; ok {
// NOTE: buildFunc can return *build.ResultHandle even on error (e.g. when it's implemented using (github.com/docker/buildx/controller/build).RunBuild).
if res != nil {
s.result = res
s.cancelBuild = cancel
s.buildOptions = req.Options
m.session[sessionID] = s
if buildErr != nil {
var ref string
var ebr *desktop.ErrorWithBuildRef
if errors.As(buildErr, &ebr) {
ref = ebr.Ref
}
buildErr = controllererrors.WrapBuild(buildErr, sessionID, ref)
}
}
} else {
m.sessionMu.Unlock()
return nil, errors.Errorf("build: unknown session ID %v", sessionID)
}
m.sessionMu.Unlock()
if buildErr != nil {
return nil, buildErr
}
if resp == nil {
resp = &client.SolveResponse{}
}
return &pb.BuildResponse{
ExporterResponse: resp.ExporterResponse,
}, nil
}
func (m *Server) Status(req *pb.StatusRequest, stream pb.Controller_StatusServer) error {
sessionID := req.SessionID
if sessionID == "" {
return errors.New("status: empty session ID")
}
// Wait and get status channel prepared by Build()
var statusChan <-chan *pb.StatusResponse
for {
// TODO: timeout?
m.sessionMu.Lock()
if _, ok := m.session[sessionID]; !ok || m.session[sessionID].statusChan == nil {
m.sessionMu.Unlock()
time.Sleep(time.Millisecond) // TODO: wait Build without busy loop and make it cancellable
continue
}
statusChan = m.session[sessionID].statusChan
m.sessionMu.Unlock()
break
}
// forward status
for ss := range statusChan {
if ss == nil {
break
}
if err := stream.Send(ss); err != nil {
return err
}
}
return nil
}
func (m *Server) Input(stream pb.Controller_InputServer) (err error) {
// Get the target ref from init message
msg, err := stream.Recv()
if err != nil {
if !errors.Is(err, io.EOF) {
return err
}
return nil
}
init := msg.GetInit()
if init == nil {
return errors.Errorf("unexpected message: %T; wanted init", msg.GetInit())
}
sessionID := init.SessionID
if sessionID == "" {
return errors.New("input: no session ID is provided")
}
// Wait and get input stream pipe prepared by Build()
var inputPipeW *io.PipeWriter
for {
// TODO: timeout?
m.sessionMu.Lock()
if _, ok := m.session[sessionID]; !ok || m.session[sessionID].inputPipe == nil {
m.sessionMu.Unlock()
time.Sleep(time.Millisecond) // TODO: wait Build without busy loop and make it cancellable
continue
}
inputPipeW = m.session[sessionID].inputPipe
m.sessionMu.Unlock()
break
}
// Forward input stream
eg, ctx := errgroup.WithContext(context.TODO())
done := make(chan struct{})
msgCh := make(chan *pb.InputMessage)
eg.Go(func() error {
defer close(msgCh)
for {
msg, err := stream.Recv()
if err != nil {
if !errors.Is(err, io.EOF) {
return err
}
return nil
}
select {
case msgCh <- msg:
case <-done:
return nil
case <-ctx.Done():
return nil
}
}
})
eg.Go(func() (retErr error) {
defer close(done)
defer func() {
if retErr != nil {
inputPipeW.CloseWithError(retErr)
return
}
inputPipeW.Close()
}()
for {
var msg *pb.InputMessage
select {
case msg = <-msgCh:
case <-ctx.Done():
return context.Cause(ctx)
}
if msg == nil {
return nil
}
if data := msg.GetData(); data != nil {
if len(data.Data) > 0 {
_, err := inputPipeW.Write(data.Data)
if err != nil {
return err
}
}
if data.EOF {
return nil
}
}
}
})
return eg.Wait()
}
func (m *Server) Invoke(srv pb.Controller_InvokeServer) error {
containerIn, containerOut := ioset.Pipe()
defer func() { containerOut.Close(); containerIn.Close() }()
initDoneCh := make(chan *processes.Process)
initErrCh := make(chan error)
eg, egCtx := errgroup.WithContext(context.TODO())
srvIOCtx, srvIOCancel := context.WithCancelCause(egCtx)
eg.Go(func() error {
defer srvIOCancel(errors.WithStack(context.Canceled))
return serveIO(srvIOCtx, srv, func(initMessage *pb.InitMessage) (retErr error) {
defer func() {
if retErr != nil {
initErrCh <- retErr
}
}()
sessionID := initMessage.SessionID
cfg := initMessage.InvokeConfig
m.sessionMu.Lock()
s, ok := m.session[sessionID]
if !ok {
m.sessionMu.Unlock()
return errors.Errorf("invoke: unknown session ID %v", sessionID)
}
m.sessionMu.Unlock()
pid := initMessage.ProcessID
if pid == "" {
return errors.Errorf("invoke: specify process ID")
}
proc, ok := s.processes.Get(pid)
if !ok {
// Start a new process.
if cfg == nil {
return errors.New("no container config is provided")
}
var err error
proc, err = s.processes.StartProcess(pid, s.result, cfg)
if err != nil {
return err
}
}
// Attach containerIn to this process
proc.ForwardIO(&containerIn, srvIOCancel)
initDoneCh <- proc
return nil
}, &ioServerConfig{
stdin: containerOut.Stdin,
stdout: containerOut.Stdout,
stderr: containerOut.Stderr,
// TODO: signal, resize
})
})
eg.Go(func() (rErr error) {
defer srvIOCancel(errors.WithStack(context.Canceled))
// Wait for init done
var proc *processes.Process
select {
case p := <-initDoneCh:
proc = p
case err := <-initErrCh:
return err
case <-egCtx.Done():
return egCtx.Err()
}
// Wait for IO done
select {
case <-srvIOCtx.Done():
return srvIOCtx.Err()
case err := <-proc.Done():
return err
case <-egCtx.Done():
return egCtx.Err()
}
})
return eg.Wait()
}

View File

@@ -28,7 +28,6 @@ Start a build
| [`--cgroup-parent`](#cgroup-parent) | `string` | | Set the parent cgroup for the `RUN` instructions during build |
| [`--check`](#check) | `bool` | | Shorthand for `--call=check` |
| `-D`, `--debug` | `bool` | | Enable debug logging |
| `--detach` | `bool` | | Detach buildx server (supported only on linux) (EXPERIMENTAL) |
| [`-f`](#file), [`--file`](#file) | `string` | | Name of the Dockerfile (default: `PATH/Dockerfile`) |
| `--iidfile` | `string` | | Write the image ID to a file |
| `--label` | `stringArray` | | Set metadata for an image |
@@ -44,10 +43,8 @@ Start a build
| `--pull` | `bool` | | Always attempt to pull all referenced images |
| [`--push`](#push) | `bool` | | Shorthand for `--output=type=registry` |
| `-q`, `--quiet` | `bool` | | Suppress the build output and print image ID on success |
| `--root` | `string` | | Specify root directory of server to connect (EXPERIMENTAL) |
| [`--sbom`](#sbom) | `string` | | Shorthand for `--attest=type=sbom` |
| [`--secret`](#secret) | `stringArray` | | Secret to expose to the build (format: `id=mysecret[,src=/local/secret]`) |
| `--server-config` | `string` | | Specify buildx server config file (used only when launching new server) (EXPERIMENTAL) |
| [`--shm-size`](#shm-size) | `bytes` | `0` | Shared memory size for build containers |
| [`--ssh`](#ssh) | `stringArray` | | SSH agent socket or keys to expose to the build (format: `default\|<id>[=<socket>\|<key>[,<key>]]`) |
| [`-t`](#tag), [`--tag`](#tag) | `stringArray` | | Name and optionally a tag (format: `name:tag`) |

View File

@@ -12,16 +12,13 @@ Start debugger (EXPERIMENTAL)
### Options
| Name | Type | Default | Description |
|:------------------|:---------|:--------|:--------------------------------------------------------------------------------------------------------------------|
| `--builder` | `string` | | Override the configured builder instance |
| `-D`, `--debug` | `bool` | | Enable debug logging |
| `--detach` | `bool` | `true` | Detach buildx server for the monitor (supported only on linux) (EXPERIMENTAL) |
| `--invoke` | `string` | | Launch a monitor with executing specified command (EXPERIMENTAL) |
| `--on` | `string` | `error` | When to launch the monitor ([always, error]) (EXPERIMENTAL) |
| `--progress` | `string` | `auto` | Set type of progress output (`auto`, `plain`, `tty`, `rawjson`) for the monitor. Use plain to show container output |
| `--root` | `string` | | Specify root directory of server to connect for the monitor (EXPERIMENTAL) |
| `--server-config` | `string` | | Specify buildx server config file for the monitor (used only when launching new server) (EXPERIMENTAL) |
| Name | Type | Default | Description |
|:----------------|:---------|:--------|:--------------------------------------------------------------------------------------------------------------------|
| `--builder` | `string` | | Override the configured builder instance |
| `-D`, `--debug` | `bool` | | Enable debug logging |
| `--invoke` | `string` | | Launch a monitor with executing specified command (EXPERIMENTAL) |
| `--on` | `string` | `error` | When to launch the monitor ([always, error]) (EXPERIMENTAL) |
| `--progress` | `string` | `auto` | Set type of progress output (`auto`, `plain`, `tty`, `rawjson`) for the monitor. Use plain to show container output |
<!---MARKER_GEN_END-->

View File

@@ -24,7 +24,6 @@ Start a build
| `--cgroup-parent` | `string` | | Set the parent cgroup for the `RUN` instructions during build |
| `--check` | `bool` | | Shorthand for `--call=check` |
| `-D`, `--debug` | `bool` | | Enable debug logging |
| `--detach` | `bool` | | Detach buildx server (supported only on linux) (EXPERIMENTAL) |
| `-f`, `--file` | `string` | | Name of the Dockerfile (default: `PATH/Dockerfile`) |
| `--iidfile` | `string` | | Write the image ID to a file |
| `--label` | `stringArray` | | Set metadata for an image |
@@ -40,10 +39,8 @@ Start a build
| `--pull` | `bool` | | Always attempt to pull all referenced images |
| `--push` | `bool` | | Shorthand for `--output=type=registry` |
| `-q`, `--quiet` | `bool` | | Suppress the build output and print image ID on success |
| `--root` | `string` | | Specify root directory of server to connect (EXPERIMENTAL) |
| `--sbom` | `string` | | Shorthand for `--attest=type=sbom` |
| `--secret` | `stringArray` | | Secret to expose to the build (format: `id=mysecret[,src=/local/secret]`) |
| `--server-config` | `string` | | Specify buildx server config file (used only when launching new server) (EXPERIMENTAL) |
| `--shm-size` | `bytes` | `0` | Shared memory size for build containers |
| `--ssh` | `stringArray` | | SSH agent socket or keys to expose to the build (format: `default\|<id>[=<socket>\|<key>[,<key>]]`) |
| `-t`, `--tag` | `stringArray` | | Name and optionally a tag (format: `name:tag`) |

View File

@@ -27,3 +27,32 @@ Commands to work on build records
<!---MARKER_GEN_END-->
### Build references
Most `buildx history` subcommands accept a build reference to identify which
build to act on. You can specify the build in two ways:
- By build ID, fetched by `docker buildx history ls`:
```console
docker buildx history export qu2gsuo8ejqrwdfii23xkkckt --output build.dockerbuild
```
- By relative offset, to refer to recent builds:
```console
docker buildx history export ^1 --output build.dockerbuild
```
- `^0` or no reference targets the most recent build
- `^1` refers to the build before the most recent
- `^2` refers to two builds back, and so on
Offset references are supported in the following `buildx history` commands:
- `logs`
- `inspect`
- `open`
- `trace`
- `export`
- `rm`

View File

@@ -5,13 +5,77 @@ Export a build into Docker Desktop bundle
### Options
| Name | Type | Default | Description |
|:-----------------|:---------|:--------|:-----------------------------------------|
| `--all` | `bool` | | Export all records for the builder |
| `--builder` | `string` | | Override the configured builder instance |
| `-D`, `--debug` | `bool` | | Enable debug logging |
| `-o`, `--output` | `string` | | Output file path |
| Name | Type | Default | Description |
|:---------------------------------------|:---------|:--------|:-----------------------------------------|
| [`--all`](#all) | `bool` | | Export all records for the builder |
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
| [`-D`](#debug), [`--debug`](#debug) | `bool` | | Enable debug logging |
| [`-o`](#output), [`--output`](#output) | `string` | | Output file path |
<!---MARKER_GEN_END-->
## Description
Export one or more build records to `.dockerbuild` archive files. These archives
contain metadata, logs, and build outputs, and can be imported into Docker
Desktop or shared across environments.
## Examples
### <a name="output"></a> Export a single build to a custom file (--output)
```console
docker buildx history export qu2gsuo8ejqrwdfii23xkkckt --output mybuild.dockerbuild
```
You can find build IDs by running:
```console
docker buildx history ls
```
### <a name="o"></a> Export multiple builds to individual `.dockerbuild` files (-o)
To export two builds to separate files:
```console
# Using build IDs
docker buildx history export qu2gsuo8ejqrwdfii23xkkckt qsiifiuf1ad9pa9qvppc0z1l3 -o multi.dockerbuild
# Or using relative offsets
docker buildx history export ^1 ^2 -o multi.dockerbuild
```
Or use shell redirection:
```console
docker buildx history export ^1 > mybuild.dockerbuild
docker buildx history export ^2 > backend-build.dockerbuild
```
### <a name="all"></a> Export all build records to a file (--all)
Use the `--all` flag and redirect the output:
```console
docker buildx history export --all > all-builds.dockerbuild
```
Or use the `--output` flag:
```console
docker buildx history export --all -o all-builds.dockerbuild
```
### <a name="builder"></a> Use a specific builder instance (--builder)
```console
docker buildx history export --builder builder0 ^1 -o builder0-build.dockerbuild
```
### <a name="debug"></a> Enable debug logging (--debug)
```console
docker buildx history export --debug qu2gsuo8ejqrwdfii23xkkckt -o debug-build.dockerbuild
```

View File

@@ -5,12 +5,43 @@ Import a build into Docker Desktop
### Options
| Name | Type | Default | Description |
|:----------------|:--------------|:--------|:-----------------------------------------|
| `--builder` | `string` | | Override the configured builder instance |
| `-D`, `--debug` | `bool` | | Enable debug logging |
| `-f`, `--file` | `stringArray` | | Import from a file path |
| Name | Type | Default | Description |
|:---------------------------------|:--------------|:--------|:-----------------------------------------|
| `--builder` | `string` | | Override the configured builder instance |
| `-D`, `--debug` | `bool` | | Enable debug logging |
| [`-f`](#file), [`--file`](#file) | `stringArray` | | Import from a file path |
<!---MARKER_GEN_END-->
## Description
Import a build record from a `.dockerbuild` archive into Docker Desktop. This
lets you view, inspect, and analyze builds created in other environments or CI
pipelines.
## Examples
### Import a `.dockerbuild` archive from standard input
```console
docker buildx history import < mybuild.dockerbuild
```
### <a name="file"></a> Import a build archive from a file (--file)
```console
docker buildx history import --file ./artifacts/backend-build.dockerbuild
```
### Open a build manually
By default, the `import` command automatically opens the imported build in Docker
Desktop. You don't need to run `open` unless you're opening a specific build
or re-opening it later.
If you've imported multiple builds, you can open one manually:
```console
docker buildx history open ci-build
```

View File

@@ -21,13 +21,61 @@ Inspect a build
<!---MARKER_GEN_END-->
## Description
Inspect a build record to view metadata such as duration, status, build inputs,
platforms, outputs, and attached artifacts. You can also use flags to extract
provenance, SBOMs, or other detailed information.
## Examples
### Inspect the most recent build
```console
$ docker buildx history inspect
Name: buildx (binaries)
Context: .
Dockerfile: Dockerfile
VCS Repository: https://github.com/crazy-max/buildx.git
VCS Revision: f15eaa1ee324ffbbab29605600d27a84cab86361
Target: binaries
Platforms: linux/amd64
Keep Git Dir: true
Started: 2025-02-07 11:56:24
Duration: 1m 1s
Build Steps: 16/16 (25% cached)
Image Resolve Mode: local
Materials:
URI DIGEST
pkg:docker/docker/dockerfile@1 sha256:93bfd3b68c109427185cd78b4779fc82b484b0b7618e36d0f104d4d801e66d25
pkg:docker/golang@1.23-alpine3.21?platform=linux%2Famd64 sha256:2c49857f2295e89b23b28386e57e018a86620a8fede5003900f2d138ba9c4037
pkg:docker/tonistiigi/xx@1.6.1?platform=linux%2Famd64 sha256:923441d7c25f1e2eb5789f82d987693c47b8ed987c4ab3b075d6ed2b5d6779a3
Attachments:
DIGEST PLATFORM TYPE
sha256:217329d2af959d4f02e3a96dcbe62bf100cab1feb8006a047ddfe51a5397f7e3 https://slsa.dev/provenance/v0.2
```
### Inspect a specific build
```console
# Using a build ID
docker buildx history inspect qu2gsuo8ejqrwdfii23xkkckt
# Or using a relative offset
docker buildx history inspect ^1
```
### <a name="format"></a> Format the output (--format)
The formatting options (`--format`) pretty-prints the output to `pretty` (default),
`json` or using a Go template.
**Pretty output**
```console
$ docker buildx history inspect
Name: buildx (binaries)
@@ -57,6 +105,7 @@ sha256:217329d2af959d4f02e3a96dcbe62bf100cab1feb8006a047ddfe51a5397f7e3
Print build logs: docker buildx history logs g9808bwrjrlkbhdamxklx660b
```
**JSON output**
```console
$ docker buildx history inspect --format json
@@ -111,6 +160,8 @@ $ docker buildx history inspect --format json
}
```
**Go template output**
```console
$ docker buildx history inspect --format "{{.Name}}: {{.VCSRepository}} ({{.VCSRevision}})"
buildx (binaries): https://github.com/crazy-max/buildx.git (f15eaa1ee324ffbbab29605600d27a84cab86361)

View File

@@ -5,13 +5,78 @@ Inspect a build attachment
### Options
| Name | Type | Default | Description |
|:----------------|:---------|:--------|:-----------------------------------------|
| `--builder` | `string` | | Override the configured builder instance |
| `-D`, `--debug` | `bool` | | Enable debug logging |
| `--platform` | `string` | | Platform of attachment |
| `--type` | `string` | | Type of attachment |
| Name | Type | Default | Description |
|:------------------|:---------|:--------|:-----------------------------------------|
| `--builder` | `string` | | Override the configured builder instance |
| `-D`, `--debug` | `bool` | | Enable debug logging |
| `--platform` | `string` | | Platform of attachment |
| [`--type`](#type) | `string` | | Type of attachment |
<!---MARKER_GEN_END-->
## Description
Inspect a specific attachment from a build record, such as a provenance file or
SBOM. Attachments are optional artifacts stored with the build and may be
platform-specific.
## Examples
### <a name="type"></a> Inspect a provenance attachment from a build (--type)
Supported types include `provenance` and `sbom`.
```console
$ docker buildx history inspect attachment qu2gsuo8ejqrwdfii23xkkckt --type provenance
{
"_type": "https://slsa.dev/provenance/v0.2",
"buildDefinition": {
"buildType": "https://build.docker.com/BuildKit@v1",
"externalParameters": {
"target": "app",
"platforms": ["linux/amd64"]
}
},
"runDetails": {
"builder": "docker",
"by": "ci@docker.com"
}
}
```
### Inspect a SBOM for linux/amd64
```console
$ docker buildx history inspect attachment ^0 \
--type sbom \
--platform linux/amd64
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"version": 1,
"components": [
{
"type": "library",
"name": "alpine",
"version": "3.18.2"
}
]
}
```
### Inspect an attachment by digest
You can inspect an attachment directly using its digset, which you can get from
the `inspect` output:
```console
# Using a build ID
docker buildx history inspect attachment qu2gsuo8ejqrwdfii23xkkckt sha256:abcdef123456...
# Or using a relative offset
docker buildx history inspect attachment ^0 sha256:abcdef123456...
```
Use `--type sbom` or `--type provenance` to filter attachments by type. To
inspect a specific attachment by digest, omit the `--type` flag.

View File

@@ -5,12 +5,61 @@ Print the logs of a build
### Options
| Name | Type | Default | Description |
|:----------------|:---------|:--------|:--------------------------------------------------|
| `--builder` | `string` | | Override the configured builder instance |
| `-D`, `--debug` | `bool` | | Enable debug logging |
| `--progress` | `string` | `plain` | Set type of progress output (plain, rawjson, tty) |
| Name | Type | Default | Description |
|:--------------------------|:---------|:--------|:--------------------------------------------------|
| `--builder` | `string` | | Override the configured builder instance |
| `-D`, `--debug` | `bool` | | Enable debug logging |
| [`--progress`](#progress) | `string` | `plain` | Set type of progress output (plain, rawjson, tty) |
<!---MARKER_GEN_END-->
## Description
Print the logs for a completed build. The output appears in the same format as
`--progress=plain`, showing the full logs for each step.
By default, this shows logs for the most recent build on the current builder.
You can also specify an earlier build using an offset. For example:
- `^1` shows logs for the build before the most recent
- `^2` shows logs for the build two steps back
## Examples
### Print logs for the most recent build
```console
$ docker buildx history logs
#1 [internal] load build definition from Dockerfile
#1 transferring dockerfile: 31B done
#1 DONE 0.0s
#2 [internal] load .dockerignore
#2 transferring context: 2B done
#2 DONE 0.0s
...
```
By default, this shows logs for the most recent build on the current builder.
### Print logs for a specific build
To print logs for a specific build, use a build ID or offset:
```console
# Using a build ID
docker buildx history logs qu2gsuo8ejqrwdfii23xkkckt
# Or using a relative offset
docker buildx history logs ^1
```
### <a name="progress"></a> Set type of progress output (--progress)
```console
$ docker buildx history logs ^1 --progress rawjson
{"id":"buildx_step_1","status":"START","timestamp":"2024-05-01T12:34:56.789Z","detail":"[internal] load build definition from Dockerfile"}
{"id":"buildx_step_1","status":"COMPLETE","timestamp":"2024-05-01T12:34:57.001Z","duration":212000000}
...
```

View File

@@ -5,15 +5,98 @@ List build records
### Options
| Name | Type | Default | Description |
|:----------------|:--------------|:--------|:---------------------------------------------|
| `--builder` | `string` | | Override the configured builder instance |
| `-D`, `--debug` | `bool` | | Enable debug logging |
| `--filter` | `stringArray` | | Provide filter values (e.g., `status=error`) |
| `--format` | `string` | `table` | Format the output |
| `--local` | `bool` | | List records for current repository only |
| `--no-trunc` | `bool` | | Don't truncate output |
| Name | Type | Default | Description |
|:--------------------------|:--------------|:--------|:---------------------------------------------|
| `--builder` | `string` | | Override the configured builder instance |
| `-D`, `--debug` | `bool` | | Enable debug logging |
| [`--filter`](#filter) | `stringArray` | | Provide filter values (e.g., `status=error`) |
| [`--format`](#format) | `string` | `table` | Format the output |
| [`--local`](#local) | `bool` | | List records for current repository only |
| [`--no-trunc`](#no-trunc) | `bool` | | Don't truncate output |
<!---MARKER_GEN_END-->
## Description
List completed builds recorded by the active builder. Each entry includes the
build ID, name, status, timestamp, and duration.
By default, only records for the current builder are shown. You can filter
results using flags.
## Examples
### List all build records for the current builder
```console
$ docker buildx history ls
BUILD ID NAME STATUS CREATED AT DURATION
qu2gsuo8ejqrwdfii23xkkckt .dev/2850 Completed 3 days ago 1.4s
qsiifiuf1ad9pa9qvppc0z1l3 .dev/2850 Completed 3 days ago 1.3s
g9808bwrjrlkbhdamxklx660b .dev/3120 Completed 5 days ago 2.1s
```
### <a name="filter"></a> List failed builds (--filter)
```console
docker buildx history ls --filter status=error
```
You can filter the list using the `--filter` flag. Supported filters include:
| Filter | Supported comparisons | Example |
|:-------|:----------------------|:--------|
| `ref`, `repository`, `status` | Support `=` and `!=` comparisons | `--filter status!=success` |
| `startedAt`, `completedAt`, `duration` | Support `<` and `>` comparisons with time values | `--filter duration>30s` |
You can combine multiple filters by repeating the `--filter` flag:
```console
docker buildx history ls --filter status=error --filter duration>30s
```
### <a name="local"></a> List builds from the current project (--local)
```console
docker buildx history ls --local
```
### <a name="no-trunc"></a> Display full output without truncation (--no-trunc)
```console
docker buildx history ls --no-trunc
```
### <a name="format"></a> Format output (--format)
**JSON output**
```console
$ docker buildx history ls --format json
[
{
"ID": "qu2gsuo8ejqrwdfii23xkkckt",
"Name": ".dev/2850",
"Status": "Completed",
"CreatedAt": "2025-04-15T12:33:00Z",
"Duration": "1.4s"
},
{
"ID": "qsiifiuf1ad9pa9qvppc0z1l3",
"Name": ".dev/2850",
"Status": "Completed",
"CreatedAt": "2025-04-15T12:29:00Z",
"Duration": "1.3s"
}
]
```
**Go template output**
```console
$ docker buildx history ls --format '{{.Name}} - {{.Duration}}'
.dev/2850 - 1.4s
.dev/2850 - 1.3s
.dev/3120 - 2.1s
```

View File

@@ -13,3 +13,27 @@ Open a build in Docker Desktop
<!---MARKER_GEN_END-->
## Description
Open a build record in Docker Desktop for visual inspection. This requires
Docker Desktop to be installed and running on the host machine.
## Examples
### Open the most recent build in Docker Desktop
```console
docker buildx history open
```
By default, this opens the most recent build on the current builder.
### Open a specific build
```console
# Using a build ID
docker buildx history open qu2gsuo8ejqrwdfii23xkkckt
# Or using a relative offset
docker buildx history open ^1
```

View File

@@ -14,3 +14,36 @@ Remove build records
<!---MARKER_GEN_END-->
## Description
Remove one or more build records from the current builders history. You can
remove specific builds by ID or offset, or delete all records at once using
the `--all` flag.
## Examples
### Remove a specific build
```console
# Using a build ID
docker buildx history rm qu2gsuo8ejqrwdfii23xkkckt
# Or using a relative offset
docker buildx history rm ^1
```
### Remove multiple builds
```console
# Using build IDs
docker buildx history rm qu2gsuo8ejqrwdfii23xkkckt qsiifiuf1ad9pa9qvppc0z1l3
# Or using relative offsets
docker buildx history rm ^1 ^2
```
### Remove all build records from the current builder
```console
docker buildx history rm --all
```

View File

@@ -5,13 +5,65 @@ Show the OpenTelemetry trace of a build record
### Options
| Name | Type | Default | Description |
|:----------------|:---------|:--------------|:-----------------------------------------|
| `--addr` | `string` | `127.0.0.1:0` | Address to bind the UI server |
| `--builder` | `string` | | Override the configured builder instance |
| `--compare` | `string` | | Compare with another build reference |
| `-D`, `--debug` | `bool` | | Enable debug logging |
| Name | Type | Default | Description |
|:------------------------|:---------|:--------------|:-----------------------------------------|
| [`--addr`](#addr) | `string` | `127.0.0.1:0` | Address to bind the UI server |
| `--builder` | `string` | | Override the configured builder instance |
| [`--compare`](#compare) | `string` | | Compare with another build reference |
| `-D`, `--debug` | `bool` | | Enable debug logging |
<!---MARKER_GEN_END-->
## Description
View the OpenTelemetry trace for a completed build. This command loads the
trace into a Jaeger UI viewer and opens it in your browser.
This helps analyze build performance, step timing, and internal execution flows.
## Examples
### Open the OpenTelemetry trace for the most recent build
This command starts a temporary Jaeger UI server and opens your default browser
to view the trace.
```console
docker buildx history trace
```
### Open the trace for a specific build
```console
# Using a build ID
docker buildx history trace qu2gsuo8ejqrwdfii23xkkckt
# Or using a relative offset
docker buildx history trace ^1
```
### <a name="addr"></a> Run the Jaeger UI on a specific port (--addr)
```console
# Using a build ID
docker buildx history trace qu2gsuo8ejqrwdfii23xkkckt --addr 127.0.0.1:16686
# Or using a relative offset
docker buildx history trace ^1 --addr 127.0.0.1:16686
```
### <a name="compare"></a> Compare two build traces (--compare)
Compare two specific builds by name:
```console
# Using build IDs
docker buildx history trace --compare=qu2gsuo8ejqrwdfii23xkkckt qsiifiuf1ad9pa9qvppc0z1l3
# Or using a single relative offset
docker buildx history trace --compare=^1
```
When you use a single reference with `--compare`, it compares that build
against the most recent one.

View File

@@ -24,11 +24,10 @@ import (
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/system"
"github.com/docker/docker/errdefs"
dockerarchive "github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/pkg/stdcopy"
"github.com/moby/buildkit/client"
mobyarchive "github.com/moby/go-archive"
"github.com/pkg/errors"
)
@@ -250,8 +249,8 @@ func (d *Driver) copyToContainer(ctx context.Context, files map[string][]byte) e
if srcPath != "" {
defer os.RemoveAll(srcPath)
}
srcArchive, err := dockerarchive.TarWithOptions(srcPath, &dockerarchive.TarOptions{
ChownOpts: &idtools.Identity{UID: 0, GID: 0},
srcArchive, err := mobyarchive.TarWithOptions(srcPath, &mobyarchive.TarOptions{
ChownOpts: &mobyarchive.ChownOpts{UID: 0, GID: 0},
})
if err != nil {
return err

40
go.mod
View File

@@ -6,32 +6,32 @@ require (
github.com/Masterminds/semver/v3 v3.2.1
github.com/Microsoft/go-winio v0.6.2
github.com/aws/aws-sdk-go-v2/config v1.27.27
github.com/compose-spec/compose-go/v2 v2.4.9
github.com/compose-spec/compose-go/v2 v2.6.0
github.com/containerd/console v1.0.4
github.com/containerd/containerd/v2 v2.0.4
github.com/containerd/containerd/v2 v2.0.5
github.com/containerd/continuity v0.4.5
github.com/containerd/errdefs v1.0.0
github.com/containerd/log v0.1.0
github.com/containerd/platforms v1.0.0-rc.1
github.com/containerd/typeurl/v2 v2.2.3
github.com/creack/pty v1.1.24
github.com/davecgh/go-spew v1.1.1
github.com/distribution/reference v0.6.0
github.com/docker/cli v28.0.4+incompatible
github.com/docker/cli v28.1.1+incompatible
github.com/docker/cli-docs-tool v0.9.0
github.com/docker/docker v28.0.4+incompatible
github.com/docker/docker v28.1.1+incompatible
github.com/docker/go-units v0.5.0
github.com/gofrs/flock v0.12.1
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/google/uuid v1.6.0
github.com/hashicorp/go-cty-funcs v0.0.0-20241120183456-c51673e0b3dd
github.com/hashicorp/go-cty-funcs v0.0.0-20250210171435-dda779884a9f
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/hcl/v2 v2.23.0
github.com/in-toto/in-toto-golang v0.5.0
github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/moby/buildkit v0.21.0-rc1
github.com/moby/buildkit v0.21.0
github.com/moby/go-archive v0.1.0
github.com/moby/sys/atomicwriter v0.1.0
github.com/moby/sys/mountinfo v0.7.2
github.com/moby/sys/signal v0.7.1
github.com/morikuni/aec v1.0.0
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.1
@@ -44,10 +44,11 @@ require (
github.com/spf13/cobra v1.9.1
github.com/spf13/pflag v1.0.6
github.com/stretchr/testify v1.10.0
github.com/tonistiigi/fsutil v0.0.0-20250318190121-d73a4b3b8a7e
github.com/tonistiigi/fsutil v0.0.0-20250410151801-5b74a7ad7583
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4
github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250408171107-3dd17559e117
github.com/zclconf/go-cty v1.16.0
github.com/zclconf/go-cty v1.16.2
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0
go.opentelemetry.io/otel v1.31.0
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0
go.opentelemetry.io/otel/metric v1.31.0
@@ -86,18 +87,15 @@ require (
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
github.com/aws/smithy-go v1.20.3 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/containerd/api v1.8.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/ttrpc v1.2.7 // indirect
github.com/containerd/typeurl/v2 v2.2.3 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.9.3 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fvbommel/sortorder v1.0.1 // indirect
@@ -113,7 +111,6 @@ require (
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
@@ -124,14 +121,15 @@ require (
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mattn/go-shellwords v1.0.12 // indirect
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/spdystream v0.4.0 // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/signal v0.7.1 // indirect
github.com/moby/sys/user v0.4.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/moby/term v0.5.2 // indirect
@@ -140,10 +138,6 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect
@@ -159,7 +153,6 @@ require (
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 // indirect
@@ -168,9 +161,8 @@ require (
go.opentelemetry.io/otel/sdk/metric v1.31.0 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
golang.org/x/crypto v0.37.0 // indirect
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/oauth2 v0.29.0 // indirect
golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.32.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38 // indirect

112
go.sum
View File

@@ -14,13 +14,10 @@ github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEP
github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc=
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
github.com/apparentlymart/go-cidr v1.0.1 h1:NmIwLZ/KdsjIUlhf+/Np40atNXm/+lZ5txfTJ/SpF+U=
github.com/apparentlymart/go-cidr v1.0.1/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
@@ -52,13 +49,10 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzx
github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE=
github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
github.com/beorn7/perks v0.0.0-20150223135152-b965b613227f/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bitly/go-hostpool v0.1.0/go.mod h1:4gOCgp6+NZnVqlKyZ/iBZFTAJKembaVENUpMkpg42fw=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/bmatcuk/doublestar v1.1.5/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bugsnag/bugsnag-go v1.0.5-0.20150529004307-13fd6b8acda0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
@@ -70,16 +64,16 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
github.com/compose-spec/compose-go/v2 v2.4.9 h1:2K4TDw+1ba2idiR6empXHKRXvWYpnvAKoNQy93/sSOs=
github.com/compose-spec/compose-go/v2 v2.4.9/go.mod h1:6k5l/0TxCg0/2uLEhRVEsoBWBprS2uvZi32J7xub3lo=
github.com/compose-spec/compose-go/v2 v2.6.0 h1:/+oBD2ixSENOeN/TlJqWZmUak0xM8A7J08w/z661Wd4=
github.com/compose-spec/compose-go/v2 v2.6.0/go.mod h1:vPlkN0i+0LjLf9rv52lodNMUTJF5YHVfHVGLLIP67NA=
github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0=
github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc=
github.com/containerd/containerd/v2 v2.0.4 h1:+r7yJMwhTfMm3CDyiBjMBQO8a9CTBxL2Bg/JtqtIwB8=
github.com/containerd/containerd/v2 v2.0.4/go.mod h1:5j9QUUaV/cy9ZeAx4S+8n9ffpf+iYnEj4jiExgcbuLY=
github.com/containerd/containerd/v2 v2.0.5 h1:2vg/TjUXnaohAxiHnthQg8K06L9I4gdYEMcOLiMc8BQ=
github.com/containerd/containerd/v2 v2.0.5/go.mod h1:Qqo0UN43i2fX1FLkrSTCg6zcHNfjN7gEnx3NPRZI+N0=
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
@@ -114,15 +108,15 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v28.0.4+incompatible h1:pBJSJeNd9QeIWPjRcV91RVJihd/TXB77q1ef64XEu4A=
github.com/docker/cli v28.0.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v28.1.1+incompatible h1:eyUemzeI45DY7eDPuwUcmDyDj1pM98oD5MdSpiItp8k=
github.com/docker/cli v28.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli-docs-tool v0.9.0 h1:CVwQbE+ZziwlPqrJ7LRyUF6GvCA+6gj7MTCsayaK9t0=
github.com/docker/cli-docs-tool v0.9.0/go.mod h1:ClrwlNW+UioiRyH9GiAOe1o3J/TsY3Tr1ipoypjAUtc=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrbxjVBtS+cdcok=
github.com/docker/docker v28.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v28.1.1+incompatible h1:49M11BFLsVO1gxY9UX9p/zwkE/rswggs8AdFmXQw51I=
github.com/docker/docker v28.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
@@ -135,7 +129,6 @@ github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQ
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
@@ -148,9 +141,6 @@ github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqF
github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
@@ -164,7 +154,6 @@ github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
@@ -174,24 +163,18 @@ github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlnd
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
@@ -202,12 +185,10 @@ github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U=
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
@@ -218,8 +199,8 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-cty-funcs v0.0.0-20241120183456-c51673e0b3dd h1:nwSMaLX+rf/ZPHTJHWO9K73be04SritSKvKuvpBvC2A=
github.com/hashicorp/go-cty-funcs v0.0.0-20241120183456-c51673e0b3dd/go.mod h1:Abjk0jbRkDaNCzsRhOv2iDCofYpX1eVsjozoiK63qLA=
github.com/hashicorp/go-cty-funcs v0.0.0-20250210171435-dda779884a9f h1:c+rwTuPxlEHiskGDCl53snuw344sDO/4KU5KSoWhieI=
github.com/hashicorp/go-cty-funcs v0.0.0-20250210171435-dda779884a9f/go.mod h1:0AzBnolcBFa/tA5iF378GgeeiZ955EdVdC4TuxJ66m4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/hcl/v2 v2.23.0 h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos=
@@ -237,19 +218,15 @@ github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d/go.mod h1:h+uFLl
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -258,14 +235,12 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lib/pq v0.0.0-20150723085316-0dad96c0b94f/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
@@ -273,22 +248,25 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/moby/buildkit v0.21.0-rc1 h1:QWTyHpHUtsyUMH0CH7QStisI/FmS9njRC1FK4vVYeaE=
github.com/moby/buildkit v0.21.0-rc1/go.mod h1:coiVDxJmP1PD+79HAnTJvBMetLTdCws8gpWiYX2vcH8=
github.com/moby/buildkit v0.21.0 h1:+z4vVqgt0spLrOSxi4DLedRbIh2gbNVlZ5q4rsnNp60=
github.com/moby/buildkit v0.21.0/go.mod h1:mBq0D44uCyz2PdX8T/qym5LBbkBO3GGv0wqgX9ABYYw=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=
github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8=
github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
@@ -304,15 +282,12 @@ github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFL
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
@@ -340,7 +315,6 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -349,25 +323,15 @@ github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.0-pre1.0.20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
@@ -383,7 +347,6 @@ github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b/go.mod h1:/yeG0My
github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=
github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
@@ -417,8 +380,8 @@ github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4D
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 h1:r0p7fK56l8WPequOaR3i9LBqfPtEdXIQbUTzT55iqT4=
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY=
github.com/tonistiigi/fsutil v0.0.0-20250318190121-d73a4b3b8a7e h1:AiXT0JHwQA52AEOVMsxRytSI9mdJSie5gUp6OQ1R8fU=
github.com/tonistiigi/fsutil v0.0.0-20250318190121-d73a4b3b8a7e/go.mod h1:BKdcez7BiVtBvIcef90ZPc6ebqIWr4JWD7+EvLm6J98=
github.com/tonistiigi/fsutil v0.0.0-20250410151801-5b74a7ad7583 h1:mK+ZskNt7SG4dxfKIi27C7qHAQzyjAVt1iyTf0hmsNc=
github.com/tonistiigi/fsutil v0.0.0-20250410151801-5b74a7ad7583/go.mod h1:BKdcez7BiVtBvIcef90ZPc6ebqIWr4JWD7+EvLm6J98=
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 h1:7I5c2Ig/5FgqkYOh/N87NzoyI9U15qUPXhDD8uCupv8=
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE=
github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250408171107-3dd17559e117 h1:XFwyh2JZwR5aiKLXHX2C1n0v5F11dCJpyGL1W/Cpl3U=
@@ -429,7 +392,6 @@ github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab h1:H6aJ0yKQ0gF49Q
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc=
github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs=
github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI=
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@@ -443,9 +405,8 @@ github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/zclconf/go-cty v1.4.0/go.mod h1:nHzOclRkoj++EU9ZjSrZvRG0BXIWt8c7loYc0qXAFGQ=
github.com/zclconf/go-cty v1.16.0 h1:xPKEhst+BW5D0wxebMZkxgapvOE/dw7bFTlgSc9nD6w=
github.com/zclconf/go-cty v1.16.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
github.com/zclconf/go-cty v1.16.2 h1:LAJSwc3v81IRBZyUVQDUdZ7hs3SYs9jv0eZJDWHD/70=
github.com/zclconf/go-cty v1.16.2/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
@@ -482,36 +443,28 @@ go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeX
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -519,10 +472,8 @@ golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -535,7 +486,6 @@ golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXR
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
@@ -551,7 +501,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38 h1:2oV8dfuIkM1Ti7DwXc0BJfnwr9csz4TDXI9EmiI+Rbw=
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38/go.mod h1:vuAjtvlwkDKF6L1GQ0SokiRLCGFfeBUXWr/aFFkHACc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
@@ -564,10 +513,8 @@ google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zt
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/cenkalti/backoff.v2 v2.2.1/go.mod h1:S0QdOvT2AlerfSBkp0O+dk+bbIMaNbEmVk876gPCthU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -577,7 +524,6 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1/go.mod h1:WbjuEoo1oadwzQ4apSDU+JTvmllEHtsNHS6y7vFc7iw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -586,8 +532,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0=
k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk=
k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw=

View File

@@ -10,7 +10,8 @@ Vagrant.configure("2") do |config|
config.vm.provision "init", type: "shell", run: "once" do |sh|
sh.inline = <<~SHELL
set -x
set -ex
export PKG_PATH=https://mirrors.ocf.berkeley.edu/pub/OpenBSD/7.5/packages/amd64/
pkg_add -x git
ftp https://go.dev/dl/go#{ENV['GO_VERSION']}.openbsd-amd64.tar.gz

View File

@@ -4,8 +4,8 @@ import (
"context"
"fmt"
"io"
"slices"
cerrdefs "github.com/containerd/errdefs"
"github.com/docker/buildx/monitor/types"
"github.com/pkg/errors"
)
@@ -23,57 +23,33 @@ func NewAttachCmd(m types.Monitor, stdout io.WriteCloser) types.Command {
func (cm *AttachCmd) Info() types.CommandInfo {
return types.CommandInfo{
Name: "attach",
HelpMessage: "attach to a buildx server or a process in the container",
HelpMessage: "attach to a process in the container",
HelpMessageLong: `
Usage:
attach ID
attach PID
ID is for a session (visible via list command) or a process (visible via ps command).
If you attached to a process, use Ctrl-a-c for switching the monitor to that process's STDIO.
PID is for a process (visible via ps command).
Use Ctrl-a-c for switching the monitor to that process's STDIO.
`,
}
}
func (cm *AttachCmd) Exec(ctx context.Context, args []string) error {
if len(args) < 2 {
return errors.Errorf("ID of session or process must be passed")
return errors.Errorf("PID of process must be passed")
}
ref := args[1]
var id string
pid := args[1]
isProcess, err := isProcessID(ctx, cm.m, ref)
if err == nil && isProcess {
cm.m.Attach(ctx, ref)
id = ref
}
if id == "" {
refs, err := cm.m.List(ctx)
if err != nil {
return errors.Errorf("failed to get the list of sessions: %v", err)
}
if !slices.Contains(refs, ref) {
return errors.Errorf("unknown ID: %q", ref)
}
cm.m.Detach() // Finish existing attach
cm.m.AttachSession(ref)
}
fmt.Fprintf(cm.stdout, "Attached to process %q. Press Ctrl-a-c to switch to the new container\n", id)
return nil
}
func isProcessID(ctx context.Context, c types.Monitor, ref string) (bool, error) {
sid := c.AttachedSessionID()
if sid == "" {
return false, errors.Errorf("no attaching session")
}
infos, err := c.ListProcesses(ctx, sid)
infos, err := cm.m.ListProcesses(ctx)
if err != nil {
return false, err
return err
}
for _, p := range infos {
if p.ProcessID == ref {
return true, nil
if p.ProcessID == pid {
cm.m.Attach(ctx, pid)
fmt.Fprintf(cm.stdout, "Attached to process %q. Press Ctrl-a-c to switch to the new container\n", pid)
return nil
}
}
return false, nil
return errors.Wrapf(cerrdefs.ErrNotFound, "pid %s", pid)
}

View File

@@ -1,54 +0,0 @@
package commands
import (
"context"
"fmt"
"github.com/docker/buildx/monitor/types"
"github.com/pkg/errors"
)
type DisconnectCmd struct {
m types.Monitor
}
func NewDisconnectCmd(m types.Monitor) types.Command {
return &DisconnectCmd{m}
}
func (cm *DisconnectCmd) Info() types.CommandInfo {
return types.CommandInfo{
Name: "disconnect",
HelpMessage: "disconnect a client from a buildx server. Specific session ID can be specified an arg",
HelpMessageLong: fmt.Sprintf(`
Usage:
disconnect [ID]
ID is for a session (visible via list command). Default is %q.
`, cm.m.AttachedSessionID()),
}
}
func (cm *DisconnectCmd) Exec(ctx context.Context, args []string) error {
target := cm.m.AttachedSessionID()
if len(args) >= 2 {
target = args[1]
} else if target == "" {
return errors.Errorf("no attaching session")
}
isProcess, err := isProcessID(ctx, cm.m, target)
if err == nil && isProcess {
sid := cm.m.AttachedSessionID()
if sid == "" {
return errors.Errorf("no attaching session")
}
if err := cm.m.DisconnectProcess(ctx, sid, target); err != nil {
return errors.Errorf("disconnecting from process failed %v", target)
}
return nil
}
if err := cm.m.DisconnectSession(ctx, target); err != nil {
return errors.Errorf("disconnecting from session failed: %v", err)
}
return nil
}

View File

@@ -35,9 +35,6 @@ COMMAND and ARG... will be executed in the container.
}
func (cm *ExecCmd) Exec(ctx context.Context, args []string) error {
if ref := cm.m.AttachedSessionID(); ref == "" {
return errors.Errorf("no attaching session")
}
if len(args) < 2 {
return errors.Errorf("command must be passed")
}

View File

@@ -1,36 +0,0 @@
package commands
import (
"context"
"github.com/docker/buildx/monitor/types"
"github.com/pkg/errors"
)
type KillCmd struct {
m types.Monitor
}
func NewKillCmd(m types.Monitor) types.Command {
return &KillCmd{m}
}
func (cm *KillCmd) Info() types.CommandInfo {
return types.CommandInfo{
Name: "kill",
HelpMessage: "kill buildx server",
HelpMessageLong: `
Usage:
kill
Kills the currently connecting buildx server process.
`,
}
}
func (cm *KillCmd) Exec(ctx context.Context, args []string) error {
if err := cm.m.Kill(ctx); err != nil {
return errors.Errorf("failed to kill: %v", err)
}
return nil
}

View File

@@ -1,47 +0,0 @@
package commands
import (
"context"
"fmt"
"io"
"sort"
"text/tabwriter"
"github.com/docker/buildx/monitor/types"
)
type ListCmd struct {
m types.Monitor
stdout io.WriteCloser
}
func NewListCmd(m types.Monitor, stdout io.WriteCloser) types.Command {
return &ListCmd{m, stdout}
}
func (cm *ListCmd) Info() types.CommandInfo {
return types.CommandInfo{
Name: "list",
HelpMessage: "list buildx sessions",
HelpMessageLong: `
Usage:
list
`,
}
}
func (cm *ListCmd) Exec(ctx context.Context, args []string) error {
refs, err := cm.m.List(ctx)
if err != nil {
return err
}
sort.Strings(refs)
tw := tabwriter.NewWriter(cm.stdout, 1, 8, 1, '\t', 0)
fmt.Fprintln(tw, "ID\tCURRENT_SESSION")
for _, k := range refs {
fmt.Fprintf(tw, "%-20s\t%v\n", k, k == cm.m.AttachedSessionID())
}
tw.Flush()
return nil
}

View File

@@ -7,7 +7,6 @@ import (
"text/tabwriter"
"github.com/docker/buildx/monitor/types"
"github.com/pkg/errors"
)
type PsCmd struct {
@@ -31,11 +30,7 @@ Usage:
}
func (cm *PsCmd) Exec(ctx context.Context, args []string) error {
ref := cm.m.AttachedSessionID()
if ref == "" {
return errors.Errorf("no attaching session")
}
plist, err := cm.m.ListProcesses(ctx, ref)
plist, err := cm.m.ListProcesses(ctx)
if err != nil {
return err
}

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"io"
cbuild "github.com/docker/buildx/controller/build"
controllererrors "github.com/docker/buildx/controller/errdefs"
controllerapi "github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/monitor/types"
@@ -19,11 +20,11 @@ type ReloadCmd struct {
stdout io.WriteCloser
progress *progress.Printer
options *controllerapi.BuildOptions
options *cbuild.Options
invokeConfig *controllerapi.InvokeConfig
}
func NewReloadCmd(m types.Monitor, stdout io.WriteCloser, progress *progress.Printer, options *controllerapi.BuildOptions, invokeConfig *controllerapi.InvokeConfig) types.Command {
func NewReloadCmd(m types.Monitor, stdout io.WriteCloser, progress *progress.Printer, options *cbuild.Options, invokeConfig *controllerapi.InvokeConfig) types.Command {
return &ReloadCmd{m, stdout, progress, options, invokeConfig}
}
@@ -39,34 +40,15 @@ Usage:
}
func (cm *ReloadCmd) Exec(ctx context.Context, args []string) error {
var bo *controllerapi.BuildOptions
if ref := cm.m.AttachedSessionID(); ref != "" {
// Rebuilding an existing session; Restore the build option used for building this session.
res, err := cm.m.Inspect(ctx, ref)
if err != nil {
fmt.Printf("failed to inspect the current build session: %v\n", err)
} else {
bo = res.Options
}
} else {
bo = cm.options
}
if bo == nil {
return errors.Errorf("no build option is provided")
}
if ref := cm.m.AttachedSessionID(); ref != "" {
if err := cm.m.Disconnect(ctx, ref); err != nil {
fmt.Println("disconnect error", err)
}
}
bo := cm.m.Inspect(ctx)
var resultUpdated bool
cm.progress.Unpause()
ref, _, _, err := cm.m.Build(ctx, bo, nil, cm.progress) // TODO: support stdin, hold build ref
_, _, err := cm.m.Build(ctx, bo, nil, cm.progress) // TODO: support stdin, hold build ref
cm.progress.Pause()
if err != nil {
var be *controllererrors.BuildError
if errors.As(err, &be) {
ref = be.SessionID
resultUpdated = true
} else {
fmt.Printf("failed to reload: %v\n", err)
@@ -79,7 +61,6 @@ func (cm *ReloadCmd) Exec(ctx context.Context, args []string) error {
} else {
resultUpdated = true
}
cm.m.AttachSession(ref)
if resultUpdated {
// rollback the running container with the new result
id := cm.m.Rollback(ctx, cm.invokeConfig)

View File

@@ -7,7 +7,6 @@ import (
controllerapi "github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/monitor/types"
"github.com/pkg/errors"
)
type RollbackCmd struct {
@@ -38,9 +37,6 @@ COMMAND and ARG... will be executed in the container.
}
func (cm *RollbackCmd) Exec(ctx context.Context, args []string) error {
if ref := cm.m.AttachedSessionID(); ref == "" {
return errors.Errorf("no attaching session")
}
cfg := cm.invokeConfig
if len(args) >= 2 {
cmds := args[1:]

View File

@@ -11,6 +11,7 @@ import (
"github.com/containerd/console"
"github.com/docker/buildx/build"
cbuild "github.com/docker/buildx/controller/build"
"github.com/docker/buildx/controller/control"
controllerapi "github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/monitor/commands"
@@ -31,10 +32,10 @@ type MonitorBuildResult struct {
}
// RunMonitor provides an interactive session for running and managing containers via specified IO.
func RunMonitor(ctx context.Context, curRef string, options *controllerapi.BuildOptions, invokeConfig *controllerapi.InvokeConfig, c control.BuildxController, stdin io.ReadCloser, stdout io.WriteCloser, stderr console.File, progress *progress.Printer) (*MonitorBuildResult, error) {
func RunMonitor(ctx context.Context, curRef string, options *cbuild.Options, invokeConfig *controllerapi.InvokeConfig, c control.BuildxController, stdin io.ReadCloser, stdout io.WriteCloser, stderr console.File, progress *progress.Printer) (*MonitorBuildResult, error) {
defer func() {
if err := c.Disconnect(ctx, curRef); err != nil {
logrus.Warnf("disconnect error: %v", err)
if err := c.Close(); err != nil {
logrus.Warnf("close error: %v", err)
}
}()
@@ -95,9 +96,6 @@ func RunMonitor(ctx context.Context, curRef string, options *controllerapi.Build
availableCommands := []types.Command{
commands.NewReloadCmd(m, stdout, progress, options, invokeConfig),
commands.NewRollbackCmd(m, invokeConfig, stdout),
commands.NewListCmd(m, stdout),
commands.NewDisconnectCmd(m),
commands.NewKillCmd(m),
commands.NewAttachCmd(m, stdout),
commands.NewExecCmd(m, invokeConfig, stdout),
commands.NewPsCmd(m, stdout),
@@ -244,24 +242,12 @@ type monitor struct {
lastBuildResult *MonitorBuildResult
}
func (m *monitor) Build(ctx context.Context, options *controllerapi.BuildOptions, in io.ReadCloser, progress progress.Writer) (ref string, resp *client.SolveResponse, input *build.Inputs, err error) {
ref, resp, _, err = m.BuildxController.Build(ctx, options, in, progress)
func (m *monitor) Build(ctx context.Context, options *cbuild.Options, in io.ReadCloser, progress progress.Writer) (resp *client.SolveResponse, input *build.Inputs, err error) {
resp, _, err = m.BuildxController.Build(ctx, options, in, progress)
m.lastBuildResult = &MonitorBuildResult{Resp: resp, Err: err} // Record build result
return
}
func (m *monitor) DisconnectSession(ctx context.Context, targetID string) error {
return m.Disconnect(ctx, targetID)
}
func (m *monitor) AttachSession(ref string) {
m.ref.Store(ref)
}
func (m *monitor) AttachedSessionID() string {
return m.ref.Load().(string)
}
func (m *monitor) Rollback(ctx context.Context, cfg *controllerapi.InvokeConfig) string {
pid := identity.NewID()
cfg1 := cfg
@@ -323,9 +309,6 @@ func (m *monitor) invoke(ctx context.Context, pid string, cfg *controllerapi.Inv
if err := m.muxIO.SwitchTo(1); err != nil {
return errors.Errorf("failed to switch to process IO: %v", err)
}
if m.AttachedSessionID() == "" {
return nil
}
invokeCtx, invokeCancel := context.WithCancelCause(ctx)
containerIn, containerOut := ioset.Pipe()
@@ -343,7 +326,7 @@ func (m *monitor) invoke(ctx context.Context, pid string, cfg *controllerapi.Inv
defer invokeCancelAndDetachFn()
m.invokeCancel = invokeCancelAndDetachFn
err := m.Invoke(invokeCtx, m.AttachedSessionID(), pid, cfg, containerIn.Stdin, containerIn.Stdout, containerIn.Stderr)
err := m.Invoke(invokeCtx, pid, cfg, containerIn.Stdin, containerIn.Stdout, containerIn.Stderr)
close(waitInvokeDoneCh)
return err

View File

@@ -25,15 +25,6 @@ type Monitor interface {
// Detach detaches IO from the container.
Detach()
// DisconnectSession finishes the specified session.
DisconnectSession(ctx context.Context, targetID string) error
// AttachSession attaches the monitor to the specified session.
AttachSession(ref string)
// AttachedSessionID returns the ID of the attached session.
AttachedSessionID() string
}
// CommandInfo is information about a command.

View File

@@ -399,18 +399,25 @@ func testImageIDOutput(t *testing.T, sb integration.Sandbox) {
require.Equal(t, dgst.String(), strings.TrimSpace(stdout.String()))
// read the md.json file
dt, err = os.ReadFile(filepath.Join(targetDir, "md.json"))
require.NoError(t, err)
type mdT struct {
Digest string `json:"containerimage.digest"`
ConfigDigest string `json:"containerimage.config.digest"`
}
var md mdT
err = json.Unmarshal(dt, &md)
require.NoError(t, err)
require.NotEmpty(t, md.ConfigDigest)
require.Equal(t, dgst, digest.Digest(md.ConfigDigest))
require.NotEmpty(t, md.Digest)
// verify the image ID output is correct
// XXX: improve this by checking that it's one of the two expected digests depending on the scenario.
require.Contains(t, []digest.Digest{digest.Digest(md.ConfigDigest), digest.Digest(md.Digest)}, dgst)
}
func testBuildMobyFromLocalImage(t *testing.T, sb integration.Sandbox) {

View File

@@ -8,8 +8,8 @@ import (
"sync"
"github.com/docker/cli/cli/command"
"github.com/docker/docker/pkg/atomicwriter"
"github.com/moby/buildkit/cmd/buildkitd/config"
"github.com/moby/sys/atomicwriter"
"github.com/pelletier/go-toml"
"github.com/pkg/errors"
fs "github.com/tonistiigi/fsutil/copy"

View File

@@ -4,6 +4,8 @@ import (
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/context/docker"
dockerclient "github.com/docker/docker/client"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel/propagation"
)
// ClientAPI represents an active docker API object.
@@ -36,6 +38,10 @@ func NewClientAPI(cli command.Cli, ep string) (*ClientAPI, error) {
return nil, err
}
clientOpts = append(clientOpts, dockerclient.WithTraceOptions(otelhttp.WithPropagators(
propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}),
)))
ca.APIClient, err = dockerclient.NewClientWithOpts(clientOpts...)
if err != nil {
return nil, err

View File

@@ -13,7 +13,7 @@ function "add" {
function "list" {
params = []
variadic_param = items
variadic_params = items
result = items
}
```

View File

@@ -1,79 +0,0 @@
package userfunc
import (
"github.com/hashicorp/hcl/v2"
"github.com/zclconf/go-cty/cty/function"
)
var funcBodySchema = &hcl.BodySchema{
Attributes: []hcl.AttributeSchema{
{
Name: "params",
Required: true,
},
{
Name: "variadic_param",
Required: false,
},
{
Name: "result",
Required: true,
},
},
}
func decodeUserFunctions(body hcl.Body, blockType string, contextFunc ContextFunc) (funcs map[string]function.Function, remain hcl.Body, diags hcl.Diagnostics) {
schema := &hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{
{
Type: blockType,
LabelNames: []string{"name"},
},
},
}
content, remain, diags := body.PartialContent(schema)
if diags.HasErrors() {
return nil, remain, diags
}
// first call to getBaseCtx will populate context, and then the same
// context will be used for all subsequent calls. It's assumed that
// all functions in a given body should see an identical context.
var baseCtx *hcl.EvalContext
getBaseCtx := func() *hcl.EvalContext {
if baseCtx == nil {
if contextFunc != nil {
baseCtx = contextFunc()
}
}
// baseCtx might still be nil here, and that's okay
return baseCtx
}
funcs = make(map[string]function.Function)
for _, block := range content.Blocks {
name := block.Labels[0]
funcContent, funcDiags := block.Body.Content(funcBodySchema)
diags = append(diags, funcDiags...)
if funcDiags.HasErrors() {
continue
}
paramsExpr := funcContent.Attributes["params"].Expr
resultExpr := funcContent.Attributes["result"].Expr
var varParamExpr hcl.Expression
if funcContent.Attributes["variadic_param"] != nil {
varParamExpr = funcContent.Attributes["variadic_param"].Expr
}
f, funcDiags := NewFunction(paramsExpr, varParamExpr, resultExpr, getBaseCtx)
if funcDiags.HasErrors() {
diags = append(diags, funcDiags...)
continue
}
funcs[name] = f
}
return funcs, remain, diags
}

View File

@@ -20,28 +20,6 @@ import (
// structure a function declaration is found, etc.
type ContextFunc func() *hcl.EvalContext
// DecodeUserFunctions looks for blocks of the given type in the given body
// and, for each one found, interprets it as a custom function definition.
//
// On success, the result is a mapping of function names to implementations,
// along with a new body that represents the remaining content of the given
// body which can be used for further processing.
//
// The result expression of each function is parsed during decoding but not
// evaluated until the function is called.
//
// If the given ContextFunc is non-nil, it will be called to obtain the
// context in which the function result expressions will be evaluated. If nil,
// or if it returns nil, the result expression will have access only to
// variables named after the declared parameters. A non-nil context turns
// the returned functions into closures, bound to the given context.
//
// If the returned diagnostics set has errors then the function map and
// remain body may be nil or incomplete.
func DecodeUserFunctions(body hcl.Body, blockType string, context ContextFunc) (funcs map[string]function.Function, remain hcl.Body, diags hcl.Diagnostics) {
return decodeUserFunctions(body, blockType, context)
}
// NewFunction creates a new function instance from preparsed HCL expressions.
func NewFunction(paramsExpr, varParamExpr, resultExpr hcl.Expression, getBaseCtx func() *hcl.EvalContext) (function.Function, hcl.Diagnostics) {
var params []string
@@ -69,7 +47,7 @@ func NewFunction(paramsExpr, varParamExpr, resultExpr hcl.Expression, getBaseCtx
if varParam == "" {
return function.Function{}, hcl.Diagnostics{{
Severity: hcl.DiagError,
Summary: "Invalid variadic_param",
Summary: "Invalid variadic_params",
Detail: "The variadic parameter name must be an identifier.",
Subject: varParamExpr.Range().Ptr(),
}}

View File

@@ -1,20 +0,0 @@
Copyright (C) 2013 Blake Mizerany
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

File diff suppressed because it is too large Load Diff

View File

@@ -1,316 +0,0 @@
// Package quantile computes approximate quantiles over an unbounded data
// stream within low memory and CPU bounds.
//
// A small amount of accuracy is traded to achieve the above properties.
//
// Multiple streams can be merged before calling Query to generate a single set
// of results. This is meaningful when the streams represent the same type of
// data. See Merge and Samples.
//
// For more detailed information about the algorithm used, see:
//
// Effective Computation of Biased Quantiles over Data Streams
//
// http://www.cs.rutgers.edu/~muthu/bquant.pdf
package quantile
import (
"math"
"sort"
)
// Sample holds an observed value and meta information for compression. JSON
// tags have been added for convenience.
type Sample struct {
Value float64 `json:",string"`
Width float64 `json:",string"`
Delta float64 `json:",string"`
}
// Samples represents a slice of samples. It implements sort.Interface.
type Samples []Sample
func (a Samples) Len() int { return len(a) }
func (a Samples) Less(i, j int) bool { return a[i].Value < a[j].Value }
func (a Samples) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
type invariant func(s *stream, r float64) float64
// NewLowBiased returns an initialized Stream for low-biased quantiles
// (e.g. 0.01, 0.1, 0.5) where the needed quantiles are not known a priori, but
// error guarantees can still be given even for the lower ranks of the data
// distribution.
//
// The provided epsilon is a relative error, i.e. the true quantile of a value
// returned by a query is guaranteed to be within (1±Epsilon)*Quantile.
//
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error
// properties.
func NewLowBiased(epsilon float64) *Stream {
ƒ := func(s *stream, r float64) float64 {
return 2 * epsilon * r
}
return newStream(ƒ)
}
// NewHighBiased returns an initialized Stream for high-biased quantiles
// (e.g. 0.01, 0.1, 0.5) where the needed quantiles are not known a priori, but
// error guarantees can still be given even for the higher ranks of the data
// distribution.
//
// The provided epsilon is a relative error, i.e. the true quantile of a value
// returned by a query is guaranteed to be within 1-(1±Epsilon)*(1-Quantile).
//
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error
// properties.
func NewHighBiased(epsilon float64) *Stream {
ƒ := func(s *stream, r float64) float64 {
return 2 * epsilon * (s.n - r)
}
return newStream(ƒ)
}
// NewTargeted returns an initialized Stream concerned with a particular set of
// quantile values that are supplied a priori. Knowing these a priori reduces
// space and computation time. The targets map maps the desired quantiles to
// their absolute errors, i.e. the true quantile of a value returned by a query
// is guaranteed to be within (Quantile±Epsilon).
//
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
func NewTargeted(targetMap map[float64]float64) *Stream {
// Convert map to slice to avoid slow iterations on a map.
// ƒ is called on the hot path, so converting the map to a slice
// beforehand results in significant CPU savings.
targets := targetMapToSlice(targetMap)
ƒ := func(s *stream, r float64) float64 {
var m = math.MaxFloat64
var f float64
for _, t := range targets {
if t.quantile*s.n <= r {
f = (2 * t.epsilon * r) / t.quantile
} else {
f = (2 * t.epsilon * (s.n - r)) / (1 - t.quantile)
}
if f < m {
m = f
}
}
return m
}
return newStream(ƒ)
}
type target struct {
quantile float64
epsilon float64
}
func targetMapToSlice(targetMap map[float64]float64) []target {
targets := make([]target, 0, len(targetMap))
for quantile, epsilon := range targetMap {
t := target{
quantile: quantile,
epsilon: epsilon,
}
targets = append(targets, t)
}
return targets
}
// Stream computes quantiles for a stream of float64s. It is not thread-safe by
// design. Take care when using across multiple goroutines.
type Stream struct {
*stream
b Samples
sorted bool
}
func newStream(ƒ invariant) *Stream {
x := &stream{ƒ: ƒ}
return &Stream{x, make(Samples, 0, 500), true}
}
// Insert inserts v into the stream.
func (s *Stream) Insert(v float64) {
s.insert(Sample{Value: v, Width: 1})
}
func (s *Stream) insert(sample Sample) {
s.b = append(s.b, sample)
s.sorted = false
if len(s.b) == cap(s.b) {
s.flush()
}
}
// Query returns the computed qth percentiles value. If s was created with
// NewTargeted, and q is not in the set of quantiles provided a priori, Query
// will return an unspecified result.
func (s *Stream) Query(q float64) float64 {
if !s.flushed() {
// Fast path when there hasn't been enough data for a flush;
// this also yields better accuracy for small sets of data.
l := len(s.b)
if l == 0 {
return 0
}
i := int(math.Ceil(float64(l) * q))
if i > 0 {
i -= 1
}
s.maybeSort()
return s.b[i].Value
}
s.flush()
return s.stream.query(q)
}
// Merge merges samples into the underlying streams samples. This is handy when
// merging multiple streams from separate threads, database shards, etc.
//
// ATTENTION: This method is broken and does not yield correct results. The
// underlying algorithm is not capable of merging streams correctly.
func (s *Stream) Merge(samples Samples) {
sort.Sort(samples)
s.stream.merge(samples)
}
// Reset reinitializes and clears the list reusing the samples buffer memory.
func (s *Stream) Reset() {
s.stream.reset()
s.b = s.b[:0]
}
// Samples returns stream samples held by s.
func (s *Stream) Samples() Samples {
if !s.flushed() {
return s.b
}
s.flush()
return s.stream.samples()
}
// Count returns the total number of samples observed in the stream
// since initialization.
func (s *Stream) Count() int {
return len(s.b) + s.stream.count()
}
func (s *Stream) flush() {
s.maybeSort()
s.stream.merge(s.b)
s.b = s.b[:0]
}
func (s *Stream) maybeSort() {
if !s.sorted {
s.sorted = true
sort.Sort(s.b)
}
}
func (s *Stream) flushed() bool {
return len(s.stream.l) > 0
}
type stream struct {
n float64
l []Sample
ƒ invariant
}
func (s *stream) reset() {
s.l = s.l[:0]
s.n = 0
}
func (s *stream) insert(v float64) {
s.merge(Samples{{v, 1, 0}})
}
func (s *stream) merge(samples Samples) {
// TODO(beorn7): This tries to merge not only individual samples, but
// whole summaries. The paper doesn't mention merging summaries at
// all. Unittests show that the merging is inaccurate. Find out how to
// do merges properly.
var r float64
i := 0
for _, sample := range samples {
for ; i < len(s.l); i++ {
c := s.l[i]
if c.Value > sample.Value {
// Insert at position i.
s.l = append(s.l, Sample{})
copy(s.l[i+1:], s.l[i:])
s.l[i] = Sample{
sample.Value,
sample.Width,
math.Max(sample.Delta, math.Floor(s.ƒ(s, r))-1),
// TODO(beorn7): How to calculate delta correctly?
}
i++
goto inserted
}
r += c.Width
}
s.l = append(s.l, Sample{sample.Value, sample.Width, 0})
i++
inserted:
s.n += sample.Width
r += sample.Width
}
s.compress()
}
func (s *stream) count() int {
return int(s.n)
}
func (s *stream) query(q float64) float64 {
t := math.Ceil(q * s.n)
t += math.Ceil(s.ƒ(s, t) / 2)
p := s.l[0]
var r float64
for _, c := range s.l[1:] {
r += p.Width
if r+c.Width+c.Delta > t {
return p.Value
}
p = c
}
return p.Value
}
func (s *stream) compress() {
if len(s.l) < 2 {
return
}
x := s.l[len(s.l)-1]
xi := len(s.l) - 1
r := s.n - 1 - x.Width
for i := len(s.l) - 2; i >= 0; i-- {
c := s.l[i]
if c.Width+x.Width+x.Delta <= s.ƒ(s, r) {
x.Width += c.Width
s.l[xi] = x
// Remove element at i.
copy(s.l[i:], s.l[i+1:])
s.l = s.l[:len(s.l)-1]
xi -= 1
} else {
x = c
xi = i
}
r -= c.Width
}
}
func (s *stream) samples() Samples {
samples := make(Samples, len(s.l))
copy(samples, s.l)
return samples
}

View File

@@ -1,22 +0,0 @@
Copyright (c) 2016 Caleb Spare
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,74 +0,0 @@
# xxhash
[![Go Reference](https://pkg.go.dev/badge/github.com/cespare/xxhash/v2.svg)](https://pkg.go.dev/github.com/cespare/xxhash/v2)
[![Test](https://github.com/cespare/xxhash/actions/workflows/test.yml/badge.svg)](https://github.com/cespare/xxhash/actions/workflows/test.yml)
xxhash is a Go implementation of the 64-bit [xxHash] algorithm, XXH64. This is a
high-quality hashing algorithm that is much faster than anything in the Go
standard library.
This package provides a straightforward API:
```
func Sum64(b []byte) uint64
func Sum64String(s string) uint64
type Digest struct{ ... }
func New() *Digest
```
The `Digest` type implements hash.Hash64. Its key methods are:
```
func (*Digest) Write([]byte) (int, error)
func (*Digest) WriteString(string) (int, error)
func (*Digest) Sum64() uint64
```
The package is written with optimized pure Go and also contains even faster
assembly implementations for amd64 and arm64. If desired, the `purego` build tag
opts into using the Go code even on those architectures.
[xxHash]: http://cyan4973.github.io/xxHash/
## Compatibility
This package is in a module and the latest code is in version 2 of the module.
You need a version of Go with at least "minimal module compatibility" to use
github.com/cespare/xxhash/v2:
* 1.9.7+ for Go 1.9
* 1.10.3+ for Go 1.10
* Go 1.11 or later
I recommend using the latest release of Go.
## Benchmarks
Here are some quick benchmarks comparing the pure-Go and assembly
implementations of Sum64.
| input size | purego | asm |
| ---------- | --------- | --------- |
| 4 B | 1.3 GB/s | 1.2 GB/s |
| 16 B | 2.9 GB/s | 3.5 GB/s |
| 100 B | 6.9 GB/s | 8.1 GB/s |
| 4 KB | 11.7 GB/s | 16.7 GB/s |
| 10 MB | 12.0 GB/s | 17.3 GB/s |
These numbers were generated on Ubuntu 20.04 with an Intel Xeon Platinum 8252C
CPU using the following commands under Go 1.19.2:
```
benchstat <(go test -tags purego -benchtime 500ms -count 15 -bench 'Sum64$')
benchstat <(go test -benchtime 500ms -count 15 -bench 'Sum64$')
```
## Projects using this package
- [InfluxDB](https://github.com/influxdata/influxdb)
- [Prometheus](https://github.com/prometheus/prometheus)
- [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics)
- [FreeCache](https://github.com/coocood/freecache)
- [FastCache](https://github.com/VictoriaMetrics/fastcache)
- [Ristretto](https://github.com/dgraph-io/ristretto)
- [Badger](https://github.com/dgraph-io/badger)

View File

@@ -1,10 +0,0 @@
#!/bin/bash
set -eu -o pipefail
# Small convenience script for running the tests with various combinations of
# arch/tags. This assumes we're running on amd64 and have qemu available.
go test ./...
go test -tags purego ./...
GOARCH=arm64 go test
GOARCH=arm64 go test -tags purego

View File

@@ -1,243 +0,0 @@
// Package xxhash implements the 64-bit variant of xxHash (XXH64) as described
// at http://cyan4973.github.io/xxHash/.
package xxhash
import (
"encoding/binary"
"errors"
"math/bits"
)
const (
prime1 uint64 = 11400714785074694791
prime2 uint64 = 14029467366897019727
prime3 uint64 = 1609587929392839161
prime4 uint64 = 9650029242287828579
prime5 uint64 = 2870177450012600261
)
// Store the primes in an array as well.
//
// The consts are used when possible in Go code to avoid MOVs but we need a
// contiguous array for the assembly code.
var primes = [...]uint64{prime1, prime2, prime3, prime4, prime5}
// Digest implements hash.Hash64.
//
// Note that a zero-valued Digest is not ready to receive writes.
// Call Reset or create a Digest using New before calling other methods.
type Digest struct {
v1 uint64
v2 uint64
v3 uint64
v4 uint64
total uint64
mem [32]byte
n int // how much of mem is used
}
// New creates a new Digest with a zero seed.
func New() *Digest {
return NewWithSeed(0)
}
// NewWithSeed creates a new Digest with the given seed.
func NewWithSeed(seed uint64) *Digest {
var d Digest
d.ResetWithSeed(seed)
return &d
}
// Reset clears the Digest's state so that it can be reused.
// It uses a seed value of zero.
func (d *Digest) Reset() {
d.ResetWithSeed(0)
}
// ResetWithSeed clears the Digest's state so that it can be reused.
// It uses the given seed to initialize the state.
func (d *Digest) ResetWithSeed(seed uint64) {
d.v1 = seed + prime1 + prime2
d.v2 = seed + prime2
d.v3 = seed
d.v4 = seed - prime1
d.total = 0
d.n = 0
}
// Size always returns 8 bytes.
func (d *Digest) Size() int { return 8 }
// BlockSize always returns 32 bytes.
func (d *Digest) BlockSize() int { return 32 }
// Write adds more data to d. It always returns len(b), nil.
func (d *Digest) Write(b []byte) (n int, err error) {
n = len(b)
d.total += uint64(n)
memleft := d.mem[d.n&(len(d.mem)-1):]
if d.n+n < 32 {
// This new data doesn't even fill the current block.
copy(memleft, b)
d.n += n
return
}
if d.n > 0 {
// Finish off the partial block.
c := copy(memleft, b)
d.v1 = round(d.v1, u64(d.mem[0:8]))
d.v2 = round(d.v2, u64(d.mem[8:16]))
d.v3 = round(d.v3, u64(d.mem[16:24]))
d.v4 = round(d.v4, u64(d.mem[24:32]))
b = b[c:]
d.n = 0
}
if len(b) >= 32 {
// One or more full blocks left.
nw := writeBlocks(d, b)
b = b[nw:]
}
// Store any remaining partial block.
copy(d.mem[:], b)
d.n = len(b)
return
}
// Sum appends the current hash to b and returns the resulting slice.
func (d *Digest) Sum(b []byte) []byte {
s := d.Sum64()
return append(
b,
byte(s>>56),
byte(s>>48),
byte(s>>40),
byte(s>>32),
byte(s>>24),
byte(s>>16),
byte(s>>8),
byte(s),
)
}
// Sum64 returns the current hash.
func (d *Digest) Sum64() uint64 {
var h uint64
if d.total >= 32 {
v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4
h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4)
h = mergeRound(h, v1)
h = mergeRound(h, v2)
h = mergeRound(h, v3)
h = mergeRound(h, v4)
} else {
h = d.v3 + prime5
}
h += d.total
b := d.mem[:d.n&(len(d.mem)-1)]
for ; len(b) >= 8; b = b[8:] {
k1 := round(0, u64(b[:8]))
h ^= k1
h = rol27(h)*prime1 + prime4
}
if len(b) >= 4 {
h ^= uint64(u32(b[:4])) * prime1
h = rol23(h)*prime2 + prime3
b = b[4:]
}
for ; len(b) > 0; b = b[1:] {
h ^= uint64(b[0]) * prime5
h = rol11(h) * prime1
}
h ^= h >> 33
h *= prime2
h ^= h >> 29
h *= prime3
h ^= h >> 32
return h
}
const (
magic = "xxh\x06"
marshaledSize = len(magic) + 8*5 + 32
)
// MarshalBinary implements the encoding.BinaryMarshaler interface.
func (d *Digest) MarshalBinary() ([]byte, error) {
b := make([]byte, 0, marshaledSize)
b = append(b, magic...)
b = appendUint64(b, d.v1)
b = appendUint64(b, d.v2)
b = appendUint64(b, d.v3)
b = appendUint64(b, d.v4)
b = appendUint64(b, d.total)
b = append(b, d.mem[:d.n]...)
b = b[:len(b)+len(d.mem)-d.n]
return b, nil
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
func (d *Digest) UnmarshalBinary(b []byte) error {
if len(b) < len(magic) || string(b[:len(magic)]) != magic {
return errors.New("xxhash: invalid hash state identifier")
}
if len(b) != marshaledSize {
return errors.New("xxhash: invalid hash state size")
}
b = b[len(magic):]
b, d.v1 = consumeUint64(b)
b, d.v2 = consumeUint64(b)
b, d.v3 = consumeUint64(b)
b, d.v4 = consumeUint64(b)
b, d.total = consumeUint64(b)
copy(d.mem[:], b)
d.n = int(d.total % uint64(len(d.mem)))
return nil
}
func appendUint64(b []byte, x uint64) []byte {
var a [8]byte
binary.LittleEndian.PutUint64(a[:], x)
return append(b, a[:]...)
}
func consumeUint64(b []byte) ([]byte, uint64) {
x := u64(b)
return b[8:], x
}
func u64(b []byte) uint64 { return binary.LittleEndian.Uint64(b) }
func u32(b []byte) uint32 { return binary.LittleEndian.Uint32(b) }
func round(acc, input uint64) uint64 {
acc += input * prime2
acc = rol31(acc)
acc *= prime1
return acc
}
func mergeRound(acc, val uint64) uint64 {
val = round(0, val)
acc ^= val
acc = acc*prime1 + prime4
return acc
}
func rol1(x uint64) uint64 { return bits.RotateLeft64(x, 1) }
func rol7(x uint64) uint64 { return bits.RotateLeft64(x, 7) }
func rol11(x uint64) uint64 { return bits.RotateLeft64(x, 11) }
func rol12(x uint64) uint64 { return bits.RotateLeft64(x, 12) }
func rol18(x uint64) uint64 { return bits.RotateLeft64(x, 18) }
func rol23(x uint64) uint64 { return bits.RotateLeft64(x, 23) }
func rol27(x uint64) uint64 { return bits.RotateLeft64(x, 27) }
func rol31(x uint64) uint64 { return bits.RotateLeft64(x, 31) }

View File

@@ -1,209 +0,0 @@
//go:build !appengine && gc && !purego
// +build !appengine
// +build gc
// +build !purego
#include "textflag.h"
// Registers:
#define h AX
#define d AX
#define p SI // pointer to advance through b
#define n DX
#define end BX // loop end
#define v1 R8
#define v2 R9
#define v3 R10
#define v4 R11
#define x R12
#define prime1 R13
#define prime2 R14
#define prime4 DI
#define round(acc, x) \
IMULQ prime2, x \
ADDQ x, acc \
ROLQ $31, acc \
IMULQ prime1, acc
// round0 performs the operation x = round(0, x).
#define round0(x) \
IMULQ prime2, x \
ROLQ $31, x \
IMULQ prime1, x
// mergeRound applies a merge round on the two registers acc and x.
// It assumes that prime1, prime2, and prime4 have been loaded.
#define mergeRound(acc, x) \
round0(x) \
XORQ x, acc \
IMULQ prime1, acc \
ADDQ prime4, acc
// blockLoop processes as many 32-byte blocks as possible,
// updating v1, v2, v3, and v4. It assumes that there is at least one block
// to process.
#define blockLoop() \
loop: \
MOVQ +0(p), x \
round(v1, x) \
MOVQ +8(p), x \
round(v2, x) \
MOVQ +16(p), x \
round(v3, x) \
MOVQ +24(p), x \
round(v4, x) \
ADDQ $32, p \
CMPQ p, end \
JLE loop
// func Sum64(b []byte) uint64
TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32
// Load fixed primes.
MOVQ ·primes+0(SB), prime1
MOVQ ·primes+8(SB), prime2
MOVQ ·primes+24(SB), prime4
// Load slice.
MOVQ b_base+0(FP), p
MOVQ b_len+8(FP), n
LEAQ (p)(n*1), end
// The first loop limit will be len(b)-32.
SUBQ $32, end
// Check whether we have at least one block.
CMPQ n, $32
JLT noBlocks
// Set up initial state (v1, v2, v3, v4).
MOVQ prime1, v1
ADDQ prime2, v1
MOVQ prime2, v2
XORQ v3, v3
XORQ v4, v4
SUBQ prime1, v4
blockLoop()
MOVQ v1, h
ROLQ $1, h
MOVQ v2, x
ROLQ $7, x
ADDQ x, h
MOVQ v3, x
ROLQ $12, x
ADDQ x, h
MOVQ v4, x
ROLQ $18, x
ADDQ x, h
mergeRound(h, v1)
mergeRound(h, v2)
mergeRound(h, v3)
mergeRound(h, v4)
JMP afterBlocks
noBlocks:
MOVQ ·primes+32(SB), h
afterBlocks:
ADDQ n, h
ADDQ $24, end
CMPQ p, end
JG try4
loop8:
MOVQ (p), x
ADDQ $8, p
round0(x)
XORQ x, h
ROLQ $27, h
IMULQ prime1, h
ADDQ prime4, h
CMPQ p, end
JLE loop8
try4:
ADDQ $4, end
CMPQ p, end
JG try1
MOVL (p), x
ADDQ $4, p
IMULQ prime1, x
XORQ x, h
ROLQ $23, h
IMULQ prime2, h
ADDQ ·primes+16(SB), h
try1:
ADDQ $4, end
CMPQ p, end
JGE finalize
loop1:
MOVBQZX (p), x
ADDQ $1, p
IMULQ ·primes+32(SB), x
XORQ x, h
ROLQ $11, h
IMULQ prime1, h
CMPQ p, end
JL loop1
finalize:
MOVQ h, x
SHRQ $33, x
XORQ x, h
IMULQ prime2, h
MOVQ h, x
SHRQ $29, x
XORQ x, h
IMULQ ·primes+16(SB), h
MOVQ h, x
SHRQ $32, x
XORQ x, h
MOVQ h, ret+24(FP)
RET
// func writeBlocks(d *Digest, b []byte) int
TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40
// Load fixed primes needed for round.
MOVQ ·primes+0(SB), prime1
MOVQ ·primes+8(SB), prime2
// Load slice.
MOVQ b_base+8(FP), p
MOVQ b_len+16(FP), n
LEAQ (p)(n*1), end
SUBQ $32, end
// Load vN from d.
MOVQ s+0(FP), d
MOVQ 0(d), v1
MOVQ 8(d), v2
MOVQ 16(d), v3
MOVQ 24(d), v4
// We don't need to check the loop condition here; this function is
// always called with at least one block of data to process.
blockLoop()
// Copy vN back to d.
MOVQ v1, 0(d)
MOVQ v2, 8(d)
MOVQ v3, 16(d)
MOVQ v4, 24(d)
// The number of bytes written is p minus the old base pointer.
SUBQ b_base+8(FP), p
MOVQ p, ret+32(FP)
RET

View File

@@ -1,183 +0,0 @@
//go:build !appengine && gc && !purego
// +build !appengine
// +build gc
// +build !purego
#include "textflag.h"
// Registers:
#define digest R1
#define h R2 // return value
#define p R3 // input pointer
#define n R4 // input length
#define nblocks R5 // n / 32
#define prime1 R7
#define prime2 R8
#define prime3 R9
#define prime4 R10
#define prime5 R11
#define v1 R12
#define v2 R13
#define v3 R14
#define v4 R15
#define x1 R20
#define x2 R21
#define x3 R22
#define x4 R23
#define round(acc, x) \
MADD prime2, acc, x, acc \
ROR $64-31, acc \
MUL prime1, acc
// round0 performs the operation x = round(0, x).
#define round0(x) \
MUL prime2, x \
ROR $64-31, x \
MUL prime1, x
#define mergeRound(acc, x) \
round0(x) \
EOR x, acc \
MADD acc, prime4, prime1, acc
// blockLoop processes as many 32-byte blocks as possible,
// updating v1, v2, v3, and v4. It assumes that n >= 32.
#define blockLoop() \
LSR $5, n, nblocks \
PCALIGN $16 \
loop: \
LDP.P 16(p), (x1, x2) \
LDP.P 16(p), (x3, x4) \
round(v1, x1) \
round(v2, x2) \
round(v3, x3) \
round(v4, x4) \
SUB $1, nblocks \
CBNZ nblocks, loop
// func Sum64(b []byte) uint64
TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32
LDP b_base+0(FP), (p, n)
LDP ·primes+0(SB), (prime1, prime2)
LDP ·primes+16(SB), (prime3, prime4)
MOVD ·primes+32(SB), prime5
CMP $32, n
CSEL LT, prime5, ZR, h // if n < 32 { h = prime5 } else { h = 0 }
BLT afterLoop
ADD prime1, prime2, v1
MOVD prime2, v2
MOVD $0, v3
NEG prime1, v4
blockLoop()
ROR $64-1, v1, x1
ROR $64-7, v2, x2
ADD x1, x2
ROR $64-12, v3, x3
ROR $64-18, v4, x4
ADD x3, x4
ADD x2, x4, h
mergeRound(h, v1)
mergeRound(h, v2)
mergeRound(h, v3)
mergeRound(h, v4)
afterLoop:
ADD n, h
TBZ $4, n, try8
LDP.P 16(p), (x1, x2)
round0(x1)
// NOTE: here and below, sequencing the EOR after the ROR (using a
// rotated register) is worth a small but measurable speedup for small
// inputs.
ROR $64-27, h
EOR x1 @> 64-27, h, h
MADD h, prime4, prime1, h
round0(x2)
ROR $64-27, h
EOR x2 @> 64-27, h, h
MADD h, prime4, prime1, h
try8:
TBZ $3, n, try4
MOVD.P 8(p), x1
round0(x1)
ROR $64-27, h
EOR x1 @> 64-27, h, h
MADD h, prime4, prime1, h
try4:
TBZ $2, n, try2
MOVWU.P 4(p), x2
MUL prime1, x2
ROR $64-23, h
EOR x2 @> 64-23, h, h
MADD h, prime3, prime2, h
try2:
TBZ $1, n, try1
MOVHU.P 2(p), x3
AND $255, x3, x1
LSR $8, x3, x2
MUL prime5, x1
ROR $64-11, h
EOR x1 @> 64-11, h, h
MUL prime1, h
MUL prime5, x2
ROR $64-11, h
EOR x2 @> 64-11, h, h
MUL prime1, h
try1:
TBZ $0, n, finalize
MOVBU (p), x4
MUL prime5, x4
ROR $64-11, h
EOR x4 @> 64-11, h, h
MUL prime1, h
finalize:
EOR h >> 33, h
MUL prime2, h
EOR h >> 29, h
MUL prime3, h
EOR h >> 32, h
MOVD h, ret+24(FP)
RET
// func writeBlocks(d *Digest, b []byte) int
TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40
LDP ·primes+0(SB), (prime1, prime2)
// Load state. Assume v[1-4] are stored contiguously.
MOVD d+0(FP), digest
LDP 0(digest), (v1, v2)
LDP 16(digest), (v3, v4)
LDP b_base+8(FP), (p, n)
blockLoop()
// Store updated state.
STP (v1, v2), 0(digest)
STP (v3, v4), 16(digest)
BIC $31, n
MOVD n, ret+32(FP)
RET

View File

@@ -1,15 +0,0 @@
//go:build (amd64 || arm64) && !appengine && gc && !purego
// +build amd64 arm64
// +build !appengine
// +build gc
// +build !purego
package xxhash
// Sum64 computes the 64-bit xxHash digest of b with a zero seed.
//
//go:noescape
func Sum64(b []byte) uint64
//go:noescape
func writeBlocks(d *Digest, b []byte) int

View File

@@ -1,76 +0,0 @@
//go:build (!amd64 && !arm64) || appengine || !gc || purego
// +build !amd64,!arm64 appengine !gc purego
package xxhash
// Sum64 computes the 64-bit xxHash digest of b with a zero seed.
func Sum64(b []byte) uint64 {
// A simpler version would be
// d := New()
// d.Write(b)
// return d.Sum64()
// but this is faster, particularly for small inputs.
n := len(b)
var h uint64
if n >= 32 {
v1 := primes[0] + prime2
v2 := prime2
v3 := uint64(0)
v4 := -primes[0]
for len(b) >= 32 {
v1 = round(v1, u64(b[0:8:len(b)]))
v2 = round(v2, u64(b[8:16:len(b)]))
v3 = round(v3, u64(b[16:24:len(b)]))
v4 = round(v4, u64(b[24:32:len(b)]))
b = b[32:len(b):len(b)]
}
h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4)
h = mergeRound(h, v1)
h = mergeRound(h, v2)
h = mergeRound(h, v3)
h = mergeRound(h, v4)
} else {
h = prime5
}
h += uint64(n)
for ; len(b) >= 8; b = b[8:] {
k1 := round(0, u64(b[:8]))
h ^= k1
h = rol27(h)*prime1 + prime4
}
if len(b) >= 4 {
h ^= uint64(u32(b[:4])) * prime1
h = rol23(h)*prime2 + prime3
b = b[4:]
}
for ; len(b) > 0; b = b[1:] {
h ^= uint64(b[0]) * prime5
h = rol11(h) * prime1
}
h ^= h >> 33
h *= prime2
h ^= h >> 29
h *= prime3
h ^= h >> 32
return h
}
func writeBlocks(d *Digest, b []byte) int {
v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4
n := len(b)
for len(b) >= 32 {
v1 = round(v1, u64(b[0:8:len(b)]))
v2 = round(v2, u64(b[8:16:len(b)]))
v3 = round(v3, u64(b[16:24:len(b)]))
v4 = round(v4, u64(b[24:32:len(b)]))
b = b[32:len(b):len(b)]
}
d.v1, d.v2, d.v3, d.v4 = v1, v2, v3, v4
return n - len(b)
}

View File

@@ -1,16 +0,0 @@
//go:build appengine
// +build appengine
// This file contains the safe implementations of otherwise unsafe-using code.
package xxhash
// Sum64String computes the 64-bit xxHash digest of s with a zero seed.
func Sum64String(s string) uint64 {
return Sum64([]byte(s))
}
// WriteString adds more data to d. It always returns len(s), nil.
func (d *Digest) WriteString(s string) (n int, err error) {
return d.Write([]byte(s))
}

View File

@@ -1,58 +0,0 @@
//go:build !appengine
// +build !appengine
// This file encapsulates usage of unsafe.
// xxhash_safe.go contains the safe implementations.
package xxhash
import (
"unsafe"
)
// In the future it's possible that compiler optimizations will make these
// XxxString functions unnecessary by realizing that calls such as
// Sum64([]byte(s)) don't need to copy s. See https://go.dev/issue/2205.
// If that happens, even if we keep these functions they can be replaced with
// the trivial safe code.
// NOTE: The usual way of doing an unsafe string-to-[]byte conversion is:
//
// var b []byte
// bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
// bh.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data
// bh.Len = len(s)
// bh.Cap = len(s)
//
// Unfortunately, as of Go 1.15.3 the inliner's cost model assigns a high enough
// weight to this sequence of expressions that any function that uses it will
// not be inlined. Instead, the functions below use a different unsafe
// conversion designed to minimize the inliner weight and allow both to be
// inlined. There is also a test (TestInlining) which verifies that these are
// inlined.
//
// See https://github.com/golang/go/issues/42739 for discussion.
// Sum64String computes the 64-bit xxHash digest of s with a zero seed.
// It may be faster than Sum64([]byte(s)) by avoiding a copy.
func Sum64String(s string) uint64 {
b := *(*[]byte)(unsafe.Pointer(&sliceHeader{s, len(s)}))
return Sum64(b)
}
// WriteString adds more data to d. It always returns len(s), nil.
// It may be faster than Write([]byte(s)) by avoiding a copy.
func (d *Digest) WriteString(s string) (n int, err error) {
d.Write(*(*[]byte)(unsafe.Pointer(&sliceHeader{s, len(s)})))
// d.Write always returns len(s), nil.
// Ignoring the return output and returning these fixed values buys a
// savings of 6 in the inliner's cost model.
return len(s), nil
}
// sliceHeader is similar to reflect.SliceHeader, but it assumes that the layout
// of the first two words is the same as the layout of a string.
type sliceHeader struct {
s string
cap int
}

View File

@@ -35,7 +35,7 @@ func GetEnvFromFile(currentEnv map[string]string, filenames []string) (map[strin
s, err := os.Stat(dotEnvFile)
if os.IsNotExist(err) {
return envMap, fmt.Errorf("Couldn't find env file: %s", dotEnvFile)
return envMap, fmt.Errorf("couldn't find env file: %s", dotEnvFile)
}
if err != nil {
return envMap, err
@@ -50,7 +50,7 @@ func GetEnvFromFile(currentEnv map[string]string, filenames []string) (map[strin
b, err := os.ReadFile(dotEnvFile)
if os.IsNotExist(err) {
return nil, fmt.Errorf("Couldn't read env file: %s", dotEnvFile)
return nil, fmt.Errorf("couldn't read env file: %s", dotEnvFile)
}
if err != nil {
return envMap, err

View File

@@ -24,28 +24,28 @@ import (
const DotEnv = ".env"
var formats = map[string]Parser{
DotEnv: func(r io.Reader, filename string, lookup func(key string) (string, bool)) (map[string]string, error) {
m, err := ParseWithLookup(r, lookup)
DotEnv: func(r io.Reader, filename string, vars map[string]string, lookup func(key string) (string, bool)) error {
err := parseWithLookup(r, vars, lookup)
if err != nil {
return nil, fmt.Errorf("failed to read %s: %w", filename, err)
return fmt.Errorf("failed to read %s: %w", filename, err)
}
return m, nil
return nil
},
}
type Parser func(r io.Reader, filename string, lookup func(key string) (string, bool)) (map[string]string, error)
type Parser func(r io.Reader, filename string, vars map[string]string, lookup func(key string) (string, bool)) error
func RegisterFormat(format string, p Parser) {
formats[format] = p
}
func ParseWithFormat(r io.Reader, filename string, resolve LookupFn, format string) (map[string]string, error) {
func ParseWithFormat(r io.Reader, filename string, vars map[string]string, resolve LookupFn, format string) error {
if format == "" {
format = DotEnv
}
fn, ok := formats[format]
if !ok {
return nil, fmt.Errorf("unsupported env_file format %q", format)
return fmt.Errorf("unsupported env_file format %q", format)
}
return fn(r, filename, resolve)
return fn(r, filename, vars, resolve)
}

View File

@@ -41,16 +41,23 @@ func Parse(r io.Reader) (map[string]string, error) {
// ParseWithLookup reads an env file from io.Reader, returning a map of keys and values.
func ParseWithLookup(r io.Reader, lookupFn LookupFn) (map[string]string, error) {
vars := map[string]string{}
err := parseWithLookup(r, vars, lookupFn)
return vars, err
}
// ParseWithLookup reads an env file from io.Reader, returning a map of keys and values.
func parseWithLookup(r io.Reader, vars map[string]string, lookupFn LookupFn) error {
data, err := io.ReadAll(r)
if err != nil {
return nil, err
return err
}
// seek past the UTF-8 BOM if it exists (particularly on Windows, some
// editors tend to add it, and it'll cause parsing to fail)
data = bytes.TrimPrefix(data, utf8BOM)
return UnmarshalBytesWithLookup(data, lookupFn)
return newParser().parse(string(data), vars, lookupFn)
}
// Load will read your env file(s) and load them into ENV for this process.

View File

@@ -18,11 +18,11 @@ package graph
import (
"fmt"
"slices"
"strings"
"github.com/compose-spec/compose-go/v2/types"
"github.com/compose-spec/compose-go/v2/utils"
"golang.org/x/exp/slices"
)
// CheckCycle analyze project's depends_on relation and report an error on cycle detection

View File

@@ -18,9 +18,9 @@ package graph
import (
"context"
"slices"
"sync"
"golang.org/x/exp/slices"
"golang.org/x/sync/errgroup"
)

View File

@@ -26,6 +26,7 @@ import (
"path/filepath"
"reflect"
"regexp"
"slices"
"strconv"
"strings"
@@ -42,7 +43,6 @@ import (
"github.com/compose-spec/compose-go/v2/validation"
"github.com/go-viper/mapstructure/v2"
"github.com/sirupsen/logrus"
"golang.org/x/exp/slices"
"gopkg.in/yaml.v3"
)
@@ -337,7 +337,7 @@ func LoadModelWithContext(ctx context.Context, configDetails types.ConfigDetails
// LoadModelWithContext reads a ConfigDetails and returns a fully loaded configuration as a yaml dictionary
func loadModelWithContext(ctx context.Context, configDetails *types.ConfigDetails, opts *Options) (map[string]any, error) {
if len(configDetails.ConfigFiles) < 1 {
return nil, errors.New("No files specified")
return nil, errors.New("no compose file specified")
}
err := projectName(configDetails, opts)
@@ -432,7 +432,7 @@ func loadYamlFile(ctx context.Context,
}
cfg, ok := converted.(map[string]interface{})
if !ok {
return errors.New("Top-level object must be a mapping")
return errors.New("top-level object must be a mapping")
}
if opts.Interpolate != nil && !opts.SkipInterpolation {
@@ -877,7 +877,7 @@ func formatInvalidKeyError(keyPrefix string, key interface{}) error {
} else {
location = fmt.Sprintf("in %s", keyPrefix)
}
return fmt.Errorf("Non-string key %s: %#v", location, key)
return fmt.Errorf("non-string key %s: %#v", location, key)
}
// Windows path, c:\\my\\path\\shiny, need to be changed to be compatible with

View File

@@ -67,6 +67,8 @@ func (p *ResetProcessor) resolveReset(node *yaml.Node, path tree.Path) (*yaml.No
p.paths = append(p.paths, path)
return node, nil
}
keys := map[string]int{}
switch node.Kind {
case yaml.SequenceNode:
var nodes []*yaml.Node
@@ -87,6 +89,10 @@ func (p *ResetProcessor) resolveReset(node *yaml.Node, path tree.Path) (*yaml.No
for idx, v := range node.Content {
if idx%2 == 0 {
key = v.Value
if line, seen := keys[key]; seen {
return nil, fmt.Errorf("line %d: mapping key %#v already defined at line %d", v.Line, key, line)
}
keys[key] = v.Line
} else {
resolved, err := p.resolveReset(v, path.Next(key))
if err != nil {

View File

@@ -29,7 +29,7 @@ import (
// checkConsistency validate a compose model is consistent
func checkConsistency(project *types.Project) error { //nolint:gocyclo
for name, s := range project.Services {
if s.Build == nil && s.Image == "" {
if s.Build == nil && s.Image == "" && s.Provider == nil {
return fmt.Errorf("service %q has neither an image nor a build context specified: %w", s.Name, errdefs.ErrInvalid)
}

View File

@@ -19,10 +19,9 @@ package override
import (
"cmp"
"fmt"
"strings"
"slices"
"github.com/compose-spec/compose-go/v2/tree"
"golang.org/x/exp/slices"
)
// Merge applies overrides to a config model
@@ -62,6 +61,7 @@ func init() {
mergeSpecials["services.*.extra_hosts"] = mergeExtraHosts
mergeSpecials["services.*.healthcheck.test"] = override
mergeSpecials["services.*.labels"] = mergeToSequence
mergeSpecials["services.*.volumes.*.volume.labels"] = mergeToSequence
mergeSpecials["services.*.logging"] = mergeLogging
mergeSpecials["services.*.networks"] = mergeNetworks
mergeSpecials["services.*.sysctls"] = mergeToSequence
@@ -104,7 +104,7 @@ func mergeYaml(e any, o any, p tree.Path) (any, error) {
func mergeMappings(mapping map[string]any, other map[string]any, p tree.Path) (map[string]any, error) {
for k, v := range other {
e, ok := mapping[k]
if !ok || strings.HasPrefix(k, "x-") {
if !ok {
mapping[k] = v
continue
}
@@ -227,9 +227,17 @@ func mergeUlimit(_ any, o any, p tree.Path) (any, error) {
func mergeIPAMConfig(c any, o any, path tree.Path) (any, error) {
var ipamConfigs []any
for _, original := range c.([]any) {
configs, ok := c.([]any)
if !ok {
return o, fmt.Errorf("%s: unexpected type %T", path, c)
}
overrides, ok := o.([]any)
if !ok {
return o, fmt.Errorf("%s: unexpected type %T", path, c)
}
for _, original := range configs {
right := convertIntoMapping(original, nil)
for _, override := range o.([]any) {
for _, override := range overrides {
left := convertIntoMapping(override, nil)
if left["subnet"] != right["subnet"] {
// check if left is already in ipamConfigs, add it if not and continue with the next config

View File

@@ -259,6 +259,20 @@
}
]
},
"provider": {
"type": "object",
"properties": {
"type": {"type": "string"},
"options": {
"type": "object",
"patternProperties": {
"^.+$": {"type": ["string", "number", "null"]}
}
}
},
"additionalProperties": false,
"patternProperties": {"^x-": {}}
},
"external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
"extra_hosts": {"$ref": "#/definitions/extra_hosts"},
"gpus": {"$ref": "#/definitions/gpus"},
@@ -279,7 +293,6 @@
"links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
"logging": {
"type": "object",
"properties": {
"driver": {"type": "string"},
"options": {
@@ -405,7 +418,9 @@
"type": "object",
"required": ["type"],
"properties": {
"type": {"type": "string"},
"type": {"type": "string",
"enum": ["bind", "volume", "tmpfs", "cluster", "image"]
},
"source": {"type": "string"},
"target": {"type": "string"},
"read_only": {"type": ["boolean", "string"]},
@@ -424,6 +439,7 @@
"volume": {
"type": "object",
"properties": {
"labels": {"$ref": "#/definitions/list_or_dict"},
"nocopy": {"type": ["boolean", "string"]},
"subpath": {"type": "string"}
},
@@ -443,6 +459,14 @@
},
"additionalProperties": false,
"patternProperties": {"^x-": {}}
},
"image": {
"type": "object",
"properties": {
"subpath": {"type": "string"}
},
"additionalProperties": false,
"patternProperties": {"^x-": {}}
}
},
"additionalProperties": false,

View File

@@ -355,9 +355,15 @@ func deriveDeepCopyService(dst, src *ServiceConfig) {
}
copy(dst.Entrypoint, src.Entrypoint)
}
if src.Provider == nil {
dst.Provider = nil
} else {
dst.Provider = new(ServiceProviderConfig)
deriveDeepCopy_13(dst.Provider, src.Provider)
}
if src.Environment != nil {
dst.Environment = make(map[string]*string, len(src.Environment))
deriveDeepCopy_13(dst.Environment, src.Environment)
deriveDeepCopy_14(dst.Environment, src.Environment)
} else {
dst.Environment = nil
}
@@ -423,7 +429,7 @@ func deriveDeepCopyService(dst, src *ServiceConfig) {
}
if src.ExtraHosts != nil {
dst.ExtraHosts = make(map[string][]string, len(src.ExtraHosts))
deriveDeepCopy_14(dst.ExtraHosts, src.ExtraHosts)
deriveDeepCopy_15(dst.ExtraHosts, src.ExtraHosts)
} else {
dst.ExtraHosts = nil
}
@@ -461,14 +467,14 @@ func deriveDeepCopyService(dst, src *ServiceConfig) {
} else {
dst.Gpus = make([]DeviceRequest, len(src.Gpus))
}
deriveDeepCopy_15(dst.Gpus, src.Gpus)
deriveDeepCopy_16(dst.Gpus, src.Gpus)
}
dst.Hostname = src.Hostname
if src.HealthCheck == nil {
dst.HealthCheck = nil
} else {
dst.HealthCheck = new(HealthCheckConfig)
deriveDeepCopy_16(dst.HealthCheck, src.HealthCheck)
deriveDeepCopy_17(dst.HealthCheck, src.HealthCheck)
}
dst.Image = src.Image
if src.Init == nil {
@@ -531,7 +537,7 @@ func deriveDeepCopyService(dst, src *ServiceConfig) {
dst.Logging = nil
} else {
dst.Logging = new(LoggingConfig)
deriveDeepCopy_17(dst.Logging, src.Logging)
deriveDeepCopy_18(dst.Logging, src.Logging)
}
dst.LogDriver = src.LogDriver
if src.LogOpt != nil {
@@ -549,7 +555,7 @@ func deriveDeepCopyService(dst, src *ServiceConfig) {
dst.NetworkMode = src.NetworkMode
if src.Networks != nil {
dst.Networks = make(map[string]*ServiceNetworkConfig, len(src.Networks))
deriveDeepCopy_18(dst.Networks, src.Networks)
deriveDeepCopy_19(dst.Networks, src.Networks)
} else {
dst.Networks = nil
}
@@ -574,7 +580,7 @@ func deriveDeepCopyService(dst, src *ServiceConfig) {
} else {
dst.Ports = make([]ServicePortConfig, len(src.Ports))
}
deriveDeepCopy_19(dst.Ports, src.Ports)
deriveDeepCopy_20(dst.Ports, src.Ports)
}
dst.Privileged = src.Privileged
dst.PullPolicy = src.PullPolicy
@@ -603,7 +609,7 @@ func deriveDeepCopyService(dst, src *ServiceConfig) {
} else {
dst.Secrets = make([]ServiceSecretConfig, len(src.Secrets))
}
deriveDeepCopy_20(dst.Secrets, src.Secrets)
deriveDeepCopy_21(dst.Secrets, src.Secrets)
}
if src.SecurityOpt == nil {
dst.SecurityOpt = nil
@@ -665,7 +671,7 @@ func deriveDeepCopyService(dst, src *ServiceConfig) {
dst.Tty = src.Tty
if src.Ulimits != nil {
dst.Ulimits = make(map[string]*UlimitsConfig, len(src.Ulimits))
deriveDeepCopy_21(dst.Ulimits, src.Ulimits)
deriveDeepCopy_22(dst.Ulimits, src.Ulimits)
} else {
dst.Ulimits = nil
}
@@ -689,7 +695,7 @@ func deriveDeepCopyService(dst, src *ServiceConfig) {
} else {
dst.Volumes = make([]ServiceVolumeConfig, len(src.Volumes))
}
deriveDeepCopy_22(dst.Volumes, src.Volumes)
deriveDeepCopy_23(dst.Volumes, src.Volumes)
}
if src.VolumesFrom == nil {
dst.VolumesFrom = nil
@@ -726,7 +732,7 @@ func deriveDeepCopyService(dst, src *ServiceConfig) {
} else {
dst.PostStart = make([]ServiceHook, len(src.PostStart))
}
deriveDeepCopy_23(dst.PostStart, src.PostStart)
deriveDeepCopy_24(dst.PostStart, src.PostStart)
}
if src.PreStop == nil {
dst.PreStop = nil
@@ -744,7 +750,7 @@ func deriveDeepCopyService(dst, src *ServiceConfig) {
} else {
dst.PreStop = make([]ServiceHook, len(src.PreStop))
}
deriveDeepCopy_23(dst.PreStop, src.PreStop)
deriveDeepCopy_24(dst.PreStop, src.PreStop)
}
if src.Extensions != nil {
dst.Extensions = make(map[string]any, len(src.Extensions))
@@ -770,7 +776,7 @@ func deriveDeepCopy_(dst, src map[string]NetworkConfig) {
for src_key, src_value := range src {
func() {
field := new(NetworkConfig)
deriveDeepCopy_24(field, &src_value)
deriveDeepCopy_25(field, &src_value)
dst[src_key] = *field
}()
}
@@ -781,7 +787,7 @@ func deriveDeepCopy_1(dst, src map[string]VolumeConfig) {
for src_key, src_value := range src {
func() {
field := new(VolumeConfig)
deriveDeepCopy_25(field, &src_value)
deriveDeepCopy_26(field, &src_value)
dst[src_key] = *field
}()
}
@@ -792,7 +798,7 @@ func deriveDeepCopy_2(dst, src map[string]SecretConfig) {
for src_key, src_value := range src {
func() {
field := new(SecretConfig)
deriveDeepCopy_26(field, &src_value)
deriveDeepCopy_27(field, &src_value)
dst[src_key] = *field
}()
}
@@ -803,7 +809,7 @@ func deriveDeepCopy_3(dst, src map[string]ConfigObjConfig) {
for src_key, src_value := range src {
func() {
field := new(ConfigObjConfig)
deriveDeepCopy_27(field, &src_value)
deriveDeepCopy_28(field, &src_value)
dst[src_key] = *field
}()
}
@@ -841,7 +847,7 @@ func deriveDeepCopy_5(dst, src *BuildConfig) {
}
if src.Args != nil {
dst.Args = make(map[string]*string, len(src.Args))
deriveDeepCopy_13(dst.Args, src.Args)
deriveDeepCopy_14(dst.Args, src.Args)
} else {
dst.Args = nil
}
@@ -915,7 +921,7 @@ func deriveDeepCopy_5(dst, src *BuildConfig) {
dst.Pull = src.Pull
if src.ExtraHosts != nil {
dst.ExtraHosts = make(map[string][]string, len(src.ExtraHosts))
deriveDeepCopy_14(dst.ExtraHosts, src.ExtraHosts)
deriveDeepCopy_15(dst.ExtraHosts, src.ExtraHosts)
} else {
dst.ExtraHosts = nil
}
@@ -938,7 +944,7 @@ func deriveDeepCopy_5(dst, src *BuildConfig) {
} else {
dst.Secrets = make([]ServiceSecretConfig, len(src.Secrets))
}
deriveDeepCopy_20(dst.Secrets, src.Secrets)
deriveDeepCopy_21(dst.Secrets, src.Secrets)
}
dst.ShmSize = src.ShmSize
if src.Tags == nil {
@@ -961,7 +967,7 @@ func deriveDeepCopy_5(dst, src *BuildConfig) {
}
if src.Ulimits != nil {
dst.Ulimits = make(map[string]*UlimitsConfig, len(src.Ulimits))
deriveDeepCopy_21(dst.Ulimits, src.Ulimits)
deriveDeepCopy_22(dst.Ulimits, src.Ulimits)
} else {
dst.Ulimits = nil
}
@@ -1010,7 +1016,7 @@ func deriveDeepCopy_6(dst, src *DevelopConfig) {
} else {
dst.Watch = make([]Trigger, len(src.Watch))
}
deriveDeepCopy_28(dst.Watch, src.Watch)
deriveDeepCopy_29(dst.Watch, src.Watch)
}
if src.Extensions != nil {
dst.Extensions = make(map[string]any, len(src.Extensions))
@@ -1039,7 +1045,7 @@ func deriveDeepCopy_7(dst, src *BlkioConfig) {
} else {
dst.WeightDevice = make([]WeightDevice, len(src.WeightDevice))
}
deriveDeepCopy_29(dst.WeightDevice, src.WeightDevice)
deriveDeepCopy_30(dst.WeightDevice, src.WeightDevice)
}
if src.DeviceReadBps == nil {
dst.DeviceReadBps = nil
@@ -1057,7 +1063,7 @@ func deriveDeepCopy_7(dst, src *BlkioConfig) {
} else {
dst.DeviceReadBps = make([]ThrottleDevice, len(src.DeviceReadBps))
}
deriveDeepCopy_30(dst.DeviceReadBps, src.DeviceReadBps)
deriveDeepCopy_31(dst.DeviceReadBps, src.DeviceReadBps)
}
if src.DeviceReadIOps == nil {
dst.DeviceReadIOps = nil
@@ -1075,7 +1081,7 @@ func deriveDeepCopy_7(dst, src *BlkioConfig) {
} else {
dst.DeviceReadIOps = make([]ThrottleDevice, len(src.DeviceReadIOps))
}
deriveDeepCopy_30(dst.DeviceReadIOps, src.DeviceReadIOps)
deriveDeepCopy_31(dst.DeviceReadIOps, src.DeviceReadIOps)
}
if src.DeviceWriteBps == nil {
dst.DeviceWriteBps = nil
@@ -1093,7 +1099,7 @@ func deriveDeepCopy_7(dst, src *BlkioConfig) {
} else {
dst.DeviceWriteBps = make([]ThrottleDevice, len(src.DeviceWriteBps))
}
deriveDeepCopy_30(dst.DeviceWriteBps, src.DeviceWriteBps)
deriveDeepCopy_31(dst.DeviceWriteBps, src.DeviceWriteBps)
}
if src.DeviceWriteIOps == nil {
dst.DeviceWriteIOps = nil
@@ -1111,7 +1117,7 @@ func deriveDeepCopy_7(dst, src *BlkioConfig) {
} else {
dst.DeviceWriteIOps = make([]ThrottleDevice, len(src.DeviceWriteIOps))
}
deriveDeepCopy_30(dst.DeviceWriteIOps, src.DeviceWriteIOps)
deriveDeepCopy_31(dst.DeviceWriteIOps, src.DeviceWriteIOps)
}
if src.Extensions != nil {
dst.Extensions = make(map[string]any, len(src.Extensions))
@@ -1126,7 +1132,7 @@ func deriveDeepCopy_8(dst, src []ServiceConfigObjConfig) {
for src_i, src_value := range src {
func() {
field := new(ServiceConfigObjConfig)
deriveDeepCopy_31(field, &src_value)
deriveDeepCopy_32(field, &src_value)
dst[src_i] = *field
}()
}
@@ -1150,7 +1156,7 @@ func deriveDeepCopy_10(dst, src map[string]ServiceDependency) {
for src_key, src_value := range src {
func() {
field := new(ServiceDependency)
deriveDeepCopy_32(field, &src_value)
deriveDeepCopy_33(field, &src_value)
dst[src_key] = *field
}()
}
@@ -1175,28 +1181,28 @@ func deriveDeepCopy_11(dst, src *DeployConfig) {
dst.UpdateConfig = nil
} else {
dst.UpdateConfig = new(UpdateConfig)
deriveDeepCopy_33(dst.UpdateConfig, src.UpdateConfig)
deriveDeepCopy_34(dst.UpdateConfig, src.UpdateConfig)
}
if src.RollbackConfig == nil {
dst.RollbackConfig = nil
} else {
dst.RollbackConfig = new(UpdateConfig)
deriveDeepCopy_33(dst.RollbackConfig, src.RollbackConfig)
deriveDeepCopy_34(dst.RollbackConfig, src.RollbackConfig)
}
func() {
field := new(Resources)
deriveDeepCopy_34(field, &src.Resources)
deriveDeepCopy_35(field, &src.Resources)
dst.Resources = *field
}()
if src.RestartPolicy == nil {
dst.RestartPolicy = nil
} else {
dst.RestartPolicy = new(RestartPolicy)
deriveDeepCopy_35(dst.RestartPolicy, src.RestartPolicy)
deriveDeepCopy_36(dst.RestartPolicy, src.RestartPolicy)
}
func() {
field := new(Placement)
deriveDeepCopy_36(field, &src.Placement)
deriveDeepCopy_37(field, &src.Placement)
dst.Placement = *field
}()
dst.EndpointMode = src.EndpointMode
@@ -1213,14 +1219,31 @@ func deriveDeepCopy_12(dst, src []DeviceMapping) {
for src_i, src_value := range src {
func() {
field := new(DeviceMapping)
deriveDeepCopy_37(field, &src_value)
deriveDeepCopy_38(field, &src_value)
dst[src_i] = *field
}()
}
}
// deriveDeepCopy_13 recursively copies the contents of src into dst.
func deriveDeepCopy_13(dst, src map[string]*string) {
func deriveDeepCopy_13(dst, src *ServiceProviderConfig) {
dst.Type = src.Type
if src.Options != nil {
dst.Options = make(map[string]string, len(src.Options))
deriveDeepCopy_4(dst.Options, src.Options)
} else {
dst.Options = nil
}
if src.Extensions != nil {
dst.Extensions = make(map[string]any, len(src.Extensions))
src.Extensions.DeepCopy(dst.Extensions)
} else {
dst.Extensions = nil
}
}
// deriveDeepCopy_14 recursively copies the contents of src into dst.
func deriveDeepCopy_14(dst, src map[string]*string) {
for src_key, src_value := range src {
if src_value == nil {
dst[src_key] = nil
@@ -1234,8 +1257,8 @@ func deriveDeepCopy_13(dst, src map[string]*string) {
}
}
// deriveDeepCopy_14 recursively copies the contents of src into dst.
func deriveDeepCopy_14(dst, src map[string][]string) {
// deriveDeepCopy_15 recursively copies the contents of src into dst.
func deriveDeepCopy_15(dst, src map[string][]string) {
for src_key, src_value := range src {
if src_value == nil {
dst[src_key] = nil
@@ -1261,19 +1284,19 @@ func deriveDeepCopy_14(dst, src map[string][]string) {
}
}
// deriveDeepCopy_15 recursively copies the contents of src into dst.
func deriveDeepCopy_15(dst, src []DeviceRequest) {
// deriveDeepCopy_16 recursively copies the contents of src into dst.
func deriveDeepCopy_16(dst, src []DeviceRequest) {
for src_i, src_value := range src {
func() {
field := new(DeviceRequest)
deriveDeepCopy_38(field, &src_value)
deriveDeepCopy_39(field, &src_value)
dst[src_i] = *field
}()
}
}
// deriveDeepCopy_16 recursively copies the contents of src into dst.
func deriveDeepCopy_16(dst, src *HealthCheckConfig) {
// deriveDeepCopy_17 recursively copies the contents of src into dst.
func deriveDeepCopy_17(dst, src *HealthCheckConfig) {
if src.Test == nil {
dst.Test = nil
} else {
@@ -1331,8 +1354,8 @@ func deriveDeepCopy_16(dst, src *HealthCheckConfig) {
}
}
// deriveDeepCopy_17 recursively copies the contents of src into dst.
func deriveDeepCopy_17(dst, src *LoggingConfig) {
// deriveDeepCopy_18 recursively copies the contents of src into dst.
func deriveDeepCopy_18(dst, src *LoggingConfig) {
dst.Driver = src.Driver
if src.Options != nil {
dst.Options = make(map[string]string, len(src.Options))
@@ -1348,8 +1371,8 @@ func deriveDeepCopy_17(dst, src *LoggingConfig) {
}
}
// deriveDeepCopy_18 recursively copies the contents of src into dst.
func deriveDeepCopy_18(dst, src map[string]*ServiceNetworkConfig) {
// deriveDeepCopy_19 recursively copies the contents of src into dst.
func deriveDeepCopy_19(dst, src map[string]*ServiceNetworkConfig) {
for src_key, src_value := range src {
if src_value == nil {
dst[src_key] = nil
@@ -1358,27 +1381,16 @@ func deriveDeepCopy_18(dst, src map[string]*ServiceNetworkConfig) {
dst[src_key] = nil
} else {
dst[src_key] = new(ServiceNetworkConfig)
deriveDeepCopy_39(dst[src_key], src_value)
deriveDeepCopy_40(dst[src_key], src_value)
}
}
}
// deriveDeepCopy_19 recursively copies the contents of src into dst.
func deriveDeepCopy_19(dst, src []ServicePortConfig) {
// deriveDeepCopy_20 recursively copies the contents of src into dst.
func deriveDeepCopy_20(dst, src []ServicePortConfig) {
for src_i, src_value := range src {
func() {
field := new(ServicePortConfig)
deriveDeepCopy_40(field, &src_value)
dst[src_i] = *field
}()
}
}
// deriveDeepCopy_20 recursively copies the contents of src into dst.
func deriveDeepCopy_20(dst, src []ServiceSecretConfig) {
for src_i, src_value := range src {
func() {
field := new(ServiceSecretConfig)
deriveDeepCopy_41(field, &src_value)
dst[src_i] = *field
}()
@@ -1386,7 +1398,18 @@ func deriveDeepCopy_20(dst, src []ServiceSecretConfig) {
}
// deriveDeepCopy_21 recursively copies the contents of src into dst.
func deriveDeepCopy_21(dst, src map[string]*UlimitsConfig) {
func deriveDeepCopy_21(dst, src []ServiceSecretConfig) {
for src_i, src_value := range src {
func() {
field := new(ServiceSecretConfig)
deriveDeepCopy_42(field, &src_value)
dst[src_i] = *field
}()
}
}
// deriveDeepCopy_22 recursively copies the contents of src into dst.
func deriveDeepCopy_22(dst, src map[string]*UlimitsConfig) {
for src_key, src_value := range src {
if src_value == nil {
dst[src_key] = nil
@@ -1395,27 +1418,16 @@ func deriveDeepCopy_21(dst, src map[string]*UlimitsConfig) {
dst[src_key] = nil
} else {
dst[src_key] = new(UlimitsConfig)
deriveDeepCopy_42(dst[src_key], src_value)
deriveDeepCopy_43(dst[src_key], src_value)
}
}
}
// deriveDeepCopy_22 recursively copies the contents of src into dst.
func deriveDeepCopy_22(dst, src []ServiceVolumeConfig) {
// deriveDeepCopy_23 recursively copies the contents of src into dst.
func deriveDeepCopy_23(dst, src []ServiceVolumeConfig) {
for src_i, src_value := range src {
func() {
field := new(ServiceVolumeConfig)
deriveDeepCopy_43(field, &src_value)
dst[src_i] = *field
}()
}
}
// deriveDeepCopy_23 recursively copies the contents of src into dst.
func deriveDeepCopy_23(dst, src []ServiceHook) {
for src_i, src_value := range src {
func() {
field := new(ServiceHook)
deriveDeepCopy_44(field, &src_value)
dst[src_i] = *field
}()
@@ -1423,7 +1435,18 @@ func deriveDeepCopy_23(dst, src []ServiceHook) {
}
// deriveDeepCopy_24 recursively copies the contents of src into dst.
func deriveDeepCopy_24(dst, src *NetworkConfig) {
func deriveDeepCopy_24(dst, src []ServiceHook) {
for src_i, src_value := range src {
func() {
field := new(ServiceHook)
deriveDeepCopy_45(field, &src_value)
dst[src_i] = *field
}()
}
}
// deriveDeepCopy_25 recursively copies the contents of src into dst.
func deriveDeepCopy_25(dst, src *NetworkConfig) {
dst.Name = src.Name
dst.Driver = src.Driver
if src.DriverOpts != nil {
@@ -1434,7 +1457,7 @@ func deriveDeepCopy_24(dst, src *NetworkConfig) {
}
func() {
field := new(IPAMConfig)
deriveDeepCopy_45(field, &src.Ipam)
deriveDeepCopy_46(field, &src.Ipam)
dst.Ipam = *field
}()
dst.External = src.External
@@ -1472,8 +1495,8 @@ func deriveDeepCopy_24(dst, src *NetworkConfig) {
}
}
// deriveDeepCopy_25 recursively copies the contents of src into dst.
func deriveDeepCopy_25(dst, src *VolumeConfig) {
// deriveDeepCopy_26 recursively copies the contents of src into dst.
func deriveDeepCopy_26(dst, src *VolumeConfig) {
dst.Name = src.Name
dst.Driver = src.Driver
if src.DriverOpts != nil {
@@ -1503,38 +1526,8 @@ func deriveDeepCopy_25(dst, src *VolumeConfig) {
}
}
// deriveDeepCopy_26 recursively copies the contents of src into dst.
func deriveDeepCopy_26(dst, src *SecretConfig) {
dst.Name = src.Name
dst.File = src.File
dst.Environment = src.Environment
dst.Content = src.Content
dst.marshallContent = src.marshallContent
dst.External = src.External
if src.Labels != nil {
dst.Labels = make(map[string]string, len(src.Labels))
deriveDeepCopy_4(dst.Labels, src.Labels)
} else {
dst.Labels = nil
}
dst.Driver = src.Driver
if src.DriverOpts != nil {
dst.DriverOpts = make(map[string]string, len(src.DriverOpts))
deriveDeepCopy_4(dst.DriverOpts, src.DriverOpts)
} else {
dst.DriverOpts = nil
}
dst.TemplateDriver = src.TemplateDriver
if src.Extensions != nil {
dst.Extensions = make(map[string]any, len(src.Extensions))
src.Extensions.DeepCopy(dst.Extensions)
} else {
dst.Extensions = nil
}
}
// deriveDeepCopy_27 recursively copies the contents of src into dst.
func deriveDeepCopy_27(dst, src *ConfigObjConfig) {
func deriveDeepCopy_27(dst, src *SecretConfig) {
dst.Name = src.Name
dst.File = src.File
dst.Environment = src.Environment
@@ -1564,21 +1557,40 @@ func deriveDeepCopy_27(dst, src *ConfigObjConfig) {
}
// deriveDeepCopy_28 recursively copies the contents of src into dst.
func deriveDeepCopy_28(dst, src []Trigger) {
for src_i, src_value := range src {
func() {
field := new(Trigger)
deriveDeepCopy_46(field, &src_value)
dst[src_i] = *field
}()
func deriveDeepCopy_28(dst, src *ConfigObjConfig) {
dst.Name = src.Name
dst.File = src.File
dst.Environment = src.Environment
dst.Content = src.Content
dst.marshallContent = src.marshallContent
dst.External = src.External
if src.Labels != nil {
dst.Labels = make(map[string]string, len(src.Labels))
deriveDeepCopy_4(dst.Labels, src.Labels)
} else {
dst.Labels = nil
}
dst.Driver = src.Driver
if src.DriverOpts != nil {
dst.DriverOpts = make(map[string]string, len(src.DriverOpts))
deriveDeepCopy_4(dst.DriverOpts, src.DriverOpts)
} else {
dst.DriverOpts = nil
}
dst.TemplateDriver = src.TemplateDriver
if src.Extensions != nil {
dst.Extensions = make(map[string]any, len(src.Extensions))
src.Extensions.DeepCopy(dst.Extensions)
} else {
dst.Extensions = nil
}
}
// deriveDeepCopy_29 recursively copies the contents of src into dst.
func deriveDeepCopy_29(dst, src []WeightDevice) {
func deriveDeepCopy_29(dst, src []Trigger) {
for src_i, src_value := range src {
func() {
field := new(WeightDevice)
field := new(Trigger)
deriveDeepCopy_47(field, &src_value)
dst[src_i] = *field
}()
@@ -1586,10 +1598,10 @@ func deriveDeepCopy_29(dst, src []WeightDevice) {
}
// deriveDeepCopy_30 recursively copies the contents of src into dst.
func deriveDeepCopy_30(dst, src []ThrottleDevice) {
func deriveDeepCopy_30(dst, src []WeightDevice) {
for src_i, src_value := range src {
func() {
field := new(ThrottleDevice)
field := new(WeightDevice)
deriveDeepCopy_48(field, &src_value)
dst[src_i] = *field
}()
@@ -1597,7 +1609,18 @@ func deriveDeepCopy_30(dst, src []ThrottleDevice) {
}
// deriveDeepCopy_31 recursively copies the contents of src into dst.
func deriveDeepCopy_31(dst, src *ServiceConfigObjConfig) {
func deriveDeepCopy_31(dst, src []ThrottleDevice) {
for src_i, src_value := range src {
func() {
field := new(ThrottleDevice)
deriveDeepCopy_49(field, &src_value)
dst[src_i] = *field
}()
}
}
// deriveDeepCopy_32 recursively copies the contents of src into dst.
func deriveDeepCopy_32(dst, src *ServiceConfigObjConfig) {
dst.Source = src.Source
dst.Target = src.Target
dst.UID = src.UID
@@ -1616,8 +1639,8 @@ func deriveDeepCopy_31(dst, src *ServiceConfigObjConfig) {
}
}
// deriveDeepCopy_32 recursively copies the contents of src into dst.
func deriveDeepCopy_32(dst, src *ServiceDependency) {
// deriveDeepCopy_33 recursively copies the contents of src into dst.
func deriveDeepCopy_33(dst, src *ServiceDependency) {
dst.Condition = src.Condition
dst.Restart = src.Restart
if src.Extensions != nil {
@@ -1629,8 +1652,8 @@ func deriveDeepCopy_32(dst, src *ServiceDependency) {
dst.Required = src.Required
}
// deriveDeepCopy_33 recursively copies the contents of src into dst.
func deriveDeepCopy_33(dst, src *UpdateConfig) {
// deriveDeepCopy_34 recursively copies the contents of src into dst.
func deriveDeepCopy_34(dst, src *UpdateConfig) {
if src.Parallelism == nil {
dst.Parallelism = nil
} else {
@@ -1650,19 +1673,19 @@ func deriveDeepCopy_33(dst, src *UpdateConfig) {
}
}
// deriveDeepCopy_34 recursively copies the contents of src into dst.
func deriveDeepCopy_34(dst, src *Resources) {
// deriveDeepCopy_35 recursively copies the contents of src into dst.
func deriveDeepCopy_35(dst, src *Resources) {
if src.Limits == nil {
dst.Limits = nil
} else {
dst.Limits = new(Resource)
deriveDeepCopy_49(dst.Limits, src.Limits)
deriveDeepCopy_50(dst.Limits, src.Limits)
}
if src.Reservations == nil {
dst.Reservations = nil
} else {
dst.Reservations = new(Resource)
deriveDeepCopy_49(dst.Reservations, src.Reservations)
deriveDeepCopy_50(dst.Reservations, src.Reservations)
}
if src.Extensions != nil {
dst.Extensions = make(map[string]any, len(src.Extensions))
@@ -1672,8 +1695,8 @@ func deriveDeepCopy_34(dst, src *Resources) {
}
}
// deriveDeepCopy_35 recursively copies the contents of src into dst.
func deriveDeepCopy_35(dst, src *RestartPolicy) {
// deriveDeepCopy_36 recursively copies the contents of src into dst.
func deriveDeepCopy_36(dst, src *RestartPolicy) {
dst.Condition = src.Condition
if src.Delay == nil {
dst.Delay = nil
@@ -1701,8 +1724,8 @@ func deriveDeepCopy_35(dst, src *RestartPolicy) {
}
}
// deriveDeepCopy_36 recursively copies the contents of src into dst.
func deriveDeepCopy_36(dst, src *Placement) {
// deriveDeepCopy_37 recursively copies the contents of src into dst.
func deriveDeepCopy_37(dst, src *Placement) {
if src.Constraints == nil {
dst.Constraints = nil
} else {
@@ -1737,7 +1760,7 @@ func deriveDeepCopy_36(dst, src *Placement) {
} else {
dst.Preferences = make([]PlacementPreferences, len(src.Preferences))
}
deriveDeepCopy_50(dst.Preferences, src.Preferences)
deriveDeepCopy_51(dst.Preferences, src.Preferences)
}
dst.MaxReplicas = src.MaxReplicas
if src.Extensions != nil {
@@ -1748,8 +1771,8 @@ func deriveDeepCopy_36(dst, src *Placement) {
}
}
// deriveDeepCopy_37 recursively copies the contents of src into dst.
func deriveDeepCopy_37(dst, src *DeviceMapping) {
// deriveDeepCopy_38 recursively copies the contents of src into dst.
func deriveDeepCopy_38(dst, src *DeviceMapping) {
dst.Source = src.Source
dst.Target = src.Target
dst.Permissions = src.Permissions
@@ -1761,8 +1784,8 @@ func deriveDeepCopy_37(dst, src *DeviceMapping) {
}
}
// deriveDeepCopy_38 recursively copies the contents of src into dst.
func deriveDeepCopy_38(dst, src *DeviceRequest) {
// deriveDeepCopy_39 recursively copies the contents of src into dst.
func deriveDeepCopy_39(dst, src *DeviceRequest) {
if src.Capabilities == nil {
dst.Capabilities = nil
} else {
@@ -1809,8 +1832,8 @@ func deriveDeepCopy_38(dst, src *DeviceRequest) {
}
}
// deriveDeepCopy_39 recursively copies the contents of src into dst.
func deriveDeepCopy_39(dst, src *ServiceNetworkConfig) {
// deriveDeepCopy_40 recursively copies the contents of src into dst.
func deriveDeepCopy_40(dst, src *ServiceNetworkConfig) {
dst.Priority = src.Priority
dst.GatewayPriority = src.GatewayPriority
if src.Aliases == nil {
@@ -1866,8 +1889,8 @@ func deriveDeepCopy_39(dst, src *ServiceNetworkConfig) {
}
}
// deriveDeepCopy_40 recursively copies the contents of src into dst.
func deriveDeepCopy_40(dst, src *ServicePortConfig) {
// deriveDeepCopy_41 recursively copies the contents of src into dst.
func deriveDeepCopy_41(dst, src *ServicePortConfig) {
dst.Name = src.Name
dst.Mode = src.Mode
dst.HostIP = src.HostIP
@@ -1883,8 +1906,8 @@ func deriveDeepCopy_40(dst, src *ServicePortConfig) {
}
}
// deriveDeepCopy_41 recursively copies the contents of src into dst.
func deriveDeepCopy_41(dst, src *ServiceSecretConfig) {
// deriveDeepCopy_42 recursively copies the contents of src into dst.
func deriveDeepCopy_42(dst, src *ServiceSecretConfig) {
dst.Source = src.Source
dst.Target = src.Target
dst.UID = src.UID
@@ -1903,8 +1926,8 @@ func deriveDeepCopy_41(dst, src *ServiceSecretConfig) {
}
}
// deriveDeepCopy_42 recursively copies the contents of src into dst.
func deriveDeepCopy_42(dst, src *UlimitsConfig) {
// deriveDeepCopy_43 recursively copies the contents of src into dst.
func deriveDeepCopy_43(dst, src *UlimitsConfig) {
dst.Single = src.Single
dst.Soft = src.Soft
dst.Hard = src.Hard
@@ -1916,8 +1939,8 @@ func deriveDeepCopy_42(dst, src *UlimitsConfig) {
}
}
// deriveDeepCopy_43 recursively copies the contents of src into dst.
func deriveDeepCopy_43(dst, src *ServiceVolumeConfig) {
// deriveDeepCopy_44 recursively copies the contents of src into dst.
func deriveDeepCopy_44(dst, src *ServiceVolumeConfig) {
dst.Type = src.Type
dst.Source = src.Source
dst.Target = src.Target
@@ -1927,19 +1950,25 @@ func deriveDeepCopy_43(dst, src *ServiceVolumeConfig) {
dst.Bind = nil
} else {
dst.Bind = new(ServiceVolumeBind)
deriveDeepCopy_51(dst.Bind, src.Bind)
deriveDeepCopy_52(dst.Bind, src.Bind)
}
if src.Volume == nil {
dst.Volume = nil
} else {
dst.Volume = new(ServiceVolumeVolume)
deriveDeepCopy_52(dst.Volume, src.Volume)
deriveDeepCopy_53(dst.Volume, src.Volume)
}
if src.Tmpfs == nil {
dst.Tmpfs = nil
} else {
dst.Tmpfs = new(ServiceVolumeTmpfs)
deriveDeepCopy_53(dst.Tmpfs, src.Tmpfs)
deriveDeepCopy_54(dst.Tmpfs, src.Tmpfs)
}
if src.Image == nil {
dst.Image = nil
} else {
dst.Image = new(ServiceVolumeImage)
deriveDeepCopy_55(dst.Image, src.Image)
}
if src.Extensions != nil {
dst.Extensions = make(map[string]any, len(src.Extensions))
@@ -1949,8 +1978,8 @@ func deriveDeepCopy_43(dst, src *ServiceVolumeConfig) {
}
}
// deriveDeepCopy_44 recursively copies the contents of src into dst.
func deriveDeepCopy_44(dst, src *ServiceHook) {
// deriveDeepCopy_45 recursively copies the contents of src into dst.
func deriveDeepCopy_45(dst, src *ServiceHook) {
if src.Command == nil {
dst.Command = nil
} else {
@@ -1974,7 +2003,7 @@ func deriveDeepCopy_44(dst, src *ServiceHook) {
dst.WorkingDir = src.WorkingDir
if src.Environment != nil {
dst.Environment = make(map[string]*string, len(src.Environment))
deriveDeepCopy_13(dst.Environment, src.Environment)
deriveDeepCopy_14(dst.Environment, src.Environment)
} else {
dst.Environment = nil
}
@@ -1986,8 +2015,8 @@ func deriveDeepCopy_44(dst, src *ServiceHook) {
}
}
// deriveDeepCopy_45 recursively copies the contents of src into dst.
func deriveDeepCopy_45(dst, src *IPAMConfig) {
// deriveDeepCopy_46 recursively copies the contents of src into dst.
func deriveDeepCopy_46(dst, src *IPAMConfig) {
dst.Driver = src.Driver
if src.Config == nil {
dst.Config = nil
@@ -2005,7 +2034,7 @@ func deriveDeepCopy_45(dst, src *IPAMConfig) {
} else {
dst.Config = make([]*IPAMPool, len(src.Config))
}
deriveDeepCopy_54(dst.Config, src.Config)
deriveDeepCopy_56(dst.Config, src.Config)
}
if src.Extensions != nil {
dst.Extensions = make(map[string]any, len(src.Extensions))
@@ -2015,14 +2044,14 @@ func deriveDeepCopy_45(dst, src *IPAMConfig) {
}
}
// deriveDeepCopy_46 recursively copies the contents of src into dst.
func deriveDeepCopy_46(dst, src *Trigger) {
// deriveDeepCopy_47 recursively copies the contents of src into dst.
func deriveDeepCopy_47(dst, src *Trigger) {
dst.Path = src.Path
dst.Action = src.Action
dst.Target = src.Target
func() {
field := new(ServiceHook)
deriveDeepCopy_44(field, &src.Exec)
deriveDeepCopy_45(field, &src.Exec)
dst.Exec = *field
}()
if src.Include == nil {
@@ -2069,8 +2098,8 @@ func deriveDeepCopy_46(dst, src *Trigger) {
}
}
// deriveDeepCopy_47 recursively copies the contents of src into dst.
func deriveDeepCopy_47(dst, src *WeightDevice) {
// deriveDeepCopy_48 recursively copies the contents of src into dst.
func deriveDeepCopy_48(dst, src *WeightDevice) {
dst.Path = src.Path
dst.Weight = src.Weight
if src.Extensions != nil {
@@ -2081,8 +2110,8 @@ func deriveDeepCopy_47(dst, src *WeightDevice) {
}
}
// deriveDeepCopy_48 recursively copies the contents of src into dst.
func deriveDeepCopy_48(dst, src *ThrottleDevice) {
// deriveDeepCopy_49 recursively copies the contents of src into dst.
func deriveDeepCopy_49(dst, src *ThrottleDevice) {
dst.Path = src.Path
dst.Rate = src.Rate
if src.Extensions != nil {
@@ -2093,8 +2122,8 @@ func deriveDeepCopy_48(dst, src *ThrottleDevice) {
}
}
// deriveDeepCopy_49 recursively copies the contents of src into dst.
func deriveDeepCopy_49(dst, src *Resource) {
// deriveDeepCopy_50 recursively copies the contents of src into dst.
func deriveDeepCopy_50(dst, src *Resource) {
dst.NanoCPUs = src.NanoCPUs
dst.MemoryBytes = src.MemoryBytes
dst.Pids = src.Pids
@@ -2114,7 +2143,7 @@ func deriveDeepCopy_49(dst, src *Resource) {
} else {
dst.Devices = make([]DeviceRequest, len(src.Devices))
}
deriveDeepCopy_15(dst.Devices, src.Devices)
deriveDeepCopy_16(dst.Devices, src.Devices)
}
if src.GenericResources == nil {
dst.GenericResources = nil
@@ -2132,7 +2161,7 @@ func deriveDeepCopy_49(dst, src *Resource) {
} else {
dst.GenericResources = make([]GenericResource, len(src.GenericResources))
}
deriveDeepCopy_55(dst.GenericResources, src.GenericResources)
deriveDeepCopy_57(dst.GenericResources, src.GenericResources)
}
if src.Extensions != nil {
dst.Extensions = make(map[string]any, len(src.Extensions))
@@ -2142,19 +2171,19 @@ func deriveDeepCopy_49(dst, src *Resource) {
}
}
// deriveDeepCopy_50 recursively copies the contents of src into dst.
func deriveDeepCopy_50(dst, src []PlacementPreferences) {
// deriveDeepCopy_51 recursively copies the contents of src into dst.
func deriveDeepCopy_51(dst, src []PlacementPreferences) {
for src_i, src_value := range src {
func() {
field := new(PlacementPreferences)
deriveDeepCopy_56(field, &src_value)
deriveDeepCopy_58(field, &src_value)
dst[src_i] = *field
}()
}
}
// deriveDeepCopy_51 recursively copies the contents of src into dst.
func deriveDeepCopy_51(dst, src *ServiceVolumeBind) {
// deriveDeepCopy_52 recursively copies the contents of src into dst.
func deriveDeepCopy_52(dst, src *ServiceVolumeBind) {
dst.SELinux = src.SELinux
dst.Propagation = src.Propagation
dst.CreateHostPath = src.CreateHostPath
@@ -2167,8 +2196,14 @@ func deriveDeepCopy_51(dst, src *ServiceVolumeBind) {
}
}
// deriveDeepCopy_52 recursively copies the contents of src into dst.
func deriveDeepCopy_52(dst, src *ServiceVolumeVolume) {
// deriveDeepCopy_53 recursively copies the contents of src into dst.
func deriveDeepCopy_53(dst, src *ServiceVolumeVolume) {
if src.Labels != nil {
dst.Labels = make(map[string]string, len(src.Labels))
deriveDeepCopy_4(dst.Labels, src.Labels)
} else {
dst.Labels = nil
}
dst.NoCopy = src.NoCopy
dst.Subpath = src.Subpath
if src.Extensions != nil {
@@ -2179,8 +2214,8 @@ func deriveDeepCopy_52(dst, src *ServiceVolumeVolume) {
}
}
// deriveDeepCopy_53 recursively copies the contents of src into dst.
func deriveDeepCopy_53(dst, src *ServiceVolumeTmpfs) {
// deriveDeepCopy_54 recursively copies the contents of src into dst.
func deriveDeepCopy_54(dst, src *ServiceVolumeTmpfs) {
dst.Size = src.Size
dst.Mode = src.Mode
if src.Extensions != nil {
@@ -2191,31 +2226,42 @@ func deriveDeepCopy_53(dst, src *ServiceVolumeTmpfs) {
}
}
// deriveDeepCopy_54 recursively copies the contents of src into dst.
func deriveDeepCopy_54(dst, src []*IPAMPool) {
// deriveDeepCopy_55 recursively copies the contents of src into dst.
func deriveDeepCopy_55(dst, src *ServiceVolumeImage) {
dst.SubPath = src.SubPath
if src.Extensions != nil {
dst.Extensions = make(map[string]any, len(src.Extensions))
src.Extensions.DeepCopy(dst.Extensions)
} else {
dst.Extensions = nil
}
}
// deriveDeepCopy_56 recursively copies the contents of src into dst.
func deriveDeepCopy_56(dst, src []*IPAMPool) {
for src_i, src_value := range src {
if src_value == nil {
dst[src_i] = nil
} else {
dst[src_i] = new(IPAMPool)
deriveDeepCopy_57(dst[src_i], src_value)
deriveDeepCopy_59(dst[src_i], src_value)
}
}
}
// deriveDeepCopy_55 recursively copies the contents of src into dst.
func deriveDeepCopy_55(dst, src []GenericResource) {
// deriveDeepCopy_57 recursively copies the contents of src into dst.
func deriveDeepCopy_57(dst, src []GenericResource) {
for src_i, src_value := range src {
func() {
field := new(GenericResource)
deriveDeepCopy_58(field, &src_value)
deriveDeepCopy_60(field, &src_value)
dst[src_i] = *field
}()
}
}
// deriveDeepCopy_56 recursively copies the contents of src into dst.
func deriveDeepCopy_56(dst, src *PlacementPreferences) {
// deriveDeepCopy_58 recursively copies the contents of src into dst.
func deriveDeepCopy_58(dst, src *PlacementPreferences) {
dst.Spread = src.Spread
if src.Extensions != nil {
dst.Extensions = make(map[string]any, len(src.Extensions))
@@ -2225,8 +2271,8 @@ func deriveDeepCopy_56(dst, src *PlacementPreferences) {
}
}
// deriveDeepCopy_57 recursively copies the contents of src into dst.
func deriveDeepCopy_57(dst, src *IPAMPool) {
// deriveDeepCopy_59 recursively copies the contents of src into dst.
func deriveDeepCopy_59(dst, src *IPAMPool) {
dst.Subnet = src.Subnet
dst.Gateway = src.Gateway
dst.IPRange = src.IPRange
@@ -2244,13 +2290,13 @@ func deriveDeepCopy_57(dst, src *IPAMPool) {
}
}
// deriveDeepCopy_58 recursively copies the contents of src into dst.
func deriveDeepCopy_58(dst, src *GenericResource) {
// deriveDeepCopy_60 recursively copies the contents of src into dst.
func deriveDeepCopy_60(dst, src *GenericResource) {
if src.DiscreteResourceSpec == nil {
dst.DiscreteResourceSpec = nil
} else {
dst.DiscreteResourceSpec = new(DiscreteGenericResource)
deriveDeepCopy_59(dst.DiscreteResourceSpec, src.DiscreteResourceSpec)
deriveDeepCopy_61(dst.DiscreteResourceSpec, src.DiscreteResourceSpec)
}
if src.Extensions != nil {
dst.Extensions = make(map[string]any, len(src.Extensions))
@@ -2260,8 +2306,8 @@ func deriveDeepCopy_58(dst, src *GenericResource) {
}
}
// deriveDeepCopy_59 recursively copies the contents of src into dst.
func deriveDeepCopy_59(dst, src *DiscreteGenericResource) {
// deriveDeepCopy_61 recursively copies the contents of src into dst.
func deriveDeepCopy_61(dst, src *DiscreteGenericResource) {
dst.Kind = src.Kind
dst.Value = src.Value
if src.Extensions != nil {

View File

@@ -21,8 +21,10 @@ import (
"context"
"encoding/json"
"fmt"
"maps"
"os"
"path/filepath"
"slices"
"sort"
"github.com/compose-spec/compose-go/v2/dotenv"
@@ -30,7 +32,6 @@ import (
"github.com/compose-spec/compose-go/v2/utils"
"github.com/distribution/reference"
godigest "github.com/opencontainers/go-digest"
"golang.org/x/exp/maps"
"golang.org/x/sync/errgroup"
"gopkg.in/yaml.v3"
)
@@ -120,21 +121,21 @@ func (p *Project) ServicesWithBuild() []string {
servicesBuild := p.Services.Filter(func(s ServiceConfig) bool {
return s.Build != nil && s.Build.Context != ""
})
return maps.Keys(servicesBuild)
return slices.Collect(maps.Keys(servicesBuild))
}
func (p *Project) ServicesWithExtends() []string {
servicesExtends := p.Services.Filter(func(s ServiceConfig) bool {
return s.Extends != nil && *s.Extends != (ExtendsConfig{})
})
return maps.Keys(servicesExtends)
return slices.Collect(maps.Keys(servicesExtends))
}
func (p *Project) ServicesWithDependsOn() []string {
servicesDependsOn := p.Services.Filter(func(s ServiceConfig) bool {
return len(s.DependsOn) > 0
})
return maps.Keys(servicesDependsOn)
return slices.Collect(maps.Keys(servicesDependsOn))
}
func (p *Project) ServicesWithCapabilities() ([]string, []string, []string) {
@@ -156,9 +157,10 @@ func (p *Project) ServicesWithCapabilities() ([]string, []string, []string) {
capabilities = append(capabilities, service.Name)
}
for _, c := range d.Capabilities {
if c == "gpu" {
switch c {
case "gpu":
gpu = append(gpu, service.Name)
} else if c == "tpu" {
case "tpu":
tpu = append(tpu, service.Name)
}
}
@@ -188,16 +190,25 @@ func (p *Project) getServicesByNames(names ...string) (Services, []string) {
if len(names) == 0 {
return p.Services, nil
}
services := Services{}
var servicesNotFound []string
for _, name := range names {
service, ok := p.Services[name]
if !ok {
servicesNotFound = append(servicesNotFound, name)
continue
matched := false
for serviceName, service := range p.Services {
match, _ := filepath.Match(name, serviceName)
if match {
services[serviceName] = service
matched = true
}
}
if !matched {
servicesNotFound = append(servicesNotFound, name)
}
services[name] = service
}
return services, servicesNotFound
}
@@ -298,16 +309,25 @@ func (p *Project) withServices(names []string, fn ServiceFunc, seen map[string]b
return nil
}
func (p *Project) GetDependentsForService(s ServiceConfig) []string {
return utils.MapKeys(p.dependentsForService(s))
func (p *Project) GetDependentsForService(s ServiceConfig, filter ...func(ServiceDependency) bool) []string {
return utils.MapKeys(p.dependentsForService(s, filter...))
}
func (p *Project) dependentsForService(s ServiceConfig) map[string]ServiceDependency {
func (p *Project) dependentsForService(s ServiceConfig, filter ...func(ServiceDependency) bool) map[string]ServiceDependency {
dependent := make(map[string]ServiceDependency)
for _, service := range p.Services {
for name, dependency := range service.DependsOn {
if name == s.Name {
dependent[service.Name] = dependency
depends := true
for _, f := range filter {
if !f(dependency) {
depends = false
break
}
}
if depends {
dependent[service.Name] = dependency
}
}
}
}
@@ -630,25 +650,25 @@ func (p Project) WithServicesEnvironmentResolved(discardEnvFiles bool) (*Project
for i, service := range newProject.Services {
service.Environment = service.Environment.Resolve(newProject.Environment.Resolve)
environment := MappingWithEquals{}
// resolve variables based on other files we already parsed, + project's environment
var resolve dotenv.LookupFn = func(s string) (string, bool) {
v, ok := environment[s]
if ok && v != nil {
return *v, ok
}
return newProject.Environment.Resolve(s)
}
environment := service.Environment.ToMapping()
for _, envFile := range service.EnvFiles {
vars, err := loadEnvFile(envFile, resolve)
err := loadEnvFile(envFile, environment, func(k string) (string, bool) {
// project.env has precedence doing interpolation
if resolve, ok := p.Environment.Resolve(k); ok {
return resolve, true
}
// then service.environment
if s, ok := service.Environment[k]; ok {
return *s, true
}
return "", false
})
if err != nil {
return nil, err
}
environment.OverrideBy(vars.ToMappingWithEquals())
}
service.Environment = environment.OverrideBy(service.Environment)
service.Environment = environment.ToMappingWithEquals().OverrideBy(service.Environment)
if discardEnvFiles {
service.EnvFiles = nil
@@ -696,15 +716,16 @@ func (p Project) WithServicesLabelsResolved(discardLabelFiles bool) (*Project, e
return newProject, nil
}
func loadEnvFile(envFile EnvFile, resolve dotenv.LookupFn) (Mapping, error) {
func loadEnvFile(envFile EnvFile, environment Mapping, resolve dotenv.LookupFn) error {
if _, err := os.Stat(envFile.Path); os.IsNotExist(err) {
if envFile.Required {
return nil, fmt.Errorf("env file %s not found: %w", envFile.Path, err)
return fmt.Errorf("env file %s not found: %w", envFile.Path, err)
}
return nil, nil
return nil
}
return loadMappingFile(envFile.Path, envFile.Format, resolve)
err := loadMappingFile(envFile.Path, envFile.Format, environment, resolve)
return err
}
func loadLabelFile(labelFile string, resolve dotenv.LookupFn) (Mapping, error) {
@@ -712,21 +733,19 @@ func loadLabelFile(labelFile string, resolve dotenv.LookupFn) (Mapping, error) {
return nil, fmt.Errorf("label file %s not found: %w", labelFile, err)
}
return loadMappingFile(labelFile, "", resolve)
labels := Mapping{}
err := loadMappingFile(labelFile, "", labels, resolve)
return labels, err
}
func loadMappingFile(path string, format string, resolve dotenv.LookupFn) (Mapping, error) {
func loadMappingFile(path string, format string, vars Mapping, resolve dotenv.LookupFn) error {
file, err := os.Open(path)
if err != nil {
return nil, err
return err
}
defer file.Close()
fileVars, err := dotenv.ParseWithFormat(file, path, resolve, format)
if err != nil {
return nil, err
}
return fileVars, nil
return dotenv.ParseWithFormat(file, path, vars, resolve, format)
}
func (p *Project) deepCopy() *Project {

View File

@@ -75,8 +75,8 @@ type ServiceConfig struct {
// If set, overrides ENTRYPOINT from the image.
//
// Set to `[]` or an empty string to clear the entrypoint from the image.
Entrypoint ShellCommand `yaml:"entrypoint,omitempty" json:"entrypoint"` // NOTE: we can NOT omitempty for JSON! see ShellCommand type for details.
Entrypoint ShellCommand `yaml:"entrypoint,omitempty" json:"entrypoint"` // NOTE: we can NOT omitempty for JSON! see ShellCommand type for details.
Provider *ServiceProviderConfig `yaml:"provider,omitempty" json:"provider,omitempty"`
Environment MappingWithEquals `yaml:"environment,omitempty" json:"environment,omitempty"`
EnvFiles []EnvFile `yaml:"env_file,omitempty" json:"env_file,omitempty"`
Expose StringOrNumberList `yaml:"expose,omitempty" json:"expose,omitempty"`
@@ -142,6 +142,12 @@ type ServiceConfig struct {
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
}
type ServiceProviderConfig struct {
Type string `yaml:"type,omitempty" json:"driver,omitempty"`
Options Options `yaml:"options,omitempty" json:"options,omitempty"`
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
}
// MarshalYAML makes ServiceConfig implement yaml.Marshaller
func (s ServiceConfig) MarshalYAML() (interface{}, error) {
type t ServiceConfig
@@ -535,6 +541,7 @@ type ServiceVolumeConfig struct {
Bind *ServiceVolumeBind `yaml:"bind,omitempty" json:"bind,omitempty"`
Volume *ServiceVolumeVolume `yaml:"volume,omitempty" json:"volume,omitempty"`
Tmpfs *ServiceVolumeTmpfs `yaml:"tmpfs,omitempty" json:"tmpfs,omitempty"`
Image *ServiceVolumeImage `yaml:"image,omitempty" json:"image,omitempty"`
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
}
@@ -569,6 +576,8 @@ const (
VolumeTypeNamedPipe = "npipe"
// VolumeTypeCluster is the type for mounting container storage interface (CSI) volumes
VolumeTypeCluster = "cluster"
// VolumeTypeImage is the tpe for mounting an image
VolumeTypeImage = "image"
// SElinuxShared share the volume content
SElinuxShared = "z"
@@ -612,8 +621,9 @@ const (
// ServiceVolumeVolume are options for a service volume of type volume
type ServiceVolumeVolume struct {
NoCopy bool `yaml:"nocopy,omitempty" json:"nocopy,omitempty"`
Subpath string `yaml:"subpath,omitempty" json:"subpath,omitempty"`
Labels Mapping `yaml:"labels,omitempty" json:"labels,omitempty"`
NoCopy bool `yaml:"nocopy,omitempty" json:"nocopy,omitempty"`
Subpath string `yaml:"subpath,omitempty" json:"subpath,omitempty"`
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
}
@@ -627,6 +637,11 @@ type ServiceVolumeTmpfs struct {
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
}
type ServiceVolumeImage struct {
SubPath string `yaml:"subpath,omitempty" json:"subpath,omitempty"`
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
}
type FileMode int64
// FileReferenceConfig for a reference to a swarm file object

Some files were not shown because too many files have changed in this diff Show More