mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-08-14 15:55:54 +08:00
Compare commits
108 Commits
v0.22.0-rc
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9e17bc7a4c | ||
![]() |
e1e8f5c68d | ||
![]() |
6ed39b2618 | ||
![]() |
03019049e8 | ||
![]() |
23ce21c341 | ||
![]() |
4dac5295a1 | ||
![]() |
9a48aca461 | ||
![]() |
679407862f | ||
![]() |
674cfff1a4 | ||
![]() |
19a241f4ed | ||
![]() |
7da31076ae | ||
![]() |
384f0565f5 | ||
![]() |
6df3a09284 | ||
![]() |
e7be640d9b | ||
![]() |
2f1be25b8f | ||
![]() |
a40edbb47b | ||
![]() |
2eaea647d8 | ||
![]() |
f3a3d9c26b | ||
![]() |
9ba3f77219 | ||
![]() |
2799ed6dd8 | ||
![]() |
719a41a4c3 | ||
![]() |
a9807be458 | ||
![]() |
7a7be2ffa1 | ||
![]() |
ab533b0cb4 | ||
![]() |
0855cab1bd | ||
![]() |
735555ff7b | ||
![]() |
67ccbd06f6 | ||
![]() |
c370f90b73 | ||
![]() |
9730a20f6b | ||
![]() |
2e93ac32bc | ||
![]() |
19c22136b4 | ||
![]() |
bad5063577 | ||
![]() |
286c018f84 | ||
![]() |
ac970c03e7 | ||
![]() |
5398c33937 | ||
![]() |
1365652a74 | ||
![]() |
a4f0a21468 | ||
![]() |
d55616b22c | ||
![]() |
113606a24c | ||
![]() |
cd38da0244 | ||
![]() |
cc6547c51d | ||
![]() |
26f2e002c6 | ||
![]() |
372feb38ff | ||
![]() |
b08d576ec0 | ||
![]() |
0034cdbffc | ||
![]() |
a9666e7df1 | ||
![]() |
b7e77af256 | ||
![]() |
d72ff8f88c | ||
![]() |
d75c650792 | ||
![]() |
8c74109330 | ||
![]() |
9f102b5c34 | ||
![]() |
b4b2dc9664 | ||
![]() |
2e81e301ae | ||
![]() |
fb4417e14d | ||
![]() |
eb74b483bd | ||
![]() |
db194abdc8 | ||
![]() |
86eb3be1c4 | ||
![]() |
a05a166f81 | ||
![]() |
cfc9d3a8c9 | ||
![]() |
5bac0b1197 | ||
![]() |
0b4e624aaa | ||
![]() |
b7b5a3a1cc | ||
![]() |
f8de3c3bdc | ||
![]() |
fa0c3e3786 | ||
![]() |
d69301d57b | ||
![]() |
ee77cdb175 | ||
![]() |
8fb1157b5f | ||
![]() |
a34cdff84e | ||
![]() |
77139daa4b | ||
![]() |
10e3892a63 | ||
![]() |
d80ece5bb3 | ||
![]() |
1f44971fc9 | ||
![]() |
a91db7ccc9 | ||
![]() |
df6d36af35 | ||
![]() |
98c3abb756 | ||
![]() |
3b824a0e39 | ||
![]() |
b0156cd631 | ||
![]() |
29614f9734 | ||
![]() |
f1b895196c | ||
![]() |
900502b139 | ||
![]() |
49bd7e4edc | ||
![]() |
8f9c25e8b0 | ||
![]() |
7659798f80 | ||
![]() |
7b8bf9f801 | ||
![]() |
8efc528b84 | ||
![]() |
8593e0397b | ||
![]() |
0c0e8eefdf | ||
![]() |
e114dd09a5 | ||
![]() |
d25e260d2e | ||
![]() |
86e4e77ac1 | ||
![]() |
534d9fc276 | ||
![]() |
e0c67bfc79 | ||
![]() |
53e576b306 | ||
![]() |
d3aef6642c | ||
![]() |
824cef1b92 | ||
![]() |
a8b0fa8965 | ||
![]() |
45dfb84361 | ||
![]() |
13ef01196d | ||
![]() |
646df6d4a0 | ||
![]() |
d46c1d8141 | ||
![]() |
c682742de0 | ||
![]() |
391acba718 | ||
![]() |
db4b96e62c | ||
![]() |
882ef0db91 | ||
![]() |
967fc2a696 | ||
![]() |
212d598ab1 | ||
![]() |
bf95aa3dfa | ||
![]() |
a41c9fa649 |
13
.github/workflows/build.yml
vendored
13
.github/workflows/build.yml
vendored
@@ -54,7 +54,7 @@ jobs:
|
||||
- master
|
||||
- latest
|
||||
- buildx-stable-1
|
||||
- v0.20.1
|
||||
- v0.20.2
|
||||
- v0.19.0
|
||||
- v0.18.2
|
||||
worker:
|
||||
@@ -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
17
.github/workflows/pr-assign-author.yml
vendored
Normal 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
|
@@ -1,9 +1,6 @@
|
||||
run:
|
||||
timeout: 30m
|
||||
modules-download-mode: vendor
|
||||
# default uses Go version from the go.mod file, fallback on the env var
|
||||
# `GOVERSION`, fallback on 1.17: https://golangci-lint.run/usage/configuration/#run-configuration
|
||||
go: "1.23"
|
||||
|
||||
linters:
|
||||
enable:
|
||||
|
@@ -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.1
|
||||
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
|
||||
|
@@ -21,7 +21,7 @@
|
||||
- [Verify essential information](#verify-essential-information)
|
||||
- [Classify the issue](#classify-the-issue)
|
||||
- [Prioritization guidelines for `kind/bug`](#prioritization-guidelines-for-kindbug)
|
||||
- [Issue lifecyle](#issue-lifecyle)
|
||||
- [Issue lifecycle](#issue-lifecycle)
|
||||
- [Examples](#examples)
|
||||
- [Submitting a bug](#submitting-a-bug)
|
||||
- [Pull request review process](#pull-request-review-process)
|
||||
@@ -308,7 +308,7 @@ Examples:
|
||||
- Bugs in non-default configurations
|
||||
- Most enhancements
|
||||
|
||||
## Issue lifecyle
|
||||
## Issue lifecycle
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
|
45
bake/bake.go
45
bake/bake.go
@@ -3,7 +3,9 @@ package bake
|
||||
import (
|
||||
"context"
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"maps"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -732,6 +734,41 @@ type Target struct {
|
||||
linked bool
|
||||
}
|
||||
|
||||
func (t *Target) MarshalJSON() ([]byte, error) {
|
||||
tgt := *t
|
||||
esc := func(s string) string {
|
||||
return strings.ReplaceAll(strings.ReplaceAll(s, "${", "$${"), "%{", "%%{")
|
||||
}
|
||||
|
||||
tgt.Annotations = slices.Clone(t.Annotations)
|
||||
for i, v := range tgt.Annotations {
|
||||
tgt.Annotations[i] = esc(v)
|
||||
}
|
||||
|
||||
if tgt.DockerfileInline != nil {
|
||||
escaped := esc(*tgt.DockerfileInline)
|
||||
tgt.DockerfileInline = &escaped
|
||||
}
|
||||
|
||||
tgt.Labels = maps.Clone(t.Labels)
|
||||
for k, v := range t.Labels {
|
||||
if v != nil {
|
||||
escaped := esc(*v)
|
||||
tgt.Labels[k] = &escaped
|
||||
}
|
||||
}
|
||||
|
||||
tgt.Args = maps.Clone(t.Args)
|
||||
for k, v := range t.Args {
|
||||
if v != nil {
|
||||
escaped := esc(*v)
|
||||
tgt.Args[k] = &escaped
|
||||
}
|
||||
}
|
||||
|
||||
return json.Marshal(tgt)
|
||||
}
|
||||
|
||||
var (
|
||||
_ hclparser.WithEvalContexts = &Target{}
|
||||
_ hclparser.WithGetName = &Target{}
|
||||
@@ -1104,9 +1141,7 @@ func (t *Target) GetEvalContexts(ectx *hcl.EvalContext, block *hcl.Block, loadDe
|
||||
e2 := ectx.NewChild()
|
||||
e2.Variables = make(map[string]cty.Value)
|
||||
if e != ectx {
|
||||
for k, v := range e.Variables {
|
||||
e2.Variables[k] = v
|
||||
}
|
||||
maps.Copy(e2.Variables, e.Variables)
|
||||
}
|
||||
e2.Variables[k] = v
|
||||
ectxs2 = append(ectxs2, e2)
|
||||
@@ -1325,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://") {
|
||||
|
@@ -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)
|
||||
}
|
||||
|
||||
@@ -2142,6 +2142,73 @@ target "app" {
|
||||
})
|
||||
}
|
||||
|
||||
func TestVariableValidationConditionNull(t *testing.T) {
|
||||
fp := File{
|
||||
Name: "docker-bake.hcl",
|
||||
Data: []byte(`
|
||||
variable "PORT" {
|
||||
default = 3000
|
||||
validation {}
|
||||
}
|
||||
target "app" {
|
||||
args = {
|
||||
PORT = PORT
|
||||
}
|
||||
}
|
||||
`),
|
||||
}
|
||||
|
||||
_, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "Condition expression must return either true or false, not null")
|
||||
}
|
||||
|
||||
func TestVariableValidationConditionUnknownValue(t *testing.T) {
|
||||
fp := File{
|
||||
Name: "docker-bake.hcl",
|
||||
Data: []byte(`
|
||||
variable "PORT" {
|
||||
default = 3000
|
||||
validation {
|
||||
condition = "foo"
|
||||
}
|
||||
}
|
||||
target "app" {
|
||||
args = {
|
||||
PORT = PORT
|
||||
}
|
||||
}
|
||||
`),
|
||||
}
|
||||
|
||||
_, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "Invalid condition result value: a bool is required")
|
||||
}
|
||||
|
||||
func TestVariableValidationInvalidErrorMessage(t *testing.T) {
|
||||
fp := File{
|
||||
Name: "docker-bake.hcl",
|
||||
Data: []byte(`
|
||||
variable "FOO" {
|
||||
default = 0
|
||||
validation {
|
||||
condition = FOO > 5
|
||||
}
|
||||
}
|
||||
target "app" {
|
||||
args = {
|
||||
FOO = FOO
|
||||
}
|
||||
}
|
||||
`),
|
||||
}
|
||||
|
||||
_, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "This check failed, but has an invalid error message")
|
||||
}
|
||||
|
||||
// https://github.com/docker/buildx/issues/2822
|
||||
func TestVariableEmpty(t *testing.T) {
|
||||
fp := File{
|
||||
|
@@ -92,6 +92,9 @@ func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Conf
|
||||
if s.Build.AdditionalContexts != nil {
|
||||
additionalContexts = map[string]string{}
|
||||
for k, v := range s.Build.AdditionalContexts {
|
||||
if strings.HasPrefix(v, "service:") {
|
||||
v = strings.Replace(v, "service:", "target:", 1)
|
||||
}
|
||||
additionalContexts[k] = v
|
||||
}
|
||||
}
|
||||
@@ -174,6 +177,7 @@ func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Conf
|
||||
CacheFrom: cacheFrom,
|
||||
CacheTo: cacheTo,
|
||||
NetworkMode: networkModeP,
|
||||
Platforms: s.Build.Platforms,
|
||||
SSH: ssh,
|
||||
Secrets: secrets,
|
||||
ShmSize: shmSize,
|
||||
@@ -214,7 +218,7 @@ func validateComposeFile(dt []byte, fn string) (bool, error) {
|
||||
}
|
||||
|
||||
func validateCompose(dt []byte, envs map[string]string) error {
|
||||
_, err := loader.Load(composetypes.ConfigDetails{
|
||||
_, err := loader.LoadWithContext(context.Background(), composetypes.ConfigDetails{
|
||||
ConfigFiles: []composetypes.ConfigFile{
|
||||
{
|
||||
Content: dt,
|
||||
|
@@ -463,6 +463,21 @@ services:
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestPlatforms(t *testing.T) {
|
||||
dt := []byte(`
|
||||
services:
|
||||
foo:
|
||||
build:
|
||||
context: .
|
||||
platforms:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
`)
|
||||
c, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []string{"linux/amd64", "linux/arm64"}, c.Targets[0].Platforms)
|
||||
}
|
||||
|
||||
func newBool(val bool) *bool {
|
||||
b := val
|
||||
return &b
|
||||
@@ -798,6 +813,37 @@ services:
|
||||
})
|
||||
}
|
||||
|
||||
func TestServiceContext(t *testing.T) {
|
||||
dt := []byte(`
|
||||
services:
|
||||
base:
|
||||
build:
|
||||
dockerfile: baseapp.Dockerfile
|
||||
command: ./entrypoint.sh
|
||||
webapp:
|
||||
build:
|
||||
context: ./dir
|
||||
additional_contexts:
|
||||
base: service:base
|
||||
`)
|
||||
|
||||
c, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(c.Groups))
|
||||
require.Equal(t, "default", c.Groups[0].Name)
|
||||
sort.Strings(c.Groups[0].Targets)
|
||||
require.Equal(t, []string{"base", "webapp"}, c.Groups[0].Targets)
|
||||
|
||||
require.Equal(t, 2, len(c.Targets))
|
||||
sort.Slice(c.Targets, func(i, j int) bool {
|
||||
return c.Targets[i].Name < c.Targets[j].Name
|
||||
})
|
||||
|
||||
require.Equal(t, "webapp", c.Targets[1].Name)
|
||||
require.Equal(t, map[string]string{"base": "target:base"}, c.Targets[1].Contexts)
|
||||
}
|
||||
|
||||
// chdir changes the current working directory to the named directory,
|
||||
// and then restore the original working directory at the end of the test.
|
||||
func chdir(t *testing.T, dir string) {
|
||||
|
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
)
|
||||
|
||||
type Opt struct {
|
||||
@@ -41,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"`
|
||||
}
|
||||
|
||||
@@ -555,27 +556,57 @@ func (p *parser) resolveBlockNames(block *hcl.Block) ([]string, error) {
|
||||
func (p *parser) validateVariables(vars map[string]*variable, ectx *hcl.EvalContext) hcl.Diagnostics {
|
||||
var diags hcl.Diagnostics
|
||||
for _, v := range vars {
|
||||
for _, validation := range v.Validations {
|
||||
condition, condDiags := validation.Condition.Value(ectx)
|
||||
for _, rule := range v.Validations {
|
||||
resultVal, condDiags := rule.Condition.Value(ectx)
|
||||
if condDiags.HasErrors() {
|
||||
diags = append(diags, condDiags...)
|
||||
continue
|
||||
}
|
||||
if !condition.True() {
|
||||
message, msgDiags := validation.ErrorMessage.Value(ectx)
|
||||
|
||||
if resultVal.IsNull() {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid condition result",
|
||||
Detail: "Condition expression must return either true or false, not null.",
|
||||
Subject: rule.Condition.Range().Ptr(),
|
||||
Expression: rule.Condition,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
var err error
|
||||
resultVal, err = convert.Convert(resultVal, cty.Bool)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid condition result",
|
||||
Detail: fmt.Sprintf("Invalid condition result value: %s", err),
|
||||
Subject: rule.Condition.Range().Ptr(),
|
||||
Expression: rule.Condition,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
if !resultVal.True() {
|
||||
message, msgDiags := rule.ErrorMessage.Value(ectx)
|
||||
if msgDiags.HasErrors() {
|
||||
diags = append(diags, msgDiags...)
|
||||
continue
|
||||
}
|
||||
errorMessage := "This check failed, but has an invalid error message."
|
||||
if !message.IsNull() {
|
||||
errorMessage = message.AsString()
|
||||
}
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Validation failed",
|
||||
Detail: message.AsString(),
|
||||
Subject: validation.Condition.Range().Ptr(),
|
||||
Detail: errorMessage,
|
||||
Subject: rule.Condition.Range().Ptr(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return diags
|
||||
}
|
||||
|
||||
|
@@ -8,6 +8,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"os"
|
||||
"slices"
|
||||
"strconv"
|
||||
@@ -204,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)
|
||||
|
||||
@@ -242,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,
|
||||
@@ -431,9 +423,7 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
|
||||
FrontendInputs: frontendInputs,
|
||||
FrontendOpt: make(map[string]string),
|
||||
}
|
||||
for k, v := range so.FrontendAttrs {
|
||||
req.FrontendOpt[k] = v
|
||||
}
|
||||
maps.Copy(req.FrontendOpt, so.FrontendAttrs)
|
||||
so.Frontend = ""
|
||||
so.FrontendInputs = nil
|
||||
|
||||
@@ -535,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 {
|
||||
@@ -571,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
|
||||
})
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package build
|
||||
|
||||
import (
|
||||
"context"
|
||||
"maps"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -127,9 +128,7 @@ func getGitAttributes(ctx context.Context, contextPath, dockerfilePath string) (
|
||||
if so.FrontendAttrs == nil {
|
||||
so.FrontendAttrs = make(map[string]string)
|
||||
}
|
||||
for k, v := range res {
|
||||
so.FrontendAttrs[k] = v
|
||||
}
|
||||
maps.Copy(so.FrontendAttrs, res)
|
||||
|
||||
if !setGitInfo || root == "" {
|
||||
return
|
||||
|
@@ -9,6 +9,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/buildx/util/gitutil"
|
||||
"github.com/docker/buildx/util/gitutil/gittestutil"
|
||||
"github.com/moby/buildkit/client"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -16,18 +17,18 @@ import (
|
||||
)
|
||||
|
||||
func setupTest(tb testing.TB) {
|
||||
gitutil.Mktmp(tb)
|
||||
gittestutil.Mktmp(tb)
|
||||
|
||||
c, err := gitutil.New()
|
||||
require.NoError(tb, err)
|
||||
gitutil.GitInit(c, tb)
|
||||
gittestutil.GitInit(c, tb)
|
||||
|
||||
df := []byte("FROM alpine:latest\n")
|
||||
require.NoError(tb, os.WriteFile("Dockerfile", df, 0644))
|
||||
|
||||
gitutil.GitAdd(c, tb, "Dockerfile")
|
||||
gitutil.GitCommit(c, tb, "initial commit")
|
||||
gitutil.GitSetRemote(c, tb, "origin", "git@github.com:docker/buildx.git")
|
||||
gittestutil.GitAdd(c, tb, "Dockerfile")
|
||||
gittestutil.GitCommit(c, tb, "initial commit")
|
||||
gittestutil.GitSetRemote(c, tb, "origin", "git@github.com:docker/buildx.git")
|
||||
}
|
||||
|
||||
func TestGetGitAttributesNotGitRepo(t *testing.T) {
|
||||
@@ -188,19 +189,19 @@ func TestLocalDirs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLocalDirsSub(t *testing.T) {
|
||||
gitutil.Mktmp(t)
|
||||
gittestutil.Mktmp(t)
|
||||
|
||||
c, err := gitutil.New()
|
||||
require.NoError(t, err)
|
||||
gitutil.GitInit(c, t)
|
||||
gittestutil.GitInit(c, t)
|
||||
|
||||
df := []byte("FROM alpine:latest\n")
|
||||
require.NoError(t, os.MkdirAll("app", 0755))
|
||||
require.NoError(t, os.WriteFile("app/Dockerfile", df, 0644))
|
||||
|
||||
gitutil.GitAdd(c, t, "app/Dockerfile")
|
||||
gitutil.GitCommit(c, t, "initial commit")
|
||||
gitutil.GitSetRemote(c, t, "origin", "git@github.com:docker/buildx.git")
|
||||
gittestutil.GitAdd(c, t, "app/Dockerfile")
|
||||
gittestutil.GitCommit(c, t, "initial commit")
|
||||
gittestutil.GitSetRemote(c, t, "origin", "git@github.com:docker/buildx.git")
|
||||
|
||||
so := &client.SolveOpt{
|
||||
FrontendAttrs: map[string]string{},
|
||||
|
@@ -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/")
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"maps"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -40,9 +41,7 @@ func setRecordProvenance(ctx context.Context, c *client.Client, sr *client.Solve
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range res {
|
||||
sr.ExporterResponse[k] = v
|
||||
}
|
||||
maps.Copy(sr.ExporterResponse, res)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
@@ -7,15 +7,13 @@ 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"
|
||||
"github.com/docker/cli/cli-plugins/manager"
|
||||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
"github.com/docker/cli/cli-plugins/plugin"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/debug"
|
||||
cliflags "github.com/docker/cli/cli/flags"
|
||||
"github.com/moby/buildkit/solver/errdefs"
|
||||
"github.com/moby/buildkit/util/stack"
|
||||
"github.com/pkg/errors"
|
||||
@@ -37,11 +35,7 @@ func init() {
|
||||
}
|
||||
|
||||
func runStandalone(cmd *command.DockerCli) error {
|
||||
if err := cmd.Initialize(cliflags.NewClientOptions()); err != nil {
|
||||
return err
|
||||
}
|
||||
defer flushMetrics(cmd)
|
||||
|
||||
executable := os.Args[0]
|
||||
rootCmd := commands.NewRootCmd(filepath.Base(executable), false, cmd)
|
||||
return rootCmd.Execute()
|
||||
@@ -64,7 +58,7 @@ func flushMetrics(cmd *command.DockerCli) {
|
||||
|
||||
func runPlugin(cmd *command.DockerCli) error {
|
||||
rootCmd := commands.NewRootCmd("buildx", true, cmd)
|
||||
return plugin.RunPlugin(cmd, rootCmd, manager.Metadata{
|
||||
return plugin.RunPlugin(cmd, rootCmd, metadata.Metadata{
|
||||
SchemaVersion: "0.1.0",
|
||||
Vendor: "Docker Inc.",
|
||||
Version: version.Version,
|
||||
@@ -117,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)
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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))
|
||||
|
160
commands/history/export.go
Normal file
160
commands/history/export.go
Normal file
@@ -0,0 +1,160 @@
|
||||
package history
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"slices"
|
||||
|
||||
"github.com/containerd/console"
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/docker/buildx/builder"
|
||||
"github.com/docker/buildx/localstate"
|
||||
"github.com/docker/buildx/util/cobrautil/completion"
|
||||
"github.com/docker/buildx/util/confutil"
|
||||
"github.com/docker/buildx/util/desktop/bundle"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type exportOptions struct {
|
||||
builder string
|
||||
refs []string
|
||||
output string
|
||||
all bool
|
||||
}
|
||||
|
||||
func runExport(ctx context.Context, dockerCli command.Cli, opts exportOptions) error {
|
||||
b, err := builder.New(dockerCli, builder.WithName(opts.builder))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nodes, err := b.LoadNodes(ctx, builder.WithData())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, node := range nodes {
|
||||
if node.Err != nil {
|
||||
return node.Err
|
||||
}
|
||||
}
|
||||
|
||||
if len(opts.refs) == 0 {
|
||||
opts.refs = []string{""}
|
||||
}
|
||||
|
||||
var res []historyRecord
|
||||
for _, ref := range opts.refs {
|
||||
recs, err := queryRecords(ctx, ref, nodes, &queryOptions{
|
||||
CompletedOnly: true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(recs) == 0 {
|
||||
if ref == "" {
|
||||
return errors.New("no records found")
|
||||
}
|
||||
return errors.Errorf("no record found for ref %q", ref)
|
||||
}
|
||||
|
||||
if ref == "" {
|
||||
slices.SortFunc(recs, func(a, b historyRecord) int {
|
||||
return b.CreatedAt.AsTime().Compare(a.CreatedAt.AsTime())
|
||||
})
|
||||
}
|
||||
|
||||
if opts.all {
|
||||
res = append(res, recs...)
|
||||
break
|
||||
} else {
|
||||
res = append(res, recs[0])
|
||||
}
|
||||
}
|
||||
|
||||
ls, err := localstate.New(confutil.NewConfig(dockerCli))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
visited := map[*builder.Node]struct{}{}
|
||||
var clients []*client.Client
|
||||
for _, rec := range res {
|
||||
if _, ok := visited[rec.node]; ok {
|
||||
continue
|
||||
}
|
||||
c, err := rec.node.Driver.Client(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clients = append(clients, c)
|
||||
}
|
||||
|
||||
toExport := make([]*bundle.Record, 0, len(res))
|
||||
for _, rec := range res {
|
||||
var defaultPlatform string
|
||||
if p := rec.node.Platforms; len(p) > 0 {
|
||||
defaultPlatform = platforms.FormatAll(platforms.Normalize(p[0]))
|
||||
}
|
||||
|
||||
var stg *localstate.StateGroup
|
||||
st, _ := ls.ReadRef(rec.node.Builder, rec.node.Name, rec.Ref)
|
||||
if st != nil && st.GroupRef != "" {
|
||||
stg, err = ls.ReadGroup(st.GroupRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
toExport = append(toExport, &bundle.Record{
|
||||
BuildHistoryRecord: rec.BuildHistoryRecord,
|
||||
DefaultPlatform: defaultPlatform,
|
||||
LocalState: st,
|
||||
StateGroup: stg,
|
||||
})
|
||||
}
|
||||
|
||||
var w io.Writer = os.Stdout
|
||||
if opts.output != "" {
|
||||
f, err := os.Create(opts.output)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to create output file %q", opts.output)
|
||||
}
|
||||
defer f.Close()
|
||||
w = f
|
||||
} else {
|
||||
if _, err := console.ConsoleFromFile(os.Stdout); err == nil {
|
||||
return errors.Errorf("refusing to write to console, use --output to specify a file")
|
||||
}
|
||||
}
|
||||
|
||||
return bundle.Export(ctx, clients, w, toExport)
|
||||
}
|
||||
|
||||
func exportCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
|
||||
var options exportOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "export [OPTIONS] [REF]",
|
||||
Short: "Export a build into Docker Desktop bundle",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if options.all && len(args) > 0 {
|
||||
return errors.New("cannot specify refs when using --all")
|
||||
}
|
||||
options.refs = args
|
||||
options.builder = *rootOpts.Builder
|
||||
return runExport(cmd.Context(), dockerCli, options)
|
||||
},
|
||||
ValidArgsFunction: completion.Disable,
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVarP(&options.output, "output", "o", "", "Output file path")
|
||||
flags.BoolVar(&options.all, "all", false, "Export all records for the builder")
|
||||
|
||||
return cmd
|
||||
}
|
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
@@ -14,10 +15,12 @@ import (
|
||||
"github.com/docker/buildx/util/cobrautil/completion"
|
||||
"github.com/docker/buildx/util/confutil"
|
||||
"github.com/docker/buildx/util/desktop"
|
||||
"github.com/docker/buildx/util/gitutil"
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -38,6 +41,9 @@ type lsOptions struct {
|
||||
builder string
|
||||
format string
|
||||
noTrunc bool
|
||||
|
||||
filters []string
|
||||
local bool
|
||||
}
|
||||
|
||||
func runLs(ctx context.Context, dockerCli command.Cli, opts lsOptions) error {
|
||||
@@ -56,7 +62,29 @@ func runLs(ctx context.Context, dockerCli command.Cli, opts lsOptions) error {
|
||||
}
|
||||
}
|
||||
|
||||
out, err := queryRecords(ctx, "", nodes, nil)
|
||||
queryOptions := &queryOptions{}
|
||||
|
||||
if opts.local {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gitc, err := gitutil.New(gitutil.WithContext(ctx), gitutil.WithWorkingDir(wd))
|
||||
if err != nil {
|
||||
if st, err1 := os.Stat(path.Join(wd, ".git")); err1 == nil && st.IsDir() {
|
||||
return errors.Wrap(err, "git was not found in the system")
|
||||
}
|
||||
return errors.Wrapf(err, "could not find git repository for local filter")
|
||||
}
|
||||
remote, err := gitc.RemoteURL()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not get remote URL for local filter")
|
||||
}
|
||||
queryOptions.Filters = append(queryOptions.Filters, fmt.Sprintf("repository=%s", remote))
|
||||
}
|
||||
queryOptions.Filters = append(queryOptions.Filters, opts.filters...)
|
||||
|
||||
out, err := queryRecords(ctx, "", nodes, queryOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -92,6 +120,8 @@ func lsCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&options.format, "format", formatter.TableFormatKey, "Format the output")
|
||||
flags.BoolVar(&options.noTrunc, "no-trunc", false, "Don't truncate output")
|
||||
flags.StringArrayVar(&options.filters, "filter", nil, `Provide filter values (e.g., "status=error")`)
|
||||
flags.BoolVar(&options.local, "local", false, "List records for current repository only")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
@@ -26,6 +26,7 @@ func RootCmd(rootcmd *cobra.Command, dockerCli command.Cli, opts RootOptions) *c
|
||||
openCmd(dockerCli, opts),
|
||||
traceCmd(dockerCli, opts),
|
||||
importCmd(dockerCli, opts),
|
||||
exportCmd(dockerCli, opts),
|
||||
)
|
||||
|
||||
return cmd
|
||||
|
@@ -1,7 +1,9 @@
|
||||
package history
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
@@ -15,10 +17,13 @@ import (
|
||||
"github.com/docker/buildx/builder"
|
||||
"github.com/docker/buildx/localstate"
|
||||
controlapi "github.com/moby/buildkit/api/services/control"
|
||||
"github.com/moby/buildkit/util/gitutil"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
const recordsLimit = 50
|
||||
|
||||
func buildName(fattrs map[string]string, ls *localstate.State) string {
|
||||
var res string
|
||||
|
||||
@@ -110,6 +115,7 @@ type historyRecord struct {
|
||||
|
||||
type queryOptions struct {
|
||||
CompletedOnly bool
|
||||
Filters []string
|
||||
}
|
||||
|
||||
func queryRecords(ctx context.Context, ref string, nodes []builder.Node, opts *queryOptions) ([]historyRecord, error) {
|
||||
@@ -126,6 +132,11 @@ func queryRecords(ctx context.Context, ref string, nodes []builder.Node, opts *q
|
||||
ref = ""
|
||||
}
|
||||
|
||||
var filters []string
|
||||
if opts != nil {
|
||||
filters = opts.Filters
|
||||
}
|
||||
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
for _, node := range nodes {
|
||||
node := node
|
||||
@@ -138,9 +149,25 @@ func queryRecords(ctx context.Context, ref string, nodes []builder.Node, opts *q
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var matchers []matchFunc
|
||||
if len(filters) > 0 {
|
||||
filters, matchers, err = dockerFiltersToBuildkit(filters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sb := bytes.NewBuffer(nil)
|
||||
w := csv.NewWriter(sb)
|
||||
w.Write(filters)
|
||||
w.Flush()
|
||||
filters = []string{strings.TrimSuffix(sb.String(), "\n")}
|
||||
}
|
||||
|
||||
serv, err := c.ControlClient().ListenBuildHistory(ctx, &controlapi.BuildHistoryRequest{
|
||||
EarlyExit: true,
|
||||
Ref: ref,
|
||||
Limit: recordsLimit,
|
||||
Filter: filters,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -158,6 +185,7 @@ func queryRecords(ctx context.Context, ref string, nodes []builder.Node, opts *q
|
||||
ts = &t
|
||||
}
|
||||
defer serv.CloseSend()
|
||||
loop0:
|
||||
for {
|
||||
he, err := serv.Recv()
|
||||
if err != nil {
|
||||
@@ -173,6 +201,13 @@ func queryRecords(ctx context.Context, ref string, nodes []builder.Node, opts *q
|
||||
continue
|
||||
}
|
||||
|
||||
// for older buildkit that don't support filters apply local filters
|
||||
for _, matcher := range matchers {
|
||||
if !matcher(he.Record) {
|
||||
continue loop0
|
||||
}
|
||||
}
|
||||
|
||||
records = append(records, historyRecord{
|
||||
BuildHistoryRecord: he.Record,
|
||||
currentTimestamp: ts,
|
||||
@@ -219,3 +254,150 @@ func formatDuration(d time.Duration) string {
|
||||
}
|
||||
return fmt.Sprintf("%dm %2ds", int(d.Minutes()), int(d.Seconds())%60)
|
||||
}
|
||||
|
||||
type matchFunc func(*controlapi.BuildHistoryRecord) bool
|
||||
|
||||
func dockerFiltersToBuildkit(in []string) ([]string, []matchFunc, error) {
|
||||
out := []string{}
|
||||
matchers := []matchFunc{}
|
||||
for _, f := range in {
|
||||
key, value, sep, found := cutAny(f, "!=", "=", "<=", "<", ">=", ">")
|
||||
if !found {
|
||||
return nil, nil, errors.Errorf("invalid filter %q", f)
|
||||
}
|
||||
switch key {
|
||||
case "ref", "repository", "status":
|
||||
if sep != "=" && sep != "!=" {
|
||||
return nil, nil, errors.Errorf("invalid separator for %q, expected = or !=", f)
|
||||
}
|
||||
matchers = append(matchers, valueFiler(key, value, sep))
|
||||
if sep == "=" {
|
||||
if key == "status" {
|
||||
sep = "=="
|
||||
} else {
|
||||
sep = "~="
|
||||
}
|
||||
}
|
||||
case "startedAt", "completedAt", "duration":
|
||||
if sep == "=" || sep == "!=" {
|
||||
return nil, nil, errors.Errorf("invalid separator for %q, expected <=, <, >= or >", f)
|
||||
}
|
||||
matcher, err := timeBasedFilter(key, value, sep)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
matchers = append(matchers, matcher)
|
||||
default:
|
||||
return nil, nil, errors.Errorf("unsupported filter %q", f)
|
||||
}
|
||||
out = append(out, key+sep+value)
|
||||
}
|
||||
return out, matchers, nil
|
||||
}
|
||||
|
||||
func valueFiler(key, value, sep string) matchFunc {
|
||||
return func(rec *controlapi.BuildHistoryRecord) bool {
|
||||
var recValue string
|
||||
switch key {
|
||||
case "ref":
|
||||
recValue = rec.Ref
|
||||
case "repository":
|
||||
v, ok := rec.FrontendAttrs["vcs:source"]
|
||||
if ok {
|
||||
recValue = v
|
||||
} else {
|
||||
if context, ok := rec.FrontendAttrs["context"]; ok {
|
||||
if ref, err := gitutil.ParseGitRef(context); err == nil {
|
||||
recValue = ref.Remote
|
||||
}
|
||||
}
|
||||
}
|
||||
case "status":
|
||||
if rec.CompletedAt != nil {
|
||||
if rec.Error != nil {
|
||||
if strings.Contains(rec.Error.Message, "context canceled") {
|
||||
recValue = "canceled"
|
||||
} else {
|
||||
recValue = "error"
|
||||
}
|
||||
} else {
|
||||
recValue = "completed"
|
||||
}
|
||||
} else {
|
||||
recValue = "running"
|
||||
}
|
||||
}
|
||||
switch sep {
|
||||
case "=":
|
||||
if key == "status" {
|
||||
return recValue == value
|
||||
}
|
||||
return strings.Contains(recValue, value)
|
||||
case "!=":
|
||||
return recValue != value
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func timeBasedFilter(key, value, sep string) (matchFunc, error) {
|
||||
var cmp int64
|
||||
switch key {
|
||||
case "startedAt", "completedAt":
|
||||
v, err := time.ParseDuration(value)
|
||||
if err == nil {
|
||||
tm := time.Now().Add(-v)
|
||||
cmp = tm.Unix()
|
||||
} else {
|
||||
tm, err := time.Parse(time.RFC3339, value)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid time %s", value)
|
||||
}
|
||||
cmp = tm.Unix()
|
||||
}
|
||||
case "duration":
|
||||
v, err := time.ParseDuration(value)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid duration %s", value)
|
||||
}
|
||||
cmp = int64(v)
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return func(rec *controlapi.BuildHistoryRecord) bool {
|
||||
var val int64
|
||||
switch key {
|
||||
case "startedAt":
|
||||
val = rec.CreatedAt.AsTime().Unix()
|
||||
case "completedAt":
|
||||
if rec.CompletedAt != nil {
|
||||
val = rec.CompletedAt.AsTime().Unix()
|
||||
}
|
||||
case "duration":
|
||||
if rec.CompletedAt != nil {
|
||||
val = int64(rec.CompletedAt.AsTime().Sub(rec.CreatedAt.AsTime()))
|
||||
}
|
||||
}
|
||||
switch sep {
|
||||
case ">=":
|
||||
return val >= cmp
|
||||
case "<=":
|
||||
return val <= cmp
|
||||
case ">":
|
||||
return val > cmp
|
||||
default:
|
||||
return val < cmp
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func cutAny(s string, seps ...string) (before, after, sep string, found bool) {
|
||||
for _, sep := range seps {
|
||||
if idx := strings.Index(s, sep); idx != -1 {
|
||||
return s[:idx], s[idx+len(sep):], sep, true
|
||||
}
|
||||
}
|
||||
return s, "", "", false
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"maps"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -409,9 +410,7 @@ func truncPlatforms(pfs []string, max int) truncatedPlatforms {
|
||||
left[ppf] = append(left[ppf], pf)
|
||||
}
|
||||
}
|
||||
for k, v := range left {
|
||||
res[k] = v
|
||||
}
|
||||
maps.Copy(res, left)
|
||||
return truncatedPlatforms{
|
||||
res: res,
|
||||
input: pfs,
|
||||
|
@@ -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"
|
||||
@@ -16,13 +15,14 @@ import (
|
||||
"github.com/docker/cli/cli-plugins/plugin"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/debug"
|
||||
cliflags "github.com/docker/cli/cli/flags"
|
||||
"github.com/moby/buildkit/util/appcontext"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func NewRootCmd(name string, isPlugin bool, dockerCli command.Cli) *cobra.Command {
|
||||
func NewRootCmd(name string, isPlugin bool, dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opt rootOptions
|
||||
cmd := &cobra.Command{
|
||||
Short: "Docker Buildx",
|
||||
@@ -40,7 +40,17 @@ func NewRootCmd(name string, isPlugin bool, dockerCli command.Cli) *cobra.Comman
|
||||
}
|
||||
cmd.SetContext(appcontext.Context())
|
||||
if !isPlugin {
|
||||
return nil
|
||||
// InstallFlags and SetDefaultOptions are necessary to match
|
||||
// the plugin mode behavior to handle env vars such as
|
||||
// DOCKER_TLS, DOCKER_TLS_VERIFY, ... and we also need to use a
|
||||
// new flagset to avoid conflict with the global debug flag
|
||||
// that we already handle in the root command otherwise it
|
||||
// would panic.
|
||||
nflags := pflag.NewFlagSet(cmd.DisplayName(), pflag.ContinueOnError)
|
||||
options := cliflags.NewClientOptions()
|
||||
options.InstallFlags(nflags)
|
||||
options.SetDefaultOptions(nflags)
|
||||
return dockerCli.Initialize(options)
|
||||
}
|
||||
return plugin.PersistentPreRunE(cmd, args)
|
||||
},
|
||||
@@ -113,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
|
||||
|
@@ -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")
|
||||
}
|
||||
|
@@ -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 {
|
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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}
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
@@ -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;
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
@@ -1,6 +1,15 @@
|
||||
package pb
|
||||
|
||||
import "github.com/moby/buildkit/client"
|
||||
import (
|
||||
"maps"
|
||||
|
||||
"github.com/moby/buildkit/client"
|
||||
)
|
||||
|
||||
type CacheOptionsEntry struct {
|
||||
Type string
|
||||
Attrs map[string]string
|
||||
}
|
||||
|
||||
func CreateCaches(entries []*CacheOptionsEntry) []client.CacheOptionsEntry {
|
||||
var outs []client.CacheOptionsEntry
|
||||
@@ -12,9 +21,7 @@ func CreateCaches(entries []*CacheOptionsEntry) []client.CacheOptionsEntry {
|
||||
Type: entry.Type,
|
||||
Attrs: map[string]string{},
|
||||
}
|
||||
for k, v := range entry.Attrs {
|
||||
out.Attrs[k] = v
|
||||
}
|
||||
maps.Copy(out.Attrs, entry.Attrs)
|
||||
outs = append(outs, out)
|
||||
}
|
||||
return outs
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
@@ -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
@@ -2,6 +2,7 @@ package pb
|
||||
|
||||
import (
|
||||
"io"
|
||||
"maps"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
@@ -10,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
|
||||
@@ -26,9 +33,7 @@ func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, []string, erro
|
||||
Type: entry.Type,
|
||||
Attrs: map[string]string{},
|
||||
}
|
||||
for k, v := range entry.Attrs {
|
||||
out.Attrs[k] = v
|
||||
}
|
||||
maps.Copy(out.Attrs, entry.Attrs)
|
||||
|
||||
supportFile := false
|
||||
supportDir := false
|
||||
|
40
controller/pb/invoke.go
Normal file
40
controller/pb/invoke.go
Normal 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
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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 {
|
||||
|
@@ -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
11
controller/pb/ulimit.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package pb
|
||||
|
||||
type UlimitOpt struct {
|
||||
Values map[string]*Ulimit
|
||||
}
|
||||
|
||||
type Ulimit struct {
|
||||
Name string
|
||||
Hard int64
|
||||
Soft int64
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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) {}
|
@@ -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
|
||||
}
|
@@ -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()
|
||||
}
|
@@ -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`) |
|
||||
@@ -944,7 +941,7 @@ $ docker buildx build --secret [type=file,]id=<ID>[,src=<FILEPATH>] .
|
||||
###### `type=file` usage
|
||||
|
||||
In the following example, `type=file` is automatically detected because no
|
||||
environment variable mathing `aws` (the ID) is set.
|
||||
environment variable matching `aws` (the ID) is set.
|
||||
|
||||
```console
|
||||
$ docker buildx build --secret id=aws,src=$HOME/.aws/credentials .
|
||||
|
@@ -13,15 +13,12 @@ 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) |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
@@ -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`) |
|
||||
|
@@ -7,6 +7,7 @@ Commands to work on build records
|
||||
|
||||
| Name | Description |
|
||||
|:---------------------------------------|:-----------------------------------------------|
|
||||
| [`export`](buildx_history_export.md) | Export a build into Docker Desktop bundle |
|
||||
| [`import`](buildx_history_import.md) | Import a build into Docker Desktop |
|
||||
| [`inspect`](buildx_history_inspect.md) | Inspect a build |
|
||||
| [`logs`](buildx_history_logs.md) | Print the logs of a build |
|
||||
@@ -26,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`
|
||||
|
81
docs/reference/buildx_history_export.md
Normal file
81
docs/reference/buildx_history_export.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# docker buildx history export
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Export a build into Docker Desktop bundle
|
||||
|
||||
### Options
|
||||
|
||||
| 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
|
||||
```
|
@@ -6,11 +6,42 @@ 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 |
|
||||
| [`-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
|
||||
```
|
||||
|
@@ -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)
|
||||
|
@@ -6,12 +6,77 @@ 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 |
|
||||
| [`--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.
|
||||
|
@@ -6,11 +6,60 @@ 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) |
|
||||
| [`--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}
|
||||
...
|
||||
```
|
||||
|
@@ -6,12 +6,97 @@ List build records
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------|:---------|:--------|:-----------------------------------------|
|
||||
|:--------------------------|:--------------|:--------|:---------------------------------------------|
|
||||
| `--builder` | `string` | | Override the configured builder instance |
|
||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||
| `--format` | `string` | `table` | Format the output |
|
||||
| `--no-trunc` | `bool` | | Don't truncate output |
|
||||
| [`--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
|
||||
```
|
||||
|
@@ -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
|
||||
```
|
||||
|
@@ -14,3 +14,36 @@ Remove build records
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
## Description
|
||||
|
||||
Remove one or more build records from the current builder’s 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
|
||||
```
|
||||
|
@@ -6,12 +6,64 @@ 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 |
|
||||
|:------------------------|:---------|:--------------|:-----------------------------------------|
|
||||
| [`--addr`](#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 |
|
||||
| [`--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.
|
||||
|
@@ -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
|
||||
|
@@ -28,7 +28,7 @@ type Driver struct {
|
||||
*tlsOpts
|
||||
defaultLoad bool
|
||||
|
||||
// remote driver caches the client because its Bootstap/Info methods reuse it internally
|
||||
// remote driver caches the client because its Bootstrap/Info methods reuse it internally
|
||||
clientOnce sync.Once
|
||||
client *client.Client
|
||||
err error
|
||||
|
82
go.mod
82
go.mod
@@ -1,63 +1,64 @@
|
||||
module github.com/docker/buildx
|
||||
|
||||
go 1.22.0
|
||||
go 1.23.0
|
||||
|
||||
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.8
|
||||
github.com/compose-spec/compose-go/v2 v2.6.0
|
||||
github.com/containerd/console v1.0.4
|
||||
github.com/containerd/containerd/v2 v2.0.3
|
||||
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.1+incompatible
|
||||
github.com/docker/cli v28.1.1+incompatible
|
||||
github.com/docker/cli-docs-tool v0.9.0
|
||||
github.com/docker/docker v28.0.1+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.20.1
|
||||
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.0
|
||||
github.com/opencontainers/image-spec v1.1.1
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10
|
||||
github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
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-20250113203817-b14e27f4135a
|
||||
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-20250211190051-7d4944a45bb6
|
||||
github.com/zclconf/go-cty v1.16.0
|
||||
github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250408171107-3dd17559e117
|
||||
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
|
||||
go.opentelemetry.io/otel/sdk v1.31.0
|
||||
go.opentelemetry.io/otel/trace v1.31.0
|
||||
golang.org/x/mod v0.22.0
|
||||
golang.org/x/sync v0.10.0
|
||||
golang.org/x/sys v0.29.0
|
||||
golang.org/x/term v0.27.0
|
||||
golang.org/x/text v0.21.0
|
||||
golang.org/x/mod v0.24.0
|
||||
golang.org/x/sync v0.13.0
|
||||
golang.org/x/sys v0.32.0
|
||||
golang.org/x/term v0.31.0
|
||||
golang.org/x/text v0.24.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38
|
||||
google.golang.org/grpc v1.69.4
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1
|
||||
@@ -71,7 +72,6 @@ require (
|
||||
require (
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d // indirect
|
||||
github.com/agext/levenshtein v1.2.3 // indirect
|
||||
github.com/apparentlymart/go-cidr v1.0.1 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
@@ -87,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.8.2 // indirect
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // 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/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fvbommel/sortorder v1.0.1 // indirect
|
||||
@@ -112,9 +109,8 @@ require (
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // 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
|
||||
@@ -123,19 +119,18 @@ require (
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // 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/miekg/pkcs11 v1.1.1 // indirect
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // 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/user v0.3.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
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
@@ -143,25 +138,21 @@ 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
|
||||
github.com/shibumi/go-pathspec v1.3.0 // indirect
|
||||
github.com/theupdateframework/notary v0.7.0 // indirect
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205 // indirect
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 // indirect
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
|
||||
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
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
|
||||
@@ -169,12 +160,11 @@ require (
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.31.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/oauth2 v0.23.0 // indirect
|
||||
golang.org/x/time v0.6.0 // indirect
|
||||
golang.org/x/tools v0.27.0 // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/net v0.39.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
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
|
208
go.sum
208
go.sum
@@ -4,7 +4,6 @@ github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 h1:dIS
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2/go.mod h1:gCLVsLfv1egrcZu+GoJATN5ts75F2s62ih/457eWzOw=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
@@ -13,17 +12,12 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA
|
||||
github.com/Microsoft/hcsshim v0.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6lLg=
|
||||
github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/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=
|
||||
@@ -55,38 +49,31 @@ 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 h1:s7+5BfS4WFJoVF9pnB8kBk03S7pZXRdKamnV0FOl5Sc=
|
||||
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 h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ=
|
||||
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
|
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o=
|
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e85keuznYcH5rqI438v41pKcBl4ZxQ=
|
||||
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.8 h1:7Myl8wDRl/4mRz77S+eyDJymGGEHu0diQdGSSeyq90A=
|
||||
github.com/compose-spec/compose-go/v2 v2.4.8/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc=
|
||||
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.3 h1:zBKgwgZsuu+LPCMzCLgA4sC4MiZzZ59ZT31XkmiISQM=
|
||||
github.com/containerd/containerd/v2 v2.0.3/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=
|
||||
@@ -110,7 +97,6 @@ github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRq
|
||||
github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
|
||||
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
|
||||
github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
@@ -122,17 +108,17 @@ 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.1+incompatible h1:g0h5NQNda3/CxIsaZfH4Tyf6vpxFth7PYl3hgCPOKzs=
|
||||
github.com/docker/cli v28.0.1+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.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0=
|
||||
github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
|
||||
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
|
||||
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=
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
@@ -143,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=
|
||||
@@ -156,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=
|
||||
@@ -171,9 +153,7 @@ github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
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 h1:pgwjLi/dvffoP9aabwkT3AKpXQM93QARkjFhDDqC1UE=
|
||||
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=
|
||||
@@ -183,28 +163,21 @@ 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 h1:jc2UWq7CbdszqeH6qu1ougXMIUBfSy8Pbh/anURYbGI=
|
||||
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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/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=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
@@ -212,25 +185,22 @@ 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=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
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=
|
||||
@@ -243,26 +213,20 @@ github.com/in-toto/in-toto-golang v0.5.0/go.mod h1:/Rq0IZHLV7Ku5gielPT4wPHJfH1Gd
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8 h1:CZkYfurY6KGhVtlalI4QwQ6T0Cu6iuY3e0x5RLu96WE=
|
||||
github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
|
||||
github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d h1:jRQLvyVGL+iVtDElaEIDdKwpPqUIZJfzkNLV34htpEc=
|
||||
github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
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.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
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=
|
||||
@@ -271,15 +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 h1:C8fxWnhYyME3n0klPOhVM7PtYUB3eV1W3DeFmN3j53Y=
|
||||
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=
|
||||
@@ -287,32 +248,33 @@ 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/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/moby/buildkit v0.20.1 h1:sT0ZXhhNo5rVbMcYfgttma3TdUHfO5JjFA0UAL8p9fY=
|
||||
github.com/moby/buildkit v0.20.1/go.mod h1:Rq9nB/fJImdk6QeM0niKtOHJqwKeYMrK847hTTDVuA4=
|
||||
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=
|
||||
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
|
||||
github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0=
|
||||
github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8=
|
||||
github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
|
||||
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
|
||||
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
||||
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
|
||||
@@ -320,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=
|
||||
@@ -345,19 +304,17 @@ github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
|
||||
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8=
|
||||
github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
|
||||
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
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=
|
||||
@@ -366,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=
|
||||
@@ -400,23 +347,19 @@ 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=
|
||||
github.com/spdx/tools-golang v0.5.3 h1:ialnHeEYUC4+hkm5vJm4qz2x+oEJbS0mAMFrNXdQraY=
|
||||
github.com/spdx/tools-golang v0.5.3/go.mod h1:/ETOahiAo96Ob0/RAIBmFZw6XN0yTnyr/uFZm2NTMhI=
|
||||
github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94 h1:JmfC365KywYwHB946TTiQWEb8kqPY+pybPLoGE9GgVk=
|
||||
github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
||||
github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/jwalterweatherman v0.0.0-20141219030609-3d60171a6431 h1:XTHrT015sxHyJ5FnQ0AeemSspZWaDq7DoTRW0EVsDCE=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/jwalterweatherman v0.0.0-20141219030609-3d60171a6431/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.0/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c h1:2EejZtjFjKJGk71ANb+wtFK5EjUzUkEM3R0xnp559xg=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -435,21 +378,20 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c=
|
||||
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205 h1:eUk79E1w8yMtXeHSzjKorxuC8qJOnyXQnLaJehxpJaI=
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a h1:EfGw4G0x/8qXWgtcZ6KVaPS+wpWOQMaypczzP8ojkMY=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a/go.mod h1:Dl/9oEjK7IqnjAm21Okx/XIxUCFJzvh+XdVHUlBwXTw=
|
||||
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-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-20250211190051-7d4944a45bb6 h1:RT/a0RvdX84iwtOrUK45+wjcNpaG+hS7n7XFYqj4axg=
|
||||
github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250211190051-7d4944a45bb6/go.mod h1:3Ez1Paeg+0Ghu3KwpEGC1HgZ4CHDlg+Ez/5Baeomk54=
|
||||
github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250408171107-3dd17559e117 h1:XFwyh2JZwR5aiKLXHX2C1n0v5F11dCJpyGL1W/Cpl3U=
|
||||
github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250408171107-3dd17559e117/go.mod h1:3Ez1Paeg+0Ghu3KwpEGC1HgZ4CHDlg+Ez/5Baeomk54=
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0=
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=
|
||||
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab h1:H6aJ0yKQ0gF49Qb2z5hI1UHxSQt4JMyxebFR15KnApw=
|
||||
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=
|
||||
@@ -459,11 +401,12 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
||||
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=
|
||||
@@ -500,76 +443,64 @@ 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.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
|
||||
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/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.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
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.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
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.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=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
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=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
|
||||
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
|
||||
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
|
||||
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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=
|
||||
@@ -582,11 +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 h1:eJ9UAg01/HIHG987TwxvnzK2MgxXq97YY6rYDpY9aII=
|
||||
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=
|
||||
@@ -594,10 +522,8 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||
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 h1:d4KQkxAaAiRY2h5Zqis161Pv91A37uZyJOx73duwUwM=
|
||||
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=
|
||||
@@ -606,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=
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
infos, err := cm.m.ListProcesses(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)
|
||||
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)
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
@@ -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")
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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:]
|
||||
|
@@ -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
|
||||
|
@@ -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.
|
||||
|
@@ -2,6 +2,7 @@ package store
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
@@ -93,9 +94,7 @@ func (ng *NodeGroup) Update(name, endpoint string, platforms []string, endpoints
|
||||
needsRestart = true
|
||||
}
|
||||
if buildkitdConfigFile != "" {
|
||||
for k, v := range files {
|
||||
n.Files[k] = v
|
||||
}
|
||||
maps.Copy(n.Files, files)
|
||||
needsRestart = true
|
||||
}
|
||||
if needsRestart {
|
||||
@@ -147,9 +146,7 @@ func (n *Node) Copy() *Node {
|
||||
buildkitdFlags := []string{}
|
||||
copy(buildkitdFlags, n.BuildkitdFlags)
|
||||
driverOpts := map[string]string{}
|
||||
for k, v := range n.DriverOpts {
|
||||
driverOpts[k] = v
|
||||
}
|
||||
maps.Copy(driverOpts, n.DriverOpts)
|
||||
files := map[string][]byte{}
|
||||
for k, v := range n.Files {
|
||||
vv := []byte{}
|
||||
|
156
tests/bake.go
156
tests/bake.go
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/containerd/continuity/fs/fstest"
|
||||
"github.com/docker/buildx/bake"
|
||||
"github.com/docker/buildx/util/gitutil"
|
||||
"github.com/docker/buildx/util/gitutil/gittestutil"
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/moby/buildkit/frontend/subrequests/lint"
|
||||
"github.com/moby/buildkit/identity"
|
||||
@@ -39,6 +40,7 @@ var bakeTests = []func(t *testing.T, sb integration.Sandbox){
|
||||
testBakePrint,
|
||||
testBakePrintSensitive,
|
||||
testBakePrintOverrideEmpty,
|
||||
testBakePrintKeepEscaped,
|
||||
testBakeLocal,
|
||||
testBakeLocalMulti,
|
||||
testBakeRemote,
|
||||
@@ -328,6 +330,66 @@ target "default" {
|
||||
}`, stdout.String())
|
||||
}
|
||||
|
||||
func testBakePrintKeepEscaped(t *testing.T, sb integration.Sandbox) {
|
||||
bakefile := []byte(`
|
||||
target "default" {
|
||||
dockerfile-inline = <<EOT
|
||||
ARG VERSION=latest
|
||||
FROM alpine:$${VERSION}
|
||||
EOT
|
||||
args = {
|
||||
VERSION = "3.21"
|
||||
}
|
||||
annotations = [
|
||||
"org.opencontainers.image.authors=$${user}"
|
||||
]
|
||||
labels = {
|
||||
foo = "hello %%{bar}"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
dir := tmpdir(t, fstest.CreateFile("docker-bake.hcl", bakefile, 0600))
|
||||
cmd := buildxCmd(sb, withDir(dir), withArgs("bake", "--print"))
|
||||
stdout := bytes.Buffer{}
|
||||
stderr := bytes.Buffer{}
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
require.NoError(t, cmd.Run(), stdout.String(), stderr.String())
|
||||
|
||||
require.JSONEq(t, `{
|
||||
"group": {
|
||||
"default": {
|
||||
"targets": [
|
||||
"default"
|
||||
]
|
||||
}
|
||||
},
|
||||
"target": {
|
||||
"default": {
|
||||
"annotations": [
|
||||
"org.opencontainers.image.authors=$${user}"
|
||||
],
|
||||
"context": ".",
|
||||
"dockerfile": "Dockerfile",
|
||||
"dockerfile-inline": "ARG VERSION=latest\nFROM alpine:$${VERSION}\n",
|
||||
"args": {
|
||||
"VERSION": "3.21"
|
||||
},
|
||||
"labels": {
|
||||
"foo": "hello %%{bar}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}`, stdout.String())
|
||||
|
||||
// test build with definition from print output
|
||||
dir = tmpdir(t, fstest.CreateFile("docker-bake.json", stdout.Bytes(), 0600))
|
||||
cmd = buildxCmd(sb, withDir(dir), withArgs("bake"))
|
||||
out, err := cmd.CombinedOutput()
|
||||
require.NoError(t, err, string(out))
|
||||
}
|
||||
|
||||
func testBakeLocal(t *testing.T, sb integration.Sandbox) {
|
||||
dockerfile := []byte(`
|
||||
FROM scratch
|
||||
@@ -415,10 +477,10 @@ EOT
|
||||
git, err := gitutil.New(gitutil.WithWorkingDir(dir))
|
||||
require.NoError(t, err)
|
||||
|
||||
gitutil.GitInit(git, t)
|
||||
gitutil.GitAdd(git, t, "docker-bake.hcl", "foo")
|
||||
gitutil.GitCommit(git, t, "initial commit")
|
||||
addr := gitutil.GitServeHTTP(git, t)
|
||||
gittestutil.GitInit(git, t)
|
||||
gittestutil.GitAdd(git, t, "docker-bake.hcl", "foo")
|
||||
gittestutil.GitCommit(git, t, "initial commit")
|
||||
addr := gittestutil.GitServeHTTP(git, t)
|
||||
|
||||
out, err := bakeCmd(sb, withDir(dir), withArgs(addr, "--set", "*.output=type=local,dest="+dirDest))
|
||||
require.NoError(t, err, out)
|
||||
@@ -445,12 +507,12 @@ EOT
|
||||
git, err := gitutil.New(gitutil.WithWorkingDir(dir))
|
||||
require.NoError(t, err)
|
||||
|
||||
gitutil.GitInit(git, t)
|
||||
gitutil.GitAdd(git, t, "docker-bake.hcl", "foo")
|
||||
gitutil.GitCommit(git, t, "initial commit")
|
||||
gittestutil.GitInit(git, t)
|
||||
gittestutil.GitAdd(git, t, "docker-bake.hcl", "foo")
|
||||
gittestutil.GitCommit(git, t, "initial commit")
|
||||
|
||||
token := identity.NewID()
|
||||
addr := gitutil.GitServeHTTP(git, t, gitutil.WithAccessToken(token))
|
||||
addr := gittestutil.GitServeHTTP(git, t, gittestutil.WithAccessToken(token))
|
||||
|
||||
out, err := bakeCmd(sb, withDir(dir),
|
||||
withEnv("BUILDX_BAKE_GIT_AUTH_TOKEN="+token),
|
||||
@@ -492,10 +554,10 @@ EOT
|
||||
git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec))
|
||||
require.NoError(t, err)
|
||||
|
||||
gitutil.GitInit(git, t)
|
||||
gitutil.GitAdd(git, t, "docker-bake.hcl", "bar")
|
||||
gitutil.GitCommit(git, t, "initial commit")
|
||||
addr := gitutil.GitServeHTTP(git, t)
|
||||
gittestutil.GitInit(git, t)
|
||||
gittestutil.GitAdd(git, t, "docker-bake.hcl", "bar")
|
||||
gittestutil.GitCommit(git, t, "initial commit")
|
||||
addr := gittestutil.GitServeHTTP(git, t)
|
||||
|
||||
out, err := bakeCmd(sb, withDir(dirSrc), withArgs(addr, "--file", "cwd://local-docker-bake.hcl", "--set", "*.output=type=local,dest="+dirDest))
|
||||
require.NoError(t, err, out)
|
||||
@@ -561,10 +623,10 @@ EOT
|
||||
git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec))
|
||||
require.NoError(t, err)
|
||||
|
||||
gitutil.GitInit(git, t)
|
||||
gitutil.GitAdd(git, t, "docker-bake.hcl")
|
||||
gitutil.GitCommit(git, t, "initial commit")
|
||||
addr := gitutil.GitServeHTTP(git, t)
|
||||
gittestutil.GitInit(git, t)
|
||||
gittestutil.GitAdd(git, t, "docker-bake.hcl")
|
||||
gittestutil.GitCommit(git, t, "initial commit")
|
||||
addr := gittestutil.GitServeHTTP(git, t)
|
||||
|
||||
out, err := bakeCmd(sb, withDir(dirSrc), withArgs(addr, "--set", "*.output=type=local,dest="+dirDest))
|
||||
require.NoError(t, err, out)
|
||||
@@ -594,17 +656,17 @@ EOT
|
||||
|
||||
gitSpec, err := gitutil.New(gitutil.WithWorkingDir(dirSpec))
|
||||
require.NoError(t, err)
|
||||
gitutil.GitInit(gitSpec, t)
|
||||
gitutil.GitAdd(gitSpec, t, "docker-bake.hcl")
|
||||
gitutil.GitCommit(gitSpec, t, "initial commit")
|
||||
addrSpec := gitutil.GitServeHTTP(gitSpec, t)
|
||||
gittestutil.GitInit(gitSpec, t)
|
||||
gittestutil.GitAdd(gitSpec, t, "docker-bake.hcl")
|
||||
gittestutil.GitCommit(gitSpec, t, "initial commit")
|
||||
addrSpec := gittestutil.GitServeHTTP(gitSpec, t)
|
||||
|
||||
gitSrc, err := gitutil.New(gitutil.WithWorkingDir(dirSrc))
|
||||
require.NoError(t, err)
|
||||
gitutil.GitInit(gitSrc, t)
|
||||
gitutil.GitAdd(gitSrc, t, "foo")
|
||||
gitutil.GitCommit(gitSrc, t, "initial commit")
|
||||
addrSrc := gitutil.GitServeHTTP(gitSrc, t)
|
||||
gittestutil.GitInit(gitSrc, t)
|
||||
gittestutil.GitAdd(gitSrc, t, "foo")
|
||||
gittestutil.GitCommit(gitSrc, t, "initial commit")
|
||||
addrSrc := gittestutil.GitServeHTTP(gitSrc, t)
|
||||
|
||||
out, err := bakeCmd(sb, withDir("/tmp"), withArgs(addrSpec, addrSrc, "--set", "*.output=type=local,dest="+dirDest))
|
||||
require.NoError(t, err, out)
|
||||
@@ -635,10 +697,10 @@ COPY super-cool.txt /
|
||||
|
||||
git, err := gitutil.New(gitutil.WithWorkingDir(dir))
|
||||
require.NoError(t, err)
|
||||
gitutil.GitInit(git, t)
|
||||
gitutil.GitAdd(git, t, "docker-bake.hcl", "bar")
|
||||
gitutil.GitCommit(git, t, "initial commit")
|
||||
addr := gitutil.GitServeHTTP(git, t)
|
||||
gittestutil.GitInit(git, t)
|
||||
gittestutil.GitAdd(git, t, "docker-bake.hcl", "bar")
|
||||
gittestutil.GitCommit(git, t, "initial commit")
|
||||
addr := gittestutil.GitServeHTTP(git, t)
|
||||
|
||||
out, err := bakeCmd(sb, withDir("/tmp"), withArgs(addr, "--set", "*.output=type=local,dest="+dirDest))
|
||||
require.NoError(t, err, out)
|
||||
@@ -676,10 +738,10 @@ EOT
|
||||
git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec))
|
||||
require.NoError(t, err)
|
||||
|
||||
gitutil.GitInit(git, t)
|
||||
gitutil.GitAdd(git, t, "docker-bake.hcl")
|
||||
gitutil.GitCommit(git, t, "initial commit")
|
||||
addr := gitutil.GitServeHTTP(git, t)
|
||||
gittestutil.GitInit(git, t)
|
||||
gittestutil.GitAdd(git, t, "docker-bake.hcl")
|
||||
gittestutil.GitCommit(git, t, "initial commit")
|
||||
addr := gittestutil.GitServeHTTP(git, t)
|
||||
|
||||
out, err := bakeCmd(
|
||||
sb,
|
||||
@@ -724,10 +786,10 @@ EOT
|
||||
git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec))
|
||||
require.NoError(t, err)
|
||||
|
||||
gitutil.GitInit(git, t)
|
||||
gitutil.GitAdd(git, t, "docker-bake.hcl")
|
||||
gitutil.GitCommit(git, t, "initial commit")
|
||||
addr := gitutil.GitServeHTTP(git, t)
|
||||
gittestutil.GitInit(git, t)
|
||||
gittestutil.GitAdd(git, t, "docker-bake.hcl")
|
||||
gittestutil.GitCommit(git, t, "initial commit")
|
||||
addr := gittestutil.GitServeHTTP(git, t)
|
||||
|
||||
out, err := bakeCmd(
|
||||
sb,
|
||||
@@ -780,13 +842,13 @@ COPY foo /foo
|
||||
git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec))
|
||||
require.NoError(t, err)
|
||||
|
||||
gitutil.GitInit(git, t)
|
||||
gitutil.GitAdd(git, t, "docker-bake.hcl")
|
||||
gitutil.GitAdd(git, t, "Dockerfile")
|
||||
gitutil.GitAdd(git, t, "foo")
|
||||
gitutil.GitAdd(git, t, "bar")
|
||||
gitutil.GitCommit(git, t, "initial commit")
|
||||
addr := gitutil.GitServeHTTP(git, t)
|
||||
gittestutil.GitInit(git, t)
|
||||
gittestutil.GitAdd(git, t, "docker-bake.hcl")
|
||||
gittestutil.GitAdd(git, t, "Dockerfile")
|
||||
gittestutil.GitAdd(git, t, "foo")
|
||||
gittestutil.GitAdd(git, t, "bar")
|
||||
gittestutil.GitCommit(git, t, "initial commit")
|
||||
addr := gittestutil.GitServeHTTP(git, t)
|
||||
|
||||
out, err := bakeCmd(
|
||||
sb,
|
||||
@@ -832,10 +894,10 @@ COPY foo /foo
|
||||
git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec))
|
||||
require.NoError(t, err)
|
||||
|
||||
gitutil.GitInit(git, t)
|
||||
gitutil.GitAdd(git, t, "docker-bake.hcl")
|
||||
gitutil.GitCommit(git, t, "initial commit")
|
||||
addr := gitutil.GitServeHTTP(git, t)
|
||||
gittestutil.GitInit(git, t)
|
||||
gittestutil.GitAdd(git, t, "docker-bake.hcl")
|
||||
gittestutil.GitCommit(git, t, "initial commit")
|
||||
addr := gittestutil.GitServeHTTP(git, t)
|
||||
|
||||
out, err := bakeCmd(
|
||||
sb,
|
||||
|
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/docker/buildx/localstate"
|
||||
"github.com/docker/buildx/util/confutil"
|
||||
"github.com/docker/buildx/util/gitutil"
|
||||
"github.com/docker/buildx/util/gitutil/gittestutil"
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/moby/buildkit/frontend/subrequests/lint"
|
||||
"github.com/moby/buildkit/frontend/subrequests/outline"
|
||||
@@ -126,10 +127,10 @@ COPY foo /foo
|
||||
git, err := gitutil.New(gitutil.WithWorkingDir(dir))
|
||||
require.NoError(t, err)
|
||||
|
||||
gitutil.GitInit(git, t)
|
||||
gitutil.GitAdd(git, t, "Dockerfile", "foo")
|
||||
gitutil.GitCommit(git, t, "initial commit")
|
||||
addr := gitutil.GitServeHTTP(git, t)
|
||||
gittestutil.GitInit(git, t)
|
||||
gittestutil.GitAdd(git, t, "Dockerfile", "foo")
|
||||
gittestutil.GitCommit(git, t, "initial commit")
|
||||
addr := gittestutil.GitServeHTTP(git, t)
|
||||
|
||||
out, err := buildCmd(sb, withDir(dir), withArgs("--output=type=local,dest="+dirDest, addr))
|
||||
require.NoError(t, err, out)
|
||||
@@ -238,10 +239,10 @@ COPY foo /foo
|
||||
git, err := gitutil.New(gitutil.WithWorkingDir(dir))
|
||||
require.NoError(t, err)
|
||||
|
||||
gitutil.GitInit(git, t)
|
||||
gitutil.GitAdd(git, t, "build.Dockerfile", "foo")
|
||||
gitutil.GitCommit(git, t, "initial commit")
|
||||
addr := gitutil.GitServeHTTP(git, t)
|
||||
gittestutil.GitInit(git, t)
|
||||
gittestutil.GitAdd(git, t, "build.Dockerfile", "foo")
|
||||
gittestutil.GitCommit(git, t, "initial commit")
|
||||
addr := gittestutil.GitServeHTTP(git, t)
|
||||
|
||||
out, err := buildCmd(sb, withDir(dir), withArgs(
|
||||
"-f", "build.Dockerfile",
|
||||
@@ -398,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) {
|
||||
|
@@ -202,7 +202,7 @@ func ParseAttests(in []string) ([]*controllerapi.Attest, error) {
|
||||
func ConvertAttests(in []*Attest) ([]*controllerapi.Attest, error) {
|
||||
out := make([]*controllerapi.Attest, 0, len(in))
|
||||
|
||||
// Check for dupplicate attestations while we convert them
|
||||
// Check for duplicate attestations while we convert them
|
||||
// to the controller API.
|
||||
found := map[string]struct{}{}
|
||||
for _, attest := range in {
|
||||
|
@@ -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"
|
||||
|
78
util/desktop/bundle/content.go
Normal file
78
util/desktop/bundle/content.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package bundle
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/content"
|
||||
cerrdefs "github.com/containerd/errdefs"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
type nsFallbackStore struct {
|
||||
main content.Store
|
||||
fb content.Store
|
||||
}
|
||||
|
||||
var _ content.Store = &nsFallbackStore{}
|
||||
|
||||
func (c *nsFallbackStore) Info(ctx context.Context, dgst digest.Digest) (content.Info, error) {
|
||||
info, err := c.main.Info(ctx, dgst)
|
||||
if err != nil {
|
||||
if cerrdefs.IsNotFound(err) {
|
||||
return c.fb.Info(ctx, dgst)
|
||||
}
|
||||
}
|
||||
return info, err
|
||||
}
|
||||
|
||||
func (c *nsFallbackStore) Update(ctx context.Context, info content.Info, fieldpaths ...string) (content.Info, error) {
|
||||
return c.main.Update(ctx, info, fieldpaths...)
|
||||
}
|
||||
|
||||
func (c *nsFallbackStore) Walk(ctx context.Context, fn content.WalkFunc, filters ...string) error {
|
||||
seen := make(map[digest.Digest]struct{})
|
||||
err := c.main.Walk(ctx, func(i content.Info) error {
|
||||
seen[i.Digest] = struct{}{}
|
||||
return fn(i)
|
||||
}, filters...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.fb.Walk(ctx, func(i content.Info) error {
|
||||
if _, ok := seen[i.Digest]; ok {
|
||||
return nil
|
||||
}
|
||||
return fn(i)
|
||||
}, filters...)
|
||||
}
|
||||
|
||||
func (c *nsFallbackStore) Delete(ctx context.Context, dgst digest.Digest) error {
|
||||
return c.main.Delete(ctx, dgst)
|
||||
}
|
||||
|
||||
func (c *nsFallbackStore) Status(ctx context.Context, ref string) (content.Status, error) {
|
||||
return c.main.Status(ctx, ref)
|
||||
}
|
||||
|
||||
func (c *nsFallbackStore) ListStatuses(ctx context.Context, filters ...string) ([]content.Status, error) {
|
||||
return c.main.ListStatuses(ctx, filters...)
|
||||
}
|
||||
|
||||
func (c *nsFallbackStore) Abort(ctx context.Context, ref string) error {
|
||||
return c.main.Abort(ctx, ref)
|
||||
}
|
||||
|
||||
func (c *nsFallbackStore) ReaderAt(ctx context.Context, desc ocispecs.Descriptor) (content.ReaderAt, error) {
|
||||
ra, err := c.main.ReaderAt(ctx, desc)
|
||||
if err != nil {
|
||||
if cerrdefs.IsNotFound(err) {
|
||||
return c.fb.ReaderAt(ctx, desc)
|
||||
}
|
||||
}
|
||||
return ra, err
|
||||
}
|
||||
|
||||
func (c *nsFallbackStore) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) {
|
||||
return c.main.Writer(ctx, opts...)
|
||||
}
|
282
util/desktop/bundle/export.go
Normal file
282
util/desktop/bundle/export.go
Normal file
@@ -0,0 +1,282 @@
|
||||
package bundle
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/content"
|
||||
"github.com/containerd/containerd/v2/core/content/proxy"
|
||||
imgarchive "github.com/containerd/containerd/v2/core/images/archive"
|
||||
"github.com/docker/buildx/localstate"
|
||||
controlapi "github.com/moby/buildkit/api/services/control"
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/moby/buildkit/util/contentutil"
|
||||
"github.com/opencontainers/go-digest"
|
||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
HistoryRecordMediaTypeV0 = "application/vnd.buildkit.historyrecord.v0"
|
||||
RefDescriptorMediaType = "vnd.export-build.descriptor.mediatype"
|
||||
)
|
||||
|
||||
type Record struct {
|
||||
*controlapi.BuildHistoryRecord
|
||||
|
||||
DefaultPlatform string
|
||||
LocalState *localstate.State `json:"localState,omitempty"`
|
||||
StateGroup *localstate.StateGroup `json:"stateGroup,omitempty"`
|
||||
}
|
||||
|
||||
func Export(ctx context.Context, c []*client.Client, w io.Writer, records []*Record) error {
|
||||
var store content.Store
|
||||
for _, c := range c {
|
||||
s := proxy.NewContentStore(c.ContentClient())
|
||||
if store == nil {
|
||||
store = s
|
||||
break
|
||||
}
|
||||
store = &nsFallbackStore{
|
||||
main: store,
|
||||
fb: s,
|
||||
}
|
||||
}
|
||||
if store == nil {
|
||||
return errors.New("no buildkit client found")
|
||||
}
|
||||
|
||||
mp := contentutil.NewMultiProvider(store)
|
||||
|
||||
desc, err := export(ctx, mp, records)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to export")
|
||||
}
|
||||
|
||||
gz := gzip.NewWriter(w)
|
||||
defer gz.Close()
|
||||
|
||||
if err := imgarchive.Export(ctx, mp, gz, imgarchive.WithManifest(desc), imgarchive.WithSkipDockerManifest()); err != nil {
|
||||
return errors.Wrap(err, "failed to create dockerbuild archive")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func export(ctx context.Context, mp *contentutil.MultiProvider, records []*Record) (ocispecs.Descriptor, error) {
|
||||
if len(records) == 1 {
|
||||
desc, err := exportRecord(ctx, mp, records[0])
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to export record")
|
||||
}
|
||||
return desc, nil
|
||||
}
|
||||
|
||||
var idx ocispecs.Index
|
||||
idx.MediaType = ocispecs.MediaTypeImageIndex
|
||||
idx.SchemaVersion = 2
|
||||
|
||||
for _, r := range records {
|
||||
desc, err := exportRecord(ctx, mp, r)
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to export record")
|
||||
}
|
||||
if desc.Annotations == nil {
|
||||
desc.Annotations = make(map[string]string)
|
||||
}
|
||||
desc.Annotations["vnd.buildkit.history.reference"] = r.Ref
|
||||
idx.Manifests = append(idx.Manifests, desc)
|
||||
}
|
||||
|
||||
desc, err := writeJSON(ctx, mp, idx.MediaType, idx)
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to write index")
|
||||
}
|
||||
|
||||
return desc, nil
|
||||
}
|
||||
|
||||
func writeJSON(ctx context.Context, mp *contentutil.MultiProvider, mt string, data any) (ocispecs.Descriptor, error) {
|
||||
dt, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to marshal data")
|
||||
}
|
||||
|
||||
desc := ocispecs.Descriptor{
|
||||
MediaType: mt,
|
||||
Size: int64(len(dt)),
|
||||
Digest: digest.FromBytes(dt),
|
||||
}
|
||||
|
||||
buf := contentutil.NewBuffer()
|
||||
if err := content.WriteBlob(ctx, buf, "blob-"+desc.Digest.String(), bytes.NewReader(dt), desc); err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to write blob")
|
||||
}
|
||||
|
||||
mp.Add(desc.Digest, buf)
|
||||
return desc, nil
|
||||
}
|
||||
|
||||
func sanitizeCacheImports(v string) (string, error) {
|
||||
type cacheImport struct {
|
||||
Type string `json:"Type"`
|
||||
Attrs map[string]string `json:"Attrs"`
|
||||
}
|
||||
var arr []cacheImport
|
||||
if err := json.Unmarshal([]byte(v), &arr); err != nil {
|
||||
return "", errors.Wrap(err, "failed to unmarshal cache imports")
|
||||
}
|
||||
for i := range arr {
|
||||
m := map[string]string{}
|
||||
for k, v := range arr[i].Attrs {
|
||||
if k == "scope" || k == "ref" {
|
||||
m[k] = v
|
||||
}
|
||||
}
|
||||
arr[i].Attrs = m
|
||||
}
|
||||
dt, err := json.Marshal(arr)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to marshal cache imports")
|
||||
}
|
||||
return string(dt), nil
|
||||
}
|
||||
|
||||
func sanitizeRecord(rec *controlapi.BuildHistoryRecord) {
|
||||
for k, v := range rec.FrontendAttrs {
|
||||
if k == "cache-imports" {
|
||||
v, err := sanitizeCacheImports(v)
|
||||
if err != nil {
|
||||
rec.FrontendAttrs[k] = ""
|
||||
} else {
|
||||
rec.FrontendAttrs[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func exportRecord(ctx context.Context, mp *contentutil.MultiProvider, record *Record) (ocispecs.Descriptor, error) {
|
||||
var mfst ocispecs.Manifest
|
||||
mfst.MediaType = ocispecs.MediaTypeImageManifest
|
||||
mfst.SchemaVersion = 2
|
||||
|
||||
sanitizeRecord(record.BuildHistoryRecord)
|
||||
|
||||
visited := map[string]struct{}{}
|
||||
|
||||
if trace := record.Trace; trace != nil {
|
||||
desc, err := loadDescriptor(ctx, mp, trace, visited)
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to load trace descriptor")
|
||||
}
|
||||
desc, err = sanitizeTrace(ctx, mp, *desc)
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to sanitize trace")
|
||||
}
|
||||
record.Trace.Digest = desc.Digest.String()
|
||||
record.Trace.Size = desc.Size
|
||||
mfst.Layers = append(mfst.Layers, *desc)
|
||||
}
|
||||
|
||||
config, err := writeJSON(ctx, mp, HistoryRecordMediaTypeV0, record)
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to write history record")
|
||||
}
|
||||
|
||||
mfst.Config = config
|
||||
|
||||
if logs := record.Logs; logs != nil {
|
||||
desc, err := loadDescriptor(ctx, mp, logs, visited)
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to load logs descriptor")
|
||||
}
|
||||
mfst.Layers = append(mfst.Layers, *desc)
|
||||
}
|
||||
|
||||
if res := record.Result; res != nil {
|
||||
results, err := loadResult(ctx, mp, res, visited)
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to load result")
|
||||
}
|
||||
mfst.Layers = append(mfst.Layers, results...)
|
||||
}
|
||||
|
||||
if exterr := record.ExternalError; exterr != nil {
|
||||
desc, err := loadDescriptor(ctx, mp, exterr, visited)
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to load external error descriptor")
|
||||
}
|
||||
mfst.Layers = append(mfst.Layers, *desc)
|
||||
}
|
||||
|
||||
for _, res := range record.Results {
|
||||
results, err := loadResult(ctx, mp, res, visited)
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to load result")
|
||||
}
|
||||
mfst.Layers = append(mfst.Layers, results...)
|
||||
}
|
||||
|
||||
desc, err := writeJSON(ctx, mp, mfst.MediaType, mfst)
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to write manifest")
|
||||
}
|
||||
|
||||
return desc, nil
|
||||
}
|
||||
|
||||
func loadResult(ctx context.Context, ip content.InfoProvider, in *controlapi.BuildResultInfo, visited map[string]struct{}) ([]ocispecs.Descriptor, error) {
|
||||
var out []ocispecs.Descriptor
|
||||
for _, attest := range in.Attestations {
|
||||
desc, err := loadDescriptor(ctx, ip, attest, visited)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to load attestation descriptor")
|
||||
}
|
||||
if desc != nil {
|
||||
out = append(out, *desc)
|
||||
}
|
||||
}
|
||||
for _, r := range in.Results {
|
||||
desc, err := loadDescriptor(ctx, ip, r, visited)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to load result descriptor")
|
||||
}
|
||||
if desc != nil {
|
||||
if desc.Annotations == nil {
|
||||
desc.Annotations = make(map[string]string)
|
||||
}
|
||||
// Override media type to avoid containerd to walk children. Also
|
||||
// keep original media type in annotations.
|
||||
desc.Annotations[RefDescriptorMediaType] = desc.MediaType
|
||||
desc.MediaType = "application/json"
|
||||
out = append(out, *desc)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func loadDescriptor(ctx context.Context, ip content.InfoProvider, in *controlapi.Descriptor, visited map[string]struct{}) (*ocispecs.Descriptor, error) {
|
||||
if _, ok := visited[in.Digest]; ok {
|
||||
return nil, nil
|
||||
}
|
||||
visited[in.Digest] = struct{}{}
|
||||
|
||||
dgst, err := digest.Parse(in.Digest)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse digest")
|
||||
}
|
||||
|
||||
if _, err := ip.Info(ctx, dgst); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get info")
|
||||
}
|
||||
|
||||
return &ocispecs.Descriptor{
|
||||
MediaType: in.MediaType,
|
||||
Digest: dgst,
|
||||
Size: in.Size,
|
||||
Annotations: in.Annotations,
|
||||
}, nil
|
||||
}
|
84
util/desktop/bundle/trace.go
Normal file
84
util/desktop/bundle/trace.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package bundle
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/content"
|
||||
"github.com/docker/buildx/util/otelutil"
|
||||
"github.com/moby/buildkit/util/contentutil"
|
||||
"github.com/opencontainers/go-digest"
|
||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
)
|
||||
|
||||
var (
|
||||
reOnce sync.Once
|
||||
reAttrs, reGhs, reGhpat *regexp.Regexp
|
||||
)
|
||||
|
||||
func sanitizeCommand(value string) string {
|
||||
reOnce.Do(func() {
|
||||
sensitiveKeys := []string{"ghtoken", "token", "access_key_id", "secret_access_key", "session_token"}
|
||||
reAttrs = regexp.MustCompile(`(?i)(` + strings.Join(sensitiveKeys, "|") + `)=[^ ,]+`)
|
||||
reGhs = regexp.MustCompile(`(?:ghu|ghs)_[A-Za-z0-9]{36}`)
|
||||
reGhpat = regexp.MustCompile(`github_pat_\w{82}`)
|
||||
})
|
||||
|
||||
value = reAttrs.ReplaceAllString(value, "${1}=xxxxx")
|
||||
// reGhs/reGhpat is just double proofing. Not really needed.
|
||||
value = reGhs.ReplaceAllString(value, "xxxxx")
|
||||
value = reGhpat.ReplaceAllString(value, "xxxxx")
|
||||
return value
|
||||
}
|
||||
|
||||
func sanitizeTrace(ctx context.Context, mp *contentutil.MultiProvider, desc ocispecs.Descriptor) (*ocispecs.Descriptor, error) {
|
||||
ra, err := mp.ReaderAt(ctx, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer ra.Close()
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
dec := json.NewDecoder(io.NewSectionReader(ra, 0, ra.Size()))
|
||||
enc := json.NewEncoder(buf)
|
||||
enc.SetIndent("", " ")
|
||||
for {
|
||||
var obj otelutil.Span
|
||||
if err := dec.Decode(&obj); err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, att := range obj.Attributes {
|
||||
v := att.Value
|
||||
if v.Type() == attribute.STRING {
|
||||
obj.Attributes[i].Value = attribute.StringValue(sanitizeCommand(v.AsString()))
|
||||
}
|
||||
}
|
||||
|
||||
if err := enc.Encode(obj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
buffer := contentutil.NewBuffer()
|
||||
newDesc := ocispecs.Descriptor{
|
||||
MediaType: desc.MediaType,
|
||||
Size: int64(buf.Len()),
|
||||
Digest: digest.FromBytes(buf.Bytes()),
|
||||
}
|
||||
if err := content.WriteBlob(ctx, buffer, "trace-sanitized", bytes.NewReader(buf.Bytes()), newDesc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mp.Add(newDesc.Digest, buffer)
|
||||
|
||||
return &newDesc, nil
|
||||
}
|
@@ -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
|
||||
|
45
util/gitutil/credentials_test.go
Normal file
45
util/gitutil/credentials_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package gitutil
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestStripCredentials(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
url string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "non-blank Password",
|
||||
url: "https://user:password@host.tld/this:that",
|
||||
want: "https://host.tld/this:that",
|
||||
},
|
||||
{
|
||||
name: "blank Password",
|
||||
url: "https://user@host.tld/this:that",
|
||||
want: "https://host.tld/this:that",
|
||||
},
|
||||
{
|
||||
name: "blank Username",
|
||||
url: "https://:password@host.tld/this:that",
|
||||
want: "https://host.tld/this:that",
|
||||
},
|
||||
{
|
||||
name: "blank Username, blank Password",
|
||||
url: "https://host.tld/this:that",
|
||||
want: "https://host.tld/this:that",
|
||||
},
|
||||
{
|
||||
name: "invalid URL",
|
||||
url: "1https://foo.com",
|
||||
want: "1https://foo.com",
|
||||
},
|
||||
}
|
||||
for _, tt := range cases {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if g, w := stripCredentials(tt.url), tt.want; g != w {
|
||||
t.Fatalf("got: %q\nwant: %q", g, w)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@@ -1,14 +1,15 @@
|
||||
package gitutil
|
||||
package gittestutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/buildx/util/gitutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func GitInit(c *Git, tb testing.TB) {
|
||||
func GitInit(c *gitutil.Git, tb testing.TB) {
|
||||
tb.Helper()
|
||||
out, err := fakeGit(c, "init")
|
||||
require.NoError(tb, err)
|
||||
@@ -18,41 +19,41 @@ func GitInit(c *Git, tb testing.TB) {
|
||||
_, _ = fakeGit(c, "branch", "-D", "master")
|
||||
}
|
||||
|
||||
func GitCommit(c *Git, tb testing.TB, msg string) {
|
||||
func GitCommit(c *gitutil.Git, tb testing.TB, msg string) {
|
||||
tb.Helper()
|
||||
out, err := fakeGit(c, "commit", "--allow-empty", "-m", msg)
|
||||
require.NoError(tb, err)
|
||||
require.Contains(tb, out, "main", msg)
|
||||
}
|
||||
|
||||
func GitTag(c *Git, tb testing.TB, tag string) {
|
||||
func GitTag(c *gitutil.Git, tb testing.TB, tag string) {
|
||||
tb.Helper()
|
||||
out, err := fakeGit(c, "tag", tag)
|
||||
require.NoError(tb, err)
|
||||
require.Empty(tb, out)
|
||||
}
|
||||
|
||||
func GitCheckoutBranch(c *Git, tb testing.TB, name string) {
|
||||
func GitCheckoutBranch(c *gitutil.Git, tb testing.TB, name string) {
|
||||
tb.Helper()
|
||||
out, err := fakeGit(c, "checkout", "-b", name)
|
||||
require.NoError(tb, err)
|
||||
require.Empty(tb, out)
|
||||
}
|
||||
|
||||
func GitAdd(c *Git, tb testing.TB, files ...string) {
|
||||
func GitAdd(c *gitutil.Git, tb testing.TB, files ...string) {
|
||||
tb.Helper()
|
||||
args := append([]string{"add"}, files...)
|
||||
_, err := fakeGit(c, args...)
|
||||
require.NoError(tb, err)
|
||||
}
|
||||
|
||||
func GitSetRemote(c *Git, tb testing.TB, name string, url string) {
|
||||
func GitSetRemote(c *gitutil.Git, tb testing.TB, name string, url string) {
|
||||
tb.Helper()
|
||||
_, err := fakeGit(c, "remote", "add", name, url)
|
||||
require.NoError(tb, err)
|
||||
}
|
||||
|
||||
func GitSetMainUpstream(c *Git, tb testing.TB, remote, target string) {
|
||||
func GitSetMainUpstream(c *gitutil.Git, tb testing.TB, remote, target string) {
|
||||
tb.Helper()
|
||||
_, err := fakeGit(c, "fetch", "--depth", "1", remote, target)
|
||||
require.NoError(tb, err)
|
||||
@@ -73,7 +74,7 @@ func Mktmp(tb testing.TB) string {
|
||||
return folder
|
||||
}
|
||||
|
||||
func fakeGit(c *Git, args ...string) (string, error) {
|
||||
func fakeGit(c *gitutil.Git, args ...string) (string, error) {
|
||||
allArgs := []string{
|
||||
"-c", "user.name=buildx",
|
||||
"-c", "user.email=buildx@docker.com",
|
||||
@@ -82,7 +83,7 @@ func fakeGit(c *Git, args ...string) (string, error) {
|
||||
"-c", "log.showSignature=false",
|
||||
}
|
||||
allArgs = append(allArgs, args...)
|
||||
return c.clean(c.run(allArgs...))
|
||||
return c.Run(allArgs...)
|
||||
}
|
||||
|
||||
func IsAmbiguousArgument(err error) bool {
|
@@ -1,4 +1,4 @@
|
||||
package gitutil
|
||||
package gittestutil
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/buildx/util/gitutil"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -23,7 +24,7 @@ func WithAccessToken(token string) GitServeOpt {
|
||||
}
|
||||
}
|
||||
|
||||
func GitServeHTTP(c *Git, t testing.TB, opts ...GitServeOpt) (url string) {
|
||||
func GitServeHTTP(c *gitutil.Git, t testing.TB, opts ...GitServeOpt) (url string) {
|
||||
t.Helper()
|
||||
gitUpdateServerInfo(c, t)
|
||||
ctx, cancel := context.WithCancelCause(context.TODO())
|
||||
@@ -91,7 +92,7 @@ func GitServeHTTP(c *Git, t testing.TB, opts ...GitServeOpt) (url string) {
|
||||
return fmt.Sprintf("http://%s/%s", addr, name)
|
||||
}
|
||||
|
||||
func gitUpdateServerInfo(c *Git, tb testing.TB) {
|
||||
func gitUpdateServerInfo(c *gitutil.Git, tb testing.TB) {
|
||||
tb.Helper()
|
||||
_, err := fakeGit(c, "update-server-info")
|
||||
require.NoError(tb, err)
|
@@ -57,17 +57,17 @@ func New(opts ...Option) (*Git, error) {
|
||||
}
|
||||
|
||||
func (c *Git) IsInsideWorkTree() bool {
|
||||
out, err := c.clean(c.run("rev-parse", "--is-inside-work-tree"))
|
||||
out, err := c.Run("rev-parse", "--is-inside-work-tree")
|
||||
return out == "true" && err == nil
|
||||
}
|
||||
|
||||
func (c *Git) IsDirty() bool {
|
||||
out, err := c.run("status", "--porcelain", "--ignored")
|
||||
out, err := c.Run("status", "--porcelain", "--ignored")
|
||||
return strings.TrimSpace(out) != "" || err != nil
|
||||
}
|
||||
|
||||
func (c *Git) RootDir() (string, error) {
|
||||
root, err := c.clean(c.run("rev-parse", "--show-toplevel"))
|
||||
root, err := c.Run("rev-parse", "--show-toplevel")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -127,6 +127,10 @@ func (c *Git) Tag() (string, error) {
|
||||
return tag, err
|
||||
}
|
||||
|
||||
func (c *Git) Run(args ...string) (string, error) {
|
||||
return c.clean(c.run(args...))
|
||||
}
|
||||
|
||||
func (c *Git) run(args ...string) (string, error) {
|
||||
var extraArgs = []string{
|
||||
"-c", "log.showSignature=false",
|
||||
@@ -161,7 +165,7 @@ func (c *Git) clean(out string, err error) (string, error) {
|
||||
}
|
||||
|
||||
func (c *Git) currentRemote() (string, error) {
|
||||
symref, err := c.clean(c.run("symbolic-ref", "-q", "HEAD"))
|
||||
symref, err := c.Run("symbolic-ref", "-q", "HEAD")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -169,7 +173,7 @@ func (c *Git) currentRemote() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
// git for-each-ref --format='%(upstream:remotename)'
|
||||
remote, err := c.clean(c.run("for-each-ref", "--format=%(upstream:remotename)", symref))
|
||||
remote, err := c.Run("for-each-ref", "--format=%(upstream:remotename)", symref)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@@ -1,32 +1,34 @@
|
||||
package gitutil
|
||||
package gitutil_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/buildx/util/gitutil"
|
||||
"github.com/docker/buildx/util/gitutil/gittestutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGit(t *testing.T) {
|
||||
c, err := New()
|
||||
c, err := gitutil.New()
|
||||
require.NoError(t, err)
|
||||
|
||||
out, err := c.run("status")
|
||||
out, err := c.Run("status")
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, out)
|
||||
|
||||
out, err = c.clean(c.run("not-exist"))
|
||||
out, err = c.Run("not-exist")
|
||||
require.Error(t, err)
|
||||
require.Empty(t, out)
|
||||
require.Equal(t, "git: 'not-exist' is not a git command. See 'git --help'.", err.Error())
|
||||
}
|
||||
|
||||
func TestGitFullCommit(t *testing.T) {
|
||||
Mktmp(t)
|
||||
c, err := New()
|
||||
gittestutil.Mktmp(t)
|
||||
c, err := gitutil.New()
|
||||
require.NoError(t, err)
|
||||
|
||||
GitInit(c, t)
|
||||
GitCommit(c, t, "bar")
|
||||
gittestutil.GitInit(c, t)
|
||||
gittestutil.GitCommit(c, t, "bar")
|
||||
|
||||
out, err := c.FullCommit()
|
||||
require.NoError(t, err)
|
||||
@@ -34,12 +36,12 @@ func TestGitFullCommit(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGitShortCommit(t *testing.T) {
|
||||
Mktmp(t)
|
||||
c, err := New()
|
||||
gittestutil.Mktmp(t)
|
||||
c, err := gitutil.New()
|
||||
require.NoError(t, err)
|
||||
|
||||
GitInit(c, t)
|
||||
GitCommit(c, t, "bar")
|
||||
gittestutil.GitInit(c, t)
|
||||
gittestutil.GitCommit(c, t, "bar")
|
||||
|
||||
out, err := c.ShortCommit()
|
||||
require.NoError(t, err)
|
||||
@@ -47,59 +49,59 @@ func TestGitShortCommit(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGitFullCommitErr(t *testing.T) {
|
||||
Mktmp(t)
|
||||
c, err := New()
|
||||
gittestutil.Mktmp(t)
|
||||
c, err := gitutil.New()
|
||||
require.NoError(t, err)
|
||||
|
||||
GitInit(c, t)
|
||||
gittestutil.GitInit(c, t)
|
||||
|
||||
_, err = c.FullCommit()
|
||||
require.Error(t, err)
|
||||
require.True(t, IsUnknownRevision(err))
|
||||
require.False(t, IsAmbiguousArgument(err))
|
||||
require.True(t, gitutil.IsUnknownRevision(err))
|
||||
require.False(t, gittestutil.IsAmbiguousArgument(err))
|
||||
}
|
||||
|
||||
func TestGitShortCommitErr(t *testing.T) {
|
||||
Mktmp(t)
|
||||
c, err := New()
|
||||
gittestutil.Mktmp(t)
|
||||
c, err := gitutil.New()
|
||||
require.NoError(t, err)
|
||||
|
||||
GitInit(c, t)
|
||||
gittestutil.GitInit(c, t)
|
||||
|
||||
_, err = c.ShortCommit()
|
||||
require.Error(t, err)
|
||||
require.True(t, IsUnknownRevision(err))
|
||||
require.False(t, IsAmbiguousArgument(err))
|
||||
require.True(t, gitutil.IsUnknownRevision(err))
|
||||
require.False(t, gittestutil.IsAmbiguousArgument(err))
|
||||
}
|
||||
|
||||
func TestGitTagsPointsAt(t *testing.T) {
|
||||
Mktmp(t)
|
||||
c, err := New()
|
||||
gittestutil.Mktmp(t)
|
||||
c, err := gitutil.New()
|
||||
require.NoError(t, err)
|
||||
|
||||
GitInit(c, t)
|
||||
GitCommit(c, t, "bar")
|
||||
GitTag(c, t, "v0.8.0")
|
||||
GitCommit(c, t, "foo")
|
||||
GitTag(c, t, "v0.9.0")
|
||||
gittestutil.GitInit(c, t)
|
||||
gittestutil.GitCommit(c, t, "bar")
|
||||
gittestutil.GitTag(c, t, "v0.8.0")
|
||||
gittestutil.GitCommit(c, t, "foo")
|
||||
gittestutil.GitTag(c, t, "v0.9.0")
|
||||
|
||||
out, err := c.clean(c.run("tag", "--points-at", "HEAD", "--sort", "-version:creatordate"))
|
||||
out, err := c.Run("tag", "--points-at", "HEAD", "--sort", "-version:creatordate")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "v0.9.0", out)
|
||||
}
|
||||
|
||||
func TestGitDescribeTags(t *testing.T) {
|
||||
Mktmp(t)
|
||||
c, err := New()
|
||||
gittestutil.Mktmp(t)
|
||||
c, err := gitutil.New()
|
||||
require.NoError(t, err)
|
||||
|
||||
GitInit(c, t)
|
||||
GitCommit(c, t, "bar")
|
||||
GitTag(c, t, "v0.8.0")
|
||||
GitCommit(c, t, "foo")
|
||||
GitTag(c, t, "v0.9.0")
|
||||
gittestutil.GitInit(c, t)
|
||||
gittestutil.GitCommit(c, t, "bar")
|
||||
gittestutil.GitTag(c, t, "v0.8.0")
|
||||
gittestutil.GitCommit(c, t, "foo")
|
||||
gittestutil.GitTag(c, t, "v0.9.0")
|
||||
|
||||
out, err := c.clean(c.run("describe", "--tags", "--abbrev=0"))
|
||||
out, err := c.Run("describe", "--tags", "--abbrev=0")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "v0.9.0", out)
|
||||
}
|
||||
@@ -200,16 +202,16 @@ func TestGitRemoteURL(t *testing.T) {
|
||||
for _, tt := range cases {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
Mktmp(t)
|
||||
c, err := New()
|
||||
gittestutil.Mktmp(t)
|
||||
c, err := gitutil.New()
|
||||
require.NoError(t, err)
|
||||
|
||||
GitInit(c, t)
|
||||
GitCommit(c, t, "initial commit")
|
||||
gittestutil.GitInit(c, t)
|
||||
gittestutil.GitCommit(c, t, "initial commit")
|
||||
for _, r := range tt.remotes {
|
||||
GitSetRemote(c, t, r.name, r.url)
|
||||
gittestutil.GitSetRemote(c, t, r.name, r.url)
|
||||
if r.tracking != "" {
|
||||
GitSetMainUpstream(c, t, r.name, r.tracking)
|
||||
gittestutil.GitSetMainUpstream(c, t, r.name, r.tracking)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,45 +225,3 @@ func TestGitRemoteURL(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripCredentials(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
url string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "non-blank Password",
|
||||
url: "https://user:password@host.tld/this:that",
|
||||
want: "https://host.tld/this:that",
|
||||
},
|
||||
{
|
||||
name: "blank Password",
|
||||
url: "https://user@host.tld/this:that",
|
||||
want: "https://host.tld/this:that",
|
||||
},
|
||||
{
|
||||
name: "blank Username",
|
||||
url: "https://:password@host.tld/this:that",
|
||||
want: "https://host.tld/this:that",
|
||||
},
|
||||
{
|
||||
name: "blank Username, blank Password",
|
||||
url: "https://host.tld/this:that",
|
||||
want: "https://host.tld/this:that",
|
||||
},
|
||||
{
|
||||
name: "invalid URL",
|
||||
url: "1https://foo.com",
|
||||
want: "1https://foo.com",
|
||||
},
|
||||
}
|
||||
for _, tt := range cases {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if g, w := stripCredentials(tt.url), tt.want; g != w {
|
||||
t.Fatalf("got: %q\nwant: %q", g, w)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -107,9 +107,7 @@ func (r *Resolver) Combine(ctx context.Context, srcs []*Source, ann map[exptypes
|
||||
if old.Annotations == nil {
|
||||
old.Annotations = map[string]string{}
|
||||
}
|
||||
for k, v := range d.Annotations {
|
||||
old.Annotations[k] = v
|
||||
}
|
||||
maps.Copy(old.Annotations, d.Annotations)
|
||||
newDescs[idx] = old
|
||||
} else {
|
||||
m[d.Digest] = len(newDescs)
|
||||
|
@@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"maps"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -126,13 +127,9 @@ func (l *loader) Load(ctx context.Context, ref string) (*result, error) {
|
||||
}
|
||||
|
||||
var a asset
|
||||
annotations := make(map[string]string, len(mfst.manifest.Annotations)+len(mfst.desc.Annotations))
|
||||
for k, v := range mfst.desc.Annotations {
|
||||
annotations[k] = v
|
||||
}
|
||||
for k, v := range mfst.manifest.Annotations {
|
||||
annotations[k] = v
|
||||
}
|
||||
annotations := map[string]string{}
|
||||
maps.Copy(annotations, mfst.desc.Annotations)
|
||||
maps.Copy(annotations, mfst.manifest.Annotations)
|
||||
|
||||
if err := l.scanConfig(ctx, fetcher, mfst.manifest.Config, &a); err != nil {
|
||||
return nil, err
|
||||
|
@@ -189,7 +189,7 @@ func TestMuxIO(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type instruction func(m *MuxIO) (intput string, writeBackView string)
|
||||
type instruction func(m *MuxIO) (input string, writeBackView string)
|
||||
|
||||
func input(s string) instruction {
|
||||
return func(m *MuxIO) (string, string) {
|
||||
|
@@ -13,7 +13,7 @@ function "add" {
|
||||
|
||||
function "list" {
|
||||
params = []
|
||||
variadic_param = items
|
||||
variadic_params = items
|
||||
result = items
|
||||
}
|
||||
```
|
||||
|
@@ -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
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user