mirror of https://github.com/docker/buildx.git
Compare commits
95 Commits
v0.24.0-rc
...
master
Author | SHA1 | Date |
---|---|---|
|
0bed0b5653 | |
|
b034cff8c2 | |
|
fdb0ebc6cb | |
|
25a9ad6abd | |
|
a11757121a | |
|
7a05ca4547 | |
|
63bb3db985 | |
|
fba5d5e554 | |
|
179aad79b5 | |
|
d44ffb4bd4 | |
|
4c1e7b2119 | |
|
2d3a9ef229 | |
|
ec45eb6ebc | |
|
e9b6a01aef | |
|
c48ccdee36 | |
|
22f776f664 | |
|
8da4f0fe64 | |
|
2588b66fd9 | |
|
931e714919 | |
|
d5f914a263 | |
|
d09eb752a5 | |
|
3c2decea38 | |
|
18041a5855 | |
|
96ebe9d9a9 | |
|
08dd378b59 | |
|
cb29cd0efb | |
|
99f1c4b15c | |
|
77e4a88781 | |
|
7660acf9c7 | |
|
03737f11bc | |
|
4a22b92775 | |
|
ba782f195b | |
|
989978a42b | |
|
cb54ddb9fe | |
|
eb43f4c237 | |
|
43e2f27cac | |
|
7f5ff6b797 | |
|
32e9bfcba8 | |
|
e1adeee898 | |
|
1e969978aa | |
|
640541cefa | |
|
b514ed45fb | |
|
1b4bd20e6f | |
|
da426ecd3a | |
|
10618d4c73 | |
|
52b5d0862f | |
|
d1e22e5fc3 | |
|
38cf84346c | |
|
34e59ca1bd | |
|
2706e2f429 | |
|
02ab492cac | |
|
b8d8c7b1a6 | |
|
dc6ec35e1d | |
|
3f49ee5a90 | |
|
c45185fde0 | |
|
1d7cda1232 | |
|
fb916a960c | |
|
60b1eda2df | |
|
8f2604b6b4 | |
|
bb5b5e37e8 | |
|
21ebf82c99 | |
|
d61853bbb3 | |
|
65e46cc6af | |
|
6a0f5610e3 | |
|
e78aa98c92 | |
|
e6ff731323 | |
|
9bd1ba2f5c | |
|
f90170965a | |
|
b3e37e899f | |
|
a04b7d8689 | |
|
52bf4bf7ce | |
|
13031cc2ca | |
|
46fae59e2e | |
|
b40b2caf1a | |
|
1436f93aa1 | |
|
99d82e6cea | |
|
bc620fcc71 | |
|
e3c6618db2 | |
|
542bda49f2 | |
|
781a3f117a | |
|
614cc880dd | |
|
dfad6e0b1f | |
|
776dbd4086 | |
|
75f1d5e26b | |
|
291c353575 | |
|
a11bb4985c | |
|
cfeca919a9 | |
|
3c0f5c5c21 | |
|
ea2b7020a4 | |
|
5ba7d7eb4f | |
|
95ac2b4d09 | |
|
934cca3ab1 | |
|
6e562e9ede | |
|
51b8646c44 | |
|
c1209acb27 |
|
@ -48,11 +48,6 @@ area/cli:
|
|||
- cmd/**
|
||||
- commands/**
|
||||
|
||||
# Add 'area/controller' label to changes in the controller
|
||||
area/controller:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: 'controller/**'
|
||||
|
||||
# Add 'area/docs' label to markdown files in the docs folder
|
||||
area/docs:
|
||||
- changed-files:
|
||||
|
|
|
@ -54,9 +54,9 @@ jobs:
|
|||
- master
|
||||
- latest
|
||||
- buildx-stable-1
|
||||
- v0.23.1
|
||||
- v0.22.0
|
||||
- v0.21.1
|
||||
- v0.20.2
|
||||
- v0.19.0
|
||||
worker:
|
||||
- docker-container
|
||||
- remote
|
||||
|
@ -535,7 +535,7 @@ jobs:
|
|||
-
|
||||
name: GitHub Release
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2
|
||||
uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8 # v2.3.2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
|
|
@ -5,13 +5,13 @@ ARG ALPINE_VERSION=3.21
|
|||
ARG XX_VERSION=1.6.1
|
||||
|
||||
# for testing
|
||||
ARG DOCKER_VERSION=28.1.1
|
||||
ARG DOCKER_VERSION=28.3.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=3.0.0
|
||||
ARG BUILDKIT_VERSION=v0.21.1
|
||||
ARG BUILDKIT_VERSION=v0.23.1
|
||||
ARG UNDOCK_VERSION=0.9.0
|
||||
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||
|
|
8
Makefile
8
Makefile
|
@ -8,7 +8,7 @@ endif
|
|||
|
||||
export BUILDX_CMD ?= docker buildx
|
||||
|
||||
BAKE_TARGETS := binaries binaries-cross lint lint-gopls validate-vendor validate-docs validate-authors validate-generated-files
|
||||
BAKE_TARGETS := binaries binaries-cross lint lint-gopls validate-vendor validate-docs validate-authors
|
||||
|
||||
.PHONY: all
|
||||
all: binaries
|
||||
|
@ -35,7 +35,7 @@ release:
|
|||
./hack/release
|
||||
|
||||
.PHONY: validate-all
|
||||
validate-all: lint test validate-vendor validate-docs validate-generated-files
|
||||
validate-all: lint test validate-vendor validate-docs
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
|
@ -68,7 +68,3 @@ authors:
|
|||
.PHONY: mod-outdated
|
||||
mod-outdated:
|
||||
$(BUILDX_CMD) bake mod-outdated
|
||||
|
||||
.PHONY: generated-files
|
||||
generated-files:
|
||||
$(BUILDX_CMD) bake update-generated-files
|
||||
|
|
|
@ -79,7 +79,6 @@ Area or component of the project affected. Please note that the table below may
|
|||
| `area/checks` | Any | `checks` |
|
||||
| `area/ci` | Any | Project CI |
|
||||
| `area/cli` | Any | `cli` |
|
||||
| `area/controller` | Any | `controller` |
|
||||
| `area/debug` | Any | `debug` |
|
||||
| `area/dependencies` | Any | Project dependencies |
|
||||
| `area/dockerfile` | Any | `dockerfile` |
|
||||
|
|
56
bake/bake.go
56
bake/bake.go
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"os"
|
||||
|
@ -19,7 +20,6 @@ import (
|
|||
composecli "github.com/compose-spec/compose-go/v2/cli"
|
||||
"github.com/docker/buildx/bake/hclparser"
|
||||
"github.com/docker/buildx/build"
|
||||
controllerapi "github.com/docker/buildx/controller/pb"
|
||||
"github.com/docker/buildx/util/buildflags"
|
||||
"github.com/docker/buildx/util/platformutil"
|
||||
"github.com/docker/buildx/util/progress"
|
||||
|
@ -727,6 +727,7 @@ type Target struct {
|
|||
Ulimits []string `json:"ulimits,omitempty" hcl:"ulimits,optional" cty:"ulimits"`
|
||||
Call *string `json:"call,omitempty" hcl:"call,optional" cty:"call"`
|
||||
Entitlements []string `json:"entitlements,omitempty" hcl:"entitlements,optional" cty:"entitlements"`
|
||||
ExtraHosts map[string]*string `json:"extra-hosts,omitempty" hcl:"extra-hosts,optional" cty:"extra-hosts"`
|
||||
// IMPORTANT: if you add more fields here, do not forget to update newOverrides/AddOverrides and docs/bake-reference.md.
|
||||
|
||||
// linked is a private field to mark a target used as a linked one
|
||||
|
@ -765,6 +766,14 @@ func (t *Target) MarshalJSON() ([]byte, error) {
|
|||
}
|
||||
}
|
||||
|
||||
tgt.ExtraHosts = maps.Clone(t.ExtraHosts)
|
||||
for k, v := range t.ExtraHosts {
|
||||
if v != nil {
|
||||
escaped := esc(*v)
|
||||
tgt.ExtraHosts[k] = &escaped
|
||||
}
|
||||
}
|
||||
|
||||
return json.Marshal(tgt)
|
||||
}
|
||||
|
||||
|
@ -895,6 +904,15 @@ func (t *Target) Merge(t2 *Target) {
|
|||
if t2.Entitlements != nil { // merge
|
||||
t.Entitlements = append(t.Entitlements, t2.Entitlements...)
|
||||
}
|
||||
for k, v := range t2.ExtraHosts {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
if t.ExtraHosts == nil {
|
||||
t.ExtraHosts = map[string]*string{}
|
||||
}
|
||||
t.ExtraHosts[k] = v
|
||||
}
|
||||
t.Inherits = append(t.Inherits, t2.Inherits...)
|
||||
}
|
||||
|
||||
|
@ -1083,6 +1101,14 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
|
|||
return errors.Errorf("invalid value %s for boolean key load", value)
|
||||
}
|
||||
t.Outputs = setLoadOverride(t.Outputs, load)
|
||||
case "extra-hosts":
|
||||
if len(keys) != 2 {
|
||||
return errors.Errorf("invalid format for extra-hosts, expecting extra-hosts.<hostname>=<ip>")
|
||||
}
|
||||
if t.ExtraHosts == nil {
|
||||
t.ExtraHosts = map[string]*string{}
|
||||
}
|
||||
t.ExtraHosts[keys[1]] = &value
|
||||
default:
|
||||
return errors.Errorf("unknown key: %s", keys[0])
|
||||
}
|
||||
|
@ -1405,6 +1431,14 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
|
|||
}
|
||||
}
|
||||
|
||||
var extraHosts []string
|
||||
for k, v := range t.ExtraHosts {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
extraHosts = append(extraHosts, fmt.Sprintf("%s=%s", k, *v))
|
||||
}
|
||||
|
||||
bo := &build.Options{
|
||||
Inputs: bi,
|
||||
Tags: t.Tags,
|
||||
|
@ -1416,6 +1450,7 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
|
|||
NetworkMode: networkMode,
|
||||
Linked: t.linked,
|
||||
ShmSize: *shmSize,
|
||||
ExtraHosts: extraHosts,
|
||||
}
|
||||
|
||||
platforms, err := platformutil.Parse(t.Platforms)
|
||||
|
@ -1439,20 +1474,19 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
|
|||
})
|
||||
}
|
||||
}
|
||||
secrets = secrets.Normalize()
|
||||
bo.SecretSpecs = secrets.ToPB()
|
||||
secretAttachment, err := controllerapi.CreateSecrets(bo.SecretSpecs)
|
||||
bo.SecretSpecs = secrets.Normalize()
|
||||
secretAttachment, err := build.CreateSecrets(bo.SecretSpecs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bo.Session = append(bo.Session, secretAttachment)
|
||||
|
||||
bo.SSHSpecs = t.SSH.ToPB()
|
||||
bo.SSHSpecs = t.SSH
|
||||
if len(bo.SSHSpecs) == 0 && buildflags.IsGitSSH(bi.ContextPath) || (inp != nil && buildflags.IsGitSSH(inp.URL)) {
|
||||
bo.SSHSpecs = []*controllerapi.SSH{{ID: "default"}}
|
||||
bo.SSHSpecs = []*buildflags.SSH{{ID: "default"}}
|
||||
}
|
||||
|
||||
sshAttachment, err := controllerapi.CreateSSH(bo.SSHSpecs)
|
||||
sshAttachment, err := build.CreateSSH(bo.SSHSpecs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1469,13 +1503,13 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
|
|||
}
|
||||
|
||||
if t.CacheFrom != nil {
|
||||
bo.CacheFrom = controllerapi.CreateCaches(t.CacheFrom.ToPB())
|
||||
bo.CacheFrom = build.CreateCaches(t.CacheFrom)
|
||||
}
|
||||
if t.CacheTo != nil {
|
||||
bo.CacheTo = controllerapi.CreateCaches(t.CacheTo.ToPB())
|
||||
bo.CacheTo = build.CreateCaches(t.CacheTo)
|
||||
}
|
||||
|
||||
bo.Exports, bo.ExportsLocalPathsTemporary, err = controllerapi.CreateExports(t.Outputs.ToPB())
|
||||
bo.Exports, bo.ExportsLocalPathsTemporary, err = build.CreateExports(t.Outputs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1490,7 +1524,7 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
|
|||
}
|
||||
}
|
||||
|
||||
bo.Attests = controllerapi.CreateAttestations(t.Attest.ToPB())
|
||||
bo.Attests = t.Attest.ToMap()
|
||||
|
||||
bo.SourcePolicy, err = build.ReadSourcePolicy()
|
||||
if err != nil {
|
||||
|
|
|
@ -27,6 +27,9 @@ target "webDEP" {
|
|||
no-cache = true
|
||||
shm-size = "128m"
|
||||
ulimits = ["nofile=1024:1024"]
|
||||
extra-hosts = {
|
||||
my_hostname = "8.8.8.8"
|
||||
}
|
||||
}
|
||||
|
||||
target "webapp" {
|
||||
|
@ -64,6 +67,7 @@ target "webapp" {
|
|||
require.Equal(t, true, *m["webapp"].NoCache)
|
||||
require.Equal(t, "128m", *m["webapp"].ShmSize)
|
||||
require.Equal(t, []string{"nofile=1024:1024"}, m["webapp"].Ulimits)
|
||||
require.Equal(t, map[string]*string{"my_hostname": ptrstr("8.8.8.8")}, m["webapp"].ExtraHosts)
|
||||
require.Nil(t, m["webapp"].Pull)
|
||||
|
||||
require.Equal(t, 1, len(g))
|
||||
|
|
|
@ -122,6 +122,14 @@ func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Conf
|
|||
}
|
||||
}
|
||||
|
||||
extraHosts := map[string]*string{}
|
||||
if s.Build.ExtraHosts != nil {
|
||||
for k, v := range s.Build.ExtraHosts {
|
||||
vv := strings.Join(v, ",")
|
||||
extraHosts[k] = &vv
|
||||
}
|
||||
}
|
||||
|
||||
var ssh []*buildflags.SSH
|
||||
for _, bkey := range s.Build.SSH {
|
||||
sshkey := composeToBuildkitSSH(bkey)
|
||||
|
@ -180,6 +188,7 @@ func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Conf
|
|||
Secrets: secrets,
|
||||
ShmSize: shmSize,
|
||||
Ulimits: ulimits,
|
||||
ExtraHosts: extraHosts,
|
||||
}
|
||||
if err = t.composeExtTarget(s.Build.Extensions); err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -32,6 +32,10 @@ services:
|
|||
- type=local,src=path/to/cache
|
||||
cache_to:
|
||||
- type=local,dest=path/to/cache
|
||||
extra_hosts:
|
||||
- "somehost:162.242.195.82"
|
||||
- "somehost:162.242.195.83"
|
||||
- "myhostv6:::1"
|
||||
ssh:
|
||||
- key=/path/to/key
|
||||
- default
|
||||
|
@ -76,6 +80,7 @@ secrets:
|
|||
require.Equal(t, ptrstr("123"), c.Targets[1].Args["buildno"])
|
||||
require.Equal(t, []string{"type=local,src=path/to/cache"}, stringify(c.Targets[1].CacheFrom))
|
||||
require.Equal(t, []string{"type=local,dest=path/to/cache"}, stringify(c.Targets[1].CacheTo))
|
||||
require.Equal(t, map[string]*string{"myhostv6": ptrstr("::1"), "somehost": ptrstr("162.242.195.82,162.242.195.83")}, c.Targets[1].ExtraHosts)
|
||||
require.Equal(t, "none", *c.Targets[1].NetworkMode)
|
||||
require.Equal(t, []string{"default", "key=/path/to/key"}, stringify(c.Targets[1].SSH))
|
||||
require.Equal(t, []string{
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/docker/buildx/build"
|
||||
"github.com/docker/buildx/controller/pb"
|
||||
"github.com/docker/buildx/util/buildflags"
|
||||
"github.com/docker/buildx/util/osutil"
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
"github.com/moby/buildkit/util/entitlements"
|
||||
|
@ -264,7 +264,7 @@ func TestValidateEntitlements(t *testing.T) {
|
|||
{
|
||||
name: "SSHMissing",
|
||||
opt: build.Options{
|
||||
SSHSpecs: []*pb.SSH{
|
||||
SSHSpecs: []*buildflags.SSH{
|
||||
{
|
||||
ID: "test",
|
||||
},
|
||||
|
@ -296,7 +296,7 @@ func TestValidateEntitlements(t *testing.T) {
|
|||
{
|
||||
name: "SecretFromSubFile",
|
||||
opt: build.Options{
|
||||
SecretSpecs: []*pb.Secret{
|
||||
SecretSpecs: []*buildflags.Secret{
|
||||
{
|
||||
FilePath: filepath.Join(dir1, "subfile"),
|
||||
},
|
||||
|
@ -309,7 +309,7 @@ func TestValidateEntitlements(t *testing.T) {
|
|||
{
|
||||
name: "SecretFromEscapeLink",
|
||||
opt: build.Options{
|
||||
SecretSpecs: []*pb.Secret{
|
||||
SecretSpecs: []*buildflags.Secret{
|
||||
{
|
||||
FilePath: escapeLink,
|
||||
},
|
||||
|
@ -325,7 +325,7 @@ func TestValidateEntitlements(t *testing.T) {
|
|||
{
|
||||
name: "SecretFromEscapeLinkAllowRoot",
|
||||
opt: build.Options{
|
||||
SecretSpecs: []*pb.Secret{
|
||||
SecretSpecs: []*buildflags.Secret{
|
||||
{
|
||||
FilePath: escapeLink,
|
||||
},
|
||||
|
@ -352,7 +352,7 @@ func TestValidateEntitlements(t *testing.T) {
|
|||
{
|
||||
name: "SecretFromEscapeLinkAllowAny",
|
||||
opt: build.Options{
|
||||
SecretSpecs: []*pb.Secret{
|
||||
SecretSpecs: []*buildflags.Secret{
|
||||
{
|
||||
FilePath: escapeLink,
|
||||
},
|
||||
|
|
101
bake/hcl_test.go
101
bake/hcl_test.go
|
@ -423,6 +423,63 @@ func TestHCLNullVariables(t *testing.T) {
|
|||
require.Equal(t, ptrstr("bar"), c.Targets[0].Args["foo"])
|
||||
}
|
||||
|
||||
func TestHCLTypedNullVariables(t *testing.T) {
|
||||
types := []string{
|
||||
"any",
|
||||
"string", "number", "bool",
|
||||
"list(string)", "set(string)", "map(string)",
|
||||
"tuple([string])", "object({val: string})",
|
||||
}
|
||||
for _, varType := range types {
|
||||
tName := fmt.Sprintf("variable typed %q with null default remains null", varType)
|
||||
t.Run(tName, func(t *testing.T) {
|
||||
dt := fmt.Sprintf(`
|
||||
variable "FOO" {
|
||||
type = %s
|
||||
default = null
|
||||
}
|
||||
|
||||
target "default" {
|
||||
args = {
|
||||
foo = equal(FOO, null)
|
||||
}
|
||||
}`, varType)
|
||||
c, err := ParseFile([]byte(dt), "docker-bake.hcl")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, "true", *c.Targets[0].Args["foo"])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHCLTypedValuelessVariables(t *testing.T) {
|
||||
types := []string{
|
||||
"any",
|
||||
"string", "number", "bool",
|
||||
"list(string)", "set(string)", "map(string)",
|
||||
"tuple([string])", "object({val: string})",
|
||||
}
|
||||
for _, varType := range types {
|
||||
tName := fmt.Sprintf("variable typed %q with no default is null", varType)
|
||||
t.Run(tName, func(t *testing.T) {
|
||||
dt := fmt.Sprintf(`
|
||||
variable "FOO" {
|
||||
type = %s
|
||||
}
|
||||
|
||||
target "default" {
|
||||
args = {
|
||||
foo = equal(FOO, null)
|
||||
}
|
||||
}`, varType)
|
||||
c, err := ParseFile([]byte(dt), "docker-bake.hcl")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, "true", *c.Targets[0].Args["foo"])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONNullVariables(t *testing.T) {
|
||||
dt := []byte(`{
|
||||
"variable": {
|
||||
|
@ -1565,6 +1622,20 @@ target "two" {
|
|||
require.Equal(t, map[string]*string{"b": ptrstr("pre-jkl")}, c.Targets[1].Args)
|
||||
}
|
||||
|
||||
func TestEmptyVariable(t *testing.T) {
|
||||
dt := []byte(`
|
||||
variable "FOO" {}
|
||||
target "default" {
|
||||
args = {
|
||||
foo = equal(FOO, "")
|
||||
}
|
||||
}`)
|
||||
c, err := ParseFile(dt, "docker-bake.hcl")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, "true", *c.Targets[0].Args["foo"])
|
||||
}
|
||||
|
||||
func TestEmptyVariableJSON(t *testing.T) {
|
||||
dt := []byte(`{
|
||||
"variable": {
|
||||
|
@ -1877,19 +1948,6 @@ func TestTypedVarOverrides(t *testing.T) {
|
|||
override: `"hello"`,
|
||||
wantValue: `"hello"`,
|
||||
},
|
||||
{
|
||||
name: "any",
|
||||
varType: "any",
|
||||
override: "[1,2]",
|
||||
wantValue: "[1,2]",
|
||||
},
|
||||
{
|
||||
name: "any never convert to complex types",
|
||||
varType: "any",
|
||||
override: "[1,2]",
|
||||
argValue: "length(FOO)",
|
||||
wantErrorMsg: "collection must be a list",
|
||||
},
|
||||
{
|
||||
name: "proper CSV list of strings",
|
||||
varType: "list(string)",
|
||||
|
@ -2090,19 +2148,6 @@ func TestTypedVarOverrides_JSON(t *testing.T) {
|
|||
override: `"hello"`,
|
||||
wantValue: "hello",
|
||||
},
|
||||
{
|
||||
name: "any",
|
||||
varType: "any",
|
||||
override: "[1,2]",
|
||||
wantValue: "[1,2]",
|
||||
},
|
||||
{
|
||||
name: "any never convert to complex types",
|
||||
varType: "any",
|
||||
override: "[1,2]",
|
||||
argValue: "length(FOO)",
|
||||
wantErrorMsg: "collection must be a list",
|
||||
},
|
||||
{
|
||||
name: "list of strings",
|
||||
varType: "list(string)",
|
||||
|
@ -2313,6 +2358,7 @@ func TestJSONOverridePriority(t *testing.T) {
|
|||
dt := []byte(`
|
||||
variable "foo" {
|
||||
type = number
|
||||
default = 101
|
||||
}
|
||||
|
||||
target "default" {
|
||||
|
@ -2325,8 +2371,7 @@ func TestJSONOverridePriority(t *testing.T) {
|
|||
c, err := ParseFile(dt, "docker-bake.hcl")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
// a variable with no value has always resulted in an empty string
|
||||
require.Equal(t, "", *c.Targets[0].Args["bar"])
|
||||
require.Equal(t, "101", *c.Targets[0].Args["bar"])
|
||||
|
||||
t.Setenv("foo_JSON", "42")
|
||||
c, err = ParseFile(dt, "docker-bake.hcl")
|
||||
|
|
|
@ -38,6 +38,9 @@ type variable struct {
|
|||
Validations []*variableValidation `json:"validation,omitempty" hcl:"validation,block"`
|
||||
Body hcl.Body `json:"-" hcl:",body"`
|
||||
Remain hcl.Body `json:"-" hcl:",remain"`
|
||||
|
||||
// the type described by Type if it was specified
|
||||
constraint *cty.Type
|
||||
}
|
||||
|
||||
type variableValidation struct {
|
||||
|
@ -282,7 +285,7 @@ func (p *parser) resolveValue(ectx *hcl.EvalContext, name string) (err error) {
|
|||
}
|
||||
|
||||
var diags hcl.Diagnostics
|
||||
varType := cty.DynamicPseudoType
|
||||
varType, typeSpecified := cty.DynamicPseudoType, false
|
||||
def, ok := p.attrs[name]
|
||||
if !ok {
|
||||
vr, ok := p.vars[name]
|
||||
|
@ -295,12 +298,16 @@ func (p *parser) resolveValue(ectx *hcl.EvalContext, name string) (err error) {
|
|||
if diags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
typeSpecified = !varType.Equals(cty.DynamicPseudoType) || hcl.ExprAsKeyword(vr.Type) == "any"
|
||||
if typeSpecified {
|
||||
vr.constraint = &varType
|
||||
}
|
||||
}
|
||||
|
||||
if def == nil {
|
||||
// lack of specified value is considered to have an empty string value,
|
||||
// but any overrides get type checked
|
||||
if _, ok, _ := p.valueHasOverride(name, false); !ok {
|
||||
// Lack of specified value, when untyped is considered to have an empty string value.
|
||||
// A typed variable with no value will result in (typed) nil.
|
||||
if _, ok, _ := p.valueHasOverride(name, false); !ok && !typeSpecified {
|
||||
vv := cty.StringVal("")
|
||||
v = &vv
|
||||
return
|
||||
|
@ -322,9 +329,6 @@ func (p *parser) resolveValue(ectx *hcl.EvalContext, name string) (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Not entirely true... this doesn't differentiate between a user that specified 'any'
|
||||
// and a user that specified nothing. But the result is the same; both are treated as strings.
|
||||
typeSpecified := !varType.Equals(cty.DynamicPseudoType)
|
||||
envv, hasEnv, jsonEnv := p.valueHasOverride(name, typeSpecified)
|
||||
_, isVar := p.vars[name]
|
||||
|
||||
|
@ -676,6 +680,7 @@ func (p *parser) validateVariables(vars map[string]*variable, ectx *hcl.EvalCont
|
|||
type Variable struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Value *string `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
|
@ -805,13 +810,31 @@ func Parse(b hcl.Body, opt Opt, val any) (*ParseMeta, hcl.Diagnostics) {
|
|||
Name: p.vars[k].Name,
|
||||
Description: p.vars[k].Description,
|
||||
}
|
||||
tc := p.vars[k].constraint
|
||||
if tc != nil {
|
||||
v.Type = tc.FriendlyNameForConstraint()
|
||||
}
|
||||
if vv := p.ectx.Variables[k]; !vv.IsNull() {
|
||||
var s string
|
||||
switch vv.Type() {
|
||||
case cty.String:
|
||||
s = vv.AsString()
|
||||
case cty.Bool:
|
||||
s = strconv.FormatBool(vv.True())
|
||||
switch {
|
||||
case tc != nil:
|
||||
if bs, err := ctyjson.Marshal(vv, *tc); err == nil {
|
||||
s = string(bs)
|
||||
// untyped strings were always unquoted, so be consistent with typed strings as well
|
||||
if tc.Equals(cty.String) {
|
||||
s = strings.Trim(s, "\"")
|
||||
}
|
||||
}
|
||||
case vv.Type().IsPrimitiveType():
|
||||
// all primitives can convert to string, so error should never occur
|
||||
if val, err := convert.Convert(vv, cty.String); err == nil {
|
||||
s = val.AsString()
|
||||
}
|
||||
default:
|
||||
// must be an (inferred) tuple or object
|
||||
if bs, err := ctyjson.Marshal(vv, vv.Type()); err == nil {
|
||||
s = string(bs)
|
||||
}
|
||||
}
|
||||
v.Value = &s
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
// Forked from https://github.com/hashicorp/hcl/blob/4679383728fe331fc8a6b46036a27b8f818d9bc0/merged.go
|
||||
|
||||
package hclparser
|
||||
|
||||
import (
|
||||
|
|
|
@ -0,0 +1,687 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package hclparser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
)
|
||||
|
||||
func TestMergedBodiesContent(t *testing.T) {
|
||||
tests := []struct {
|
||||
Bodies []hcl.Body
|
||||
Schema *hcl.BodySchema
|
||||
Want *hcl.BodyContent
|
||||
DiagCount int
|
||||
}{
|
||||
{
|
||||
[]hcl.Body{},
|
||||
&hcl.BodySchema{},
|
||||
&hcl.BodyContent{
|
||||
Attributes: map[string]*hcl.Attribute{},
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
[]hcl.Body{},
|
||||
&hcl.BodySchema{
|
||||
Attributes: []hcl.AttributeSchema{
|
||||
{
|
||||
Name: "name",
|
||||
},
|
||||
},
|
||||
},
|
||||
&hcl.BodyContent{
|
||||
Attributes: map[string]*hcl.Attribute{},
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
[]hcl.Body{},
|
||||
&hcl.BodySchema{
|
||||
Attributes: []hcl.AttributeSchema{
|
||||
{
|
||||
Name: "name",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
&hcl.BodyContent{
|
||||
Attributes: map[string]*hcl.Attribute{},
|
||||
},
|
||||
1,
|
||||
},
|
||||
{
|
||||
[]hcl.Body{
|
||||
&testMergedBodiesVictim{
|
||||
HasAttributes: []string{"name"},
|
||||
},
|
||||
},
|
||||
&hcl.BodySchema{
|
||||
Attributes: []hcl.AttributeSchema{
|
||||
{
|
||||
Name: "name",
|
||||
},
|
||||
},
|
||||
},
|
||||
&hcl.BodyContent{
|
||||
Attributes: map[string]*hcl.Attribute{
|
||||
"name": {
|
||||
Name: "name",
|
||||
},
|
||||
},
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
[]hcl.Body{
|
||||
&testMergedBodiesVictim{
|
||||
Name: "first",
|
||||
HasAttributes: []string{"name"},
|
||||
},
|
||||
&testMergedBodiesVictim{
|
||||
Name: "second",
|
||||
HasAttributes: []string{"name"},
|
||||
},
|
||||
},
|
||||
&hcl.BodySchema{
|
||||
Attributes: []hcl.AttributeSchema{
|
||||
{
|
||||
Name: "name",
|
||||
},
|
||||
},
|
||||
},
|
||||
&hcl.BodyContent{
|
||||
Attributes: map[string]*hcl.Attribute{
|
||||
"name": {
|
||||
Name: "name",
|
||||
NameRange: hcl.Range{Filename: "second"},
|
||||
},
|
||||
},
|
||||
},
|
||||
1,
|
||||
},
|
||||
{
|
||||
[]hcl.Body{
|
||||
&testMergedBodiesVictim{
|
||||
Name: "first",
|
||||
HasAttributes: []string{"name"},
|
||||
},
|
||||
&testMergedBodiesVictim{
|
||||
Name: "second",
|
||||
HasAttributes: []string{"age"},
|
||||
},
|
||||
},
|
||||
&hcl.BodySchema{
|
||||
Attributes: []hcl.AttributeSchema{
|
||||
{
|
||||
Name: "name",
|
||||
},
|
||||
{
|
||||
Name: "age",
|
||||
},
|
||||
},
|
||||
},
|
||||
&hcl.BodyContent{
|
||||
Attributes: map[string]*hcl.Attribute{
|
||||
"name": {
|
||||
Name: "name",
|
||||
NameRange: hcl.Range{Filename: "first"},
|
||||
},
|
||||
"age": {
|
||||
Name: "age",
|
||||
NameRange: hcl.Range{Filename: "second"},
|
||||
},
|
||||
},
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
[]hcl.Body{},
|
||||
&hcl.BodySchema{
|
||||
Blocks: []hcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: "pizza",
|
||||
},
|
||||
},
|
||||
},
|
||||
&hcl.BodyContent{
|
||||
Attributes: map[string]*hcl.Attribute{},
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
[]hcl.Body{
|
||||
&testMergedBodiesVictim{
|
||||
HasBlocks: map[string]int{
|
||||
"pizza": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
&hcl.BodySchema{
|
||||
Blocks: []hcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: "pizza",
|
||||
},
|
||||
},
|
||||
},
|
||||
&hcl.BodyContent{
|
||||
Attributes: map[string]*hcl.Attribute{},
|
||||
Blocks: hcl.Blocks{
|
||||
{
|
||||
Type: "pizza",
|
||||
},
|
||||
},
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
[]hcl.Body{
|
||||
&testMergedBodiesVictim{
|
||||
HasBlocks: map[string]int{
|
||||
"pizza": 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
&hcl.BodySchema{
|
||||
Blocks: []hcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: "pizza",
|
||||
},
|
||||
},
|
||||
},
|
||||
&hcl.BodyContent{
|
||||
Attributes: map[string]*hcl.Attribute{},
|
||||
Blocks: hcl.Blocks{
|
||||
{
|
||||
Type: "pizza",
|
||||
},
|
||||
{
|
||||
Type: "pizza",
|
||||
},
|
||||
},
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
[]hcl.Body{
|
||||
&testMergedBodiesVictim{
|
||||
Name: "first",
|
||||
HasBlocks: map[string]int{
|
||||
"pizza": 1,
|
||||
},
|
||||
},
|
||||
&testMergedBodiesVictim{
|
||||
Name: "second",
|
||||
HasBlocks: map[string]int{
|
||||
"pizza": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
&hcl.BodySchema{
|
||||
Blocks: []hcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: "pizza",
|
||||
},
|
||||
},
|
||||
},
|
||||
&hcl.BodyContent{
|
||||
Attributes: map[string]*hcl.Attribute{},
|
||||
Blocks: hcl.Blocks{
|
||||
{
|
||||
Type: "pizza",
|
||||
DefRange: hcl.Range{Filename: "first"},
|
||||
},
|
||||
{
|
||||
Type: "pizza",
|
||||
DefRange: hcl.Range{Filename: "second"},
|
||||
},
|
||||
},
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
[]hcl.Body{
|
||||
&testMergedBodiesVictim{
|
||||
Name: "first",
|
||||
},
|
||||
&testMergedBodiesVictim{
|
||||
Name: "second",
|
||||
HasBlocks: map[string]int{
|
||||
"pizza": 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
&hcl.BodySchema{
|
||||
Blocks: []hcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: "pizza",
|
||||
},
|
||||
},
|
||||
},
|
||||
&hcl.BodyContent{
|
||||
Attributes: map[string]*hcl.Attribute{},
|
||||
Blocks: hcl.Blocks{
|
||||
{
|
||||
Type: "pizza",
|
||||
DefRange: hcl.Range{Filename: "second"},
|
||||
},
|
||||
{
|
||||
Type: "pizza",
|
||||
DefRange: hcl.Range{Filename: "second"},
|
||||
},
|
||||
},
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
[]hcl.Body{
|
||||
&testMergedBodiesVictim{
|
||||
Name: "first",
|
||||
HasBlocks: map[string]int{
|
||||
"pizza": 2,
|
||||
},
|
||||
},
|
||||
&testMergedBodiesVictim{
|
||||
Name: "second",
|
||||
},
|
||||
},
|
||||
&hcl.BodySchema{
|
||||
Blocks: []hcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: "pizza",
|
||||
},
|
||||
},
|
||||
},
|
||||
&hcl.BodyContent{
|
||||
Attributes: map[string]*hcl.Attribute{},
|
||||
Blocks: hcl.Blocks{
|
||||
{
|
||||
Type: "pizza",
|
||||
DefRange: hcl.Range{Filename: "first"},
|
||||
},
|
||||
{
|
||||
Type: "pizza",
|
||||
DefRange: hcl.Range{Filename: "first"},
|
||||
},
|
||||
},
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
[]hcl.Body{
|
||||
&testMergedBodiesVictim{
|
||||
Name: "first",
|
||||
},
|
||||
&testMergedBodiesVictim{
|
||||
Name: "second",
|
||||
},
|
||||
},
|
||||
&hcl.BodySchema{
|
||||
Blocks: []hcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: "pizza",
|
||||
},
|
||||
},
|
||||
},
|
||||
&hcl.BodyContent{
|
||||
Attributes: map[string]*hcl.Attribute{},
|
||||
},
|
||||
0,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) {
|
||||
merged := MergeBodies(test.Bodies)
|
||||
got, diags := merged.Content(test.Schema)
|
||||
|
||||
if len(diags) != test.DiagCount {
|
||||
t.Errorf("Wrong number of diagnostics %d; want %d", len(diags), test.DiagCount)
|
||||
for _, diag := range diags {
|
||||
t.Logf(" - %s", diag)
|
||||
}
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, test.Want) {
|
||||
t.Errorf("wrong result\ngot: %s\nwant: %s", spew.Sdump(got), spew.Sdump(test.Want))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeBodiesPartialContent(t *testing.T) {
|
||||
tests := []struct {
|
||||
Bodies []hcl.Body
|
||||
Schema *hcl.BodySchema
|
||||
WantContent *hcl.BodyContent
|
||||
WantRemain hcl.Body
|
||||
DiagCount int
|
||||
}{
|
||||
{
|
||||
[]hcl.Body{},
|
||||
&hcl.BodySchema{},
|
||||
&hcl.BodyContent{
|
||||
Attributes: map[string]*hcl.Attribute{},
|
||||
},
|
||||
mergedBodies{},
|
||||
0,
|
||||
},
|
||||
{
|
||||
[]hcl.Body{
|
||||
&testMergedBodiesVictim{
|
||||
Name: "first",
|
||||
HasAttributes: []string{"name", "age"},
|
||||
},
|
||||
},
|
||||
&hcl.BodySchema{
|
||||
Attributes: []hcl.AttributeSchema{
|
||||
{
|
||||
Name: "name",
|
||||
},
|
||||
},
|
||||
},
|
||||
&hcl.BodyContent{
|
||||
Attributes: map[string]*hcl.Attribute{
|
||||
"name": {
|
||||
Name: "name",
|
||||
NameRange: hcl.Range{Filename: "first"},
|
||||
},
|
||||
},
|
||||
},
|
||||
mergedBodies{
|
||||
&testMergedBodiesVictim{
|
||||
Name: "first",
|
||||
HasAttributes: []string{"age"},
|
||||
},
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
[]hcl.Body{
|
||||
&testMergedBodiesVictim{
|
||||
Name: "first",
|
||||
HasAttributes: []string{"name", "age"},
|
||||
},
|
||||
&testMergedBodiesVictim{
|
||||
Name: "second",
|
||||
HasAttributes: []string{"name", "pizza"},
|
||||
},
|
||||
},
|
||||
&hcl.BodySchema{
|
||||
Attributes: []hcl.AttributeSchema{
|
||||
{
|
||||
Name: "name",
|
||||
},
|
||||
},
|
||||
},
|
||||
&hcl.BodyContent{
|
||||
Attributes: map[string]*hcl.Attribute{
|
||||
"name": {
|
||||
Name: "name",
|
||||
NameRange: hcl.Range{Filename: "second"},
|
||||
},
|
||||
},
|
||||
},
|
||||
mergedBodies{
|
||||
&testMergedBodiesVictim{
|
||||
Name: "first",
|
||||
HasAttributes: []string{"age"},
|
||||
},
|
||||
&testMergedBodiesVictim{
|
||||
Name: "second",
|
||||
HasAttributes: []string{"pizza"},
|
||||
},
|
||||
},
|
||||
1,
|
||||
},
|
||||
{
|
||||
[]hcl.Body{
|
||||
&testMergedBodiesVictim{
|
||||
Name: "first",
|
||||
HasAttributes: []string{"name", "age"},
|
||||
},
|
||||
&testMergedBodiesVictim{
|
||||
Name: "second",
|
||||
HasAttributes: []string{"pizza", "soda"},
|
||||
},
|
||||
},
|
||||
&hcl.BodySchema{
|
||||
Attributes: []hcl.AttributeSchema{
|
||||
{
|
||||
Name: "name",
|
||||
},
|
||||
{
|
||||
Name: "soda",
|
||||
},
|
||||
},
|
||||
},
|
||||
&hcl.BodyContent{
|
||||
Attributes: map[string]*hcl.Attribute{
|
||||
"name": {
|
||||
Name: "name",
|
||||
NameRange: hcl.Range{Filename: "first"},
|
||||
},
|
||||
"soda": {
|
||||
Name: "soda",
|
||||
NameRange: hcl.Range{Filename: "second"},
|
||||
},
|
||||
},
|
||||
},
|
||||
mergedBodies{
|
||||
&testMergedBodiesVictim{
|
||||
Name: "first",
|
||||
HasAttributes: []string{"age"},
|
||||
},
|
||||
&testMergedBodiesVictim{
|
||||
Name: "second",
|
||||
HasAttributes: []string{"pizza"},
|
||||
},
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
[]hcl.Body{
|
||||
&testMergedBodiesVictim{
|
||||
Name: "first",
|
||||
HasBlocks: map[string]int{
|
||||
"pizza": 1,
|
||||
},
|
||||
},
|
||||
&testMergedBodiesVictim{
|
||||
Name: "second",
|
||||
HasBlocks: map[string]int{
|
||||
"pizza": 1,
|
||||
"soda": 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
&hcl.BodySchema{
|
||||
Blocks: []hcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: "pizza",
|
||||
},
|
||||
},
|
||||
},
|
||||
&hcl.BodyContent{
|
||||
Attributes: map[string]*hcl.Attribute{},
|
||||
Blocks: hcl.Blocks{
|
||||
{
|
||||
Type: "pizza",
|
||||
DefRange: hcl.Range{Filename: "first"},
|
||||
},
|
||||
{
|
||||
Type: "pizza",
|
||||
DefRange: hcl.Range{Filename: "second"},
|
||||
},
|
||||
},
|
||||
},
|
||||
mergedBodies{
|
||||
&testMergedBodiesVictim{
|
||||
Name: "first",
|
||||
HasAttributes: []string{},
|
||||
HasBlocks: map[string]int{},
|
||||
},
|
||||
&testMergedBodiesVictim{
|
||||
Name: "second",
|
||||
HasAttributes: []string{},
|
||||
HasBlocks: map[string]int{
|
||||
"soda": 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
0,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) {
|
||||
merged := MergeBodies(test.Bodies)
|
||||
got, gotRemain, diags := merged.PartialContent(test.Schema)
|
||||
|
||||
if len(diags) != test.DiagCount {
|
||||
t.Errorf("Wrong number of diagnostics %d; want %d", len(diags), test.DiagCount)
|
||||
for _, diag := range diags {
|
||||
t.Logf(" - %s", diag)
|
||||
}
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, test.WantContent) {
|
||||
t.Errorf("wrong content result\ngot: %s\nwant: %s", spew.Sdump(got), spew.Sdump(test.WantContent))
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(gotRemain, test.WantRemain) {
|
||||
t.Errorf("wrong remaining result\ngot: %s\nwant: %s", spew.Sdump(gotRemain), spew.Sdump(test.WantRemain))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type testMergedBodiesVictim struct {
|
||||
Name string
|
||||
HasAttributes []string
|
||||
HasBlocks map[string]int
|
||||
DiagCount int
|
||||
}
|
||||
|
||||
func (v *testMergedBodiesVictim) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) {
|
||||
c, _, d := v.PartialContent(schema)
|
||||
return c, d
|
||||
}
|
||||
|
||||
func (v *testMergedBodiesVictim) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
|
||||
remain := &testMergedBodiesVictim{
|
||||
Name: v.Name,
|
||||
HasAttributes: []string{},
|
||||
}
|
||||
|
||||
hasAttrs := map[string]struct{}{}
|
||||
for _, n := range v.HasAttributes {
|
||||
hasAttrs[n] = struct{}{}
|
||||
|
||||
var found bool
|
||||
for _, attrS := range schema.Attributes {
|
||||
if n == attrS.Name {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
remain.HasAttributes = append(remain.HasAttributes, n)
|
||||
}
|
||||
}
|
||||
|
||||
content := &hcl.BodyContent{
|
||||
Attributes: map[string]*hcl.Attribute{},
|
||||
}
|
||||
|
||||
rng := hcl.Range{
|
||||
Filename: v.Name,
|
||||
}
|
||||
|
||||
for _, attrS := range schema.Attributes {
|
||||
_, has := hasAttrs[attrS.Name]
|
||||
if has {
|
||||
content.Attributes[attrS.Name] = &hcl.Attribute{
|
||||
Name: attrS.Name,
|
||||
NameRange: rng,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if v.HasBlocks != nil {
|
||||
for _, blockS := range schema.Blocks {
|
||||
num := v.HasBlocks[blockS.Type]
|
||||
for range num {
|
||||
content.Blocks = append(content.Blocks, &hcl.Block{
|
||||
Type: blockS.Type,
|
||||
DefRange: rng,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
remain.HasBlocks = map[string]int{}
|
||||
for n := range v.HasBlocks {
|
||||
var found bool
|
||||
for _, blockS := range schema.Blocks {
|
||||
if blockS.Type == n {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
remain.HasBlocks[n] = v.HasBlocks[n]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
diags := make(hcl.Diagnostics, v.DiagCount)
|
||||
for i := range diags {
|
||||
diags[i] = &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Fake diagnostic %d", i),
|
||||
Detail: "For testing only.",
|
||||
Context: &rng,
|
||||
}
|
||||
}
|
||||
|
||||
return content, remain, diags
|
||||
}
|
||||
|
||||
func (v *testMergedBodiesVictim) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
|
||||
attrs := make(map[string]*hcl.Attribute)
|
||||
|
||||
rng := hcl.Range{
|
||||
Filename: v.Name,
|
||||
}
|
||||
|
||||
for _, name := range v.HasAttributes {
|
||||
attrs[name] = &hcl.Attribute{
|
||||
Name: name,
|
||||
NameRange: rng,
|
||||
}
|
||||
}
|
||||
|
||||
diags := make(hcl.Diagnostics, v.DiagCount)
|
||||
for i := range diags {
|
||||
diags[i] = &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Fake diagnostic %d", i),
|
||||
Detail: "For testing only.",
|
||||
Context: &rng,
|
||||
}
|
||||
}
|
||||
|
||||
return attrs, diags
|
||||
}
|
||||
|
||||
func (v *testMergedBodiesVictim) MissingItemRange() hcl.Range {
|
||||
return hcl.Range{
|
||||
Filename: v.Name,
|
||||
}
|
||||
}
|
|
@ -7,9 +7,10 @@ import (
|
|||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/buildx/build"
|
||||
"github.com/docker/buildx/builder"
|
||||
controllerapi "github.com/docker/buildx/controller/pb"
|
||||
"github.com/docker/buildx/driver"
|
||||
"github.com/docker/buildx/util/buildflags"
|
||||
"github.com/docker/buildx/util/progress"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/moby/buildkit/client"
|
||||
|
@ -33,27 +34,27 @@ func ReadRemoteFiles(ctx context.Context, nodes []builder.Node, url string, name
|
|||
|
||||
st, ok := dockerui.DetectGitContext(url, false)
|
||||
if ok {
|
||||
if ssh, err := controllerapi.CreateSSH([]*controllerapi.SSH{{
|
||||
if ssh, err := build.CreateSSH([]*buildflags.SSH{{
|
||||
ID: "default",
|
||||
Paths: strings.Split(os.Getenv("BUILDX_BAKE_GIT_SSH"), ","),
|
||||
}}); err == nil {
|
||||
sessions = append(sessions, ssh)
|
||||
}
|
||||
var gitAuthSecrets []*controllerapi.Secret
|
||||
var gitAuthSecrets []*buildflags.Secret
|
||||
if _, ok := os.LookupEnv("BUILDX_BAKE_GIT_AUTH_TOKEN"); ok {
|
||||
gitAuthSecrets = append(gitAuthSecrets, &controllerapi.Secret{
|
||||
gitAuthSecrets = append(gitAuthSecrets, &buildflags.Secret{
|
||||
ID: llb.GitAuthTokenKey,
|
||||
Env: "BUILDX_BAKE_GIT_AUTH_TOKEN",
|
||||
})
|
||||
}
|
||||
if _, ok := os.LookupEnv("BUILDX_BAKE_GIT_AUTH_HEADER"); ok {
|
||||
gitAuthSecrets = append(gitAuthSecrets, &controllerapi.Secret{
|
||||
gitAuthSecrets = append(gitAuthSecrets, &buildflags.Secret{
|
||||
ID: llb.GitAuthHeaderKey,
|
||||
Env: "BUILDX_BAKE_GIT_AUTH_HEADER",
|
||||
})
|
||||
}
|
||||
if len(gitAuthSecrets) > 0 {
|
||||
if secrets, err := controllerapi.CreateSecrets(gitAuthSecrets); err == nil {
|
||||
if secrets, err := build.CreateSecrets(gitAuthSecrets); err == nil {
|
||||
sessions = append(sessions, secrets)
|
||||
}
|
||||
}
|
||||
|
|
306
build/build.go
306
build/build.go
|
@ -19,8 +19,8 @@ import (
|
|||
"github.com/containerd/containerd/v2/core/images"
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/buildx/builder"
|
||||
controllerapi "github.com/docker/buildx/controller/pb"
|
||||
"github.com/docker/buildx/driver"
|
||||
"github.com/docker/buildx/util/buildflags"
|
||||
"github.com/docker/buildx/util/confutil"
|
||||
"github.com/docker/buildx/util/desktop"
|
||||
"github.com/docker/buildx/util/dockerutil"
|
||||
|
@ -59,6 +59,8 @@ const (
|
|||
printLintFallbackImage = "docker/dockerfile:1.8.1@sha256:e87caa74dcb7d46cd820352bfea12591f3dba3ddc4285e19c7dcd13359f7cefd"
|
||||
)
|
||||
|
||||
var ErrRestart = errors.New("build: restart")
|
||||
|
||||
type Options struct {
|
||||
Inputs Inputs
|
||||
|
||||
|
@ -78,8 +80,8 @@ type Options struct {
|
|||
NoCacheFilter []string
|
||||
Platforms []ocispecs.Platform
|
||||
Pull bool
|
||||
SecretSpecs []*controllerapi.Secret
|
||||
SSHSpecs []*controllerapi.SSH
|
||||
SecretSpecs buildflags.Secrets
|
||||
SSHSpecs []*buildflags.SSH
|
||||
ShmSize opts.MemBytes
|
||||
Tags []string
|
||||
Target string
|
||||
|
@ -138,75 +140,68 @@ func filterAvailableNodes(nodes []builder.Node) ([]builder.Node, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
func toRepoOnly(in string) (string, error) {
|
||||
m := map[string]struct{}{}
|
||||
p := strings.Split(in, ",")
|
||||
for _, pp := range p {
|
||||
n, err := reference.ParseNormalizedNamed(pp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
m[n.Name()] = struct{}{}
|
||||
}
|
||||
out := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
out = append(out, k)
|
||||
}
|
||||
return strings.Join(out, ","), nil
|
||||
}
|
||||
|
||||
func Build(ctx context.Context, nodes []builder.Node, opts map[string]Options, docker *dockerutil.Client, cfg *confutil.Config, w progress.Writer) (resp map[string]*client.SolveResponse, err error) {
|
||||
return BuildWithResultHandler(ctx, nodes, opts, docker, cfg, w, nil)
|
||||
}
|
||||
|
||||
func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[string]Options, docker *dockerutil.Client, cfg *confutil.Config, w progress.Writer, resultHandleFunc func(driverIndex int, rCtx *ResultHandle)) (resp map[string]*client.SolveResponse, err error) {
|
||||
if len(nodes) == 0 {
|
||||
return nil, errors.Errorf("driver required for build")
|
||||
}
|
||||
|
||||
nodes, err = filterAvailableNodes(nodes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "no valid drivers found")
|
||||
}
|
||||
|
||||
var noMobyDriver *driver.DriverHandle
|
||||
// findNonMobyDriver returns the first non-moby based driver.
|
||||
func findNonMobyDriver(nodes []builder.Node) *driver.DriverHandle {
|
||||
for _, n := range nodes {
|
||||
if !n.Driver.IsMobyDriver() {
|
||||
noMobyDriver = n.Driver
|
||||
break
|
||||
return n.Driver
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// warnOnNoOutput will check if the given nodes and options would result in an output
|
||||
// and prints a warning if it would not.
|
||||
func warnOnNoOutput(ctx context.Context, nodes []builder.Node, opts map[string]Options) {
|
||||
// Return immediately if default load is explicitly disabled or a call
|
||||
// function is used.
|
||||
if noDefaultLoad() || !noCallFunc(opts) {
|
||||
return
|
||||
}
|
||||
|
||||
// Find the first non-moby driver and return if it either doesn't exist
|
||||
// or if the driver has default load enabled.
|
||||
noMobyDriver := findNonMobyDriver(nodes)
|
||||
if noMobyDriver == nil || noMobyDriver.Features(ctx)[driver.DefaultLoad] {
|
||||
return
|
||||
}
|
||||
|
||||
// Produce a warning describing the targets affected.
|
||||
var noOutputTargets []string
|
||||
for name, opt := range opts {
|
||||
if !opt.Linked && len(opt.Exports) == 0 {
|
||||
noOutputTargets = append(noOutputTargets, name)
|
||||
}
|
||||
}
|
||||
|
||||
if noMobyDriver != nil && !noDefaultLoad() && noCallFunc(opts) {
|
||||
var noOutputTargets []string
|
||||
for name, opt := range opts {
|
||||
if noMobyDriver.Features(ctx)[driver.DefaultLoad] {
|
||||
continue
|
||||
}
|
||||
|
||||
if !opt.Linked && len(opt.Exports) == 0 {
|
||||
noOutputTargets = append(noOutputTargets, name)
|
||||
}
|
||||
}
|
||||
if len(noOutputTargets) > 0 {
|
||||
var warnNoOutputBuf bytes.Buffer
|
||||
warnNoOutputBuf.WriteString("No output specified ")
|
||||
if len(noOutputTargets) == 1 && noOutputTargets[0] == "default" {
|
||||
warnNoOutputBuf.WriteString(fmt.Sprintf("with %s driver", noMobyDriver.Factory().Name()))
|
||||
} else {
|
||||
warnNoOutputBuf.WriteString(fmt.Sprintf("for %s target(s) with %s driver", strings.Join(noOutputTargets, ", "), noMobyDriver.Factory().Name()))
|
||||
}
|
||||
logrus.Warnf("%s. Build result will only remain in the build cache. To push result image into registry use --push or to load image into docker use --load", warnNoOutputBuf.String())
|
||||
}
|
||||
if len(noOutputTargets) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
drivers, err := resolveDrivers(ctx, nodes, opts, w)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var warnNoOutputBuf bytes.Buffer
|
||||
warnNoOutputBuf.WriteString("No output specified ")
|
||||
if len(noOutputTargets) == 1 && noOutputTargets[0] == "default" {
|
||||
warnNoOutputBuf.WriteString(fmt.Sprintf("with %s driver", noMobyDriver.Factory().Name()))
|
||||
} else {
|
||||
warnNoOutputBuf.WriteString(fmt.Sprintf("for %s target(s) with %s driver", strings.Join(noOutputTargets, ", "), noMobyDriver.Factory().Name()))
|
||||
}
|
||||
logrus.Warnf("%s. Build result will only remain in the build cache. To push result image into registry use --push or to load image into docker use --load", warnNoOutputBuf.String())
|
||||
}
|
||||
|
||||
func newBuildRequests(ctx context.Context, docker *dockerutil.Client, cfg *confutil.Config, drivers map[string][]*resolvedNode, w progress.Writer, opts map[string]Options) (_ map[string][]*reqForNode, _ func(), retErr error) {
|
||||
reqForNodes := make(map[string][]*reqForNode)
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
var releasers []func()
|
||||
releaseAll := func() {
|
||||
for _, fn := range releasers {
|
||||
fn()
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
releaseAll()
|
||||
}
|
||||
}()
|
||||
|
||||
for k, opt := range opts {
|
||||
multiDriver := len(drivers[k]) > 1
|
||||
|
@ -226,17 +221,17 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
|
|||
opt.Platforms = np.platforms
|
||||
gatewayOpts, err := np.BuildOpts(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
localOpt := opt
|
||||
so, release, err := toSolveOpt(ctx, np.Node(), multiDriver, &localOpt, gatewayOpts, cfg, w, docker)
|
||||
opts[k] = localOpt
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
defer release()
|
||||
releasers = append(releasers, release)
|
||||
if err := saveLocalState(so, k, opt, np.Node(), cfg); err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
addGitAttrs(so)
|
||||
reqn = append(reqn, &reqForNode{
|
||||
|
@ -261,15 +256,17 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
|
|||
for _, e := range np.so.Exports {
|
||||
if e.Type == "moby" {
|
||||
if ok, _ := strconv.ParseBool(e.Attrs["push"]); ok {
|
||||
return nil, errors.Errorf("multi-node push can't currently be performed with the docker driver, please switch to a different driver")
|
||||
return nil, nil, errors.Errorf("multi-node push can't currently be performed with the docker driver, please switch to a different driver")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return reqForNodes, releaseAll, nil
|
||||
}
|
||||
|
||||
// validate that all links between targets use same drivers
|
||||
func validateTargetLinks(reqForNodes map[string][]*reqForNode, drivers map[string][]*resolvedNode, opts map[string]Options) error {
|
||||
for name := range opts {
|
||||
dps := reqForNodes[name]
|
||||
for i, dp := range dps {
|
||||
|
@ -279,8 +276,9 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
|
|||
k2 := strings.TrimPrefix(v, "target:")
|
||||
dps2, ok := drivers[k2]
|
||||
if !ok {
|
||||
return nil, errors.Errorf("failed to find target %s for context %s", k2, strings.TrimPrefix(k, "context:")) // should be validated before already
|
||||
return errors.Errorf("failed to find target %s for context %s", k2, strings.TrimPrefix(k, "context:")) // should be validated before already
|
||||
}
|
||||
|
||||
var found bool
|
||||
for _, dp2 := range dps2 {
|
||||
if dp2.driverIndex == dp.driverIndex {
|
||||
|
@ -289,12 +287,67 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
|
|||
}
|
||||
}
|
||||
if !found {
|
||||
return nil, errors.Errorf("failed to use %s as context %s for %s because targets build with different drivers", k2, strings.TrimPrefix(k, "context:"), name)
|
||||
return errors.Errorf("failed to use %s as context %s for %s because targets build with different drivers", k2, strings.TrimPrefix(k, "context:"), name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func toRepoOnly(in string) (string, error) {
|
||||
m := map[string]struct{}{}
|
||||
p := strings.Split(in, ",")
|
||||
for _, pp := range p {
|
||||
n, err := reference.ParseNormalizedNamed(pp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
m[n.Name()] = struct{}{}
|
||||
}
|
||||
out := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
out = append(out, k)
|
||||
}
|
||||
return strings.Join(out, ","), nil
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
Evaluate func(ctx context.Context, c gateway.Client, res *gateway.Result) error
|
||||
}
|
||||
|
||||
func Build(ctx context.Context, nodes []builder.Node, opts map[string]Options, docker *dockerutil.Client, cfg *confutil.Config, w progress.Writer) (resp map[string]*client.SolveResponse, err error) {
|
||||
return BuildWithResultHandler(ctx, nodes, opts, docker, cfg, w, nil)
|
||||
}
|
||||
|
||||
func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[string]Options, docker *dockerutil.Client, cfg *confutil.Config, w progress.Writer, bh *Handler) (resp map[string]*client.SolveResponse, err error) {
|
||||
if len(nodes) == 0 {
|
||||
return nil, errors.Errorf("driver required for build")
|
||||
}
|
||||
|
||||
nodes, err = filterAvailableNodes(nodes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "no valid drivers found")
|
||||
}
|
||||
warnOnNoOutput(ctx, nodes, opts)
|
||||
|
||||
drivers, err := resolveDrivers(ctx, nodes, opts, w)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
reqForNodes, release, err := newBuildRequests(ctx, docker, cfg, drivers, w, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer release()
|
||||
|
||||
// validate that all links between targets use same drivers
|
||||
if err := validateTargetLinks(reqForNodes, drivers, opts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sharedSessions, err := detectSharedMounts(ctx, reqForNodes)
|
||||
if err != nil {
|
||||
|
@ -311,7 +364,6 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
|
|||
|
||||
for k, opt := range opts {
|
||||
err := func(k string) (err error) {
|
||||
opt := opt
|
||||
dps := drivers[k]
|
||||
multiDriver := len(drivers[k]) > 1
|
||||
|
||||
|
@ -429,9 +481,14 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
|
|||
ch, done := progress.NewChannel(pw)
|
||||
defer func() { <-done }()
|
||||
|
||||
cc := c
|
||||
var callRes map[string][]byte
|
||||
buildFunc := func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
|
||||
var (
|
||||
callRes map[string][]byte
|
||||
frontendErr error
|
||||
)
|
||||
buildFunc := func(ctx context.Context, c gateway.Client) (_ *gateway.Result, retErr error) {
|
||||
// Capture the error from this build function.
|
||||
defer catchFrontendError(&retErr, &frontendErr)
|
||||
|
||||
if opt.CallFunc != nil {
|
||||
if _, ok := req.FrontendOpt["frontend.caps"]; !ok {
|
||||
req.FrontendOpt["frontend.caps"] = "moby.buildkit.frontend.subrequests+forward"
|
||||
|
@ -441,19 +498,11 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
|
|||
req.FrontendOpt["requestid"] = "frontend." + opt.CallFunc.Name
|
||||
}
|
||||
|
||||
res, err := c.Solve(ctx, req)
|
||||
res, err := solve(ctx, c, req)
|
||||
if err != nil {
|
||||
req, ok := fallbackPrintError(err, req)
|
||||
if ok {
|
||||
res2, err2 := c.Solve(ctx, req)
|
||||
if err2 != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = res2
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if opt.CallFunc != nil {
|
||||
callRes = res.Metadata
|
||||
}
|
||||
|
@ -461,41 +510,27 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
|
|||
rKey := resultKey(dp.driverIndex, k)
|
||||
results.Set(rKey, res)
|
||||
|
||||
if children, ok := childTargets[rKey]; ok && len(children) > 0 {
|
||||
// wait for the child targets to register their LLB before evaluating
|
||||
_, err := results.Get(ctx, children...)
|
||||
if err != nil {
|
||||
if children := childTargets[rKey]; len(children) > 0 {
|
||||
if err := waitForChildren(ctx, bh, c, res, results, children); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// we need to wait until the child targets have completed before we can release
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
eg.Go(func() error {
|
||||
return res.EachRef(func(ref gateway.Reference) error {
|
||||
return ref.Evaluate(ctx)
|
||||
})
|
||||
})
|
||||
eg.Go(func() error {
|
||||
_, err := results.Get(ctx, children...)
|
||||
return err
|
||||
})
|
||||
if err := eg.Wait(); err != nil {
|
||||
} else if bh != nil && bh.Evaluate != nil {
|
||||
if err := bh.Evaluate(ctx, c, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
buildRef := fmt.Sprintf("%s/%s/%s", node.Builder, node.Name, so.Ref)
|
||||
var rr *client.SolveResponse
|
||||
if resultHandleFunc != nil {
|
||||
var resultHandle *ResultHandle
|
||||
resultHandle, rr, err = NewResultHandle(ctx, cc, *so, "buildx", buildFunc, ch)
|
||||
resultHandleFunc(dp.driverIndex, resultHandle)
|
||||
} else {
|
||||
span, ctx := tracing.StartSpan(ctx, "build")
|
||||
rr, err = c.Build(ctx, *so, "buildx", buildFunc, ch)
|
||||
tracing.FinishWithError(span, err)
|
||||
|
||||
span, ctx := tracing.StartSpan(ctx, "build")
|
||||
rr, err := c.Build(ctx, *so, "buildx", buildFunc, ch)
|
||||
if errors.Is(frontendErr, ErrRestart) {
|
||||
err = ErrRestart
|
||||
}
|
||||
tracing.FinishWithError(span, err)
|
||||
|
||||
if !so.Internal && desktop.BuildBackendEnabled() && node.Driver.HistoryAPISupported(ctx) {
|
||||
if err != nil {
|
||||
return &desktop.ErrorWithBuildRef{
|
||||
|
@ -1146,3 +1181,52 @@ func ReadSourcePolicy() (*spb.Policy, error) {
|
|||
|
||||
return &pol, nil
|
||||
}
|
||||
|
||||
func solve(ctx context.Context, c gateway.Client, req gateway.SolveRequest) (*gateway.Result, error) {
|
||||
res, err := c.Solve(ctx, req)
|
||||
if err != nil {
|
||||
req, ok := fallbackPrintError(err, req)
|
||||
if ok {
|
||||
res2, err2 := c.Solve(ctx, req)
|
||||
if err2 != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = res2
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func waitForChildren(ctx context.Context, bh *Handler, c gateway.Client, res *gateway.Result, results *waitmap.Map, children []string) error {
|
||||
// wait for the child targets to register their LLB before evaluating
|
||||
_, err := results.Get(ctx, children...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// we need to wait until the child targets have completed before we can release
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
eg.Go(func() error {
|
||||
if bh != nil && bh.Evaluate != nil {
|
||||
return bh.Evaluate(ctx, c, res)
|
||||
}
|
||||
return res.EachRef(func(ref gateway.Reference) error {
|
||||
return ref.Evaluate(ctx)
|
||||
})
|
||||
})
|
||||
eg.Go(func() error {
|
||||
_, err := results.Get(ctx, children...)
|
||||
return err
|
||||
})
|
||||
return eg.Wait()
|
||||
}
|
||||
|
||||
func catchFrontendError(retErr, frontendErr *error) {
|
||||
*frontendErr = *retErr
|
||||
if errors.Is(*retErr, ErrRestart) {
|
||||
// Overwrite the sentinel error with a more user friendly message.
|
||||
// This gets stored only in the return error.
|
||||
*retErr = errors.New("build restarted by client")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,12 +8,41 @@ import (
|
|||
"sync/atomic"
|
||||
"syscall"
|
||||
|
||||
controllerapi "github.com/docker/buildx/controller/pb"
|
||||
gateway "github.com/moby/buildkit/frontend/gateway/client"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
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
|
||||
SuspendOn SuspendOn
|
||||
}
|
||||
|
||||
func (cfg *InvokeConfig) NeedsDebug(err error) bool {
|
||||
return cfg.SuspendOn.DebugEnabled(err)
|
||||
}
|
||||
|
||||
type SuspendOn int
|
||||
|
||||
const (
|
||||
SuspendError SuspendOn = iota
|
||||
SuspendAlways
|
||||
)
|
||||
|
||||
func (s SuspendOn) DebugEnabled(err error) bool {
|
||||
return err != nil || s == SuspendAlways
|
||||
}
|
||||
|
||||
type Container struct {
|
||||
cancelOnce sync.Once
|
||||
containerCancel func(error)
|
||||
|
@ -24,29 +53,21 @@ type Container struct {
|
|||
resultCtx *ResultHandle
|
||||
}
|
||||
|
||||
func NewContainer(ctx context.Context, resultCtx *ResultHandle, cfg *controllerapi.InvokeConfig) (*Container, error) {
|
||||
func NewContainer(ctx context.Context, resultCtx *ResultHandle, cfg *InvokeConfig) (*Container, error) {
|
||||
mainCtx := ctx
|
||||
|
||||
ctrCh := make(chan *Container)
|
||||
errCh := make(chan error)
|
||||
ctrCh := make(chan *Container, 1)
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
err := resultCtx.build(func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
|
||||
ctx, cancel := context.WithCancelCause(ctx)
|
||||
go func() {
|
||||
<-mainCtx.Done()
|
||||
cancel(errors.WithStack(context.Canceled))
|
||||
}()
|
||||
|
||||
containerCfg, err := resultCtx.getContainerConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err := func() error {
|
||||
containerCtx, containerCancel := context.WithCancelCause(ctx)
|
||||
defer containerCancel(errors.WithStack(context.Canceled))
|
||||
bkContainer, err := c.NewContainer(containerCtx, containerCfg)
|
||||
|
||||
bkContainer, err := resultCtx.NewContainer(containerCtx, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
releaseCh := make(chan struct{})
|
||||
container := &Container{
|
||||
containerCancel: containerCancel,
|
||||
|
@ -63,8 +84,8 @@ func NewContainer(ctx context.Context, resultCtx *ResultHandle, cfg *controllera
|
|||
ctrCh <- container
|
||||
<-container.releaseCh
|
||||
|
||||
return nil, bkContainer.Release(ctx)
|
||||
})
|
||||
return bkContainer.Release(ctx)
|
||||
}()
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
}
|
||||
|
@ -97,7 +118,7 @@ func (c *Container) markUnavailable() {
|
|||
c.isUnavailable.Store(true)
|
||||
}
|
||||
|
||||
func (c *Container) Exec(ctx context.Context, cfg *controllerapi.InvokeConfig, stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error {
|
||||
func (c *Container) Exec(ctx context.Context, cfg *InvokeConfig, stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error {
|
||||
if isInit := c.initStarted.CompareAndSwap(false, true); isInit {
|
||||
defer func() {
|
||||
// container can't be used after init exits
|
||||
|
@ -112,7 +133,7 @@ func (c *Container) Exec(ctx context.Context, cfg *controllerapi.InvokeConfig, s
|
|||
return err
|
||||
}
|
||||
|
||||
func exec(ctx context.Context, resultCtx *ResultHandle, cfg *controllerapi.InvokeConfig, ctr gateway.Container, stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error {
|
||||
func exec(ctx context.Context, resultCtx *ResultHandle, cfg *InvokeConfig, ctr gateway.Container, stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error {
|
||||
processCfg, err := resultCtx.getProcessConfig(cfg, stdin, stdout, stderr)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
225
build/opt.go
225
build/opt.go
|
@ -4,6 +4,7 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"maps"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
|
@ -11,12 +12,15 @@ import (
|
|||
"strings"
|
||||
"syscall"
|
||||
|
||||
awsconfig "github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/containerd/console"
|
||||
"github.com/containerd/containerd/v2/core/content"
|
||||
"github.com/containerd/containerd/v2/plugins/content/local"
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/buildx/builder"
|
||||
"github.com/docker/buildx/driver"
|
||||
"github.com/docker/buildx/util/buildflags"
|
||||
"github.com/docker/buildx/util/confutil"
|
||||
"github.com/docker/buildx/util/dockerutil"
|
||||
"github.com/docker/buildx/util/osutil"
|
||||
|
@ -26,6 +30,9 @@ import (
|
|||
"github.com/moby/buildkit/client/ociindex"
|
||||
gateway "github.com/moby/buildkit/frontend/gateway/client"
|
||||
"github.com/moby/buildkit/identity"
|
||||
"github.com/moby/buildkit/session"
|
||||
"github.com/moby/buildkit/session/secrets/secretsprovider"
|
||||
"github.com/moby/buildkit/session/sshforward/sshprovider"
|
||||
"github.com/moby/buildkit/session/upload/uploadprovider"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
"github.com/moby/buildkit/util/apicaps"
|
||||
|
@ -659,3 +666,221 @@ type fs struct {
|
|||
}
|
||||
|
||||
var _ fsutil.FS = &fs{}
|
||||
|
||||
func CreateSSH(ssh []*buildflags.SSH) (session.Attachable, error) {
|
||||
configs := make([]sshprovider.AgentConfig, 0, len(ssh))
|
||||
for _, ssh := range ssh {
|
||||
cfg := sshprovider.AgentConfig{
|
||||
ID: ssh.ID,
|
||||
Paths: slices.Clone(ssh.Paths),
|
||||
}
|
||||
configs = append(configs, cfg)
|
||||
}
|
||||
return sshprovider.NewSSHAgentProvider(configs)
|
||||
}
|
||||
|
||||
func CreateSecrets(secrets []*buildflags.Secret) (session.Attachable, error) {
|
||||
fs := make([]secretsprovider.Source, 0, len(secrets))
|
||||
for _, secret := range secrets {
|
||||
fs = append(fs, secretsprovider.Source{
|
||||
ID: secret.ID,
|
||||
FilePath: secret.FilePath,
|
||||
Env: secret.Env,
|
||||
})
|
||||
}
|
||||
store, err := secretsprovider.NewStore(fs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return secretsprovider.NewSecretProvider(store), nil
|
||||
}
|
||||
|
||||
func CreateExports(entries []*buildflags.ExportEntry) ([]client.ExportEntry, []string, error) {
|
||||
var outs []client.ExportEntry
|
||||
var localPaths []string
|
||||
if len(entries) == 0 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
var stdoutUsed bool
|
||||
for _, entry := range entries {
|
||||
if entry.Type == "" {
|
||||
return nil, nil, errors.Errorf("type is required for output")
|
||||
}
|
||||
|
||||
out := client.ExportEntry{
|
||||
Type: entry.Type,
|
||||
Attrs: map[string]string{},
|
||||
}
|
||||
maps.Copy(out.Attrs, entry.Attrs)
|
||||
|
||||
supportFile := false
|
||||
supportDir := false
|
||||
switch out.Type {
|
||||
case client.ExporterLocal:
|
||||
supportDir = true
|
||||
case client.ExporterTar:
|
||||
supportFile = true
|
||||
case client.ExporterOCI, client.ExporterDocker:
|
||||
tar, err := strconv.ParseBool(out.Attrs["tar"])
|
||||
if err != nil {
|
||||
tar = true
|
||||
}
|
||||
supportFile = tar
|
||||
supportDir = !tar
|
||||
case "registry":
|
||||
out.Type = client.ExporterImage
|
||||
out.Attrs["push"] = "true"
|
||||
}
|
||||
|
||||
if supportDir {
|
||||
if entry.Destination == "" {
|
||||
return nil, nil, errors.Errorf("dest is required for %s exporter", out.Type)
|
||||
}
|
||||
if entry.Destination == "-" {
|
||||
return nil, nil, errors.Errorf("dest cannot be stdout for %s exporter", out.Type)
|
||||
}
|
||||
|
||||
fi, err := os.Stat(entry.Destination)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, nil, errors.Wrapf(err, "invalid destination directory: %s", entry.Destination)
|
||||
}
|
||||
if err == nil && !fi.IsDir() {
|
||||
return nil, nil, errors.Errorf("destination directory %s is a file", entry.Destination)
|
||||
}
|
||||
out.OutputDir = entry.Destination
|
||||
localPaths = append(localPaths, entry.Destination)
|
||||
}
|
||||
if supportFile {
|
||||
if entry.Destination == "" && out.Type != client.ExporterDocker {
|
||||
entry.Destination = "-"
|
||||
}
|
||||
if entry.Destination == "-" {
|
||||
if stdoutUsed {
|
||||
return nil, nil, errors.Errorf("multiple outputs configured to write to stdout")
|
||||
}
|
||||
if _, err := console.ConsoleFromFile(os.Stdout); err == nil {
|
||||
return nil, nil, errors.Errorf("dest file is required for %s exporter. refusing to write to console", out.Type)
|
||||
}
|
||||
out.Output = wrapWriteCloser(os.Stdout)
|
||||
stdoutUsed = true
|
||||
} else if entry.Destination != "" {
|
||||
fi, err := os.Stat(entry.Destination)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, nil, errors.Wrapf(err, "invalid destination file: %s", entry.Destination)
|
||||
}
|
||||
if err == nil && fi.IsDir() {
|
||||
return nil, nil, errors.Errorf("destination file %s is a directory", entry.Destination)
|
||||
}
|
||||
f, err := os.Create(entry.Destination)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Errorf("failed to open %s", err)
|
||||
}
|
||||
out.Output = wrapWriteCloser(f)
|
||||
localPaths = append(localPaths, entry.Destination)
|
||||
}
|
||||
}
|
||||
|
||||
outs = append(outs, out)
|
||||
}
|
||||
return outs, localPaths, nil
|
||||
}
|
||||
|
||||
func wrapWriteCloser(wc io.WriteCloser) func(map[string]string) (io.WriteCloser, error) {
|
||||
return func(map[string]string) (io.WriteCloser, error) {
|
||||
return wc, nil
|
||||
}
|
||||
}
|
||||
|
||||
func CreateCaches(entries []*buildflags.CacheOptionsEntry) []client.CacheOptionsEntry {
|
||||
var outs []client.CacheOptionsEntry
|
||||
if len(entries) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
out := client.CacheOptionsEntry{
|
||||
Type: entry.Type,
|
||||
Attrs: map[string]string{},
|
||||
}
|
||||
maps.Copy(out.Attrs, entry.Attrs)
|
||||
addGithubToken(&out)
|
||||
addAwsCredentials(&out)
|
||||
if !isActive(&out) {
|
||||
continue
|
||||
}
|
||||
outs = append(outs, out)
|
||||
}
|
||||
return outs
|
||||
}
|
||||
|
||||
func addGithubToken(ci *client.CacheOptionsEntry) {
|
||||
if ci.Type != "gha" {
|
||||
return
|
||||
}
|
||||
version, ok := ci.Attrs["version"]
|
||||
if !ok {
|
||||
// https://github.com/actions/toolkit/blob/2b08dc18f261b9fdd978b70279b85cbef81af8bc/packages/cache/src/internal/config.ts#L19
|
||||
if v, ok := os.LookupEnv("ACTIONS_CACHE_SERVICE_V2"); ok {
|
||||
if b, err := strconv.ParseBool(v); err == nil && b {
|
||||
version = "2"
|
||||
}
|
||||
}
|
||||
}
|
||||
if _, ok := ci.Attrs["token"]; !ok {
|
||||
if v, ok := os.LookupEnv("ACTIONS_RUNTIME_TOKEN"); ok {
|
||||
ci.Attrs["token"] = v
|
||||
}
|
||||
}
|
||||
if _, ok := ci.Attrs["url_v2"]; !ok && version == "2" {
|
||||
// https://github.com/actions/toolkit/blob/2b08dc18f261b9fdd978b70279b85cbef81af8bc/packages/cache/src/internal/config.ts#L34-L35
|
||||
if v, ok := os.LookupEnv("ACTIONS_RESULTS_URL"); ok {
|
||||
ci.Attrs["url_v2"] = v
|
||||
}
|
||||
}
|
||||
if _, ok := ci.Attrs["url"]; !ok {
|
||||
// https://github.com/actions/toolkit/blob/2b08dc18f261b9fdd978b70279b85cbef81af8bc/packages/cache/src/internal/config.ts#L28-L33
|
||||
if v, ok := os.LookupEnv("ACTIONS_CACHE_URL"); ok {
|
||||
ci.Attrs["url"] = v
|
||||
} else if v, ok := os.LookupEnv("ACTIONS_RESULTS_URL"); ok {
|
||||
ci.Attrs["url"] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addAwsCredentials(ci *client.CacheOptionsEntry) {
|
||||
if ci.Type != "s3" {
|
||||
return
|
||||
}
|
||||
_, okAccessKeyID := ci.Attrs["access_key_id"]
|
||||
_, okSecretAccessKey := ci.Attrs["secret_access_key"]
|
||||
// If the user provides access_key_id, secret_access_key, do not override the session token.
|
||||
if okAccessKeyID && okSecretAccessKey {
|
||||
return
|
||||
}
|
||||
ctx := context.TODO()
|
||||
awsConfig, err := awsconfig.LoadDefaultConfig(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
credentials, err := awsConfig.Credentials.Retrieve(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !okAccessKeyID && credentials.AccessKeyID != "" {
|
||||
ci.Attrs["access_key_id"] = credentials.AccessKeyID
|
||||
}
|
||||
if !okSecretAccessKey && credentials.SecretAccessKey != "" {
|
||||
ci.Attrs["secret_access_key"] = credentials.SecretAccessKey
|
||||
}
|
||||
if _, ok := ci.Attrs["session_token"]; !ok && credentials.SessionToken != "" {
|
||||
ci.Attrs["session_token"] = credentials.SessionToken
|
||||
}
|
||||
}
|
||||
|
||||
func isActive(ce *client.CacheOptionsEntry) bool {
|
||||
// Always active if not gha.
|
||||
if ce.Type != "gha" {
|
||||
return true
|
||||
}
|
||||
return ce.Attrs["token"] != "" && (ce.Attrs["url"] != "" || ce.Attrs["url_v2"] != "")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
package build
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/buildx/util/buildflags"
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCacheOptions_DerivedVars(t *testing.T) {
|
||||
t.Setenv("ACTIONS_RUNTIME_TOKEN", "sensitive_token")
|
||||
t.Setenv("ACTIONS_CACHE_URL", "https://cache.github.com")
|
||||
t.Setenv("AWS_ACCESS_KEY_ID", "definitely_dont_look_here")
|
||||
t.Setenv("AWS_SECRET_ACCESS_KEY", "hackers_please_dont_steal")
|
||||
t.Setenv("AWS_SESSION_TOKEN", "not_a_mitm_attack")
|
||||
|
||||
cacheFrom, err := buildflags.ParseCacheEntry([]string{"type=gha", "type=s3,region=us-west-2,bucket=my_bucket,name=my_image"})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []client.CacheOptionsEntry{
|
||||
{
|
||||
Type: "gha",
|
||||
Attrs: map[string]string{
|
||||
"token": "sensitive_token",
|
||||
"url": "https://cache.github.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "s3",
|
||||
Attrs: map[string]string{
|
||||
"region": "us-west-2",
|
||||
"bucket": "my_bucket",
|
||||
"name": "my_image",
|
||||
"access_key_id": "definitely_dont_look_here",
|
||||
"secret_access_key": "hackers_please_dont_steal",
|
||||
"session_token": "not_a_mitm_attack",
|
||||
},
|
||||
},
|
||||
}, CreateCaches(cacheFrom))
|
||||
}
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/containerd/containerd/v2/core/content/proxy"
|
||||
"github.com/docker/buildx/util/confutil"
|
||||
"github.com/docker/buildx/util/progress"
|
||||
slsa1 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1"
|
||||
controlapi "github.com/moby/buildkit/api/services/control"
|
||||
"github.com/moby/buildkit/client"
|
||||
provenancetypes "github.com/moby/buildkit/solver/llbsolver/provenance/types"
|
||||
|
@ -22,15 +23,6 @@ import (
|
|||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type provenancePredicate struct {
|
||||
Builder *provenanceBuilder `json:"builder,omitempty"`
|
||||
provenancetypes.ProvenancePredicate
|
||||
}
|
||||
|
||||
type provenanceBuilder struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
func setRecordProvenance(ctx context.Context, c *client.Client, sr *client.SolveResponse, ref string, mode confutil.MetadataProvenanceMode, pw progress.Writer) error {
|
||||
if mode == confutil.MetadataProvenanceModeDisabled {
|
||||
return nil
|
||||
|
@ -69,7 +61,7 @@ func fetchProvenance(ctx context.Context, c *client.Client, ref string, mode con
|
|||
continue
|
||||
}
|
||||
if ev.Record.Result != nil {
|
||||
desc := lookupProvenance(ev.Record.Result)
|
||||
desc, predicateType := lookupProvenance(ev.Record.Result)
|
||||
if desc == nil {
|
||||
continue
|
||||
}
|
||||
|
@ -78,7 +70,7 @@ func fetchProvenance(ctx context.Context, c *client.Client, ref string, mode con
|
|||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to load provenance blob from build record")
|
||||
}
|
||||
prv, err := encodeProvenance(dt, mode)
|
||||
prv, err := encodeProvenance(dt, predicateType, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -92,7 +84,7 @@ func fetchProvenance(ctx context.Context, c *client.Client, ref string, mode con
|
|||
})
|
||||
} else if ev.Record.Results != nil {
|
||||
for platform, res := range ev.Record.Results {
|
||||
desc := lookupProvenance(res)
|
||||
desc, predicateType := lookupProvenance(res)
|
||||
if desc == nil {
|
||||
continue
|
||||
}
|
||||
|
@ -101,7 +93,7 @@ func fetchProvenance(ctx context.Context, c *client.Client, ref string, mode con
|
|||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to load provenance blob from build record")
|
||||
}
|
||||
prv, err := encodeProvenance(dt, mode)
|
||||
prv, err := encodeProvenance(dt, predicateType, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -119,7 +111,7 @@ func fetchProvenance(ctx context.Context, c *client.Client, ref string, mode con
|
|||
return out, eg.Wait()
|
||||
}
|
||||
|
||||
func lookupProvenance(res *controlapi.BuildResultInfo) *ocispecs.Descriptor {
|
||||
func lookupProvenance(res *controlapi.BuildResultInfo) (*ocispecs.Descriptor, string) {
|
||||
for _, a := range res.Attestations {
|
||||
if a.MediaType == "application/vnd.in-toto+json" && strings.HasPrefix(a.Annotations["in-toto.io/predicate-type"], "https://slsa.dev/provenance/") {
|
||||
return &ocispecs.Descriptor{
|
||||
|
@ -127,27 +119,29 @@ func lookupProvenance(res *controlapi.BuildResultInfo) *ocispecs.Descriptor {
|
|||
Size: a.Size,
|
||||
MediaType: a.MediaType,
|
||||
Annotations: a.Annotations,
|
||||
}
|
||||
}, a.Annotations["in-toto.io/predicate-type"]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
func encodeProvenance(dt []byte, mode confutil.MetadataProvenanceMode) (string, error) {
|
||||
var prv provenancePredicate
|
||||
if err := json.Unmarshal(dt, &prv); err != nil {
|
||||
func encodeProvenance(dt []byte, predicateType string, mode confutil.MetadataProvenanceMode) (string, error) {
|
||||
var pred *provenancetypes.ProvenancePredicateSLSA02
|
||||
if predicateType == slsa1.PredicateSLSAProvenance {
|
||||
var pred1 *provenancetypes.ProvenancePredicateSLSA1
|
||||
if err := json.Unmarshal(dt, &pred1); err != nil {
|
||||
return "", errors.Wrapf(err, "failed to unmarshal provenance")
|
||||
}
|
||||
pred = pred1.ConvertToSLSA02()
|
||||
} else if err := json.Unmarshal(dt, &pred); err != nil {
|
||||
return "", errors.Wrapf(err, "failed to unmarshal provenance")
|
||||
}
|
||||
if prv.Builder != nil && prv.Builder.ID == "" {
|
||||
// reset builder if id is empty
|
||||
prv.Builder = nil
|
||||
}
|
||||
if mode == confutil.MetadataProvenanceModeMin {
|
||||
// reset fields for minimal provenance
|
||||
prv.BuildConfig = nil
|
||||
prv.Metadata = nil
|
||||
pred.BuildConfig = nil
|
||||
pred.Metadata = nil
|
||||
}
|
||||
dtprv, err := json.Marshal(prv)
|
||||
dtprv, err := json.Marshal(pred)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to marshal provenance")
|
||||
}
|
||||
|
|
263
build/result.go
263
build/result.go
|
@ -7,260 +7,41 @@ import (
|
|||
"io"
|
||||
"sync"
|
||||
|
||||
controllerapi "github.com/docker/buildx/controller/pb"
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/moby/buildkit/exporter/containerimage/exptypes"
|
||||
gateway "github.com/moby/buildkit/frontend/gateway/client"
|
||||
"github.com/moby/buildkit/solver/errdefs"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
"github.com/moby/buildkit/solver/result"
|
||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
// NewResultHandle makes a call to client.Build, additionally returning a
|
||||
// opaque ResultHandle alongside the standard response and error.
|
||||
// NewResultHandle stores a gateway client, gateway result, and the error from
|
||||
// an evaluate call if it is present.
|
||||
//
|
||||
// This ResultHandle can be used to execute additional build steps in the same
|
||||
// context as the build occurred, which can allow easy debugging of build
|
||||
// failures and successes.
|
||||
//
|
||||
// If the returned ResultHandle is not nil, the caller must call Done() on it.
|
||||
func NewResultHandle(ctx context.Context, cc *client.Client, opt client.SolveOpt, product string, buildFunc gateway.BuildFunc, ch chan *client.SolveStatus) (*ResultHandle, *client.SolveResponse, error) {
|
||||
// Create a new context to wrap the original, and cancel it when the
|
||||
// caller-provided context is cancelled.
|
||||
//
|
||||
// We derive the context from the background context so that we can forbid
|
||||
// cancellation of the build request after <-done is closed (which we do
|
||||
// before returning the ResultHandle).
|
||||
baseCtx := ctx
|
||||
ctx, cancel := context.WithCancelCause(context.Background())
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
select {
|
||||
case <-baseCtx.Done():
|
||||
cancel(baseCtx.Err())
|
||||
case <-done:
|
||||
// Once done is closed, we've recorded a ResultHandle, so we
|
||||
// shouldn't allow cancelling the underlying build request anymore.
|
||||
}
|
||||
}()
|
||||
|
||||
// Create a new channel to forward status messages to the original.
|
||||
//
|
||||
// We do this so that we can discard status messages after the main portion
|
||||
// of the build is complete. This is necessary for the solve error case,
|
||||
// where the original gateway is kept open until the ResultHandle is
|
||||
// closed - we don't want progress messages from operations in that
|
||||
// ResultHandle to display after this function exits.
|
||||
//
|
||||
// Additionally, callers should wait for the progress channel to be closed.
|
||||
// If we keep the session open and never close the progress channel, the
|
||||
// caller will likely hang.
|
||||
baseCh := ch
|
||||
ch = make(chan *client.SolveStatus)
|
||||
go func() {
|
||||
for {
|
||||
s, ok := <-ch
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
select {
|
||||
case <-baseCh:
|
||||
// base channel is closed, discard status messages
|
||||
default:
|
||||
baseCh <- s
|
||||
}
|
||||
}
|
||||
}()
|
||||
defer close(baseCh)
|
||||
|
||||
var resp *client.SolveResponse
|
||||
var respErr error
|
||||
var respHandle *ResultHandle
|
||||
|
||||
go func() {
|
||||
defer func() { cancel(errors.WithStack(context.Canceled)) }() // ensure no dangling processes
|
||||
|
||||
var res *gateway.Result
|
||||
var err error
|
||||
resp, err = cc.Build(ctx, opt, product, func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
|
||||
var err error
|
||||
res, err = buildFunc(ctx, c)
|
||||
|
||||
if res != nil && err == nil {
|
||||
// Force evaluation of the build result (otherwise, we likely
|
||||
// won't get a solve error)
|
||||
def, err2 := getDefinition(ctx, res)
|
||||
if err2 != nil {
|
||||
return nil, err2
|
||||
}
|
||||
res, err = evalDefinition(ctx, c, def)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
// Scenario 1: we failed to evaluate a node somewhere in the
|
||||
// build graph.
|
||||
//
|
||||
// In this case, we construct a ResultHandle from this
|
||||
// original Build session, and return it alongside the original
|
||||
// build error. We then need to keep the gateway session open
|
||||
// until the caller explicitly closes the ResultHandle.
|
||||
|
||||
var se *errdefs.SolveError
|
||||
if errors.As(err, &se) {
|
||||
respHandle = &ResultHandle{
|
||||
done: make(chan struct{}),
|
||||
solveErr: se,
|
||||
gwClient: c,
|
||||
gwCtx: ctx,
|
||||
}
|
||||
respErr = err // return original error to preserve stacktrace
|
||||
close(done)
|
||||
|
||||
// Block until the caller closes the ResultHandle.
|
||||
select {
|
||||
case <-respHandle.done:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}
|
||||
}
|
||||
return res, err
|
||||
}, ch)
|
||||
if respHandle != nil {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
// Something unexpected failed during the build, we didn't succeed,
|
||||
// but we also didn't make it far enough to create a ResultHandle.
|
||||
respErr = err
|
||||
close(done)
|
||||
return
|
||||
}
|
||||
|
||||
// Scenario 2: we successfully built the image with no errors.
|
||||
//
|
||||
// In this case, the original gateway session has now been closed
|
||||
// since the Build has been completed. So, we need to create a new
|
||||
// gateway session to populate the ResultHandle. To do this, we
|
||||
// need to re-evaluate the target result, in this new session. This
|
||||
// should be instantaneous since the result should be cached.
|
||||
|
||||
def, err := getDefinition(ctx, res)
|
||||
if err != nil {
|
||||
respErr = err
|
||||
close(done)
|
||||
return
|
||||
}
|
||||
|
||||
// NOTE: ideally this second connection should be lazily opened
|
||||
opt := opt
|
||||
opt.Ref = ""
|
||||
opt.Exports = nil
|
||||
opt.CacheExports = nil
|
||||
opt.Internal = true
|
||||
_, respErr = cc.Build(ctx, opt, "buildx", func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
|
||||
res, err := evalDefinition(ctx, c, def)
|
||||
if err != nil {
|
||||
// This should probably not happen, since we've previously
|
||||
// successfully evaluated the same result with no issues.
|
||||
return nil, errors.Wrap(err, "inconsistent solve result")
|
||||
}
|
||||
respHandle = &ResultHandle{
|
||||
done: make(chan struct{}),
|
||||
res: res,
|
||||
gwClient: c,
|
||||
gwCtx: ctx,
|
||||
}
|
||||
close(done)
|
||||
|
||||
// Block until the caller closes the ResultHandle.
|
||||
select {
|
||||
case <-respHandle.done:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
return nil, context.Cause(ctx)
|
||||
}, nil)
|
||||
if respHandle != nil {
|
||||
return
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
|
||||
// Block until the other thread signals that it's completed the build.
|
||||
select {
|
||||
case <-done:
|
||||
case <-baseCtx.Done():
|
||||
if respErr == nil {
|
||||
respErr = baseCtx.Err()
|
||||
}
|
||||
func NewResultHandle(ctx context.Context, c gateway.Client, res *gateway.Result, err error) *ResultHandle {
|
||||
rCtx := &ResultHandle{
|
||||
res: res,
|
||||
gwClient: c,
|
||||
}
|
||||
return respHandle, resp, respErr
|
||||
}
|
||||
|
||||
// getDefinition converts a gateway result into a collection of definitions for
|
||||
// each ref in the result.
|
||||
func getDefinition(ctx context.Context, res *gateway.Result) (*result.Result[*pb.Definition], error) {
|
||||
return result.ConvertResult(res, func(ref gateway.Reference) (*pb.Definition, error) {
|
||||
st, err := ref.ToState()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
def, err := st.Marshal(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return def.ToPB(), nil
|
||||
})
|
||||
}
|
||||
|
||||
// evalDefinition performs the reverse of getDefinition, converting a
|
||||
// collection of definitions into a gateway result.
|
||||
func evalDefinition(ctx context.Context, c gateway.Client, defs *result.Result[*pb.Definition]) (*gateway.Result, error) {
|
||||
// force evaluation of all targets in parallel
|
||||
results := make(map[*pb.Definition]*gateway.Result)
|
||||
resultsMu := sync.Mutex{}
|
||||
eg, egCtx := errgroup.WithContext(ctx)
|
||||
defs.EachRef(func(def *pb.Definition) error {
|
||||
eg.Go(func() error {
|
||||
res, err := c.Solve(egCtx, gateway.SolveRequest{
|
||||
Evaluate: true,
|
||||
Definition: def,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resultsMu.Lock()
|
||||
results[def] = res
|
||||
resultsMu.Unlock()
|
||||
return nil
|
||||
})
|
||||
if err != nil && !errors.As(err, &rCtx.solveErr) {
|
||||
return nil
|
||||
})
|
||||
if err := eg.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, _ := result.ConvertResult(defs, func(def *pb.Definition) (gateway.Reference, error) {
|
||||
if res, ok := results[def]; ok {
|
||||
return res.Ref, nil
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
return res, nil
|
||||
return rCtx
|
||||
}
|
||||
|
||||
// ResultHandle is a build result with the client that built it.
|
||||
type ResultHandle struct {
|
||||
res *gateway.Result
|
||||
solveErr *errdefs.SolveError
|
||||
|
||||
done chan struct{}
|
||||
doneOnce sync.Once
|
||||
|
||||
gwClient gateway.Client
|
||||
gwCtx context.Context
|
||||
|
||||
doneOnce sync.Once
|
||||
|
||||
cleanups []func()
|
||||
cleanupsMu sync.Mutex
|
||||
|
@ -275,9 +56,6 @@ func (r *ResultHandle) Done() {
|
|||
for _, f := range cleanups {
|
||||
f()
|
||||
}
|
||||
|
||||
close(r.done)
|
||||
<-r.gwCtx.Done()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -287,12 +65,15 @@ func (r *ResultHandle) registerCleanup(f func()) {
|
|||
r.cleanupsMu.Unlock()
|
||||
}
|
||||
|
||||
func (r *ResultHandle) build(buildFunc gateway.BuildFunc) (err error) {
|
||||
_, err = buildFunc(r.gwCtx, r.gwClient)
|
||||
return err
|
||||
func (r *ResultHandle) NewContainer(ctx context.Context, cfg *InvokeConfig) (gateway.Container, error) {
|
||||
req, err := r.getContainerConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.gwClient.NewContainer(ctx, req)
|
||||
}
|
||||
|
||||
func (r *ResultHandle) getContainerConfig(cfg *controllerapi.InvokeConfig) (containerCfg gateway.NewContainerRequest, _ error) {
|
||||
func (r *ResultHandle) getContainerConfig(cfg *InvokeConfig) (containerCfg gateway.NewContainerRequest, _ error) {
|
||||
if r.res != nil && r.solveErr == nil {
|
||||
logrus.Debugf("creating container from successful build")
|
||||
ccfg, err := containerConfigFromResult(r.res, cfg)
|
||||
|
@ -311,7 +92,7 @@ func (r *ResultHandle) getContainerConfig(cfg *controllerapi.InvokeConfig) (cont
|
|||
return containerCfg, nil
|
||||
}
|
||||
|
||||
func (r *ResultHandle) getProcessConfig(cfg *controllerapi.InvokeConfig, stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) (_ gateway.StartRequest, err error) {
|
||||
func (r *ResultHandle) getProcessConfig(cfg *InvokeConfig, stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) (_ gateway.StartRequest, err error) {
|
||||
processCfg := newStartRequest(stdin, stdout, stderr)
|
||||
if r.res != nil && r.solveErr == nil {
|
||||
logrus.Debugf("creating container from successful build")
|
||||
|
@ -327,7 +108,7 @@ func (r *ResultHandle) getProcessConfig(cfg *controllerapi.InvokeConfig, stdin i
|
|||
return processCfg, nil
|
||||
}
|
||||
|
||||
func containerConfigFromResult(res *gateway.Result, cfg *controllerapi.InvokeConfig) (*gateway.NewContainerRequest, error) {
|
||||
func containerConfigFromResult(res *gateway.Result, cfg *InvokeConfig) (*gateway.NewContainerRequest, error) {
|
||||
if cfg.Initial {
|
||||
return nil, errors.Errorf("starting from the container from the initial state of the step is supported only on the failed steps")
|
||||
}
|
||||
|
@ -352,7 +133,7 @@ func containerConfigFromResult(res *gateway.Result, cfg *controllerapi.InvokeCon
|
|||
}, nil
|
||||
}
|
||||
|
||||
func populateProcessConfigFromResult(req *gateway.StartRequest, res *gateway.Result, cfg *controllerapi.InvokeConfig) error {
|
||||
func populateProcessConfigFromResult(req *gateway.StartRequest, res *gateway.Result, cfg *InvokeConfig) error {
|
||||
imgData := res.Metadata[exptypes.ExporterImageConfigKey]
|
||||
var img *ocispecs.Image
|
||||
if len(imgData) > 0 {
|
||||
|
@ -403,7 +184,7 @@ func populateProcessConfigFromResult(req *gateway.StartRequest, res *gateway.Res
|
|||
return nil
|
||||
}
|
||||
|
||||
func containerConfigFromError(solveErr *errdefs.SolveError, cfg *controllerapi.InvokeConfig) (*gateway.NewContainerRequest, error) {
|
||||
func containerConfigFromError(solveErr *errdefs.SolveError, cfg *InvokeConfig) (*gateway.NewContainerRequest, error) {
|
||||
exec, err := execOpFromError(solveErr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -431,7 +212,7 @@ func containerConfigFromError(solveErr *errdefs.SolveError, cfg *controllerapi.I
|
|||
}, nil
|
||||
}
|
||||
|
||||
func populateProcessConfigFromError(req *gateway.StartRequest, solveErr *errdefs.SolveError, cfg *controllerapi.InvokeConfig) error {
|
||||
func populateProcessConfigFromError(req *gateway.StartRequest, solveErr *errdefs.SolveError, cfg *InvokeConfig) error {
|
||||
exec, err := execOpFromError(solveErr)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -77,24 +77,30 @@ func toBuildkitExtraHosts(ctx context.Context, inp []string, nodeDriver *driver.
|
|||
}
|
||||
// If the IP Address is a "host-gateway", replace this value with the
|
||||
// IP address provided by the worker's label.
|
||||
var ips []string
|
||||
if ip == mobyHostGatewayName {
|
||||
hgip, err := nodeDriver.HostGatewayIP(ctx)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "unable to derive the IP value for host-gateway")
|
||||
}
|
||||
ip = hgip.String()
|
||||
ips = append(ips, hgip.String())
|
||||
} else {
|
||||
// If the address is enclosed in square brackets, extract it (for IPv6, but
|
||||
// permit it for IPv4 as well; we don't know the address family here, but it's
|
||||
// unambiguous).
|
||||
if len(ip) > 2 && ip[0] == '[' && ip[len(ip)-1] == ']' {
|
||||
ip = ip[1 : len(ip)-1]
|
||||
}
|
||||
if net.ParseIP(ip) == nil {
|
||||
return "", errors.Errorf("invalid host %s", h)
|
||||
for _, v := range strings.Split(ip, ",") {
|
||||
// If the address is enclosed in square brackets, extract it
|
||||
// (for IPv6, but permit it for IPv4 as well; we don't know the
|
||||
// address family here, but it's unambiguous).
|
||||
if len(v) > 2 && v[0] == '[' && v[len(v)-1] == ']' {
|
||||
v = v[1 : len(v)-1]
|
||||
}
|
||||
if net.ParseIP(v) == nil {
|
||||
return "", errors.Errorf("invalid host %s", h)
|
||||
}
|
||||
ips = append(ips, v)
|
||||
}
|
||||
}
|
||||
hosts = append(hosts, host+"="+ip)
|
||||
for _, v := range ips {
|
||||
hosts = append(hosts, host+"="+v)
|
||||
}
|
||||
}
|
||||
return strings.Join(hosts, ","), nil
|
||||
}
|
||||
|
|
|
@ -72,6 +72,11 @@ func TestToBuildkitExtraHosts(t *testing.T) {
|
|||
doc: "IPv6 localhost, non-canonical, eq sep",
|
||||
input: []string{`ipv6local=0:0:0:0:0:0:0:1`},
|
||||
},
|
||||
{
|
||||
doc: "Multi IPs",
|
||||
input: []string{`myhost=162.242.195.82,162.242.195.83`},
|
||||
expectedOut: `myhost=162.242.195.82,myhost=162.242.195.83`,
|
||||
},
|
||||
{
|
||||
doc: "IPv6 localhost, non-canonical, eq sep, brackets",
|
||||
input: []string{`ipv6local=[0:0:0:0:0:0:0:1]`},
|
||||
|
|
|
@ -14,10 +14,12 @@ import (
|
|||
"github.com/docker/cli/cli-plugins/plugin"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/debug"
|
||||
"github.com/moby/buildkit/solver/errdefs"
|
||||
solvererrdefs "github.com/moby/buildkit/solver/errdefs"
|
||||
"github.com/moby/buildkit/util/grpcerrors"
|
||||
"github.com/moby/buildkit/util/stack"
|
||||
"github.com/pkg/errors"
|
||||
"go.opentelemetry.io/otel"
|
||||
"google.golang.org/grpc/codes"
|
||||
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
|
||||
|
||||
|
@ -99,7 +101,7 @@ func main() {
|
|||
os.Exit(sterr.StatusCode)
|
||||
}
|
||||
|
||||
for _, s := range errdefs.Sources(err) {
|
||||
for _, s := range solvererrdefs.Sources(err) {
|
||||
s.Print(cmd.Err())
|
||||
}
|
||||
if debug.IsEnabled() {
|
||||
|
@ -113,5 +115,19 @@ func main() {
|
|||
ebr.Print(cmd.Err())
|
||||
}
|
||||
|
||||
os.Exit(1)
|
||||
exitCode := 1
|
||||
switch grpcerrors.Code(err) {
|
||||
case codes.Internal:
|
||||
exitCode = 100 // https://github.com/square/exit/blob/v1.3.0/exit.go#L70
|
||||
case codes.ResourceExhausted:
|
||||
exitCode = 102
|
||||
case codes.Canceled:
|
||||
exitCode = 130
|
||||
default:
|
||||
if errors.Is(err, context.Canceled) {
|
||||
exitCode = 130
|
||||
}
|
||||
}
|
||||
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import (
|
|||
"github.com/docker/buildx/bake/hclparser"
|
||||
"github.com/docker/buildx/build"
|
||||
"github.com/docker/buildx/builder"
|
||||
"github.com/docker/buildx/controller/pb"
|
||||
"github.com/docker/buildx/localstate"
|
||||
"github.com/docker/buildx/util/buildflags"
|
||||
"github.com/docker/buildx/util/cobrautil/completion"
|
||||
|
@ -41,6 +40,11 @@ import (
|
|||
"go.opentelemetry.io/otel/attribute"
|
||||
)
|
||||
|
||||
const (
|
||||
bakeEnvFileSeparator = "BUILDX_BAKE_PATH_SEPARATOR"
|
||||
bakeEnvFilePath = "BUILDX_BAKE_FILE"
|
||||
)
|
||||
|
||||
type bakeOptions struct {
|
||||
files []string
|
||||
overrides []string
|
||||
|
@ -63,7 +67,7 @@ type bakeOptions struct {
|
|||
listVars bool
|
||||
}
|
||||
|
||||
func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in bakeOptions, cFlags commonFlags) (err error) {
|
||||
func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in bakeOptions, cFlags commonFlags, filesFromEnv bool) (err error) {
|
||||
mp := dockerCli.MeterProvider()
|
||||
|
||||
ctx, end, err := tracing.TraceCurrentCommand(ctx, append([]string{"bake"}, targets...),
|
||||
|
@ -163,7 +167,13 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
|
|||
attributes := bakeMetricAttributes(dockerCli, driverType, url, cmdContext, targets, &in)
|
||||
|
||||
progressMode := progressui.DisplayMode(cFlags.progress)
|
||||
|
||||
var printer *progress.Printer
|
||||
defer func() {
|
||||
if printer != nil {
|
||||
printer.Wait()
|
||||
}
|
||||
}()
|
||||
|
||||
makePrinter := func() error {
|
||||
var err error
|
||||
|
@ -181,7 +191,7 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
|
|||
return err
|
||||
}
|
||||
|
||||
files, inp, err := readBakeFiles(ctx, nodes, url, in.files, dockerCli.In(), printer)
|
||||
files, inp, err := readBakeFiles(ctx, nodes, url, in.files, dockerCli.In(), printer, filesFromEnv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -348,7 +358,7 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
|
|||
continue
|
||||
}
|
||||
|
||||
pf := &pb.CallFunc{
|
||||
pf := &buildflags.CallFunc{
|
||||
Name: req.CallFunc.Name,
|
||||
Format: req.CallFunc.Format,
|
||||
IgnoreStatus: req.CallFunc.IgnoreStatus,
|
||||
|
@ -453,6 +463,15 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
|||
Aliases: []string{"f"},
|
||||
Short: "Build from a file",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
filesFromEnv := false
|
||||
if len(options.files) == 0 {
|
||||
envFiles, err := bakeEnvFiles(os.LookupEnv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.files = envFiles
|
||||
filesFromEnv = true
|
||||
}
|
||||
// reset to nil to avoid override is unset
|
||||
if !cmd.Flags().Lookup("no-cache").Changed {
|
||||
cFlags.noCache = nil
|
||||
|
@ -470,7 +489,7 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
|||
options.builder = rootOpts.builder
|
||||
options.metadataFile = cFlags.metadataFile
|
||||
// Other common flags (noCache, pull and progress) are processed in runBake function.
|
||||
return runBake(cmd.Context(), dockerCli, args, options, cFlags)
|
||||
return runBake(cmd.Context(), dockerCli, args, options, cFlags, filesFromEnv)
|
||||
},
|
||||
ValidArgsFunction: completion.BakeTargets(options.files),
|
||||
}
|
||||
|
@ -505,6 +524,37 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func bakeEnvFiles(lookup func(string string) (string, bool)) ([]string, error) {
|
||||
sep, _ := lookup(bakeEnvFileSeparator)
|
||||
if sep == "" {
|
||||
sep = string(os.PathListSeparator)
|
||||
}
|
||||
f, ok := lookup(bakeEnvFilePath)
|
||||
if ok {
|
||||
return cleanPaths(strings.Split(f, sep))
|
||||
}
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func cleanPaths(p []string) ([]string, error) {
|
||||
var paths []string
|
||||
for _, f := range p {
|
||||
f = strings.TrimSpace(f)
|
||||
if f == "" {
|
||||
continue
|
||||
}
|
||||
if f == "-" {
|
||||
paths = append(paths, f)
|
||||
continue
|
||||
}
|
||||
if _, err := os.Stat(f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
paths = append(paths, f)
|
||||
}
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
func saveLocalStateGroup(dockerCli command.Cli, in bakeOptions, targets []string, bo map[string]build.Options) error {
|
||||
l, err := localstate.New(confutil.NewConfig(dockerCli))
|
||||
if err != nil {
|
||||
|
@ -554,7 +604,7 @@ func bakeArgs(args []string) (url, cmdContext string, targets []string) {
|
|||
return url, cmdContext, targets
|
||||
}
|
||||
|
||||
func readBakeFiles(ctx context.Context, nodes []builder.Node, url string, names []string, stdin io.Reader, pw progress.Writer) (files []bake.File, inp *bake.Input, err error) {
|
||||
func readBakeFiles(ctx context.Context, nodes []builder.Node, url string, names []string, stdin io.Reader, pw progress.Writer, filesFromEnv bool) (files []bake.File, inp *bake.Input, err error) {
|
||||
var lnames []string // local
|
||||
var rnames []string // remote
|
||||
var anames []string // both
|
||||
|
@ -579,7 +629,11 @@ func readBakeFiles(ctx context.Context, nodes []builder.Node, url string, names
|
|||
|
||||
if len(lnames) > 0 || url == "" {
|
||||
var lfiles []bake.File
|
||||
progress.Wrap("[internal] load local bake definitions", pw.Write, func(sub progress.SubLogger) error {
|
||||
where := ""
|
||||
if filesFromEnv {
|
||||
where = " from " + bakeEnvFilePath + " env"
|
||||
}
|
||||
progress.Wrap("[internal] load local bake definitions"+where, pw.Write, func(sub progress.SubLogger) error {
|
||||
if url != "" {
|
||||
lfiles, err = bake.ReadLocalFiles(lnames, stdin, sub)
|
||||
} else {
|
||||
|
@ -663,7 +717,7 @@ func printVars(w io.Writer, format string, vars []*hclparser.Variable) error {
|
|||
tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
|
||||
defer tw.Flush()
|
||||
|
||||
tw.Write([]byte("VARIABLE\tVALUE\tDESCRIPTION\n"))
|
||||
tw.Write([]byte("VARIABLE\tTYPE\tVALUE\tDESCRIPTION\n"))
|
||||
|
||||
for _, v := range vars {
|
||||
var value string
|
||||
|
@ -672,7 +726,7 @@ func printVars(w io.Writer, format string, vars []*hclparser.Variable) error {
|
|||
} else {
|
||||
value = "<null>"
|
||||
}
|
||||
fmt.Fprintf(tw, "%s\t%s\t%s\n", v.Name, value, v.Description)
|
||||
fmt.Fprintf(tw, "%s\t%s\t%s\t%s\n", v.Name, v.Type, value, v.Description)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -20,12 +20,6 @@ import (
|
|||
"github.com/containerd/console"
|
||||
"github.com/docker/buildx/build"
|
||||
"github.com/docker/buildx/builder"
|
||||
"github.com/docker/buildx/commands/debug"
|
||||
"github.com/docker/buildx/controller"
|
||||
cbuild "github.com/docker/buildx/controller/build"
|
||||
"github.com/docker/buildx/controller/control"
|
||||
controllererrors "github.com/docker/buildx/controller/errdefs"
|
||||
controllerapi "github.com/docker/buildx/controller/pb"
|
||||
"github.com/docker/buildx/monitor"
|
||||
"github.com/docker/buildx/store"
|
||||
"github.com/docker/buildx/store/storeutil"
|
||||
|
@ -33,9 +27,10 @@ import (
|
|||
"github.com/docker/buildx/util/cobrautil"
|
||||
"github.com/docker/buildx/util/confutil"
|
||||
"github.com/docker/buildx/util/desktop"
|
||||
"github.com/docker/buildx/util/ioset"
|
||||
"github.com/docker/buildx/util/dockerutil"
|
||||
"github.com/docker/buildx/util/metricutil"
|
||||
"github.com/docker/buildx/util/osutil"
|
||||
"github.com/docker/buildx/util/platformutil"
|
||||
"github.com/docker/buildx/util/progress"
|
||||
"github.com/docker/buildx/util/tracing"
|
||||
"github.com/docker/cli/cli"
|
||||
|
@ -48,8 +43,10 @@ import (
|
|||
"github.com/moby/buildkit/frontend/subrequests/lint"
|
||||
"github.com/moby/buildkit/frontend/subrequests/outline"
|
||||
"github.com/moby/buildkit/frontend/subrequests/targets"
|
||||
"github.com/moby/buildkit/session/auth/authprovider"
|
||||
"github.com/moby/buildkit/solver/errdefs"
|
||||
solverpb "github.com/moby/buildkit/solver/pb"
|
||||
sourcepolicy "github.com/moby/buildkit/sourcepolicy/pb"
|
||||
"github.com/moby/buildkit/util/grpcerrors"
|
||||
"github.com/moby/buildkit/util/progress/progressui"
|
||||
"github.com/moby/sys/atomicwriter"
|
||||
|
@ -107,7 +104,7 @@ type buildOptions struct {
|
|||
invokeConfig *invokeConfig
|
||||
}
|
||||
|
||||
func (o *buildOptions) toControllerOptions() (*cbuild.Options, error) {
|
||||
func (o *buildOptions) toOptions() (*BuildOptions, error) {
|
||||
var err error
|
||||
|
||||
buildArgs, err := listToMap(o.buildArgs, true)
|
||||
|
@ -120,7 +117,7 @@ func (o *buildOptions) toControllerOptions() (*cbuild.Options, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
opts := cbuild.Options{
|
||||
opts := BuildOptions{
|
||||
Allow: o.allow,
|
||||
Annotations: o.annotations,
|
||||
BuildArgs: buildArgs,
|
||||
|
@ -135,7 +132,7 @@ func (o *buildOptions) toControllerOptions() (*cbuild.Options, error) {
|
|||
ShmSize: int64(o.shmSize),
|
||||
Tags: o.tags,
|
||||
Target: o.target,
|
||||
Ulimits: dockerUlimitToControllerUlimit(o.ulimits),
|
||||
Ulimits: o.ulimits,
|
||||
Builder: o.builder,
|
||||
NoCache: o.noCache,
|
||||
Pull: o.pull,
|
||||
|
@ -182,17 +179,15 @@ func (o *buildOptions) toControllerOptions() (*cbuild.Options, error) {
|
|||
}
|
||||
}
|
||||
|
||||
cacheFrom, err := buildflags.ParseCacheEntry(o.cacheFrom)
|
||||
opts.CacheFrom, err = buildflags.ParseCacheEntry(o.cacheFrom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts.CacheFrom = cacheFrom.ToPB()
|
||||
|
||||
cacheTo, err := buildflags.ParseCacheEntry(o.cacheTo)
|
||||
opts.CacheTo, err = buildflags.ParseCacheEntry(o.cacheTo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts.CacheTo = cacheTo.ToPB()
|
||||
|
||||
opts.Secrets, err = buildflags.ParseSecretSpecs(o.secrets)
|
||||
if err != nil {
|
||||
|
@ -296,7 +291,7 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
|
|||
end(err)
|
||||
}()
|
||||
|
||||
opts, err := options.toControllerOptions()
|
||||
opts, err := options.toOptions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -353,14 +348,7 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
|
|||
}
|
||||
|
||||
done := timeBuildCommand(mp, attributes)
|
||||
var resp *client.SolveResponse
|
||||
var inputs *build.Inputs
|
||||
var retErr error
|
||||
if confutil.IsExperimental() {
|
||||
resp, inputs, retErr = runControllerBuild(ctx, dockerCli, opts, options, printer)
|
||||
} else {
|
||||
resp, inputs, retErr = runBasicBuild(ctx, dockerCli, opts, printer)
|
||||
}
|
||||
resp, inputs, retErr := runBuildWithOptions(ctx, dockerCli, opts, options, printer)
|
||||
|
||||
if err := printer.Wait(); retErr == nil {
|
||||
retErr = err
|
||||
|
@ -418,134 +406,41 @@ func getImageID(resp map[string]string) string {
|
|||
return dgst
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
return resp, dfmap, err
|
||||
}
|
||||
|
||||
func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *cbuild.Options, options buildOptions, printer *progress.Printer) (*client.SolveResponse, *build.Inputs, error) {
|
||||
func runBuildWithOptions(ctx context.Context, dockerCli command.Cli, opts *BuildOptions, options buildOptions, printer *progress.Printer) (_ *client.SolveResponse, _ *build.Inputs, retErr 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 := controller.NewController(ctx, dockerCli)
|
||||
defer func() {
|
||||
if err := c.Close(); err != nil {
|
||||
logrus.Warnf("failed to close server connection %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// 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 := cbuild.ResolveOptionPaths(opts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var ref string
|
||||
var retErr error
|
||||
var resp *client.SolveResponse
|
||||
var inputs *build.Inputs
|
||||
|
||||
var f *ioset.SingleForwarder
|
||||
var pr io.ReadCloser
|
||||
var pw io.WriteCloser
|
||||
var (
|
||||
in io.ReadCloser
|
||||
m *monitor.Monitor
|
||||
bh build.Handler
|
||||
)
|
||||
if options.invokeConfig == nil {
|
||||
pr = dockerCli.In()
|
||||
in = dockerCli.In()
|
||||
} else {
|
||||
f = ioset.NewSingleForwarder()
|
||||
f.SetReader(dockerCli.In())
|
||||
pr, pw = io.Pipe()
|
||||
f.SetWriter(pw, func() io.WriteCloser {
|
||||
pw.Close() // propagate EOF
|
||||
logrus.Debug("propagating stdin close")
|
||||
return nil
|
||||
})
|
||||
m = monitor.New(&options.invokeConfig.InvokeConfig, dockerCli.In(), os.Stdout, os.Stderr, printer)
|
||||
defer m.Close()
|
||||
|
||||
bh = m.Handler()
|
||||
}
|
||||
|
||||
resp, inputs, err = c.Build(ctx, opts, pr, printer)
|
||||
if err != nil {
|
||||
var be *controllererrors.BuildError
|
||||
if errors.As(err, &be) {
|
||||
retErr = err
|
||||
// We can proceed to monitor
|
||||
} else {
|
||||
for {
|
||||
resp, inputs, err := RunBuild(ctx, dockerCli, opts, in, printer, &bh)
|
||||
if err != nil {
|
||||
if errors.Is(err, build.ErrRestart) {
|
||||
retErr = nil
|
||||
continue
|
||||
}
|
||||
return nil, nil, errors.Wrapf(err, "failed to build")
|
||||
}
|
||||
|
||||
return resp, inputs, err
|
||||
}
|
||||
|
||||
if options.invokeConfig != nil {
|
||||
if err := pw.Close(); err != nil {
|
||||
logrus.Debug("failed to close stdin pipe writer")
|
||||
}
|
||||
if err := pr.Close(); err != nil {
|
||||
logrus.Debug("failed to close stdin pipe reader")
|
||||
}
|
||||
}
|
||||
|
||||
if options.invokeConfig != nil && options.invokeConfig.needsDebug(retErr) {
|
||||
// Print errors before launching monitor
|
||||
if err := printError(retErr, printer); err != nil {
|
||||
logrus.Warnf("failed to print error information: %v", err)
|
||||
}
|
||||
|
||||
pr2, pw2 := io.Pipe()
|
||||
f.SetWriter(pw2, func() io.WriteCloser {
|
||||
pw2.Close() // propagate EOF
|
||||
return nil
|
||||
})
|
||||
monitorBuildResult, err := options.invokeConfig.runDebug(ctx, ref, opts, c, pr2, os.Stdout, os.Stderr, printer)
|
||||
if err := pw2.Close(); err != nil {
|
||||
logrus.Debug("failed to close monitor stdin pipe reader")
|
||||
}
|
||||
if err != nil {
|
||||
logrus.Warnf("failed to run monitor: %v", err)
|
||||
}
|
||||
if monitorBuildResult != nil {
|
||||
// Update return values with the last build result from monitor
|
||||
resp, retErr = monitorBuildResult.Resp, monitorBuildResult.Err
|
||||
}
|
||||
} else {
|
||||
if err := c.Close(); err != nil {
|
||||
logrus.Warnf("close error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return resp, inputs, retErr
|
||||
}
|
||||
|
||||
func printError(err error, printer *progress.Printer) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if err := printer.Pause(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer printer.Unpause()
|
||||
for _, s := range errdefs.Sources(err) {
|
||||
s.Print(os.Stderr)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
func newDebuggableBuild(dockerCli command.Cli, rootOpts *rootOptions) debug.DebuggableCmd {
|
||||
return &debuggableBuild{dockerCli: dockerCli, rootOpts: rootOpts}
|
||||
}
|
||||
|
||||
type debuggableBuild struct {
|
||||
dockerCli command.Cli
|
||||
rootOpts *rootOptions
|
||||
}
|
||||
|
||||
func (b *debuggableBuild) NewDebugger(cfg *debug.DebugConfig) *cobra.Command {
|
||||
return buildCmd(b.dockerCli, b.rootOpts, cfg)
|
||||
}
|
||||
|
||||
func buildCmd(dockerCli command.Cli, rootOpts *rootOptions, debugConfig *debug.DebugConfig) *cobra.Command {
|
||||
func buildCmd(dockerCli command.Cli, rootOpts *rootOptions, debugConfig *debugOptions) *cobra.Command {
|
||||
cFlags := &commonFlags{}
|
||||
options := &buildOptions{}
|
||||
|
||||
|
@ -832,21 +727,6 @@ func listToMap(values []string, defaultEnv bool) (map[string]string, error) {
|
|||
return result, nil
|
||||
}
|
||||
|
||||
func dockerUlimitToControllerUlimit(u *dockeropts.UlimitOpt) *controllerapi.UlimitOpt {
|
||||
if u == nil {
|
||||
return nil
|
||||
}
|
||||
values := make(map[string]*controllerapi.Ulimit)
|
||||
for _, u := range u.GetList() {
|
||||
values[u.Name] = &controllerapi.Ulimit{
|
||||
Name: u.Name,
|
||||
Hard: u.Hard,
|
||||
Soft: u.Soft,
|
||||
}
|
||||
}
|
||||
return &controllerapi.UlimitOpt{Values: values}
|
||||
}
|
||||
|
||||
func printWarnings(w io.Writer, warnings []client.VertexWarning, mode progressui.DisplayMode) {
|
||||
if len(warnings) == 0 || mode == progressui.QuietMode || mode == progressui.RawJSONMode {
|
||||
return
|
||||
|
@ -886,7 +766,7 @@ func printWarnings(w io.Writer, warnings []client.VertexWarning, mode progressui
|
|||
}
|
||||
}
|
||||
|
||||
func printResult(w io.Writer, f *controllerapi.CallFunc, res map[string]string, target string, inp *build.Inputs) (int, error) {
|
||||
func printResult(w io.Writer, f *buildflags.CallFunc, res map[string]string, target string, inp *build.Inputs) (int, error) {
|
||||
switch f.Name {
|
||||
case "outline":
|
||||
return 0, printValue(w, outline.PrintOutline, outline.SubrequestsOutlineDefinition.Version, f.Format, res)
|
||||
|
@ -987,37 +867,22 @@ func printValue(w io.Writer, printer callFunc, version string, format string, re
|
|||
}
|
||||
|
||||
type invokeConfig struct {
|
||||
controllerapi.InvokeConfig
|
||||
onFlag string
|
||||
build.InvokeConfig
|
||||
invokeFlag string
|
||||
}
|
||||
|
||||
func (cfg *invokeConfig) needsDebug(retErr error) bool {
|
||||
switch cfg.onFlag {
|
||||
case "always":
|
||||
return true
|
||||
case "error":
|
||||
return retErr != nil
|
||||
default:
|
||||
return cfg.invokeFlag != ""
|
||||
}
|
||||
}
|
||||
|
||||
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.Close(); err != nil {
|
||||
logrus.Warnf("close error: %v", err)
|
||||
}
|
||||
return nil, errors.Errorf("failed to configure terminal: %v", err)
|
||||
}
|
||||
defer con.Reset()
|
||||
return monitor.RunMonitor(ctx, ref, options, &cfg.InvokeConfig, c, stdin, stdout, stderr, progress)
|
||||
}
|
||||
|
||||
func (cfg *invokeConfig) parseInvokeConfig(invoke, on string) error {
|
||||
cfg.onFlag = on
|
||||
switch on {
|
||||
case "always":
|
||||
cfg.SuspendOn = build.SuspendAlways
|
||||
case "error":
|
||||
cfg.SuspendOn = build.SuspendError
|
||||
default:
|
||||
if invoke != "" {
|
||||
cfg.SuspendOn = build.SuspendAlways
|
||||
}
|
||||
}
|
||||
|
||||
cfg.invokeFlag = invoke
|
||||
cfg.Tty = true
|
||||
cfg.NoCmd = true
|
||||
|
@ -1139,3 +1004,209 @@ func otelErrorType(err error) string {
|
|||
}
|
||||
return name
|
||||
}
|
||||
|
||||
const defaultTargetName = "default"
|
||||
|
||||
type BuildOptions struct {
|
||||
ContextPath string
|
||||
DockerfileName string
|
||||
CallFunc *buildflags.CallFunc
|
||||
NamedContexts map[string]string
|
||||
Allow []string
|
||||
Attests buildflags.Attests
|
||||
BuildArgs map[string]string
|
||||
CacheFrom []*buildflags.CacheOptionsEntry
|
||||
CacheTo []*buildflags.CacheOptionsEntry
|
||||
CgroupParent string
|
||||
Exports []*buildflags.ExportEntry
|
||||
ExtraHosts []string
|
||||
Labels map[string]string
|
||||
NetworkMode string
|
||||
NoCacheFilter []string
|
||||
Platforms []string
|
||||
Secrets buildflags.Secrets
|
||||
ShmSize int64
|
||||
SSH []*buildflags.SSH
|
||||
Tags []string
|
||||
Target string
|
||||
Ulimits *dockeropts.UlimitOpt
|
||||
Builder string
|
||||
NoCache bool
|
||||
Pull bool
|
||||
ExportPush bool
|
||||
ExportLoad bool
|
||||
SourcePolicy *sourcepolicy.Policy
|
||||
Ref string
|
||||
GroupRef string
|
||||
Annotations []string
|
||||
ProvenanceResponseMode string
|
||||
}
|
||||
|
||||
// RunBuild runs the specified build and returns the result.
|
||||
func RunBuild(ctx context.Context, dockerCli command.Cli, in *BuildOptions, inStream io.Reader, progress progress.Writer, bh *build.Handler) (*client.SolveResponse, *build.Inputs, error) {
|
||||
if in.NoCache && len(in.NoCacheFilter) > 0 {
|
||||
return nil, nil, errors.Errorf("--no-cache and --no-cache-filter cannot currently be used together")
|
||||
}
|
||||
|
||||
contexts := map[string]build.NamedContext{}
|
||||
for name, path := range in.NamedContexts {
|
||||
contexts[name] = build.NamedContext{Path: path}
|
||||
}
|
||||
|
||||
opts := build.Options{
|
||||
Inputs: build.Inputs{
|
||||
ContextPath: in.ContextPath,
|
||||
DockerfilePath: in.DockerfileName,
|
||||
InStream: build.NewSyncMultiReader(inStream),
|
||||
NamedContexts: contexts,
|
||||
},
|
||||
Ref: in.Ref,
|
||||
BuildArgs: in.BuildArgs,
|
||||
CgroupParent: in.CgroupParent,
|
||||
ExtraHosts: in.ExtraHosts,
|
||||
Labels: in.Labels,
|
||||
NetworkMode: in.NetworkMode,
|
||||
NoCache: in.NoCache,
|
||||
NoCacheFilter: in.NoCacheFilter,
|
||||
Pull: in.Pull,
|
||||
ShmSize: dockeropts.MemBytes(in.ShmSize),
|
||||
Tags: in.Tags,
|
||||
Target: in.Target,
|
||||
Ulimits: in.Ulimits,
|
||||
GroupRef: in.GroupRef,
|
||||
ProvenanceResponseMode: confutil.ParseMetadataProvenance(in.ProvenanceResponseMode),
|
||||
}
|
||||
|
||||
platforms, err := platformutil.Parse(in.Platforms)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opts.Platforms = platforms
|
||||
|
||||
dockerConfig := dockerCli.ConfigFile()
|
||||
opts.Session = append(opts.Session, authprovider.NewDockerAuthProvider(authprovider.DockerAuthProviderConfig{
|
||||
ConfigFile: dockerConfig,
|
||||
}))
|
||||
|
||||
secrets, err := build.CreateSecrets(in.Secrets)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opts.Session = append(opts.Session, secrets)
|
||||
|
||||
sshSpecs := in.SSH
|
||||
if len(sshSpecs) == 0 && buildflags.IsGitSSH(in.ContextPath) {
|
||||
sshSpecs = append(sshSpecs, &buildflags.SSH{ID: "default"})
|
||||
}
|
||||
ssh, err := build.CreateSSH(sshSpecs)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opts.Session = append(opts.Session, ssh)
|
||||
|
||||
outputs, _, err := build.CreateExports(in.Exports)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if in.ExportPush {
|
||||
var pushUsed bool
|
||||
for i := range outputs {
|
||||
if outputs[i].Type == client.ExporterImage {
|
||||
outputs[i].Attrs["push"] = "true"
|
||||
pushUsed = true
|
||||
}
|
||||
}
|
||||
if !pushUsed {
|
||||
outputs = append(outputs, client.ExportEntry{
|
||||
Type: client.ExporterImage,
|
||||
Attrs: map[string]string{
|
||||
"push": "true",
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
if in.ExportLoad {
|
||||
var loadUsed bool
|
||||
for i := range outputs {
|
||||
if outputs[i].Type == client.ExporterDocker {
|
||||
if _, ok := outputs[i].Attrs["dest"]; !ok {
|
||||
loadUsed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !loadUsed {
|
||||
outputs = append(outputs, client.ExportEntry{
|
||||
Type: client.ExporterDocker,
|
||||
Attrs: map[string]string{},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
annotations, err := buildflags.ParseAnnotations(in.Annotations)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "parse annotations")
|
||||
}
|
||||
|
||||
for _, o := range outputs {
|
||||
for k, v := range annotations {
|
||||
o.Attrs[k.String()] = v
|
||||
}
|
||||
}
|
||||
|
||||
opts.Exports = outputs
|
||||
|
||||
opts.CacheFrom = build.CreateCaches(in.CacheFrom)
|
||||
opts.CacheTo = build.CreateCaches(in.CacheTo)
|
||||
|
||||
opts.Attests = in.Attests.ToMap()
|
||||
|
||||
opts.SourcePolicy = in.SourcePolicy
|
||||
|
||||
allow, err := buildflags.ParseEntitlements(in.Allow)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opts.Allow = allow
|
||||
|
||||
if in.CallFunc != nil {
|
||||
opts.CallFunc = &build.CallFunc{
|
||||
Name: in.CallFunc.Name,
|
||||
Format: in.CallFunc.Format,
|
||||
IgnoreStatus: in.CallFunc.IgnoreStatus,
|
||||
}
|
||||
}
|
||||
|
||||
// key string used for kubernetes "sticky" mode
|
||||
contextPathHash, err := filepath.Abs(in.ContextPath)
|
||||
if err != nil {
|
||||
contextPathHash = in.ContextPath
|
||||
}
|
||||
|
||||
b, err := builder.New(dockerCli,
|
||||
builder.WithName(in.Builder),
|
||||
builder.WithContextPathHash(contextPathHash),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err = updateLastActivity(dockerCli, b.NodeGroup); err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "failed to update builder last activity time")
|
||||
}
|
||||
nodes, err := b.LoadNodes(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var inputs *build.Inputs
|
||||
buildOptions := map[string]build.Options{defaultTargetName: opts}
|
||||
resp, err := build.BuildWithResultHandler(ctx, nodes, buildOptions, dockerutil.NewClient(dockerCli), confutil.NewConfig(dockerCli), progress, bh)
|
||||
err = wrapBuildError(err, false)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if i, ok := buildOptions[defaultTargetName]; ok {
|
||||
inputs = &i.Inputs
|
||||
}
|
||||
return resp[defaultTargetName], inputs, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"github.com/docker/buildx/util/cobrautil"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type debugOptions struct {
|
||||
// InvokeFlag is a flag to configure the launched debugger and the commaned executed on the debugger.
|
||||
InvokeFlag string
|
||||
|
||||
// OnFlag is a flag to configure the timing of launching the debugger.
|
||||
OnFlag string
|
||||
}
|
||||
|
||||
func debugCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
||||
var options debugOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "debug",
|
||||
Short: "Start debugger",
|
||||
}
|
||||
cobrautil.MarkCommandExperimental(cmd)
|
||||
|
||||
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])")
|
||||
|
||||
cobrautil.MarkFlagsExperimental(flags, "invoke", "on")
|
||||
|
||||
cmd.AddCommand(buildCmd(dockerCli, rootOpts, &options))
|
||||
return cmd
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
package debug
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/containerd/console"
|
||||
"github.com/docker/buildx/controller"
|
||||
controllerapi "github.com/docker/buildx/controller/pb"
|
||||
"github.com/docker/buildx/monitor"
|
||||
"github.com/docker/buildx/util/cobrautil"
|
||||
"github.com/docker/buildx/util/progress"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/moby/buildkit/util/progress/progressui"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// DebugConfig is a user-specified configuration for the debugger.
|
||||
type DebugConfig struct {
|
||||
// InvokeFlag is a flag to configure the launched debugger and the commaned executed on the debugger.
|
||||
InvokeFlag string
|
||||
|
||||
// OnFlag is a flag to configure the timing of launching the debugger.
|
||||
OnFlag string
|
||||
}
|
||||
|
||||
// DebuggableCmd is a command that supports debugger with recognizing the user-specified DebugConfig.
|
||||
type DebuggableCmd interface {
|
||||
// NewDebugger returns the new *cobra.Command with support for the debugger with recognizing DebugConfig.
|
||||
NewDebugger(*DebugConfig) *cobra.Command
|
||||
}
|
||||
|
||||
func RootCmd(dockerCli command.Cli, children ...DebuggableCmd) *cobra.Command {
|
||||
var progressMode string
|
||||
var options DebugConfig
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "debug",
|
||||
Short: "Start debugger",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
printer, err := progress.NewPrinter(context.TODO(), os.Stderr, progressui.DisplayMode(progressMode))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := context.TODO()
|
||||
c := controller.NewController(ctx, dockerCli)
|
||||
defer func() {
|
||||
if err := c.Close(); err != nil {
|
||||
logrus.Warnf("failed to close server connection %v", err)
|
||||
}
|
||||
}()
|
||||
con := console.Current()
|
||||
if err := con.SetRaw(); err != nil {
|
||||
return errors.Errorf("failed to configure terminal: %v", err)
|
||||
}
|
||||
|
||||
_, err = monitor.RunMonitor(ctx, "", nil, &controllerapi.InvokeConfig{
|
||||
Tty: true,
|
||||
}, c, dockerCli.In(), os.Stdout, os.Stderr, printer)
|
||||
con.Reset()
|
||||
return err
|
||||
},
|
||||
}
|
||||
cobrautil.MarkCommandExperimental(cmd)
|
||||
|
||||
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(&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")
|
||||
|
||||
for _, c := range children {
|
||||
cmd.AddCommand(c.NewDebugger(&options))
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
|
@ -426,23 +426,32 @@ workers0:
|
|||
}
|
||||
|
||||
provIndex := slices.IndexFunc(attachments, func(a attachment) bool {
|
||||
return descrType(a.descr) == slsa02.PredicateSLSAProvenance
|
||||
return strings.HasPrefix(descrType(a.descr), "https://slsa.dev/provenance/")
|
||||
})
|
||||
if provIndex != -1 {
|
||||
prov := attachments[provIndex]
|
||||
predType := descrType(prov.descr)
|
||||
dt, err := content.ReadBlob(ctx, store, prov.descr)
|
||||
if err != nil {
|
||||
return errors.Errorf("failed to read provenance %s: %v", prov.descr.Digest, err)
|
||||
}
|
||||
var pred provenancetypes.ProvenancePredicate
|
||||
if err := json.Unmarshal(dt, &pred); err != nil {
|
||||
var pred *provenancetypes.ProvenancePredicateSLSA1
|
||||
if predType == slsa02.PredicateSLSAProvenance {
|
||||
var pred02 *provenancetypes.ProvenancePredicateSLSA02
|
||||
if err := json.Unmarshal(dt, &pred02); err != nil {
|
||||
return errors.Errorf("failed to unmarshal provenance %s: %v", prov.descr.Digest, err)
|
||||
}
|
||||
pred = pred02.ConvertToSLSA1()
|
||||
} else if err := json.Unmarshal(dt, &pred); err != nil {
|
||||
return errors.Errorf("failed to unmarshal provenance %s: %v", prov.descr.Digest, err)
|
||||
}
|
||||
for _, m := range pred.Materials {
|
||||
out.Materials = append(out.Materials, materialOutput{
|
||||
URI: m.URI,
|
||||
Digests: digestSetToDigests(m.Digest),
|
||||
})
|
||||
if pred != nil {
|
||||
for _, m := range pred.BuildDefinition.ResolvedDependencies {
|
||||
out.Materials = append(out.Materials, materialOutput{
|
||||
URI: m.URI,
|
||||
Digests: digestSetToDigests(m.Digest),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -836,6 +845,7 @@ func ociDesc(in *controlapi.Descriptor) ocispecs.Descriptor {
|
|||
Annotations: in.Annotations,
|
||||
}
|
||||
}
|
||||
|
||||
func descrType(desc ocispecs.Descriptor) string {
|
||||
if typ, ok := desc.Annotations["in-toto.io/predicate-type"]; ok {
|
||||
return typ
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/docker/cli/cli/command"
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
slsa02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
|
||||
slsa1 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1"
|
||||
"github.com/opencontainers/go-digest"
|
||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
|
@ -76,25 +77,30 @@ func runAttachment(ctx context.Context, dockerCli command.Cli, opts attachmentOp
|
|||
return err
|
||||
}
|
||||
|
||||
typ := opts.typ
|
||||
switch typ {
|
||||
types := make(map[string]struct{})
|
||||
switch opts.typ {
|
||||
case "index":
|
||||
typ = ocispecs.MediaTypeImageIndex
|
||||
types[ocispecs.MediaTypeImageIndex] = struct{}{}
|
||||
case "manifest":
|
||||
typ = ocispecs.MediaTypeImageManifest
|
||||
types[ocispecs.MediaTypeImageManifest] = struct{}{}
|
||||
case "image":
|
||||
typ = ocispecs.MediaTypeImageConfig
|
||||
types[ocispecs.MediaTypeImageConfig] = struct{}{}
|
||||
case "provenance":
|
||||
typ = slsa02.PredicateSLSAProvenance
|
||||
types[slsa1.PredicateSLSAProvenance] = struct{}{}
|
||||
types[slsa02.PredicateSLSAProvenance] = struct{}{}
|
||||
case "sbom":
|
||||
typ = intoto.PredicateSPDX
|
||||
types[intoto.PredicateSPDX] = struct{}{}
|
||||
default:
|
||||
if opts.typ != "" {
|
||||
types[opts.typ] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
for _, a := range attachments {
|
||||
if opts.platform != "" && (a.platform == nil || platforms.FormatAll(*a.platform) != opts.platform) {
|
||||
continue
|
||||
}
|
||||
if typ != "" && descrType(a.descr) != typ {
|
||||
if _, ok := types[descrType(a.descr)]; opts.typ != "" && !ok {
|
||||
continue
|
||||
}
|
||||
ra, err := store.ReaderAt(ctx, a.descr)
|
||||
|
@ -112,9 +118,9 @@ func attachmentCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
|
|||
var options attachmentOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "attachment [OPTIONS] REF [DIGEST]",
|
||||
Use: "attachment [OPTIONS] [REF [DIGEST]]",
|
||||
Short: "Inspect a build record attachment",
|
||||
Args: cobra.RangeArgs(1, 2),
|
||||
Args: cobra.MaximumNArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 0 {
|
||||
options.ref = args[0]
|
||||
|
|
|
@ -182,7 +182,7 @@ func pruneCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
|||
flags.BoolVarP(&options.force, "force", "f", false, "Do not prompt for confirmation")
|
||||
|
||||
flags.Var(&options.reservedSpace, "keep-storage", "Amount of disk space to keep for cache")
|
||||
flags.MarkDeprecated("keep-storage", "keep-storage flag has been changed to max-storage")
|
||||
flags.MarkDeprecated("keep-storage", "keep-storage flag has been changed to reserved-space")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
|
||||
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/util/cobrautil/completion"
|
||||
|
@ -120,9 +119,7 @@ func addCommands(cmd *cobra.Command, opts *rootOptions, dockerCli command.Cli) {
|
|||
historycmd.RootCmd(cmd, dockerCli, historycmd.RootOptions{Builder: &opts.builder}),
|
||||
)
|
||||
if confutil.IsExperimental() {
|
||||
cmd.AddCommand(debugcmd.RootCmd(dockerCli,
|
||||
newDebuggableBuild(dockerCli, opts),
|
||||
))
|
||||
cmd.AddCommand(debugCmd(dockerCli, opts))
|
||||
}
|
||||
|
||||
cmd.RegisterFlagCompletionFunc( //nolint:errcheck
|
||||
|
|
|
@ -1,288 +0,0 @@
|
|||
package build
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/buildx/build"
|
||||
"github.com/docker/buildx/builder"
|
||||
controllerapi "github.com/docker/buildx/controller/pb"
|
||||
"github.com/docker/buildx/store"
|
||||
"github.com/docker/buildx/store/storeutil"
|
||||
"github.com/docker/buildx/util/buildflags"
|
||||
"github.com/docker/buildx/util/confutil"
|
||||
"github.com/docker/buildx/util/dockerutil"
|
||||
"github.com/docker/buildx/util/platformutil"
|
||||
"github.com/docker/buildx/util/progress"
|
||||
"github.com/docker/cli/cli/command"
|
||||
dockeropts "github.com/docker/cli/opts"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/moby/buildkit/session/auth/authprovider"
|
||||
"github.com/moby/buildkit/util/grpcerrors"
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
const defaultTargetName = "default"
|
||||
|
||||
// RunBuild runs the specified build and returns the result.
|
||||
//
|
||||
// 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 *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")
|
||||
}
|
||||
|
||||
contexts := map[string]build.NamedContext{}
|
||||
for name, path := range in.NamedContexts {
|
||||
contexts[name] = build.NamedContext{Path: path}
|
||||
}
|
||||
|
||||
opts := build.Options{
|
||||
Inputs: build.Inputs{
|
||||
ContextPath: in.ContextPath,
|
||||
DockerfilePath: in.DockerfileName,
|
||||
InStream: build.NewSyncMultiReader(inStream),
|
||||
NamedContexts: contexts,
|
||||
},
|
||||
Ref: in.Ref,
|
||||
BuildArgs: in.BuildArgs,
|
||||
CgroupParent: in.CgroupParent,
|
||||
ExtraHosts: in.ExtraHosts,
|
||||
Labels: in.Labels,
|
||||
NetworkMode: in.NetworkMode,
|
||||
NoCache: in.NoCache,
|
||||
NoCacheFilter: in.NoCacheFilter,
|
||||
Pull: in.Pull,
|
||||
ShmSize: dockeropts.MemBytes(in.ShmSize),
|
||||
Tags: in.Tags,
|
||||
Target: in.Target,
|
||||
Ulimits: controllerUlimitOpt2DockerUlimit(in.Ulimits),
|
||||
GroupRef: in.GroupRef,
|
||||
ProvenanceResponseMode: confutil.ParseMetadataProvenance(in.ProvenanceResponseMode),
|
||||
}
|
||||
|
||||
platforms, err := platformutil.Parse(in.Platforms)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
opts.Platforms = platforms
|
||||
|
||||
dockerConfig := dockerCli.ConfigFile()
|
||||
opts.Session = append(opts.Session, authprovider.NewDockerAuthProvider(authprovider.DockerAuthProviderConfig{
|
||||
ConfigFile: dockerConfig,
|
||||
}))
|
||||
|
||||
secrets, err := controllerapi.CreateSecrets(in.Secrets)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
opts.Session = append(opts.Session, secrets)
|
||||
|
||||
sshSpecs := in.SSH
|
||||
if len(sshSpecs) == 0 && buildflags.IsGitSSH(in.ContextPath) {
|
||||
sshSpecs = append(sshSpecs, &controllerapi.SSH{ID: "default"})
|
||||
}
|
||||
ssh, err := controllerapi.CreateSSH(sshSpecs)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
opts.Session = append(opts.Session, ssh)
|
||||
|
||||
outputs, _, err := controllerapi.CreateExports(in.Exports)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
if in.ExportPush {
|
||||
var pushUsed bool
|
||||
for i := range outputs {
|
||||
if outputs[i].Type == client.ExporterImage {
|
||||
outputs[i].Attrs["push"] = "true"
|
||||
pushUsed = true
|
||||
}
|
||||
}
|
||||
if !pushUsed {
|
||||
outputs = append(outputs, client.ExportEntry{
|
||||
Type: client.ExporterImage,
|
||||
Attrs: map[string]string{
|
||||
"push": "true",
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
if in.ExportLoad {
|
||||
var loadUsed bool
|
||||
for i := range outputs {
|
||||
if outputs[i].Type == client.ExporterDocker {
|
||||
if _, ok := outputs[i].Attrs["dest"]; !ok {
|
||||
loadUsed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !loadUsed {
|
||||
outputs = append(outputs, client.ExportEntry{
|
||||
Type: client.ExporterDocker,
|
||||
Attrs: map[string]string{},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
annotations, err := buildflags.ParseAnnotations(in.Annotations)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrap(err, "parse annotations")
|
||||
}
|
||||
|
||||
for _, o := range outputs {
|
||||
for k, v := range annotations {
|
||||
o.Attrs[k.String()] = v
|
||||
}
|
||||
}
|
||||
|
||||
opts.Exports = outputs
|
||||
|
||||
opts.CacheFrom = controllerapi.CreateCaches(in.CacheFrom)
|
||||
opts.CacheTo = controllerapi.CreateCaches(in.CacheTo)
|
||||
|
||||
opts.Attests = controllerapi.CreateAttestations(in.Attests)
|
||||
|
||||
opts.SourcePolicy = in.SourcePolicy
|
||||
|
||||
allow, err := buildflags.ParseEntitlements(in.Allow)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
opts.Allow = allow
|
||||
|
||||
if in.CallFunc != nil {
|
||||
opts.CallFunc = &build.CallFunc{
|
||||
Name: in.CallFunc.Name,
|
||||
Format: in.CallFunc.Format,
|
||||
IgnoreStatus: in.CallFunc.IgnoreStatus,
|
||||
}
|
||||
}
|
||||
|
||||
// key string used for kubernetes "sticky" mode
|
||||
contextPathHash, err := filepath.Abs(in.ContextPath)
|
||||
if err != nil {
|
||||
contextPathHash = in.ContextPath
|
||||
}
|
||||
|
||||
// TODO: this should not be loaded this side of the controller api
|
||||
b, err := builder.New(dockerCli,
|
||||
builder.WithName(in.Builder),
|
||||
builder.WithContextPathHash(contextPathHash),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
if err = updateLastActivity(dockerCli, b.NodeGroup); err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "failed to update builder last activity time")
|
||||
}
|
||||
nodes, err := b.LoadNodes(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
var inputs *build.Inputs
|
||||
buildOptions := map[string]build.Options{defaultTargetName: opts}
|
||||
resp, res, err := buildTargets(ctx, dockerCli, nodes, buildOptions, progress, generateResult)
|
||||
err = wrapBuildError(err, false)
|
||||
if err != nil {
|
||||
// NOTE: buildTargets can return *build.ResultHandle even on error.
|
||||
return nil, res, nil, err
|
||||
}
|
||||
if i, ok := buildOptions[defaultTargetName]; ok {
|
||||
inputs = &i.Inputs
|
||||
}
|
||||
return resp, res, inputs, nil
|
||||
}
|
||||
|
||||
// buildTargets runs the specified build and returns the result.
|
||||
//
|
||||
// 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 buildTargets(ctx context.Context, dockerCli command.Cli, nodes []builder.Node, opts map[string]build.Options, progress progress.Writer, generateResult bool) (*client.SolveResponse, *build.ResultHandle, error) {
|
||||
var res *build.ResultHandle
|
||||
var resp map[string]*client.SolveResponse
|
||||
var err error
|
||||
if generateResult {
|
||||
var mu sync.Mutex
|
||||
var idx int
|
||||
resp, err = build.BuildWithResultHandler(ctx, nodes, opts, dockerutil.NewClient(dockerCli), confutil.NewConfig(dockerCli), progress, func(driverIndex int, gotRes *build.ResultHandle) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if res == nil || driverIndex < idx {
|
||||
idx, res = driverIndex, gotRes
|
||||
}
|
||||
})
|
||||
} else {
|
||||
resp, err = build.Build(ctx, nodes, opts, dockerutil.NewClient(dockerCli), confutil.NewConfig(dockerCli), progress)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, res, err
|
||||
}
|
||||
return resp[defaultTargetName], res, err
|
||||
}
|
||||
|
||||
func wrapBuildError(err error, bake bool) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
st, ok := grpcerrors.AsGRPCStatus(err)
|
||||
if ok {
|
||||
if st.Code() == codes.Unimplemented && strings.Contains(st.Message(), "unsupported frontend capability moby.buildkit.frontend.contexts") {
|
||||
msg := "current frontend does not support --build-context."
|
||||
if bake {
|
||||
msg = "current frontend does not support defining additional contexts for targets."
|
||||
}
|
||||
msg += " Named contexts are supported since Dockerfile v1.4. Use #syntax directive in Dockerfile or update to latest BuildKit."
|
||||
return &wrapped{err, msg}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type wrapped struct {
|
||||
err error
|
||||
msg string
|
||||
}
|
||||
|
||||
func (w *wrapped) Error() string {
|
||||
return w.msg
|
||||
}
|
||||
|
||||
func (w *wrapped) Unwrap() error {
|
||||
return w.err
|
||||
}
|
||||
|
||||
func updateLastActivity(dockerCli command.Cli, ng *store.NodeGroup) error {
|
||||
txn, release, err := storeutil.GetStore(dockerCli)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer release()
|
||||
return txn.UpdateLastActivity(ng)
|
||||
}
|
||||
|
||||
func controllerUlimitOpt2DockerUlimit(u *controllerapi.UlimitOpt) *dockeropts.UlimitOpt {
|
||||
if u == nil {
|
||||
return nil
|
||||
}
|
||||
values := make(map[string]*container.Ulimit)
|
||||
for k, v := range u.Values {
|
||||
values[k] = &container.Ulimit{
|
||||
Name: v.Name,
|
||||
Hard: v.Hard,
|
||||
Soft: v.Soft,
|
||||
}
|
||||
}
|
||||
return dockeropts.NewUlimitOpt(&values)
|
||||
}
|
|
@ -1,216 +0,0 @@
|
|||
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 *Options) (_ *Options, err error) {
|
||||
localContext := false
|
||||
if options.ContextPath != "" && options.ContextPath != "-" {
|
||||
if !isRemoteURL(options.ContextPath) {
|
||||
localContext = true
|
||||
options.ContextPath, err = filepath.Abs(options.ContextPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if options.DockerfileName != "" && options.DockerfileName != "-" {
|
||||
if localContext && !isHTTPURL(options.DockerfileName) {
|
||||
options.DockerfileName, err = filepath.Abs(options.DockerfileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var contexts map[string]string
|
||||
for k, v := range options.NamedContexts {
|
||||
if isRemoteURL(v) || strings.HasPrefix(v, "docker-image://") {
|
||||
// url prefix, this is a remote path
|
||||
} else if p, ok := strings.CutPrefix(v, "oci-layout://"); ok {
|
||||
// oci layout prefix, this is a local path
|
||||
p, err = filepath.Abs(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v = "oci-layout://" + p
|
||||
} else {
|
||||
// no prefix, assume local path
|
||||
v, err = filepath.Abs(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if contexts == nil {
|
||||
contexts = make(map[string]string)
|
||||
}
|
||||
contexts[k] = v
|
||||
}
|
||||
options.NamedContexts = contexts
|
||||
|
||||
var cacheFrom []*pb.CacheOptionsEntry
|
||||
for _, co := range options.CacheFrom {
|
||||
switch co.Type {
|
||||
case "local":
|
||||
var attrs map[string]string
|
||||
for k, v := range co.Attrs {
|
||||
if attrs == nil {
|
||||
attrs = make(map[string]string)
|
||||
}
|
||||
switch k {
|
||||
case "src":
|
||||
p := v
|
||||
if p != "" {
|
||||
p, err = filepath.Abs(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
attrs[k] = p
|
||||
default:
|
||||
attrs[k] = v
|
||||
}
|
||||
}
|
||||
co.Attrs = attrs
|
||||
cacheFrom = append(cacheFrom, co)
|
||||
default:
|
||||
cacheFrom = append(cacheFrom, co)
|
||||
}
|
||||
}
|
||||
options.CacheFrom = cacheFrom
|
||||
|
||||
var cacheTo []*pb.CacheOptionsEntry
|
||||
for _, co := range options.CacheTo {
|
||||
switch co.Type {
|
||||
case "local":
|
||||
var attrs map[string]string
|
||||
for k, v := range co.Attrs {
|
||||
if attrs == nil {
|
||||
attrs = make(map[string]string)
|
||||
}
|
||||
switch k {
|
||||
case "dest":
|
||||
p := v
|
||||
if p != "" {
|
||||
p, err = filepath.Abs(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
attrs[k] = p
|
||||
default:
|
||||
attrs[k] = v
|
||||
}
|
||||
}
|
||||
co.Attrs = attrs
|
||||
cacheTo = append(cacheTo, co)
|
||||
default:
|
||||
cacheTo = append(cacheTo, co)
|
||||
}
|
||||
}
|
||||
options.CacheTo = cacheTo
|
||||
var exports []*pb.ExportEntry
|
||||
for _, e := range options.Exports {
|
||||
if e.Destination != "" && e.Destination != "-" {
|
||||
e.Destination, err = filepath.Abs(e.Destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
exports = append(exports, e)
|
||||
}
|
||||
options.Exports = exports
|
||||
|
||||
var secrets []*pb.Secret
|
||||
for _, s := range options.Secrets {
|
||||
if s.FilePath != "" {
|
||||
s.FilePath, err = filepath.Abs(s.FilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
secrets = append(secrets, s)
|
||||
}
|
||||
options.Secrets = secrets
|
||||
|
||||
var ssh []*pb.SSH
|
||||
for _, s := range options.SSH {
|
||||
var ps []string
|
||||
for _, pt := range s.Paths {
|
||||
p := pt
|
||||
if p != "" {
|
||||
p, err = filepath.Abs(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
ps = append(ps, p)
|
||||
}
|
||||
s.Paths = ps
|
||||
ssh = append(ssh, s)
|
||||
}
|
||||
options.SSH = ssh
|
||||
|
||||
return options, nil
|
||||
}
|
||||
|
||||
// isHTTPURL returns true if the provided str is an HTTP(S) URL by checking if it
|
||||
// has a http:// or https:// scheme. No validation is performed to verify if the
|
||||
// URL is well-formed.
|
||||
func isHTTPURL(str string) bool {
|
||||
return strings.HasPrefix(str, "https://") || strings.HasPrefix(str, "http://")
|
||||
}
|
||||
|
||||
func isRemoteURL(c string) bool {
|
||||
if isHTTPURL(c) {
|
||||
return true
|
||||
}
|
||||
if _, err := gitutil.ParseGitRef(c); err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -1,249 +0,0 @@
|
|||
package build
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/buildx/controller/pb"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestResolvePaths(t *testing.T) {
|
||||
tmpwd, err := os.MkdirTemp("", "testresolvepaths")
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tmpwd)
|
||||
require.NoError(t, os.Chdir(tmpwd))
|
||||
tests := []struct {
|
||||
name string
|
||||
options *Options
|
||||
want *Options
|
||||
}{
|
||||
{
|
||||
name: "contextpath",
|
||||
options: &Options{ContextPath: "test"},
|
||||
want: &Options{ContextPath: filepath.Join(tmpwd, "test")},
|
||||
},
|
||||
{
|
||||
name: "contextpath-cwd",
|
||||
options: &Options{ContextPath: "."},
|
||||
want: &Options{ContextPath: tmpwd},
|
||||
},
|
||||
{
|
||||
name: "contextpath-dash",
|
||||
options: &Options{ContextPath: "-"},
|
||||
want: &Options{ContextPath: "-"},
|
||||
},
|
||||
{
|
||||
name: "contextpath-ssh",
|
||||
options: &Options{ContextPath: "git@github.com:docker/buildx.git"},
|
||||
want: &Options{ContextPath: "git@github.com:docker/buildx.git"},
|
||||
},
|
||||
{
|
||||
name: "dockerfilename",
|
||||
options: &Options{DockerfileName: "test", ContextPath: "."},
|
||||
want: &Options{DockerfileName: filepath.Join(tmpwd, "test"), ContextPath: tmpwd},
|
||||
},
|
||||
{
|
||||
name: "dockerfilename-dash",
|
||||
options: &Options{DockerfileName: "-", ContextPath: "."},
|
||||
want: &Options{DockerfileName: "-", ContextPath: tmpwd},
|
||||
},
|
||||
{
|
||||
name: "dockerfilename-remote",
|
||||
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: &Options{NamedContexts: map[string]string{
|
||||
"a": "test1", "b": "test2",
|
||||
"alpine": "docker-image://alpine@sha256:0123456789", "project": "https://github.com/myuser/project.git",
|
||||
}},
|
||||
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: &Options{
|
||||
CacheFrom: []*pb.CacheOptionsEntry{
|
||||
{
|
||||
Type: "local",
|
||||
Attrs: map[string]string{"src": "test"},
|
||||
},
|
||||
{
|
||||
Type: "registry",
|
||||
Attrs: map[string]string{"ref": "user/app"},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &Options{
|
||||
CacheFrom: []*pb.CacheOptionsEntry{
|
||||
{
|
||||
Type: "local",
|
||||
Attrs: map[string]string{"src": filepath.Join(tmpwd, "test")},
|
||||
},
|
||||
{
|
||||
Type: "registry",
|
||||
Attrs: map[string]string{"ref": "user/app"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cache-to",
|
||||
options: &Options{
|
||||
CacheTo: []*pb.CacheOptionsEntry{
|
||||
{
|
||||
Type: "local",
|
||||
Attrs: map[string]string{"dest": "test"},
|
||||
},
|
||||
{
|
||||
Type: "registry",
|
||||
Attrs: map[string]string{"ref": "user/app"},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &Options{
|
||||
CacheTo: []*pb.CacheOptionsEntry{
|
||||
{
|
||||
Type: "local",
|
||||
Attrs: map[string]string{"dest": filepath.Join(tmpwd, "test")},
|
||||
},
|
||||
{
|
||||
Type: "registry",
|
||||
Attrs: map[string]string{"ref": "user/app"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "exports",
|
||||
options: &Options{
|
||||
Exports: []*pb.ExportEntry{
|
||||
{
|
||||
Type: "local",
|
||||
Destination: "-",
|
||||
},
|
||||
{
|
||||
Type: "local",
|
||||
Destination: "test1",
|
||||
},
|
||||
{
|
||||
Type: "tar",
|
||||
Destination: "test3",
|
||||
},
|
||||
{
|
||||
Type: "oci",
|
||||
Destination: "-",
|
||||
},
|
||||
{
|
||||
Type: "docker",
|
||||
Destination: "test4",
|
||||
},
|
||||
{
|
||||
Type: "image",
|
||||
Attrs: map[string]string{"push": "true"},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &Options{
|
||||
Exports: []*pb.ExportEntry{
|
||||
{
|
||||
Type: "local",
|
||||
Destination: "-",
|
||||
},
|
||||
{
|
||||
Type: "local",
|
||||
Destination: filepath.Join(tmpwd, "test1"),
|
||||
},
|
||||
{
|
||||
Type: "tar",
|
||||
Destination: filepath.Join(tmpwd, "test3"),
|
||||
},
|
||||
{
|
||||
Type: "oci",
|
||||
Destination: "-",
|
||||
},
|
||||
{
|
||||
Type: "docker",
|
||||
Destination: filepath.Join(tmpwd, "test4"),
|
||||
},
|
||||
{
|
||||
Type: "image",
|
||||
Attrs: map[string]string{"push": "true"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "secrets",
|
||||
options: &Options{
|
||||
Secrets: []*pb.Secret{
|
||||
{
|
||||
FilePath: "test1",
|
||||
},
|
||||
{
|
||||
ID: "val",
|
||||
Env: "a",
|
||||
},
|
||||
{
|
||||
ID: "test",
|
||||
FilePath: "test3",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &Options{
|
||||
Secrets: []*pb.Secret{
|
||||
{
|
||||
FilePath: filepath.Join(tmpwd, "test1"),
|
||||
},
|
||||
{
|
||||
ID: "val",
|
||||
Env: "a",
|
||||
},
|
||||
{
|
||||
ID: "test",
|
||||
FilePath: filepath.Join(tmpwd, "test3"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ssh",
|
||||
options: &Options{
|
||||
SSH: []*pb.SSH{
|
||||
{
|
||||
ID: "default",
|
||||
Paths: []string{"test1", "test2"},
|
||||
},
|
||||
{
|
||||
ID: "a",
|
||||
Paths: []string{"test3"},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &Options{
|
||||
SSH: []*pb.SSH{
|
||||
{
|
||||
ID: "default",
|
||||
Paths: []string{filepath.Join(tmpwd, "test1"), filepath.Join(tmpwd, "test2")},
|
||||
},
|
||||
{
|
||||
ID: "a",
|
||||
Paths: []string{filepath.Join(tmpwd, "test3")},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ResolveOptionPaths(tt.options)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package control
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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 *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, pid string, options *controllerapi.InvokeConfig, ioIn io.ReadCloser, ioOut io.WriteCloser, ioErr io.WriteCloser) error
|
||||
Close() error
|
||||
ListProcesses(ctx context.Context) (infos []*processes.ProcessInfo, retErr error)
|
||||
DisconnectProcess(ctx context.Context, pid string) error
|
||||
Inspect(ctx context.Context) *cbuild.Options
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/buildx/controller/control"
|
||||
"github.com/docker/buildx/controller/local"
|
||||
"github.com/docker/cli/cli/command"
|
||||
)
|
||||
|
||||
func NewController(ctx context.Context, dockerCli command.Cli) control.BuildxController {
|
||||
return local.NewLocalBuildxController(ctx, dockerCli)
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package errdefs
|
||||
|
||||
type BuildError struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *BuildError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
func (e *BuildError) Error() string {
|
||||
return e.err.Error()
|
||||
}
|
||||
|
||||
func WrapBuild(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &BuildError{err: err}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
package local
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/docker/buildx/build"
|
||||
cbuild "github.com/docker/buildx/controller/build"
|
||||
"github.com/docker/buildx/controller/control"
|
||||
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/ioset"
|
||||
"github.com/docker/buildx/util/progress"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func NewLocalBuildxController(ctx context.Context, dockerCli command.Cli) control.BuildxController {
|
||||
return &localController{
|
||||
dockerCli: dockerCli,
|
||||
processes: processes.NewManager(),
|
||||
}
|
||||
}
|
||||
|
||||
type buildConfig struct {
|
||||
// TODO: these two structs should be merged
|
||||
// Discussion: https://github.com/docker/buildx/pull/1640#discussion_r1113279719
|
||||
resultCtx *build.ResultHandle
|
||||
buildOptions *cbuild.Options
|
||||
}
|
||||
|
||||
type localController struct {
|
||||
dockerCli command.Cli
|
||||
buildConfig buildConfig
|
||||
processes *processes.Manager
|
||||
|
||||
buildOnGoing atomic.Bool
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
defer b.buildOnGoing.Store(false)
|
||||
|
||||
resp, res, dockerfileMappings, buildErr := cbuild.RunBuild(ctx, b.dockerCli, options, in, progress, true)
|
||||
// NOTE: RunBuild can return *build.ResultHandle even on error.
|
||||
if res != nil {
|
||||
b.buildConfig = buildConfig{
|
||||
resultCtx: res,
|
||||
buildOptions: options,
|
||||
}
|
||||
if buildErr != nil {
|
||||
buildErr = controllererrors.WrapBuild(buildErr)
|
||||
}
|
||||
}
|
||||
if buildErr != nil {
|
||||
return nil, nil, buildErr
|
||||
}
|
||||
return resp, dockerfileMappings, nil
|
||||
}
|
||||
|
||||
func (b *localController) ListProcesses(ctx context.Context) (infos []*processes.ProcessInfo, retErr error) {
|
||||
return b.processes.ListProcesses(), nil
|
||||
}
|
||||
|
||||
func (b *localController) DisconnectProcess(ctx context.Context, pid string) error {
|
||||
return b.processes.DeleteProcess(pid)
|
||||
}
|
||||
|
||||
func (b *localController) cancelRunningProcesses() {
|
||||
b.processes.CancelRunningProcesses()
|
||||
}
|
||||
|
||||
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.
|
||||
if b.buildConfig.resultCtx == nil {
|
||||
return errors.New("no build result is registered")
|
||||
}
|
||||
var err error
|
||||
proc, err = b.processes.StartProcess(pid, b.buildConfig.resultCtx, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Attach containerIn to this process
|
||||
ioCancelledCh := make(chan struct{})
|
||||
proc.ForwardIO(&ioset.In{Stdin: ioIn, Stdout: ioOut, Stderr: ioErr}, func(error) { close(ioCancelledCh) })
|
||||
|
||||
select {
|
||||
case <-ioCancelledCh:
|
||||
return errors.Errorf("io cancelled")
|
||||
case err := <-proc.Done():
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
return context.Cause(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *localController) Close() error {
|
||||
b.cancelRunningProcesses()
|
||||
if b.buildConfig.resultCtx != nil {
|
||||
b.buildConfig.resultCtx.Done()
|
||||
}
|
||||
// TODO: cancel ongoing builds?
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *localController) Inspect(ctx context.Context) *cbuild.Options {
|
||||
return b.buildConfig.buildOptions
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
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 {
|
||||
// ignore duplicates
|
||||
if _, ok := result[attest.Type]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if attest.Disabled {
|
||||
result[attest.Type] = nil
|
||||
continue
|
||||
}
|
||||
|
||||
attrs := attest.Attrs
|
||||
result[attest.Type] = &attrs
|
||||
}
|
||||
return result
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
package pb
|
||||
|
||||
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
|
||||
if len(entries) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, entry := range entries {
|
||||
out := client.CacheOptionsEntry{
|
||||
Type: entry.Type,
|
||||
Attrs: map[string]string{},
|
||||
}
|
||||
maps.Copy(out.Attrs, entry.Attrs)
|
||||
outs = append(outs, out)
|
||||
}
|
||||
return outs
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
package pb
|
||||
|
||||
import (
|
||||
"io"
|
||||
"maps"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/containerd/console"
|
||||
"github.com/moby/buildkit/client"
|
||||
"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
|
||||
if len(entries) == 0 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
var stdoutUsed bool
|
||||
for _, entry := range entries {
|
||||
if entry.Type == "" {
|
||||
return nil, nil, errors.Errorf("type is required for output")
|
||||
}
|
||||
|
||||
out := client.ExportEntry{
|
||||
Type: entry.Type,
|
||||
Attrs: map[string]string{},
|
||||
}
|
||||
maps.Copy(out.Attrs, entry.Attrs)
|
||||
|
||||
supportFile := false
|
||||
supportDir := false
|
||||
switch out.Type {
|
||||
case client.ExporterLocal:
|
||||
supportDir = true
|
||||
case client.ExporterTar:
|
||||
supportFile = true
|
||||
case client.ExporterOCI, client.ExporterDocker:
|
||||
tar, err := strconv.ParseBool(out.Attrs["tar"])
|
||||
if err != nil {
|
||||
tar = true
|
||||
}
|
||||
supportFile = tar
|
||||
supportDir = !tar
|
||||
case "registry":
|
||||
out.Type = client.ExporterImage
|
||||
out.Attrs["push"] = "true"
|
||||
}
|
||||
|
||||
if supportDir {
|
||||
if entry.Destination == "" {
|
||||
return nil, nil, errors.Errorf("dest is required for %s exporter", out.Type)
|
||||
}
|
||||
if entry.Destination == "-" {
|
||||
return nil, nil, errors.Errorf("dest cannot be stdout for %s exporter", out.Type)
|
||||
}
|
||||
|
||||
fi, err := os.Stat(entry.Destination)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, nil, errors.Wrapf(err, "invalid destination directory: %s", entry.Destination)
|
||||
}
|
||||
if err == nil && !fi.IsDir() {
|
||||
return nil, nil, errors.Errorf("destination directory %s is a file", entry.Destination)
|
||||
}
|
||||
out.OutputDir = entry.Destination
|
||||
localPaths = append(localPaths, entry.Destination)
|
||||
}
|
||||
if supportFile {
|
||||
if entry.Destination == "" && out.Type != client.ExporterDocker {
|
||||
entry.Destination = "-"
|
||||
}
|
||||
if entry.Destination == "-" {
|
||||
if stdoutUsed {
|
||||
return nil, nil, errors.Errorf("multiple outputs configured to write to stdout")
|
||||
}
|
||||
if _, err := console.ConsoleFromFile(os.Stdout); err == nil {
|
||||
return nil, nil, errors.Errorf("dest file is required for %s exporter. refusing to write to console", out.Type)
|
||||
}
|
||||
out.Output = wrapWriteCloser(os.Stdout)
|
||||
stdoutUsed = true
|
||||
} else if entry.Destination != "" {
|
||||
fi, err := os.Stat(entry.Destination)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, nil, errors.Wrapf(err, "invalid destination file: %s", entry.Destination)
|
||||
}
|
||||
if err == nil && fi.IsDir() {
|
||||
return nil, nil, errors.Errorf("destination file %s is a directory", entry.Destination)
|
||||
}
|
||||
f, err := os.Create(entry.Destination)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Errorf("failed to open %s", err)
|
||||
}
|
||||
out.Output = wrapWriteCloser(f)
|
||||
localPaths = append(localPaths, entry.Destination)
|
||||
}
|
||||
}
|
||||
|
||||
outs = append(outs, out)
|
||||
}
|
||||
return outs, localPaths, nil
|
||||
}
|
||||
|
||||
func wrapWriteCloser(wc io.WriteCloser) func(map[string]string) (io.WriteCloser, error) {
|
||||
return func(map[string]string) (io.WriteCloser, error) {
|
||||
return wc, nil
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
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,28 +0,0 @@
|
|||
package pb
|
||||
|
||||
import (
|
||||
"github.com/moby/buildkit/session"
|
||||
"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 {
|
||||
fs = append(fs, secretsprovider.Source{
|
||||
ID: secret.ID,
|
||||
FilePath: secret.FilePath,
|
||||
Env: secret.Env,
|
||||
})
|
||||
}
|
||||
store, err := secretsprovider.NewStore(fs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return secretsprovider.NewSecretProvider(store), nil
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package pb
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"github.com/moby/buildkit/session"
|
||||
"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 {
|
||||
cfg := sshprovider.AgentConfig{
|
||||
ID: ssh.ID,
|
||||
Paths: slices.Clone(ssh.Paths),
|
||||
}
|
||||
configs = append(configs, cfg)
|
||||
}
|
||||
return sshprovider.NewSSHAgentProvider(configs)
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package pb
|
||||
|
||||
type UlimitOpt struct {
|
||||
Values map[string]*Ulimit
|
||||
}
|
||||
|
||||
type Ulimit struct {
|
||||
Name string
|
||||
Hard int64
|
||||
Soft int64
|
||||
}
|
|
@ -73,6 +73,13 @@ target "lint-gopls" {
|
|||
target = "gopls-analyze"
|
||||
}
|
||||
|
||||
target "modernize-fix" {
|
||||
inherits = ["_common"]
|
||||
dockerfile = "./hack/dockerfiles/lint.Dockerfile"
|
||||
target = "modernize-fix"
|
||||
output = ["."]
|
||||
}
|
||||
|
||||
target "validate-vendor" {
|
||||
inherits = ["_common"]
|
||||
dockerfile = "./hack/dockerfiles/vendor.Dockerfile"
|
||||
|
@ -98,13 +105,6 @@ target "validate-authors" {
|
|||
output = ["type=cacheonly"]
|
||||
}
|
||||
|
||||
target "validate-generated-files" {
|
||||
inherits = ["_common"]
|
||||
dockerfile = "./hack/dockerfiles/generated-files.Dockerfile"
|
||||
target = "validate"
|
||||
output = ["type=cacheonly"]
|
||||
}
|
||||
|
||||
target "update-vendor" {
|
||||
inherits = ["_common"]
|
||||
dockerfile = "./hack/dockerfiles/vendor.Dockerfile"
|
||||
|
@ -130,13 +130,6 @@ target "update-authors" {
|
|||
output = ["."]
|
||||
}
|
||||
|
||||
target "update-generated-files" {
|
||||
inherits = ["_common"]
|
||||
dockerfile = "./hack/dockerfiles/generated-files.Dockerfile"
|
||||
target = "update"
|
||||
output = ["."]
|
||||
}
|
||||
|
||||
target "mod-outdated" {
|
||||
inherits = ["_common"]
|
||||
dockerfile = "./hack/dockerfiles/vendor.Dockerfile"
|
||||
|
|
|
@ -227,6 +227,8 @@ The following table shows the complete list of attributes that you can assign to
|
|||
| [`description`](#targetdescription) | String | Description of a target |
|
||||
| [`dockerfile-inline`](#targetdockerfile-inline) | String | Inline Dockerfile string |
|
||||
| [`dockerfile`](#targetdockerfile) | String | Dockerfile location |
|
||||
| [`entitlements`](#targetentitlements) | List | Permissions that the build process requires to run |
|
||||
| [`extra-hosts`](#targetextra-hosts) | List | Customs host-to-IP mapping |
|
||||
| [`inherits`](#targetinherits) | List | Inherit attributes from other targets |
|
||||
| [`labels`](#targetlabels) | Map | Metadata for images |
|
||||
| [`matrix`](#targetmatrix) | Map | Define a set of variables that forks a target into multiple targets. |
|
||||
|
@ -583,6 +585,20 @@ target "integration-tests" {
|
|||
|
||||
Entitlements are enabled with a two-step process. First, a target must declare the entitlements it requires. Secondly, when invoking the `bake` command, the user must grant the entitlements by passing the `--allow` flag or confirming the entitlements when prompted in an interactive terminal. This is to ensure that the user is aware of the possibly insecure permissions they are granting to the build process.
|
||||
|
||||
### `target.extra-hosts`
|
||||
|
||||
Use the `extra-hosts` attribute to define customs host-to-IP mapping for the
|
||||
target. This has the same effect as passing a [`--add-host`][add-host] flag to
|
||||
the build command.
|
||||
|
||||
```hcl
|
||||
target "default" {
|
||||
extra-hosts = {
|
||||
my_hostname = "8.8.8.8"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `target.inherits`
|
||||
|
||||
A target can inherit attributes from other targets.
|
||||
|
@ -1081,6 +1097,7 @@ or interpolate them in attribute values in your Bake file.
|
|||
|
||||
```hcl
|
||||
variable "TAG" {
|
||||
type = string
|
||||
default = "latest"
|
||||
}
|
||||
|
||||
|
@ -1102,6 +1119,206 @@ overriding the default `latest` value shown in the previous example.
|
|||
$ TAG=dev docker buildx bake webapp-dev
|
||||
```
|
||||
|
||||
Variables can also be assigned an explicit type.
|
||||
If provided, it will be used to validate the default value (if set), as well as any overrides.
|
||||
This is particularly useful when using complex types which are intended to be overridden.
|
||||
The previous example could be expanded to apply an arbitrary series of tags.
|
||||
```hcl
|
||||
variable "TAGS" {
|
||||
default = ["latest"]
|
||||
type = list(string)
|
||||
}
|
||||
|
||||
target "webapp-dev" {
|
||||
dockerfile = "Dockerfile.webapp"
|
||||
tags = [for tag in TAGS: "docker.io/username/webapp:${tag}"]
|
||||
}
|
||||
```
|
||||
|
||||
This example shows how to generate three tags without changing the file
|
||||
or using custom functions/parsing:
|
||||
```console
|
||||
$ TAGS=dev,latest,2 docker buildx bake webapp-dev
|
||||
```
|
||||
|
||||
### Variable typing
|
||||
|
||||
The following primitive types are available:
|
||||
* `string`
|
||||
* `number`
|
||||
* `bool`
|
||||
|
||||
The type is expressed like a keyword; it must be expressed as a literal:
|
||||
```hcl
|
||||
variable "OK" {
|
||||
type = string
|
||||
}
|
||||
|
||||
# cannot be an actual string
|
||||
variable "BAD" {
|
||||
type = "string"
|
||||
}
|
||||
|
||||
# cannot be the result of an expression
|
||||
variable "ALSO_BAD" {
|
||||
type = lower("string")
|
||||
}
|
||||
```
|
||||
Specifying primitive types can be valuable to show intent (especially when a default is not provided),
|
||||
but bake will generally behave as expected without explicit typing.
|
||||
|
||||
Complex types are expressed with "type constructors"; they are:
|
||||
* `tuple([<type>,...])`
|
||||
* `list(<type>)`
|
||||
* `set(<type>)`
|
||||
* `map(<type>)`
|
||||
* `object({<attr>=<type>},...})`
|
||||
|
||||
The following are examples of each of those, as well as how the (optional) default value would be expressed:
|
||||
```hcl
|
||||
# structured way to express "1.2.3-alpha"
|
||||
variable "MY_VERSION" {
|
||||
type = tuple([number, number, number, string])
|
||||
default = [1, 2, 3, "alpha"]
|
||||
}
|
||||
|
||||
# JDK versions used in a matrix build
|
||||
variable "JDK_VERSIONS" {
|
||||
type = list(number)
|
||||
default = [11, 17, 21]
|
||||
}
|
||||
|
||||
# better way to express the previous example; this will also
|
||||
# enforce set semantics and allow use of set-based functions
|
||||
variable "JDK_VERSIONS" {
|
||||
type = set(number)
|
||||
default = [11, 17, 21]
|
||||
}
|
||||
|
||||
# with the help of lookup(), translate a 'feature' to a tag
|
||||
variable "FEATURE_TO_NAME" {
|
||||
type = map(string)
|
||||
default = {featureA = "slim", featureB = "tiny"}
|
||||
}
|
||||
|
||||
# map a branch name to a registry location
|
||||
variable "PUSH_DESTINATION" {
|
||||
type = object({branch = string, registry = string})
|
||||
default = {branch = "main", registry = "prod-registry.invalid.com"}
|
||||
}
|
||||
|
||||
# make the previous example more useful with composition
|
||||
variable "PUSH_DESTINATIONS" {
|
||||
type = list(object({branch = string, registry = string}))
|
||||
default = [
|
||||
{branch = "develop", registry = "test-registry.invalid.com"},
|
||||
{branch = "main", registry = "prod-registry.invalid.com"},
|
||||
]
|
||||
}
|
||||
```
|
||||
Note that in each example, the default value would be valid even if typing was not present.
|
||||
If typing was omitted, the first three would all be considered `tuple`;
|
||||
you would be restricted to functions that operate on `tuple` and, for example, not be able to add elements.
|
||||
Similarly, the third and fourth would both be considered `object`, with the limits and semantics of that type.
|
||||
In short, in the absence of a type, any value delimited with `[]` is a `tuple`
|
||||
and value delimited with `{}` is an `object`.
|
||||
Explicit typing for complex types not only opens up the ability to use functions applicable to that specialized type,
|
||||
but is also a precondition for providing overrides.
|
||||
|
||||
> [!NOTE]
|
||||
> See [HCL Type Expressions][typeexpr] page for more details.
|
||||
|
||||
### Overriding variables
|
||||
|
||||
As mentioned in the [intro to variables](#variable), primitive types (`string`, `number`, and `bool`)
|
||||
can be overridden without typing and will generally behave as expected.
|
||||
(When explicit typing is not provided, a variable is assumed to be primitive when the default value lacks `{}` or `[]` delimiters;
|
||||
a variable with neither typing nor a default value is treated as `string`.)
|
||||
Naturally, these same overrides can be used alongside explicit typing too;
|
||||
they may help in edge cases where you want `VAR=true` to be a `string`, where without typing,
|
||||
it may be a `string` or a `bool` depending on how/where it's used.
|
||||
Overriding a variable with a complex type can only be done when the type is provided.
|
||||
This is still done via environment variables, but the values can be provided via CSV or JSON.
|
||||
|
||||
#### CSV overrides
|
||||
|
||||
This is considered the canonical method and is well suited to interactive usage.
|
||||
It is assumed that `list` and `set` will be the most common complex type,
|
||||
as well as the most common complex type designed to be overridden.
|
||||
Thus, there is full CSV support for `list` and `set`
|
||||
(and `tuple`; despite being considered a structural type, it is more like a collection type in this regard).
|
||||
|
||||
|
||||
There is limited support for `map` and `object` and no support for composite types;
|
||||
for these advanced cases, an alternative mechanism [using JSON](#json-overrides) is available.
|
||||
|
||||
#### JSON overrides
|
||||
|
||||
Overrides can also be provided via JSON.
|
||||
This is the only method available for providing some complex types and may be convenient if overrides are already JSON
|
||||
(for example, if they come from a JSON API).
|
||||
It can also be used when dealing with values are difficult or impossible to specify using CSV (e.g., values containing quotes or commas).
|
||||
To use JSON, simply append `_JSON` to the variable name.
|
||||
In this contrived example, CSV cannot handle the second value; despite being a supported CSV type, JSON must be used:
|
||||
```hcl
|
||||
variable "VALS" {
|
||||
type = list(string)
|
||||
default = ["some", "list"]
|
||||
}
|
||||
```
|
||||
```console
|
||||
$ cat data.json
|
||||
["hello","with,comma","with\"quote"]
|
||||
$ VALS_JSON=$(< data.json) docker buildx bake
|
||||
|
||||
# CSV equivalent, though the second value cannot be expressed at all
|
||||
$ VALS='hello,"with""quote"' docker buildx bake
|
||||
```
|
||||
|
||||
This example illustrates some precedence and usage rules:
|
||||
```hcl
|
||||
variable "FOO" {
|
||||
type = string
|
||||
default = "foo"
|
||||
}
|
||||
|
||||
variable "FOO_JSON" {
|
||||
type = string
|
||||
default = "foo"
|
||||
}
|
||||
```
|
||||
|
||||
The variable `FOO` can *only* be overridden using CSV because `FOO_JSON`, which would typically used for a JSON override,
|
||||
is already a defined variable.
|
||||
Since `FOO_JSON` is an actual variable, setting that environment variable would be expected to a CSV value.
|
||||
A JSON override *is* possible for this variable, using environment variable `FOO_JSON_JSON`.
|
||||
|
||||
```Console
|
||||
# These three are all equivalent, setting variable FOO=bar
|
||||
$ FOO=bar docker buildx bake <...>
|
||||
$ FOO='bar' docker buildx bake <...>
|
||||
$ FOO="bar" docker buildx bake <...>
|
||||
|
||||
# Sets *only* variable FOO_JSON; FOO is untouched
|
||||
$ FOO_JSON=bar docker buildx bake <...>
|
||||
|
||||
# This also sets FOO_JSON, but will fail due to not being valid JSON
|
||||
$ FOO_JSON_JSON=bar docker buildx bake <...>
|
||||
|
||||
# These are all equivalent
|
||||
$ cat data.json
|
||||
"bar"
|
||||
$ FOO_JSON_JSON=$(< data.json) docker buildx bake <...>
|
||||
$ FOO_JSON_JSON='"bar"' docker buildx bake <...>
|
||||
$ FOO_JSON=bar docker buildx bake <...>
|
||||
|
||||
# This results in setting two different variables, both specified as CSV (FOO=bar and FOO_JSON="baz")
|
||||
$ FOO=bar FOO_JSON='"baz"' docker buildx bake <...>
|
||||
|
||||
# These refer to the same variable with FOO_JSON_JSON having precedence and read as JSON (FOO_JSON=baz)
|
||||
$ FOO_JSON=bar FOO_JSON_JSON='"baz"' docker buildx bake <...>
|
||||
```
|
||||
|
||||
### Built-in variables
|
||||
|
||||
The following variables are built-ins that you can use with Bake without having
|
||||
|
@ -1221,6 +1438,7 @@ target "webapp-dev" {
|
|||
|
||||
<!-- external links -->
|
||||
|
||||
[add-host]: https://docs.docker.com/reference/cli/docker/buildx/build/#add-host
|
||||
[attestations]: https://docs.docker.com/build/attestations/
|
||||
[bake_stdlib]: https://github.com/docker/buildx/blob/master/bake/hclparser/stdlib.go
|
||||
[build-arg]: https://docs.docker.com/reference/cli/docker/image/build/#build-arg
|
||||
|
@ -1239,4 +1457,5 @@ target "webapp-dev" {
|
|||
[ssh]: https://docs.docker.com/reference/cli/docker/buildx/build/#ssh
|
||||
[tag]: https://docs.docker.com/reference/cli/docker/image/build/#tag
|
||||
[target]: https://docs.docker.com/reference/cli/docker/image/build/#target
|
||||
[typeexpr]: https://github.com/hashicorp/hcl/tree/main/ext/typeexpr
|
||||
[userfunc]: https://github.com/hashicorp/hcl/tree/main/ext/userfunc
|
||||
|
|
|
@ -26,7 +26,6 @@ Arguments available after `buildx debug build` are the same as the normal `build
|
|||
```console
|
||||
$ docker buildx debug --invoke /bin/sh build .
|
||||
[+] Building 4.2s (19/19) FINISHED
|
||||
=> [internal] connecting to local controller 0.0s
|
||||
=> [internal] load build definition from Dockerfile 0.0s
|
||||
=> => transferring dockerfile: 32B 0.0s
|
||||
=> [internal] load .dockerignore 0.0s
|
||||
|
@ -68,7 +67,6 @@ If you want to start a debug session when a build fails, you can use
|
|||
```console
|
||||
$ docker buildx debug --on=error build .
|
||||
[+] Building 4.2s (19/19) FINISHED
|
||||
=> [internal] connecting to local controller 0.0s
|
||||
=> [internal] load build definition from Dockerfile 0.0s
|
||||
=> => transferring dockerfile: 32B 0.0s
|
||||
=> [internal] load .dockerignore 0.0s
|
||||
|
@ -94,7 +92,6 @@ can use `buildx debug` command to start a debug session.
|
|||
```
|
||||
$ docker buildx debug
|
||||
[+] Building 4.2s (19/19) FINISHED
|
||||
=> [internal] connecting to local controller 0.0s
|
||||
(buildx)
|
||||
```
|
||||
|
||||
|
@ -125,41 +122,3 @@ Available commands are:
|
|||
rollback re-runs the interactive container with the step's rootfs contents
|
||||
```
|
||||
|
||||
## Build controllers
|
||||
|
||||
Debugging is performed using a buildx "controller", which provides a high-level
|
||||
abstraction to perform builds. By default, the local controller is used for a
|
||||
more stable experience which runs all builds in-process. However, you can also
|
||||
use the remote controller to detach the build process from the CLI.
|
||||
|
||||
To detach the build process from the CLI, you can use the `--detach=true` flag with
|
||||
the build command.
|
||||
|
||||
```console
|
||||
$ docker buildx debug --invoke /bin/sh build --detach=true .
|
||||
```
|
||||
|
||||
If you start a debugging session using the `--invoke` flag with a detached
|
||||
build, then you can attach to it using the `buildx debug` command to
|
||||
immediately enter the monitor mode.
|
||||
|
||||
```console
|
||||
$ docker buildx debug
|
||||
[+] Building 0.0s (1/1) FINISHED
|
||||
=> [internal] connecting to remote controller
|
||||
(buildx) list
|
||||
ID CURRENT_SESSION
|
||||
xfe1162ovd9def8yapb4ys66t false
|
||||
(buildx) attach xfe1162ovd9def8yapb4ys66t
|
||||
Attached to process "". Press Ctrl-a-c to switch to the new container
|
||||
(buildx) ps
|
||||
PID CURRENT_SESSION COMMAND
|
||||
3ug8iqaufiwwnukimhqqt06jz false [sh]
|
||||
(buildx) attach 3ug8iqaufiwwnukimhqqt06jz
|
||||
Attached to process "3ug8iqaufiwwnukimhqqt06jz". Press Ctrl-a-c to switch to the new container
|
||||
(buildx) Switched IO
|
||||
/ # ls
|
||||
bin etc lib mnt proc run srv tmp var
|
||||
dev home media opt root sbin sys usr work
|
||||
/ #
|
||||
```
|
||||
|
|
|
@ -143,6 +143,11 @@ Use the `-f` / `--file` option to specify the build definition file to use.
|
|||
The file can be an HCL, JSON or Compose file. If multiple files are specified,
|
||||
all are read and the build configurations are combined.
|
||||
|
||||
Alternatively, the environment variable `BUILDX_BAKE_FILE` can be used to specify the build definition to use.
|
||||
This is mutually exclusive with `-f` / `--file`; if both are specified, the environment variable is ignored.
|
||||
Multiple definitions can be specified by separating them with the system's path separator
|
||||
(typically `;` on Windows and `:` elsewhere), but can be changed with `BUILDX_BAKE_PATH_SEPARATOR`.
|
||||
|
||||
You can pass the names of the targets to build, to build only specific target(s).
|
||||
The following example builds the `db` and `webapp-release` targets that are
|
||||
defined in the `docker-bake.dev.hcl` file:
|
||||
|
@ -198,12 +203,15 @@ To list variables:
|
|||
|
||||
```console
|
||||
$ docker buildx bake --list=variables
|
||||
VARIABLE VALUE DESCRIPTION
|
||||
REGISTRY docker.io/username Registry and namespace
|
||||
IMAGE_NAME my-app Image name
|
||||
GO_VERSION <null>
|
||||
VARIABLE TYPE VALUE DESCRIPTION
|
||||
REGISTRY string docker.io/username Registry and namespace
|
||||
IMAGE_NAME string my-app Image name
|
||||
GO_VERSION <null>
|
||||
DEBUG bool false Add debug symbols
|
||||
```
|
||||
|
||||
Variable types will be shown when set using the `type` property in the Bake file.
|
||||
|
||||
By default, the output of `docker buildx bake --list` is presented in a table
|
||||
format. Alternatively, you can use a long-form CSV syntax and specify a
|
||||
`format` attribute to output the list in JSON.
|
||||
|
@ -363,6 +371,7 @@ You can override the following fields:
|
|||
* `context`
|
||||
* `dockerfile`
|
||||
* `entitlements`
|
||||
* `extra-hosts`
|
||||
* `labels`
|
||||
* `load`
|
||||
* `no-cache`
|
||||
|
|
|
@ -240,13 +240,15 @@ Learn more about the built-in build arguments in the [Dockerfile reference docs]
|
|||
Define additional build context with specified contents. In Dockerfile the context can be accessed when `FROM name` or `--from=name` is used.
|
||||
When Dockerfile defines a stage with the same name it is overwritten.
|
||||
|
||||
The value can be a local source directory, [local OCI layout compliant directory](https://github.com/opencontainers/image-spec/blob/main/image-layout.md), container image (with docker-image:// prefix), Git or HTTP URL.
|
||||
The value can be a:
|
||||
|
||||
Replace `alpine:latest` with a pinned one:
|
||||
- local source directory
|
||||
- [local OCI layout compliant directory](https://github.com/opencontainers/image-spec/blob/main/image-layout.md)
|
||||
- container image
|
||||
- Git URL
|
||||
- HTTP URL
|
||||
|
||||
```console
|
||||
$ docker buildx build --build-context alpine=docker-image://alpine@sha256:0123456789 .
|
||||
```
|
||||
#### <a name="local-path"></a> Use a local path
|
||||
|
||||
Expose a secondary local source directory:
|
||||
|
||||
|
@ -255,6 +257,16 @@ $ docker buildx build --build-context project=path/to/project/source .
|
|||
# docker buildx build --build-context project=https://github.com/myuser/project.git .
|
||||
```
|
||||
|
||||
#### <a name="docker-image"></a> Use a container image
|
||||
|
||||
Use the `docker-image://` scheme.
|
||||
|
||||
Replace `alpine:latest` with a pinned one:
|
||||
|
||||
```console
|
||||
$ docker buildx build --build-context alpine=docker-image://alpine@sha256:0123456789 .
|
||||
```
|
||||
|
||||
```dockerfile
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM alpine
|
||||
|
@ -263,7 +275,10 @@ COPY --from=project myfile /
|
|||
|
||||
#### <a name="source-oci-layout"></a> Use an OCI layout directory as build context
|
||||
|
||||
Source an image from a local [OCI layout compliant directory](https://github.com/opencontainers/image-spec/blob/main/image-layout.md),
|
||||
Use the `oci-layout:///` scheme.
|
||||
|
||||
Source an image from a local
|
||||
[OCI layout compliant directory](https://github.com/opencontainers/image-spec/blob/main/image-layout.md),
|
||||
either by tag, or by digest:
|
||||
|
||||
```console
|
||||
|
@ -281,7 +296,6 @@ FROM foo
|
|||
```
|
||||
|
||||
The OCI layout directory must be compliant with the [OCI layout specification](https://github.com/opencontainers/image-spec/blob/main/image-layout.md).
|
||||
You can reference an image in the layout using either tags, or the exact digest.
|
||||
|
||||
### <a name="builder"></a> Override the configured builder instance (--builder)
|
||||
|
||||
|
|
|
@ -12,13 +12,12 @@ Start debugger (EXPERIMENTAL)
|
|||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------|:---------|:--------|:--------------------------------------------------------------------------------------------------------------------|
|
||||
| `--builder` | `string` | | Override the configured builder instance |
|
||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||
| `--invoke` | `string` | | Launch a monitor with executing specified command (EXPERIMENTAL) |
|
||||
| `--on` | `string` | `error` | When to launch the monitor ([always, error]) (EXPERIMENTAL) |
|
||||
| `--progress` | `string` | `auto` | Set type of progress output (`auto`, `plain`, `tty`, `rawjson`) for the monitor. Use plain to show container output |
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------|:---------|:--------|:-----------------------------------------------------------------|
|
||||
| `--builder` | `string` | | Override the configured builder instance |
|
||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||
| `--invoke` | `string` | | Launch a monitor with executing specified command (EXPERIMENTAL) |
|
||||
| `--on` | `string` | `error` | When to launch the monitor ([always, error]) (EXPERIMENTAL) |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# docker buildx history inspect attachment
|
||||
|
||||
```text
|
||||
docker buildx history inspect attachment [OPTIONS] REF [DIGEST]
|
||||
docker buildx history inspect attachment [OPTIONS] [REF [DIGEST]]
|
||||
```
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
|
@ -9,12 +9,12 @@ Inspect a build record 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`](#type) | `string` | | Type of attachment |
|
||||
| Name | Type | Default | Description |
|
||||
|:--------------------------|:---------|:--------|:-----------------------------------------|
|
||||
| `--builder` | `string` | | Override the configured builder instance |
|
||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||
| [`--platform`](#platform) | `string` | | Platform of attachment |
|
||||
| [`--type`](#type) | `string` | | Type of attachment |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
@ -27,48 +27,160 @@ platform-specific.
|
|||
|
||||
## Examples
|
||||
|
||||
### <a name="type"></a> Inspect a provenance attachment from a build (--type)
|
||||
|
||||
Supported types include `provenance` and `sbom`.
|
||||
### <a name="platform"></a> Inspect an attachment by platform (--platform)
|
||||
|
||||
```console
|
||||
$ docker buildx history inspect attachment qu2gsuo8ejqrwdfii23xkkckt --type provenance
|
||||
$ docker buildx history inspect attachment --platform linux/amd64
|
||||
{
|
||||
"_type": "https://slsa.dev/provenance/v0.2",
|
||||
"buildDefinition": {
|
||||
"buildType": "https://build.docker.com/BuildKit@v1",
|
||||
"externalParameters": {
|
||||
"target": "app",
|
||||
"platforms": ["linux/amd64"]
|
||||
}
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"digest": "sha256:814e63f06465bc78123775714e4df1ebdda37e6403e0b4f481df74947c047163",
|
||||
"size": 600
|
||||
},
|
||||
"runDetails": {
|
||||
"builder": "docker",
|
||||
"by": "ci@docker.com"
|
||||
}
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"digest": "sha256:36537f3920ae948ce3e12b4ae34c21190280e6e7d58eeabde0dff3fdfb43b6b0",
|
||||
"size": 21664137
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Inspect a SBOM for linux/amd64
|
||||
### <a name="type"></a> Inspect an attachment by type (--type)
|
||||
|
||||
Supported types include:
|
||||
* `index`
|
||||
* `manifest`
|
||||
* `image`
|
||||
* `provenance`
|
||||
* `sbom`
|
||||
|
||||
#### Index
|
||||
|
||||
```console
|
||||
$ docker buildx history inspect attachment ^0 \
|
||||
--type sbom \
|
||||
--platform linux/amd64
|
||||
$ docker buildx history inspect attachment --type index
|
||||
{
|
||||
"bomFormat": "CycloneDX",
|
||||
"specVersion": "1.5",
|
||||
"version": 1,
|
||||
"components": [
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.index.v1+json",
|
||||
"manifests": [
|
||||
{
|
||||
"type": "library",
|
||||
"name": "alpine",
|
||||
"version": "3.18.2"
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:a194e24f47dc6d0e65992c09577b9bc4e7bd0cd5cc4f81e7738918f868aa397b",
|
||||
"size": 481,
|
||||
"platform": {
|
||||
"architecture": "amd64",
|
||||
"os": "linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:49e40223d6a96ea0667a12737fd3dde004cf217eb48cb28c9191288cd44c6ace",
|
||||
"size": 839,
|
||||
"annotations": {
|
||||
"vnd.docker.reference.digest": "sha256:a194e24f47dc6d0e65992c09577b9bc4e7bd0cd5cc4f81e7738918f868aa397b",
|
||||
"vnd.docker.reference.type": "attestation-manifest"
|
||||
},
|
||||
"platform": {
|
||||
"architecture": "unknown",
|
||||
"os": "unknown"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Manifest
|
||||
|
||||
```console
|
||||
$ docker buildx history inspect attachment --type manifest
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"digest": "sha256:814e63f06465bc78123775714e4df1ebdda37e6403e0b4f481df74947c047163",
|
||||
"size": 600
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"digest": "sha256:36537f3920ae948ce3e12b4ae34c21190280e6e7d58eeabde0dff3fdfb43b6b0",
|
||||
"size": 21664137
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Provenance
|
||||
|
||||
```console
|
||||
$ docker buildx history inspect attachment --type provenance
|
||||
{
|
||||
"builder": {
|
||||
"id": ""
|
||||
},
|
||||
"buildType": "https://mobyproject.org/buildkit@v1",
|
||||
"materials": [
|
||||
{
|
||||
"uri": "pkg:docker/docker/dockerfile@1",
|
||||
"digest": {
|
||||
"sha256": "9ba7531bd80fb0a858632727cf7a112fbfd19b17e94c4e84ced81e24ef1a0dbc"
|
||||
}
|
||||
},
|
||||
{
|
||||
"uri": "pkg:docker/golang@1.19.4-alpine?platform=linux%2Farm64",
|
||||
"digest": {
|
||||
"sha256": "a9b24b67dc83b3383d22a14941c2b2b2ca6a103d805cac6820fd1355943beaf1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"invocation": {
|
||||
"configSource": {
|
||||
"entryPoint": "Dockerfile"
|
||||
},
|
||||
"parameters": {
|
||||
"frontend": "gateway.v0",
|
||||
"args": {
|
||||
"cmdline": "docker/dockerfile:1",
|
||||
"source": "docker/dockerfile:1",
|
||||
"target": "binaries"
|
||||
},
|
||||
"locals": [
|
||||
{
|
||||
"name": "context"
|
||||
},
|
||||
{
|
||||
"name": "dockerfile"
|
||||
}
|
||||
]
|
||||
},
|
||||
"environment": {
|
||||
"platform": "linux/arm64"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"buildInvocationID": "c4a87v0sxhliuewig10gnsb6v",
|
||||
"buildStartedOn": "2022-12-16T08:26:28.651359794Z",
|
||||
"buildFinishedOn": "2022-12-16T08:26:29.625483253Z",
|
||||
"reproducible": false,
|
||||
"completeness": {
|
||||
"parameters": true,
|
||||
"environment": true,
|
||||
"materials": false
|
||||
},
|
||||
"https://mobyproject.org/buildkit@v1#metadata": {
|
||||
"vcs": {
|
||||
"revision": "a9ba846486420e07d30db1107411ac3697ecab68",
|
||||
"source": "git@github.com:<org>/<repo>.git"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Inspect an attachment by digest
|
||||
|
||||
You can inspect an attachment directly using its digset, which you can get from
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
cerrdefs "github.com/containerd/errdefs"
|
||||
"github.com/docker/buildx/driver"
|
||||
"github.com/docker/buildx/driver/bkimage"
|
||||
"github.com/docker/buildx/util/confutil"
|
||||
|
@ -23,7 +24,6 @@ import (
|
|||
"github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/api/types/system"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/moby/buildkit/client"
|
||||
|
@ -70,7 +70,7 @@ func (d *Driver) Bootstrap(ctx context.Context, l progress.Logger) error {
|
|||
return progress.Wrap("[internal] booting buildkit", l, func(sub progress.SubLogger) error {
|
||||
_, err := d.DockerAPI.ContainerInspect(ctx, d.Name)
|
||||
if err != nil {
|
||||
if errdefs.IsNotFound(err) {
|
||||
if cerrdefs.IsNotFound(err) {
|
||||
return d.create(ctx, sub)
|
||||
}
|
||||
return err
|
||||
|
@ -183,7 +183,7 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error {
|
|||
}
|
||||
}
|
||||
_, err := d.DockerAPI.ContainerCreate(ctx, cfg, hc, &network.NetworkingConfig{}, nil, d.Name)
|
||||
if err != nil && !errdefs.IsConflict(err) {
|
||||
if err != nil && !cerrdefs.IsConflict(err) {
|
||||
return err
|
||||
}
|
||||
if err == nil {
|
||||
|
@ -310,7 +310,7 @@ func (d *Driver) start(ctx context.Context) error {
|
|||
func (d *Driver) Info(ctx context.Context) (*driver.Info, error) {
|
||||
ctn, err := d.DockerAPI.ContainerInspect(ctx, d.Name)
|
||||
if err != nil {
|
||||
if errdefs.IsNotFound(err) {
|
||||
if cerrdefs.IsNotFound(err) {
|
||||
return &driver.Info{
|
||||
Status: driver.Inactive,
|
||||
}, nil
|
||||
|
|
|
@ -286,6 +286,10 @@ func (f *factory) processDriverOpts(deploymentName string, namespace string, cfg
|
|||
if v != "" {
|
||||
deploymentOpt.Qemu.Image = v
|
||||
}
|
||||
case "buildkit-root-volume-memory":
|
||||
if v != "" {
|
||||
deploymentOpt.BuildKitRootVolumeMemory = v
|
||||
}
|
||||
case "default-load":
|
||||
defaultLoad, err = strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
|
|
|
@ -32,6 +32,7 @@ type DeploymentOpt struct {
|
|||
// files mounted at /etc/buildkitd
|
||||
ConfigFiles map[string][]byte
|
||||
|
||||
BuildKitRootVolumeMemory string
|
||||
Rootless bool
|
||||
NodeSelector map[string]string
|
||||
CustomAnnotations map[string]string
|
||||
|
@ -50,6 +51,8 @@ const (
|
|||
containerName = "buildkitd"
|
||||
AnnotationPlatform = "buildx.docker.com/platform"
|
||||
LabelApp = "app"
|
||||
rootVolumeName = "buildkit-memory"
|
||||
rootVolumePath = "/var/lib/buildkit"
|
||||
)
|
||||
|
||||
type ErrReservedAnnotationPlatform struct{}
|
||||
|
@ -247,6 +250,26 @@ func NewDeployment(opt *DeploymentOpt) (d *appsv1.Deployment, c []*corev1.Config
|
|||
d.Spec.Template.Spec.Containers[0].Resources.Limits[corev1.ResourceEphemeralStorage] = limEphemeralStorage
|
||||
}
|
||||
|
||||
if opt.BuildKitRootVolumeMemory != "" {
|
||||
buildKitRootVolumeMemory, err := resource.ParseQuantity(opt.BuildKitRootVolumeMemory)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
d.Spec.Template.Spec.Volumes = append(d.Spec.Template.Spec.Volumes, corev1.Volume{
|
||||
Name: rootVolumeName,
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
EmptyDir: &corev1.EmptyDirVolumeSource{
|
||||
Medium: "Memory",
|
||||
SizeLimit: &buildKitRootVolumeMemory,
|
||||
},
|
||||
},
|
||||
})
|
||||
d.Spec.Template.Spec.Containers[0].VolumeMounts = append(d.Spec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{
|
||||
Name: rootVolumeName,
|
||||
MountPath: rootVolumePath,
|
||||
})
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
|
83
go.mod
83
go.mod
|
@ -6,9 +6,9 @@ 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.6.2
|
||||
github.com/containerd/console v1.0.4
|
||||
github.com/containerd/containerd/v2 v2.0.5
|
||||
github.com/compose-spec/compose-go/v2 v2.6.3
|
||||
github.com/containerd/console v1.0.5
|
||||
github.com/containerd/containerd/v2 v2.1.1
|
||||
github.com/containerd/continuity v0.4.5
|
||||
github.com/containerd/errdefs v1.0.0
|
||||
github.com/containerd/log v0.1.0
|
||||
|
@ -16,9 +16,9 @@ require (
|
|||
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.1.1+incompatible
|
||||
github.com/docker/cli-docs-tool v0.9.0
|
||||
github.com/docker/docker v28.1.1+incompatible
|
||||
github.com/docker/cli v28.3.0+incompatible
|
||||
github.com/docker/cli-docs-tool v0.10.0
|
||||
github.com/docker/docker v28.3.0+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
|
||||
|
@ -26,9 +26,9 @@ require (
|
|||
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/in-toto/in-toto-golang v0.9.0
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||
github.com/moby/buildkit v0.22.0-rc1
|
||||
github.com/moby/buildkit v0.23.0-rc1.0.20250618182037-9b91d20367db // master
|
||||
github.com/moby/go-archive v0.1.0
|
||||
github.com/moby/sys/atomicwriter v0.1.0
|
||||
github.com/moby/sys/mountinfo v0.7.2
|
||||
|
@ -44,33 +44,32 @@ require (
|
|||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/pflag v1.0.6
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250417144416-3f76f8130144
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0
|
||||
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/contrib/instrumentation/net/http/otelhttp v0.60.0
|
||||
go.opentelemetry.io/otel v1.35.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
|
||||
go.opentelemetry.io/otel/metric v1.35.0
|
||||
go.opentelemetry.io/otel/sdk v1.35.0
|
||||
go.opentelemetry.io/otel/trace v1.35.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/sync v0.14.0
|
||||
golang.org/x/sys v0.33.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/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a
|
||||
google.golang.org/grpc v1.72.2
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1
|
||||
google.golang.org/protobuf v1.35.2
|
||||
google.golang.org/protobuf v1.36.6
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.31.2
|
||||
k8s.io/apimachinery v0.31.2
|
||||
k8s.io/client-go v0.31.2
|
||||
k8s.io/api v0.32.3
|
||||
k8s.io/apimachinery v0.32.3
|
||||
k8s.io/client-go v0.32.3
|
||||
)
|
||||
|
||||
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/agext/levenshtein v1.2.3 // indirect
|
||||
github.com/apparentlymart/go-cidr v1.0.1 // indirect
|
||||
|
@ -88,7 +87,7 @@ require (
|
|||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
|
||||
github.com/aws/smithy-go v1.20.3 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/containerd/containerd/api v1.8.0 // indirect
|
||||
github.com/containerd/containerd/api v1.9.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
|
||||
|
@ -102,9 +101,9 @@ require (
|
|||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.4 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
|
@ -112,10 +111,9 @@ require (
|
|||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
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
|
||||
|
@ -140,7 +138,7 @@ require (
|
|||
github.com/pmezard/go-difflib v1.0.0 // 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/secure-systems-lab/go-securesystemslib v0.6.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-20250318174251-73d941a28323 // indirect
|
||||
|
@ -151,28 +149,29 @@ require (
|
|||
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/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace 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
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 // indirect
|
||||
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
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.5.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
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
|
||||
|
|
205
go.sum
205
go.sum
|
@ -1,7 +1,5 @@
|
|||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 h1:dIScnXFlF784X79oi7MzVT6GWqr/W1uUt0pB5CsDs9M=
|
||||
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/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
|
@ -9,8 +7,8 @@ github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0
|
|||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/Microsoft/hcsshim v0.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6lLg=
|
||||
github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y=
|
||||
github.com/Microsoft/hcsshim v0.13.0 h1:/BcXOiS6Qi7N9XqUcv27vkIuVOkBEcWstd2pMlWSeaA=
|
||||
github.com/Microsoft/hcsshim v0.13.0/go.mod h1:9KWJ/8DgU+QzYGupX4tzMhRQE8h6w90lH6HAaclpEok=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
|
||||
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
|
@ -64,16 +62,16 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
|
|||
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
|
||||
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
|
||||
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
|
||||
github.com/compose-spec/compose-go/v2 v2.6.2 h1:31uZNNLeRrKjtUCc56CzPpPykW1Tm6SxLn4gx9Jjzqw=
|
||||
github.com/compose-spec/compose-go/v2 v2.6.2/go.mod h1:vPlkN0i+0LjLf9rv52lodNMUTJF5YHVfHVGLLIP67NA=
|
||||
github.com/compose-spec/compose-go/v2 v2.6.3 h1:zfW1Qp605ESySyth/zR+6yLr55XE0AiOAUlZLHKMoW0=
|
||||
github.com/compose-spec/compose-go/v2 v2.6.3/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.5 h1:2vg/TjUXnaohAxiHnthQg8K06L9I4gdYEMcOLiMc8BQ=
|
||||
github.com/containerd/containerd/v2 v2.0.5/go.mod h1:Qqo0UN43i2fX1FLkrSTCg6zcHNfjN7gEnx3NPRZI+N0=
|
||||
github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc=
|
||||
github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||
github.com/containerd/containerd/api v1.9.0 h1:HZ/licowTRazus+wt9fM6r/9BQO7S0vD5lMcWspGIg0=
|
||||
github.com/containerd/containerd/api v1.9.0/go.mod h1:GhghKFmTR3hNtyznBoQ0EMWr9ju5AqHjcZPsSpTKutI=
|
||||
github.com/containerd/containerd/v2 v2.1.1 h1:znnkm7Ajz8lg8BcIPMhc/9yjBRN3B+OkNKqKisKfwwM=
|
||||
github.com/containerd/containerd/v2 v2.1.1/go.mod h1:zIfkQj4RIodclYQkX7GSSswSwgP8d/XxDOtOAoSDIGU=
|
||||
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=
|
||||
|
@ -84,8 +82,8 @@ github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY
|
|||
github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/nydus-snapshotter v0.15.0 h1:RqZRs1GPeM6T3wmuxJV9u+2Rg4YETVMwTmiDeX+iWC8=
|
||||
github.com/containerd/nydus-snapshotter v0.15.0/go.mod h1:biq0ijpeZe0I5yZFSJyHzFSjjRZQ7P7y/OuHyd7hYOw=
|
||||
github.com/containerd/nydus-snapshotter v0.15.2 h1:qsHI4M+Wwrf6Jr4eBqhNx8qh+YU0dSiJ+WPmcLFWNcg=
|
||||
github.com/containerd/nydus-snapshotter v0.15.2/go.mod h1:FfwH2KBkNYoisK/e+KsmNr7xTU53DmnavQHMFOcXwfM=
|
||||
github.com/containerd/platforms v1.0.0-rc.1 h1:83KIq4yy1erSRgOVHNk1HYdPvzdJ5CnsWaRoJX4C41E=
|
||||
github.com/containerd/platforms v1.0.0-rc.1/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4=
|
||||
github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y=
|
||||
|
@ -108,15 +106,15 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/cli v28.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/cli v28.3.0+incompatible h1:s+ttruVLhB5ayeuf2BciwDVxYdKi+RoUlxmwNHV3Vfo=
|
||||
github.com/docker/cli v28.3.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli-docs-tool v0.10.0 h1:bOD6mKynPQgojQi3s2jgcUWGp/Ebqy1SeCr9VfKQLLU=
|
||||
github.com/docker/cli-docs-tool v0.10.0/go.mod h1:5EM5zPnT2E7yCLERZmrDA234Vwn09fzRHP4aX1qwp1U=
|
||||
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.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 v28.3.0+incompatible h1:ffS62aKWupCWdvcee7nBU9fhnmknOqDPaJAMtfK0ImQ=
|
||||
github.com/docker/docker v28.3.0+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=
|
||||
|
@ -146,13 +144,14 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
|||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
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-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
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=
|
||||
|
@ -181,8 +180,8 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX
|
|||
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=
|
||||
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM=
|
||||
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
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.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
|
@ -191,8 +190,8 @@ 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/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/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
|
||||
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=
|
||||
|
@ -206,10 +205,8 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9
|
|||
github.com/hashicorp/hcl/v2 v2.23.0 h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos=
|
||||
github.com/hashicorp/hcl/v2 v2.23.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
github.com/in-toto/in-toto-golang v0.5.0 h1:hb8bgwr0M2hGdDsLjkJ3ZqJ8JFLL/tgYdAxF/XEFBbY=
|
||||
github.com/in-toto/in-toto-golang v0.5.0/go.mod h1:/Rq0IZHLV7Ku5gielPT4wPHJfH1GdHMCq8+WPxw8/BE=
|
||||
github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU=
|
||||
github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo=
|
||||
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=
|
||||
|
@ -253,8 +250,8 @@ github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZX
|
|||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/moby/buildkit v0.22.0-rc1 h1:Q47jZZws7+0WhucTcm35NRV8NcO6n1SwIikzfqcGKLo=
|
||||
github.com/moby/buildkit v0.22.0-rc1/go.mod h1:j4pP5hxiTWcz7xuTK2cyxQislHl/N2WWHzOy43DlLJw=
|
||||
github.com/moby/buildkit v0.23.0-rc1.0.20250618182037-9b91d20367db h1:ZzrDuG9G1A/RwJvuogNplxCEKsIUQh1CqEnqbOGFgKE=
|
||||
github.com/moby/buildkit v0.23.0-rc1.0.20250618182037-9b91d20367db/go.mod h1:v5jMDvQgUyidk3wu3NvVAAd5JJo83nfet9Gf/o0+EAQ=
|
||||
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=
|
||||
|
@ -294,22 +291,22 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
|
|||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
|
||||
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
|
||||
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
|
||||
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
|
||||
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
||||
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
|
||||
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
|
||||
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
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.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/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww=
|
||||
github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8=
|
||||
github.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0htPSrlgOOysQaU62U=
|
||||
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=
|
||||
|
@ -323,14 +320,14 @@ 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 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_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
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.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
|
@ -340,8 +337,8 @@ github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR
|
|||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/FulN9fTtqYUdS5+Oxzt+DUE=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.4.0/go.mod h1:FGBZgq2tXWICsxWQW1msNf49F0Pf2Op5Htayx335Qbs=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.6.0 h1:T65atpAVCJQK14UA57LMdZGpHi4QYSH/9FZyNGqMYIA=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.6.0/go.mod h1:8Mtpo9JKks/qhPG4HGZ2LGMvrPbzuxwfz/f/zLfEWkk=
|
||||
github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b h1:h+3JX2VoWTFuyQEo87pStk/a99dzIO1mM9KxIyLPGTU=
|
||||
github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc=
|
||||
github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=
|
||||
|
@ -350,8 +347,8 @@ github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjM
|
|||
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/spdx/tools-golang v0.5.5 h1:61c0KLfAcNqAjlg6UNMdkwpMernhw3zVRwDZ2x9XOmk=
|
||||
github.com/spdx/tools-golang v0.5.5/go.mod h1:MVIsXx8ZZzaRWNQpUDhC4Dud34edUYJYecciXgrw5vE=
|
||||
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.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
|
@ -380,10 +377,10 @@ github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4D
|
|||
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 h1:r0p7fK56l8WPequOaR3i9LBqfPtEdXIQbUTzT55iqT4=
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250417144416-3f76f8130144 h1:k9tdF32oJYwtjzMx+D26M6eYiCaAPdJ7tyN7tF1oU5Q=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250417144416-3f76f8130144/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/fsutil v0.0.0-20250605211040-586307ad452f h1:MoxeMfHAe5Qj/ySSBfL8A7l1V+hxuluj8owsIEEZipI=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f/go.mod h1:BKdcez7BiVtBvIcef90ZPc6ebqIWr4JWD7+EvLm6J98=
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 h1:2f304B10LaZdB8kkVEaoXvAMVan2tl9AiK4G0odjQtE=
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE=
|
||||
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=
|
||||
|
@ -411,36 +408,38 @@ github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6
|
|||
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 h1:yMkBS9yViCc7U7yeLzJPM2XizlfdVvBRSmsQDWu6qc0=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0/go.mod h1:n8MR6/liuGB5EmTETUBeU5ZgqMOlqKRxUaqPQBOANZ8=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 h1:4BZHA+B1wXEQoGNHxW8mURaLhcdGwvRnmhGbm+odRbc=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0/go.mod h1:3qi2EEwMgB4xnKgPLqsDP3j9qxnHDZeHsnAxfjQqTko=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
|
||||
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
|
||||
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0 h1:FZ6ei8GFW7kyPYdxJaV2rgI6M+4tvZzhYsQ2wgyVC08=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0/go.mod h1:MdEu/mC6j3D+tTEfvI15b5Ci2Fn7NneJ71YMoiS3tpI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0 h1:ZsXq73BERAiNuuFXYqP4MR5hBrjXfMGSO+Cx7qoOZiM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0/go.mod h1:hg1zaDMpyZJuUzjFxFsRYBoccE86tM9Uf4IqNMUxvrY=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 h1:FFeLy03iVTXP6ffeN2iXrxfGsZGCjVx0/4KlizjyBwU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0/go.mod h1:TMu73/k1CP8nBUpDLc71Wj/Kf7ZS9FK5b53VapRsP9o=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0 h1:UGZ1QwZWY67Z6BmckTU+9Rxn04m2bD3gD6Mk0OIOCPk=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0/go.mod h1:fcwWuDuaObkkChiDlhEpSq9+X1C0omv+s5mBtToAQ64=
|
||||
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
|
||||
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
|
||||
go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
|
||||
go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
|
||||
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
|
||||
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
|
||||
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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
|
@ -468,8 +467,8 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
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.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -480,8 +479,8 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
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.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.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.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
||||
|
@ -501,23 +500,25 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
|||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/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=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ=
|
||||
google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
|
||||
google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
|
||||
google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8=
|
||||
google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA=
|
||||
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
|
||||
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
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-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=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
|
@ -527,28 +528,26 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
|
|||
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=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
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.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=
|
||||
k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
|
||||
k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc=
|
||||
k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs=
|
||||
k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
|
||||
k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
|
||||
k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
|
||||
k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
|
||||
k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU=
|
||||
k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Forked from https://github.com/moby/buildkit/blob/e1b3b6c4abf7684f13e6391e5f7bc9210752687a/hack/dockerfiles/generated-files.Dockerfile
|
||||
# Copyright The BuildKit Authors.
|
||||
# Copyright The Buildx Authors.
|
||||
# Licensed under the Apache License, Version 2.0
|
||||
|
||||
ARG GO_VERSION=1.24
|
||||
ARG PROTOC_VERSION=3.11.4
|
||||
ARG PROTOC_GOOGLEAPIS_VERSION=2af421884dd468d565137215c946ebe4e245ae26
|
||||
|
||||
# protoc is dynamically linked to glibc so can't use alpine base
|
||||
FROM golang:${GO_VERSION}-bookworm AS base
|
||||
RUN apt-get update && apt-get --no-install-recommends install -y git unzip
|
||||
|
||||
FROM base AS protoc
|
||||
ARG PROTOC_VERSION
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
RUN <<EOT
|
||||
set -e
|
||||
arch=$(echo $TARGETARCH | sed -e s/amd64/x86_64/ -e s/arm64/aarch_64/)
|
||||
wget -q https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-${TARGETOS}-${arch}.zip
|
||||
unzip protoc-${PROTOC_VERSION}-${TARGETOS}-${arch}.zip -d /opt/protoc
|
||||
EOT
|
||||
|
||||
FROM base AS googleapis
|
||||
ARG PROTOC_GOOGLEAPIS_VERSION
|
||||
RUN <<EOT
|
||||
set -e
|
||||
wget -q https://github.com/googleapis/googleapis/archive/${PROTOC_GOOGLEAPIS_VERSION}.zip -O googleapis.zip
|
||||
unzip googleapis.zip '*.proto' -d /opt
|
||||
mkdir -p /opt/googleapis
|
||||
mv /opt/googleapis-${PROTOC_GOOGLEAPIS_VERSION} /opt/googleapis/include
|
||||
EOT
|
||||
|
||||
FROM base AS gobuild-base
|
||||
WORKDIR /app
|
||||
|
||||
FROM gobuild-base AS vtprotobuf
|
||||
RUN --mount=type=bind,source=go.mod,target=/app/go.mod \
|
||||
--mount=type=bind,source=go.sum,target=/app/go.sum \
|
||||
--mount=type=cache,target=/root/.cache \
|
||||
--mount=type=cache,target=/go/pkg/mod <<EOT
|
||||
set -e
|
||||
mkdir -p /opt/vtprotobuf
|
||||
go mod download github.com/planetscale/vtprotobuf
|
||||
cp -r $(go list -m -f='{{.Dir}}' github.com/planetscale/vtprotobuf)/include /opt/vtprotobuf
|
||||
EOT
|
||||
|
||||
FROM gobuild-base AS vendored
|
||||
RUN --mount=type=bind,source=vendor,target=/app <<EOT
|
||||
set -e
|
||||
mkdir -p /opt/vendored/include
|
||||
find . -name '*.proto' | tar -cf - --files-from - | tar -C /opt/vendored/include -xf -
|
||||
EOT
|
||||
|
||||
FROM gobuild-base AS tools
|
||||
RUN --mount=type=bind,source=go.mod,target=/app/go.mod,ro \
|
||||
--mount=type=bind,source=go.sum,target=/app/go.sum,ro \
|
||||
--mount=type=cache,target=/root/.cache \
|
||||
--mount=type=cache,target=/go/pkg/mod \
|
||||
go install \
|
||||
github.com/planetscale/vtprotobuf/cmd/protoc-gen-go-vtproto \
|
||||
google.golang.org/protobuf/cmd/protoc-gen-go \
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc
|
||||
COPY --link --from=protoc /opt/protoc /usr/local
|
||||
COPY --link --from=googleapis /opt/googleapis /usr/local
|
||||
COPY --link --from=vtprotobuf /opt/vtprotobuf /usr/local
|
||||
COPY --link --from=vendored /opt/vendored /usr/local
|
||||
|
||||
FROM tools AS generated
|
||||
RUN --mount=type=bind,target=github.com/docker/buildx,ro <<EOT
|
||||
set -ex
|
||||
mkdir /out
|
||||
find github.com/docker/buildx -name '*.proto' -o -name vendor -prune -false | xargs \
|
||||
protoc --go_out=/out --go-grpc_out=require_unimplemented_servers=false:/out \
|
||||
--go-vtproto_out=features=marshal+unmarshal+size+equal+pool+clone:/out
|
||||
EOT
|
||||
|
||||
FROM scratch AS update
|
||||
COPY --from=generated /out/github.com/docker/buildx /
|
||||
|
||||
FROM gobuild-base AS validate
|
||||
RUN --mount=type=bind,target=.,rw \
|
||||
--mount=type=bind,from=update,target=/generated-files <<EOT
|
||||
set -e
|
||||
git add -A
|
||||
if [ "$(ls -A /generated-files)" ]; then
|
||||
cp -rf /generated-files/* .
|
||||
fi
|
||||
diff=$(git status --porcelain -- ':!vendor' '**/*.pb.go')
|
||||
if [ -n "$diff" ]; then
|
||||
echo >&2 'ERROR: The result of "go generate" differs. Please update with "make generated-files"'
|
||||
echo "$diff"
|
||||
exit 1
|
||||
fi
|
||||
EOT
|
|
@ -3,7 +3,7 @@
|
|||
ARG GO_VERSION=1.24
|
||||
ARG ALPINE_VERSION=3.21
|
||||
|
||||
ARG GOVULNCHECK_VERSION=v1.1.3
|
||||
ARG GOVULNCHECK_VERSION=v1.1.4
|
||||
ARG FORMAT="text"
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base
|
||||
|
@ -20,12 +20,6 @@ RUN --mount=type=bind,target=. <<EOT
|
|||
set -ex
|
||||
mkdir /out
|
||||
govulncheck -format ${FORMAT} ./... | tee /out/govulncheck.out
|
||||
if [ "${FORMAT}" = "sarif" ]; then
|
||||
# Make sure "results" field is defined in SARIF output otherwise GitHub Code Scanning
|
||||
# will fail when uploading report with "Invalid SARIF. Missing 'results' array in run."
|
||||
# Relates to https://github.com/golang/vuln/blob/ffdef74cc44d7eb71931d8d414c478b966812488/internal/sarif/sarif.go#L69
|
||||
jq '(.runs[] | select(.results == null) | .results) |= []' /out/govulncheck.out | tee >(sponge /out/govulncheck.out)
|
||||
fi
|
||||
EOT
|
||||
|
||||
FROM scratch AS output
|
||||
|
|
|
@ -13,7 +13,7 @@ ARG GOPLS_ANALYZERS="embeddirective fillreturns infertypeargs maprange modernize
|
|||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base
|
||||
RUN apk add --no-cache git gcc musl-dev
|
||||
RUN apk add --no-cache git gcc musl-dev binutils-gold
|
||||
|
||||
FROM base AS golangci-build
|
||||
WORKDIR /src
|
||||
|
@ -91,4 +91,25 @@ RUN --mount=target=. \
|
|||
done
|
||||
EOF
|
||||
|
||||
FROM base AS modernize-fix-run
|
||||
COPY --link --from=xx / /
|
||||
ARG TARGETNAME
|
||||
ARG TARGETPLATFORM
|
||||
WORKDIR /go/src/github.com/docker/buildx
|
||||
RUN --mount=target=.,rw \
|
||||
--mount=target=/root/.cache,type=cache,id=lint-cache-${TARGETNAME}-${TARGETPLATFORM} \
|
||||
--mount=target=/gopls-analyzers,from=gopls,source=/out <<EOF
|
||||
set -ex
|
||||
xx-go --wrap
|
||||
mkdir /out
|
||||
/gopls-analyzers/modernize -fix ./...
|
||||
for file in $(git status --porcelain | awk '/^ M/ {print $2}'); do
|
||||
mkdir -p /out/$(dirname $file)
|
||||
cp $file /out/$file
|
||||
done
|
||||
EOF
|
||||
|
||||
FROM scratch AS modernize-fix
|
||||
COPY --link --from=modernize-fix-run /out /
|
||||
|
||||
FROM lint
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
|
||||
controllerapi "github.com/docker/buildx/controller/pb"
|
||||
"github.com/docker/buildx/build"
|
||||
"github.com/docker/buildx/monitor/types"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
@ -13,11 +13,11 @@ import (
|
|||
type ExecCmd struct {
|
||||
m types.Monitor
|
||||
|
||||
invokeConfig *controllerapi.InvokeConfig
|
||||
invokeConfig *build.InvokeConfig
|
||||
stdout io.WriteCloser
|
||||
}
|
||||
|
||||
func NewExecCmd(m types.Monitor, invokeConfig *controllerapi.InvokeConfig, stdout io.WriteCloser) types.Command {
|
||||
func NewExecCmd(m types.Monitor, invokeConfig *build.InvokeConfig, stdout io.WriteCloser) types.Command {
|
||||
return &ExecCmd{m, invokeConfig, stdout}
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ func (cm *ExecCmd) Exec(ctx context.Context, args []string) error {
|
|||
if len(args) < 2 {
|
||||
return errors.Errorf("command must be passed")
|
||||
}
|
||||
cfg := &controllerapi.InvokeConfig{
|
||||
cfg := &build.InvokeConfig{
|
||||
Entrypoint: []string{args[1]},
|
||||
Cmd: args[2:],
|
||||
NoCmd: false,
|
||||
|
|
|
@ -2,30 +2,16 @@ package commands
|
|||
|
||||
import (
|
||||
"context"
|
||||
"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"
|
||||
"github.com/docker/buildx/util/progress"
|
||||
"github.com/moby/buildkit/solver/errdefs"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type ReloadCmd struct {
|
||||
m types.Monitor
|
||||
|
||||
stdout io.WriteCloser
|
||||
progress *progress.Printer
|
||||
|
||||
options *cbuild.Options
|
||||
invokeConfig *controllerapi.InvokeConfig
|
||||
}
|
||||
|
||||
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}
|
||||
func NewReloadCmd(m types.Monitor) types.Command {
|
||||
return &ReloadCmd{m: m}
|
||||
}
|
||||
|
||||
func (cm *ReloadCmd) Info() types.CommandInfo {
|
||||
|
@ -40,31 +26,6 @@ Usage:
|
|||
}
|
||||
|
||||
func (cm *ReloadCmd) Exec(ctx context.Context, args []string) error {
|
||||
bo := cm.m.Inspect(ctx)
|
||||
|
||||
var resultUpdated bool
|
||||
cm.progress.Unpause()
|
||||
_, _, 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) {
|
||||
resultUpdated = true
|
||||
} else {
|
||||
fmt.Printf("failed to reload: %v\n", err)
|
||||
}
|
||||
// report error
|
||||
for _, s := range errdefs.Sources(err) {
|
||||
s.Print(cm.stdout)
|
||||
}
|
||||
fmt.Fprintf(cm.stdout, "ERROR: %v\n", err)
|
||||
} else {
|
||||
resultUpdated = true
|
||||
}
|
||||
if resultUpdated {
|
||||
// rollback the running container with the new result
|
||||
id := cm.m.Rollback(ctx, cm.invokeConfig)
|
||||
fmt.Fprintf(cm.stdout, "Interactive container was restarted with process %q. Press Ctrl-a-c to switch to the new container\n", id)
|
||||
}
|
||||
cm.m.Reload()
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -5,18 +5,18 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
|
||||
controllerapi "github.com/docker/buildx/controller/pb"
|
||||
"github.com/docker/buildx/build"
|
||||
"github.com/docker/buildx/monitor/types"
|
||||
)
|
||||
|
||||
type RollbackCmd struct {
|
||||
m types.Monitor
|
||||
|
||||
invokeConfig *controllerapi.InvokeConfig
|
||||
invokeConfig *build.InvokeConfig
|
||||
stdout io.WriteCloser
|
||||
}
|
||||
|
||||
func NewRollbackCmd(m types.Monitor, invokeConfig *controllerapi.InvokeConfig, stdout io.WriteCloser) types.Command {
|
||||
func NewRollbackCmd(m types.Monitor, invokeConfig *build.InvokeConfig, stdout io.WriteCloser) types.Command {
|
||||
return &RollbackCmd{m, invokeConfig, stdout}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,45 +4,117 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
"github.com/docker/buildx/monitor/processes"
|
||||
"github.com/docker/buildx/monitor/types"
|
||||
"github.com/docker/buildx/util/ioset"
|
||||
"github.com/docker/buildx/util/progress"
|
||||
"github.com/google/shlex"
|
||||
"github.com/moby/buildkit/client"
|
||||
gateway "github.com/moby/buildkit/frontend/gateway/client"
|
||||
"github.com/moby/buildkit/identity"
|
||||
"github.com/moby/buildkit/solver/errdefs"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
type MonitorBuildResult struct {
|
||||
Resp *client.SolveResponse
|
||||
Err error
|
||||
var ErrReload = errors.New("monitor: reload")
|
||||
|
||||
type Monitor struct {
|
||||
invokeConfig *build.InvokeConfig
|
||||
printer *progress.Printer
|
||||
|
||||
stdin *ioset.SingleForwarder
|
||||
stdout io.WriteCloser
|
||||
stderr io.WriteCloser
|
||||
}
|
||||
|
||||
func New(cfg *build.InvokeConfig, stdin io.ReadCloser, stdout, stderr io.WriteCloser, printer *progress.Printer) *Monitor {
|
||||
m := &Monitor{
|
||||
invokeConfig: cfg,
|
||||
printer: printer,
|
||||
stdin: ioset.NewSingleForwarder(),
|
||||
stdout: stdout,
|
||||
stderr: stderr,
|
||||
}
|
||||
m.stdin.SetReader(stdin)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *Monitor) Handler() build.Handler {
|
||||
return build.Handler{
|
||||
Evaluate: m.Evaluate,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Monitor) Evaluate(ctx context.Context, c gateway.Client, res *gateway.Result) error {
|
||||
buildErr := res.EachRef(func(ref gateway.Reference) error {
|
||||
return ref.Evaluate(ctx)
|
||||
})
|
||||
|
||||
if m.invokeConfig.NeedsDebug(buildErr) {
|
||||
// Allow some time to ensure status updates are sent.
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// Print errors before launching monitor
|
||||
if err := printError(buildErr, m.printer); err != nil {
|
||||
logrus.Warnf("failed to print error information: %v", err)
|
||||
}
|
||||
|
||||
rCtx := build.NewResultHandle(ctx, c, res, buildErr)
|
||||
if monitorErr := m.Run(ctx, rCtx); monitorErr != nil {
|
||||
if errors.Is(monitorErr, build.ErrRestart) {
|
||||
return build.ErrRestart
|
||||
}
|
||||
logrus.Warnf("failed to run monitor: %v", monitorErr)
|
||||
}
|
||||
}
|
||||
return buildErr
|
||||
}
|
||||
|
||||
func (m *Monitor) Run(ctx context.Context, rCtx *build.ResultHandle) error {
|
||||
if rCtx != nil {
|
||||
defer rCtx.Done()
|
||||
}
|
||||
|
||||
pr, pw := io.Pipe()
|
||||
m.stdin.SetWriter(pw, func() io.WriteCloser {
|
||||
pw.Close() // propagate EOF
|
||||
return nil
|
||||
})
|
||||
|
||||
con := console.Current()
|
||||
if err := con.SetRaw(); err != nil {
|
||||
return errors.Errorf("failed to configure terminal: %v", err)
|
||||
}
|
||||
defer con.Reset()
|
||||
|
||||
monitorErr := RunMonitor(ctx, m.invokeConfig, rCtx, pr, m.stdout, m.stderr, m.printer)
|
||||
if err := pw.Close(); err != nil {
|
||||
logrus.Debug("failed to close monitor stdin pipe reader")
|
||||
}
|
||||
return monitorErr
|
||||
}
|
||||
|
||||
func (m *Monitor) Close() error {
|
||||
return m.stdin.Close()
|
||||
}
|
||||
|
||||
// RunMonitor provides an interactive session for running and managing containers via specified IO.
|
||||
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.Close(); err != nil {
|
||||
logrus.Warnf("close error: %v", err)
|
||||
}
|
||||
}()
|
||||
func RunMonitor(ctx context.Context, invokeConfig *build.InvokeConfig, rCtx *build.ResultHandle, stdin io.ReadCloser, stdout, stderr io.WriteCloser, progress *progress.Printer) error {
|
||||
progress.Pause()
|
||||
defer progress.Resume()
|
||||
|
||||
if err := progress.Pause(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer progress.Unpause()
|
||||
defer stdin.Close()
|
||||
|
||||
monitorIn, monitorOut := ioset.Pipe()
|
||||
defer func() {
|
||||
|
@ -70,8 +142,9 @@ func RunMonitor(ctx context.Context, curRef string, options *cbuild.Options, inv
|
|||
invokeForwarder := ioset.NewForwarder()
|
||||
invokeForwarder.SetIn(&containerIn)
|
||||
m := &monitor{
|
||||
BuildxController: c,
|
||||
invokeIO: invokeForwarder,
|
||||
rCtx: rCtx,
|
||||
processes: processes.NewManager(),
|
||||
invokeIO: invokeForwarder,
|
||||
muxIO: ioset.NewMuxIO(ioset.In{
|
||||
Stdin: io.NopCloser(stdin),
|
||||
Stdout: nopCloser{stdout},
|
||||
|
@ -84,7 +157,13 @@ func RunMonitor(ctx context.Context, curRef string, options *cbuild.Options, inv
|
|||
return "Switched IO\n"
|
||||
}),
|
||||
}
|
||||
m.ref.Store(curRef)
|
||||
m.ctx, m.cancel = context.WithCancelCause(context.Background())
|
||||
|
||||
defer func() {
|
||||
if err := m.Close(); err != nil {
|
||||
logrus.Warnf("close error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Start container automatically
|
||||
fmt.Fprintf(stdout, "Launching interactive container. Press Ctrl-a-c to switch to monitor console\n")
|
||||
|
@ -94,7 +173,7 @@ func RunMonitor(ctx context.Context, curRef string, options *cbuild.Options, inv
|
|||
fmt.Fprintf(stdout, "Interactive container was restarted with process %q. Press Ctrl-a-c to switch to the new container\n", id)
|
||||
|
||||
availableCommands := []types.Command{
|
||||
commands.NewReloadCmd(m, stdout, progress, options, invokeConfig),
|
||||
commands.NewReloadCmd(m),
|
||||
commands.NewRollbackCmd(m, invokeConfig, stdout),
|
||||
commands.NewAttachCmd(m, stdout),
|
||||
commands.NewExecCmd(m, invokeConfig, stdout),
|
||||
|
@ -126,6 +205,11 @@ func RunMonitor(ctx context.Context, curRef string, options *cbuild.Options, inv
|
|||
}()
|
||||
t := term.NewTerminal(readWriter{in.Stdin, in.Stdout}, "(buildx) ")
|
||||
for {
|
||||
if err := m.ctx.Err(); err != nil {
|
||||
errCh <- context.Cause(m.ctx)
|
||||
return
|
||||
}
|
||||
|
||||
l, err := t.ReadLine()
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
|
@ -174,10 +258,10 @@ func RunMonitor(ctx context.Context, curRef string, options *cbuild.Options, inv
|
|||
select {
|
||||
case <-doneCh:
|
||||
m.close()
|
||||
return m.lastBuildResult, nil
|
||||
return nil
|
||||
case err := <-errCh:
|
||||
m.close()
|
||||
return m.lastBuildResult, err
|
||||
return err
|
||||
case <-monitorDisableCh:
|
||||
}
|
||||
monitorForwarder.SetOut(nil)
|
||||
|
@ -231,36 +315,60 @@ type readWriter struct {
|
|||
}
|
||||
|
||||
type monitor struct {
|
||||
control.BuildxController
|
||||
ref atomic.Value
|
||||
ctx context.Context
|
||||
cancel context.CancelCauseFunc
|
||||
|
||||
rCtx *build.ResultHandle
|
||||
|
||||
muxIO *ioset.MuxIO
|
||||
invokeIO *ioset.Forwarder
|
||||
invokeCancel func()
|
||||
attachedPid atomic.Value
|
||||
|
||||
lastBuildResult *MonitorBuildResult
|
||||
processes *processes.Manager
|
||||
}
|
||||
|
||||
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) Invoke(ctx context.Context, pid string, cfg *build.InvokeConfig, ioIn io.ReadCloser, ioOut io.WriteCloser, ioErr io.WriteCloser) error {
|
||||
proc, ok := m.processes.Get(pid)
|
||||
if !ok {
|
||||
// Start a new process.
|
||||
if m.rCtx == nil {
|
||||
return errors.New("no build result is registered")
|
||||
}
|
||||
var err error
|
||||
proc, err = m.processes.StartProcess(pid, m.rCtx, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Attach containerIn to this process
|
||||
ioCancelledCh := make(chan struct{})
|
||||
proc.ForwardIO(&ioset.In{Stdin: ioIn, Stdout: ioOut, Stderr: ioErr}, func(error) { close(ioCancelledCh) })
|
||||
|
||||
select {
|
||||
case <-ioCancelledCh:
|
||||
return errors.Errorf("io cancelled")
|
||||
case err := <-proc.Done():
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
return context.Cause(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *monitor) Rollback(ctx context.Context, cfg *controllerapi.InvokeConfig) string {
|
||||
func (m *monitor) Rollback(ctx context.Context, cfg *build.InvokeConfig) string {
|
||||
pid := identity.NewID()
|
||||
cfg1 := cfg
|
||||
cfg1.Rollback = true
|
||||
return m.startInvoke(ctx, pid, cfg1)
|
||||
}
|
||||
|
||||
func (m *monitor) Exec(ctx context.Context, cfg *controllerapi.InvokeConfig) string {
|
||||
func (m *monitor) Exec(ctx context.Context, cfg *build.InvokeConfig) string {
|
||||
return m.startInvoke(ctx, identity.NewID(), cfg)
|
||||
}
|
||||
|
||||
func (m *monitor) Attach(ctx context.Context, pid string) {
|
||||
m.startInvoke(ctx, pid, &controllerapi.InvokeConfig{})
|
||||
m.startInvoke(ctx, pid, &build.InvokeConfig{})
|
||||
}
|
||||
|
||||
func (m *monitor) Detach() {
|
||||
|
@ -269,6 +377,10 @@ func (m *monitor) Detach() {
|
|||
}
|
||||
}
|
||||
|
||||
func (m *monitor) Reload() {
|
||||
m.cancel(build.ErrRestart)
|
||||
}
|
||||
|
||||
func (m *monitor) AttachedPID() string {
|
||||
return m.attachedPid.Load().(string)
|
||||
}
|
||||
|
@ -277,7 +389,7 @@ func (m *monitor) close() {
|
|||
m.Detach()
|
||||
}
|
||||
|
||||
func (m *monitor) startInvoke(ctx context.Context, pid string, cfg *controllerapi.InvokeConfig) string {
|
||||
func (m *monitor) startInvoke(ctx context.Context, pid string, cfg *build.InvokeConfig) string {
|
||||
if m.invokeCancel != nil {
|
||||
m.invokeCancel() // Finish existing attach
|
||||
}
|
||||
|
@ -303,7 +415,7 @@ func (m *monitor) startInvoke(ctx context.Context, pid string, cfg *controllerap
|
|||
return pid
|
||||
}
|
||||
|
||||
func (m *monitor) invoke(ctx context.Context, pid string, cfg *controllerapi.InvokeConfig) error {
|
||||
func (m *monitor) invoke(ctx context.Context, pid string, cfg *build.InvokeConfig) error {
|
||||
m.muxIO.Enable(1)
|
||||
defer m.muxIO.Disable(1)
|
||||
if err := m.muxIO.SwitchTo(1); err != nil {
|
||||
|
@ -332,8 +444,40 @@ func (m *monitor) invoke(ctx context.Context, pid string, cfg *controllerapi.Inv
|
|||
return err
|
||||
}
|
||||
|
||||
func (m *monitor) Close() error {
|
||||
m.cancelRunningProcesses()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *monitor) ListProcesses(ctx context.Context) (infos []*processes.ProcessInfo, retErr error) {
|
||||
return m.processes.ListProcesses(), nil
|
||||
}
|
||||
|
||||
func (m *monitor) DisconnectProcess(ctx context.Context, pid string) error {
|
||||
return m.processes.DeleteProcess(pid)
|
||||
}
|
||||
|
||||
func (m *monitor) cancelRunningProcesses() {
|
||||
m.processes.CancelRunningProcesses()
|
||||
}
|
||||
|
||||
type nopCloser struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (c nopCloser) Close() error { return nil }
|
||||
|
||||
func printError(err error, printer *progress.Printer) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
printer.Pause()
|
||||
defer printer.Resume()
|
||||
|
||||
for _, s := range errdefs.Sources(err) {
|
||||
s.Print(os.Stderr)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"sync/atomic"
|
||||
|
||||
"github.com/docker/buildx/build"
|
||||
"github.com/docker/buildx/controller/pb"
|
||||
"github.com/docker/buildx/util/ioset"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
@ -15,7 +14,7 @@ import (
|
|||
// Process provides methods to control a process.
|
||||
type Process struct {
|
||||
inEnd *ioset.Forwarder
|
||||
invokeConfig *pb.InvokeConfig
|
||||
invokeConfig *build.InvokeConfig
|
||||
errCh chan error
|
||||
processCancel func()
|
||||
serveIOCancel func(error)
|
||||
|
@ -98,7 +97,7 @@ func (m *Manager) DeleteProcess(id string) error {
|
|||
// When a container isn't available (i.e. first time invoking or the container has exited) or cfg.Rollback is set,
|
||||
// this method will start a new container and run the process in it. Otherwise, this method starts a new process in the
|
||||
// existing container.
|
||||
func (m *Manager) StartProcess(pid string, resultCtx *build.ResultHandle, cfg *pb.InvokeConfig) (*Process, error) {
|
||||
func (m *Manager) StartProcess(pid string, resultCtx *build.ResultHandle, cfg *build.InvokeConfig) (*Process, error) {
|
||||
// Get the target result to invoke a container from
|
||||
var ctr *build.Container
|
||||
if a := m.container.Load(); a != nil {
|
||||
|
@ -157,5 +156,5 @@ func (m *Manager) StartProcess(pid string, resultCtx *build.ResultHandle, cfg *p
|
|||
|
||||
type ProcessInfo struct {
|
||||
ProcessID string
|
||||
InvokeConfig *pb.InvokeConfig
|
||||
InvokeConfig *build.InvokeConfig
|
||||
}
|
|
@ -2,20 +2,29 @@ package types
|
|||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/docker/buildx/controller/control"
|
||||
controllerapi "github.com/docker/buildx/controller/pb"
|
||||
"github.com/docker/buildx/build"
|
||||
"github.com/docker/buildx/monitor/processes"
|
||||
)
|
||||
|
||||
// Monitor provides APIs for attaching and controlling the buildx server.
|
||||
type Monitor interface {
|
||||
control.BuildxController
|
||||
// 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, pid string, options *build.InvokeConfig, ioIn io.ReadCloser, ioOut io.WriteCloser, ioErr io.WriteCloser) error
|
||||
|
||||
ListProcesses(ctx context.Context) (infos []*processes.ProcessInfo, retErr error)
|
||||
|
||||
DisconnectProcess(ctx context.Context, pid string) error
|
||||
|
||||
// Rollback re-runs the interactive container with initial rootfs contents.
|
||||
Rollback(ctx context.Context, cfg *controllerapi.InvokeConfig) string
|
||||
Rollback(ctx context.Context, cfg *build.InvokeConfig) string
|
||||
|
||||
// Rollback executes a process in the interactive container.
|
||||
Exec(ctx context.Context, cfg *controllerapi.InvokeConfig) string
|
||||
Exec(ctx context.Context, cfg *build.InvokeConfig) string
|
||||
|
||||
// Attach attaches IO to a process in the container.
|
||||
Attach(ctx context.Context, pid string)
|
||||
|
@ -25,6 +34,11 @@ type Monitor interface {
|
|||
|
||||
// Detach detaches IO from the container.
|
||||
Detach()
|
||||
|
||||
// Reload will signal the monitor to be reloaded.
|
||||
Reload()
|
||||
|
||||
io.Closer
|
||||
}
|
||||
|
||||
// CommandInfo is information about a command.
|
||||
|
|
282
tests/bake.go
282
tests/bake.go
|
@ -74,11 +74,14 @@ var bakeTests = []func(t *testing.T, sb integration.Sandbox){
|
|||
testBakeLoadPush,
|
||||
testListTargets,
|
||||
testListVariables,
|
||||
testListTypedVariables,
|
||||
testBakeCallCheck,
|
||||
testBakeCallCheckFlag,
|
||||
testBakeCallMetadata,
|
||||
testBakeMultiPlatform,
|
||||
testBakeCheckCallOutput,
|
||||
testBakeExtraHosts,
|
||||
testBakeFileFromEnvironment,
|
||||
}
|
||||
|
||||
func testBakePrint(t *testing.T, sb integration.Sandbox) {
|
||||
|
@ -1394,9 +1397,9 @@ target "default" {
|
|||
dtprv, err := json.Marshal(md.Default.BuildProvenance)
|
||||
require.NoError(t, err)
|
||||
|
||||
var prv provenancetypes.ProvenancePredicate
|
||||
var prv provenancetypes.ProvenancePredicateSLSA02
|
||||
require.NoError(t, json.Unmarshal(dtprv, &prv))
|
||||
require.Equal(t, provenancetypes.BuildKitBuildType, prv.BuildType)
|
||||
require.Equal(t, provenancetypes.BuildKitBuildType02, prv.BuildType)
|
||||
}
|
||||
|
||||
func testBakeMetadataWarnings(t *testing.T, sb integration.Sandbox) {
|
||||
|
@ -1737,7 +1740,93 @@ target "default" {
|
|||
)
|
||||
require.NoError(t, err, out)
|
||||
|
||||
require.Equal(t, "VARIABLE\tVALUE\tDESCRIPTION\nabc\t\t<null>\t\ndef\t\t\t\nfoo\t\tbar\tThis is foo", strings.TrimSpace(out))
|
||||
require.Equal(t, "VARIABLE\tTYPE\tVALUE\tDESCRIPTION\nabc\t\t\t<null>\t\ndef\t\t\t\t\nfoo\t\t\tbar\tThis is foo", strings.TrimSpace(out))
|
||||
}
|
||||
|
||||
func testListTypedVariables(t *testing.T, sb integration.Sandbox) {
|
||||
bakefile := []byte(`
|
||||
variable "abc" {
|
||||
type = string
|
||||
default = "bar"
|
||||
description = "This is abc"
|
||||
}
|
||||
variable "def" {
|
||||
type = string
|
||||
description = "simple type, no default"
|
||||
}
|
||||
variable "ghi" {
|
||||
type = number
|
||||
default = 99
|
||||
description = "simple type w/ default"
|
||||
}
|
||||
variable "jkl" {
|
||||
type = list(string)
|
||||
default = ["hello"]
|
||||
description = "collection with quoted strings"
|
||||
}
|
||||
variable "mno" {
|
||||
type = list(number)
|
||||
description = "collection, no default"
|
||||
}
|
||||
variable "pqr" {
|
||||
type = tuple([number, string, bool])
|
||||
default = [99, "99", true]
|
||||
}
|
||||
variable "stu" {
|
||||
type = map(string)
|
||||
default = {"foo": "bar"}
|
||||
}
|
||||
variable "vwx" {
|
||||
type = set(bool)
|
||||
default = null
|
||||
description = "collection, null default"
|
||||
}
|
||||
|
||||
// untyped, but previously didn't have its value output
|
||||
variable "wxy" {
|
||||
default = ["foo"]
|
||||
description = "inferred tuple"
|
||||
}
|
||||
// untyped, but previously didn't have its value output
|
||||
variable "xyz" {
|
||||
default = {"foo": "bar"}
|
||||
description = "inferred object"
|
||||
}
|
||||
// untyped, but previously didn't have its value output
|
||||
variable "yza" {
|
||||
default = true
|
||||
description = "inferred bool"
|
||||
}
|
||||
target "default" {
|
||||
}
|
||||
`)
|
||||
dir := tmpdir(
|
||||
t,
|
||||
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
|
||||
)
|
||||
|
||||
out, err := bakeCmd(
|
||||
sb,
|
||||
withDir(dir),
|
||||
withArgs("--list=variables"),
|
||||
)
|
||||
require.NoError(t, err, out)
|
||||
require.Equal(t,
|
||||
"VARIABLE\tTYPE\t\tVALUE\t\tDESCRIPTION\n"+
|
||||
"abc\t\tstring\t\tbar\t\tThis is abc\n"+
|
||||
"def\t\tstring\t\t<null>\t\tsimple type, no default\n"+
|
||||
"ghi\t\tnumber\t\t99\t\tsimple type w/ default\n"+
|
||||
"jkl\t\tlist of string\t[\"hello\"]\tcollection with quoted strings\n"+
|
||||
"mno\t\tlist of number\t<null>\t\tcollection, no default\n"+
|
||||
// the implementation for tuple's 'friendly name' is very basic
|
||||
// and marked as TODO, so this may change/break at some point
|
||||
"pqr\t\ttuple\t\t[99,\"99\",true]\t\n"+
|
||||
"stu\t\tmap of string\t{\"foo\":\"bar\"}\t\n"+
|
||||
"vwx\t\tset of bool\t<null>\t\tcollection, null default\n"+
|
||||
"wxy\t\t\t\t[\"foo\"]\t\tinferred tuple\n"+
|
||||
"xyz\t\t\t\t{\"foo\":\"bar\"}\tinferred object\n"+
|
||||
"yza\t\t\t\ttrue\t\tinferred bool",
|
||||
strings.TrimSpace(out))
|
||||
}
|
||||
|
||||
func testBakeCallCheck(t *testing.T, sb integration.Sandbox) {
|
||||
|
@ -2075,6 +2164,193 @@ target "third" {
|
|||
})
|
||||
}
|
||||
|
||||
func testBakeExtraHosts(t *testing.T, sb integration.Sandbox) {
|
||||
dockerfile := []byte(`
|
||||
FROM busybox
|
||||
RUN cat /etc/hosts | grep myhost | grep 1.2.3.4
|
||||
RUN cat /etc/hosts | grep myhostmulti | grep 162.242.195.81
|
||||
RUN cat /etc/hosts | grep myhostmulti | grep 162.242.195.82
|
||||
`)
|
||||
bakefile := []byte(`
|
||||
target "default" {
|
||||
extra-hosts = {
|
||||
myhost = "1.2.3.4"
|
||||
myhostmulti = "162.242.195.81,162.242.195.82"
|
||||
}
|
||||
}
|
||||
`)
|
||||
dir := tmpdir(
|
||||
t,
|
||||
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
|
||||
fstest.CreateFile("Dockerfile", dockerfile, 0600),
|
||||
)
|
||||
|
||||
out, err := bakeCmd(
|
||||
sb,
|
||||
withDir(dir),
|
||||
)
|
||||
require.NoError(t, err, out)
|
||||
}
|
||||
|
||||
func testBakeFileFromEnvironment(t *testing.T, sb integration.Sandbox) {
|
||||
bakeFileFirst := []byte(`
|
||||
target "first" {
|
||||
dockerfile-inline = "FROM scratch\nCOPY first /"
|
||||
}
|
||||
`)
|
||||
bakeFileSecond := []byte(`
|
||||
target "second" {
|
||||
dockerfile-inline = "FROM scratch\nCOPY second /"
|
||||
}
|
||||
`)
|
||||
|
||||
t.Run("single file", func(t *testing.T) {
|
||||
dir := tmpdir(t,
|
||||
fstest.CreateFile("first.hcl", bakeFileFirst, 0600),
|
||||
fstest.CreateFile("first", []byte("first"), 0600),
|
||||
)
|
||||
cmd := buildxCmd(sb,
|
||||
withDir(dir),
|
||||
withArgs("bake", "--progress=plain", "first"),
|
||||
withEnv("BUILDX_BAKE_FILE=first.hcl"))
|
||||
|
||||
dt, err := cmd.CombinedOutput()
|
||||
require.NoError(t, err, string(dt))
|
||||
require.Contains(t, string(dt), `#1 [internal] load local bake definitions from BUILDX_BAKE_FILE env`)
|
||||
require.Contains(t, string(dt), `#1 reading first.hcl`)
|
||||
})
|
||||
|
||||
t.Run("single file, default ignored if present", func(t *testing.T) {
|
||||
dir := tmpdir(t,
|
||||
fstest.CreateFile("first.hcl", bakeFileFirst, 0600),
|
||||
fstest.CreateFile("first", []byte("first"), 0600),
|
||||
fstest.CreateFile("docker-bake.hcl", []byte("invalid bake file"), 0600),
|
||||
)
|
||||
cmd := buildxCmd(sb,
|
||||
withDir(dir),
|
||||
withArgs("bake", "--progress=plain", "first"),
|
||||
withEnv("BUILDX_BAKE_FILE=first.hcl"))
|
||||
|
||||
dt, err := cmd.CombinedOutput()
|
||||
require.NoError(t, err, string(dt))
|
||||
require.Contains(t, string(dt), `#1 [internal] load local bake definitions from BUILDX_BAKE_FILE env`)
|
||||
require.Contains(t, string(dt), `#1 reading first.hcl`)
|
||||
require.NotContains(t, string(dt), "docker-bake.hcl")
|
||||
})
|
||||
|
||||
t.Run("multiple files", func(t *testing.T) {
|
||||
dir := tmpdir(t,
|
||||
fstest.CreateFile("first.hcl", bakeFileFirst, 0600),
|
||||
fstest.CreateFile("first", []byte("first"), 0600),
|
||||
fstest.CreateFile("second.hcl", bakeFileSecond, 0600),
|
||||
fstest.CreateFile("second", []byte("second"), 0600),
|
||||
)
|
||||
|
||||
cmd := buildxCmd(sb,
|
||||
withDir(dir),
|
||||
withArgs("bake", "--progress=plain", "second", "first"),
|
||||
withEnv("BUILDX_BAKE_FILE=first.hcl"+string(os.PathListSeparator)+"second.hcl"))
|
||||
dt, err := cmd.CombinedOutput()
|
||||
require.NoError(t, err, string(dt))
|
||||
require.Contains(t, string(dt), `#1 [internal] load local bake definitions from BUILDX_BAKE_FILE env`)
|
||||
require.Contains(t, string(dt), `#1 reading first.hcl`)
|
||||
require.Contains(t, string(dt), `#1 reading second.hcl`)
|
||||
})
|
||||
|
||||
t.Run("multiple files, custom separator", func(t *testing.T) {
|
||||
dir := tmpdir(t,
|
||||
fstest.CreateFile("first.hcl", bakeFileFirst, 0600),
|
||||
fstest.CreateFile("first", []byte("first"), 0600),
|
||||
fstest.CreateFile("second.hcl", bakeFileSecond, 0600),
|
||||
fstest.CreateFile("second", []byte("second"), 0600),
|
||||
)
|
||||
|
||||
cmd := buildxCmd(sb,
|
||||
withDir(dir),
|
||||
withArgs("bake", "--progress=plain", "second", "first"),
|
||||
withEnv("BUILDX_BAKE_PATH_SEPARATOR=@", "BUILDX_BAKE_FILE=first.hcl@second.hcl"))
|
||||
|
||||
dt, err := cmd.CombinedOutput()
|
||||
require.NoError(t, err, string(dt))
|
||||
require.Contains(t, string(dt), `#1 [internal] load local bake definitions from BUILDX_BAKE_FILE env`)
|
||||
require.Contains(t, string(dt), `#1 reading first.hcl`)
|
||||
require.Contains(t, string(dt), `#1 reading second.hcl`)
|
||||
})
|
||||
|
||||
t.Run("multiple files, one STDIN", func(t *testing.T) {
|
||||
dir := tmpdir(t,
|
||||
fstest.CreateFile("first.hcl", bakeFileFirst, 0600),
|
||||
fstest.CreateFile("first", []byte("first"), 0600),
|
||||
fstest.CreateFile("second", []byte("second"), 0600),
|
||||
)
|
||||
|
||||
cmd := buildxCmd(sb,
|
||||
withDir(dir),
|
||||
withArgs("bake", "--progress=plain", "second", "first"),
|
||||
withEnv("BUILDX_BAKE_FILE=first.hcl"+string(os.PathListSeparator)+"-"))
|
||||
w, err := cmd.StdinPipe()
|
||||
require.NoError(t, err)
|
||||
go func() {
|
||||
defer w.Close()
|
||||
w.Write(bakeFileSecond)
|
||||
}()
|
||||
|
||||
dt, err := cmd.CombinedOutput()
|
||||
require.NoError(t, err, string(dt))
|
||||
require.Contains(t, string(dt), `#1 [internal] load local bake definitions from BUILDX_BAKE_FILE env`)
|
||||
require.Contains(t, string(dt), `#1 reading first.hcl`)
|
||||
require.Contains(t, string(dt), `#1 reading from stdin`)
|
||||
})
|
||||
|
||||
t.Run("env ignored if file arg passed", func(t *testing.T) {
|
||||
dir := tmpdir(t,
|
||||
fstest.CreateFile("first.hcl", bakeFileFirst, 0600),
|
||||
fstest.CreateFile("first", []byte("first"), 0600),
|
||||
fstest.CreateFile("second.hcl", bakeFileSecond, 0600),
|
||||
)
|
||||
cmd := buildxCmd(sb,
|
||||
withDir(dir),
|
||||
withArgs("bake", "--progress=plain", "-f", "first.hcl", "first", "second"),
|
||||
withEnv("BUILDX_BAKE_FILE=second.hcl"))
|
||||
|
||||
dt, err := cmd.CombinedOutput()
|
||||
require.Error(t, err, string(dt))
|
||||
require.Contains(t, string(dt), "failed to find target second")
|
||||
})
|
||||
|
||||
t.Run("file does not exist", func(t *testing.T) {
|
||||
dir := tmpdir(t,
|
||||
fstest.CreateFile("first.hcl", bakeFileFirst, 0600),
|
||||
fstest.CreateFile("first", []byte("first"), 0600),
|
||||
)
|
||||
cmd := buildxCmd(sb,
|
||||
withDir(dir),
|
||||
withArgs("bake", "--progress=plain", "first"),
|
||||
withEnv("BUILDX_BAKE_FILE=wrong.hcl"))
|
||||
|
||||
dt, err := cmd.CombinedOutput()
|
||||
require.Error(t, err, string(dt))
|
||||
require.Contains(t, string(dt), "wrong.hcl: no such file or directory")
|
||||
})
|
||||
|
||||
for kind, val := range map[string]string{"missing": "", "whitespace": " "} {
|
||||
t.Run(kind+" value ignored", func(t *testing.T) {
|
||||
dir := tmpdir(t,
|
||||
fstest.CreateFile("first.hcl", bakeFileFirst, 0600),
|
||||
fstest.CreateFile("first", []byte("first"), 0600),
|
||||
)
|
||||
cmd := buildxCmd(sb,
|
||||
withDir(dir),
|
||||
withArgs("bake", "--progress=plain", "first"),
|
||||
withEnv(fmt.Sprintf("BUILDX_BAKE_FILE=%s", val)))
|
||||
|
||||
dt, err := cmd.CombinedOutput()
|
||||
require.Error(t, err, string(dt))
|
||||
require.Contains(t, string(dt), "couldn't find a bake definition")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func writeTempPrivateKey(fp string) error {
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
|
|
|
@ -77,6 +77,7 @@ var buildTests = []func(t *testing.T, sb integration.Sandbox){
|
|||
testBuildDefaultLoad,
|
||||
testBuildCall,
|
||||
testCheckCallOutput,
|
||||
testBuildExtraHosts,
|
||||
}
|
||||
|
||||
func testBuild(t *testing.T, sb integration.Sandbox) {
|
||||
|
@ -832,9 +833,9 @@ func buildMetadataProvenance(t *testing.T, sb integration.Sandbox, metadataMode
|
|||
dtprv, err := json.Marshal(md.BuildProvenance)
|
||||
require.NoError(t, err)
|
||||
|
||||
var prv provenancetypes.ProvenancePredicate
|
||||
var prv provenancetypes.ProvenancePredicateSLSA02
|
||||
require.NoError(t, json.Unmarshal(dtprv, &prv))
|
||||
require.Equal(t, provenancetypes.BuildKitBuildType, prv.BuildType)
|
||||
require.Equal(t, provenancetypes.BuildKitBuildType02, prv.BuildType)
|
||||
}
|
||||
|
||||
func testBuildMetadataWarnings(t *testing.T, sb integration.Sandbox) {
|
||||
|
@ -1322,6 +1323,24 @@ cOpy Dockerfile .
|
|||
})
|
||||
}
|
||||
|
||||
func testBuildExtraHosts(t *testing.T, sb integration.Sandbox) {
|
||||
dockerfile := []byte(`
|
||||
FROM busybox
|
||||
RUN cat /etc/hosts | grep myhost | grep 1.2.3.4
|
||||
RUN cat /etc/hosts | grep myhostmulti | grep 162.242.195.81
|
||||
RUN cat /etc/hosts | grep myhostmulti | grep 162.242.195.82
|
||||
`)
|
||||
dir := tmpdir(t, fstest.CreateFile("Dockerfile", dockerfile, 0600))
|
||||
cmd := buildxCmd(sb, withArgs("build",
|
||||
"--add-host=myhost=1.2.3.4",
|
||||
"--add-host=myhostmulti=162.242.195.81",
|
||||
"--add-host=myhostmulti=162.242.195.82",
|
||||
"--output=type=cacheonly", dir),
|
||||
)
|
||||
out, err := cmd.CombinedOutput()
|
||||
require.NoError(t, err, string(out))
|
||||
}
|
||||
|
||||
func createTestProject(t *testing.T) string {
|
||||
dockerfile := []byte(`
|
||||
FROM busybox:latest AS base
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
controllerapi "github.com/docker/buildx/controller/pb"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tonistiigi/go-csvvalue"
|
||||
)
|
||||
|
@ -33,16 +32,32 @@ func (a Attests) Normalize() Attests {
|
|||
return removeAttestDupes(a)
|
||||
}
|
||||
|
||||
func (a Attests) ToPB() []*controllerapi.Attest {
|
||||
if len(a) == 0 {
|
||||
return nil
|
||||
}
|
||||
func (a Attests) ToMap() map[string]*string {
|
||||
result := map[string]*string{}
|
||||
for _, attest := range a {
|
||||
// ignore duplicates
|
||||
if _, ok := result[attest.Type]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
entries := make([]*controllerapi.Attest, len(a))
|
||||
for i, entry := range a {
|
||||
entries[i] = entry.ToPB()
|
||||
if attest.Disabled {
|
||||
result[attest.Type] = nil
|
||||
continue
|
||||
}
|
||||
|
||||
var b csvBuilder
|
||||
if attest.Type != "" {
|
||||
b.Write("type", attest.Type)
|
||||
}
|
||||
if attest.Disabled {
|
||||
b.Write("disabled", "true")
|
||||
}
|
||||
b.WriteAttributes(attest.Attrs)
|
||||
|
||||
s := b.String()
|
||||
result[attest.Type] = &s
|
||||
}
|
||||
return entries
|
||||
return result
|
||||
}
|
||||
|
||||
type Attest struct {
|
||||
|
@ -72,23 +87,6 @@ func (a *Attest) String() string {
|
|||
return b.String()
|
||||
}
|
||||
|
||||
func (a *Attest) ToPB() *controllerapi.Attest {
|
||||
var b csvBuilder
|
||||
if a.Type != "" {
|
||||
b.Write("type", a.Type)
|
||||
}
|
||||
if a.Disabled {
|
||||
b.Write("disabled", "true")
|
||||
}
|
||||
b.WriteAttributes(a.Attrs)
|
||||
|
||||
return &controllerapi.Attest{
|
||||
Type: a.Type,
|
||||
Disabled: a.Disabled,
|
||||
Attrs: b.String(),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Attest) MarshalJSON() ([]byte, error) {
|
||||
m := make(map[string]any, len(a.Attrs)+2)
|
||||
for k, v := range a.Attrs {
|
||||
|
@ -182,7 +180,7 @@ func CanonicalizeAttest(attestType string, in string) string {
|
|||
return fmt.Sprintf("type=%s,%s", attestType, in)
|
||||
}
|
||||
|
||||
func ParseAttests(in []string) ([]*controllerapi.Attest, error) {
|
||||
func ParseAttests(in []string) (Attests, error) {
|
||||
var outs []*Attest
|
||||
for _, s := range in {
|
||||
var out Attest
|
||||
|
@ -191,66 +189,7 @@ func ParseAttests(in []string) ([]*controllerapi.Attest, error) {
|
|||
}
|
||||
outs = append(outs, &out)
|
||||
}
|
||||
return ConvertAttests(outs)
|
||||
}
|
||||
|
||||
// ConvertAttests converts Attestations for the controller API from
|
||||
// the ones in this package.
|
||||
//
|
||||
// Attestations of the same type will cause an error. Some tools,
|
||||
// like bake, remove the duplicates before calling this function.
|
||||
func ConvertAttests(in []*Attest) ([]*controllerapi.Attest, error) {
|
||||
out := make([]*controllerapi.Attest, 0, len(in))
|
||||
|
||||
// Check for duplicate attestations while we convert them
|
||||
// to the controller API.
|
||||
found := map[string]struct{}{}
|
||||
for _, attest := range in {
|
||||
if _, ok := found[attest.Type]; ok {
|
||||
return nil, errors.Errorf("duplicate attestation field %s", attest.Type)
|
||||
}
|
||||
found[attest.Type] = struct{}{}
|
||||
out = append(out, attest.ToPB())
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func ParseAttest(in string) (*controllerapi.Attest, error) {
|
||||
if in == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
fields, err := csvvalue.Fields(in, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attest := controllerapi.Attest{
|
||||
Attrs: in,
|
||||
}
|
||||
for _, field := range fields {
|
||||
key, value, ok := strings.Cut(field, "=")
|
||||
if !ok {
|
||||
return nil, errors.Errorf("invalid value %s", field)
|
||||
}
|
||||
key = strings.TrimSpace(strings.ToLower(key))
|
||||
|
||||
switch key {
|
||||
case "type":
|
||||
attest.Type = value
|
||||
case "disabled":
|
||||
disabled, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid value %s", field)
|
||||
}
|
||||
attest.Disabled = disabled
|
||||
}
|
||||
}
|
||||
if attest.Type == "" {
|
||||
return nil, errors.Errorf("attestation type not specified")
|
||||
}
|
||||
|
||||
return &attest, nil
|
||||
return outs, nil
|
||||
}
|
||||
|
||||
func removeAttestDupes(s []*Attest) []*Attest {
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
package buildflags
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"maps"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
awsconfig "github.com/aws/aws-sdk-go-v2/config"
|
||||
controllerapi "github.com/docker/buildx/controller/pb"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tonistiigi/go-csvvalue"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
@ -35,22 +30,6 @@ func (o CacheOptions) Normalize() CacheOptions {
|
|||
return removeDupes(o)
|
||||
}
|
||||
|
||||
func (o CacheOptions) ToPB() []*controllerapi.CacheOptionsEntry {
|
||||
if len(o) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var outs []*controllerapi.CacheOptionsEntry
|
||||
for _, entry := range o {
|
||||
pb := entry.ToPB()
|
||||
if !isActive(pb) {
|
||||
continue
|
||||
}
|
||||
outs = append(outs, pb)
|
||||
}
|
||||
return outs
|
||||
}
|
||||
|
||||
type CacheOptionsEntry struct {
|
||||
Type string `json:"type"`
|
||||
Attrs map[string]string `json:"attrs,omitempty"`
|
||||
|
@ -81,16 +60,6 @@ func (e *CacheOptionsEntry) String() string {
|
|||
return b.String()
|
||||
}
|
||||
|
||||
func (e *CacheOptionsEntry) ToPB() *controllerapi.CacheOptionsEntry {
|
||||
ci := &controllerapi.CacheOptionsEntry{
|
||||
Type: e.Type,
|
||||
Attrs: maps.Clone(e.Attrs),
|
||||
}
|
||||
addGithubToken(ci)
|
||||
addAwsCredentials(ci)
|
||||
return ci
|
||||
}
|
||||
|
||||
func (e *CacheOptionsEntry) MarshalJSON() ([]byte, error) {
|
||||
m := maps.Clone(e.Attrs)
|
||||
if m == nil {
|
||||
|
@ -204,75 +173,3 @@ func ParseCacheEntry(in []string) (CacheOptions, error) {
|
|||
}
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
func addGithubToken(ci *controllerapi.CacheOptionsEntry) {
|
||||
if ci.Type != "gha" {
|
||||
return
|
||||
}
|
||||
version, ok := ci.Attrs["version"]
|
||||
if !ok {
|
||||
// https://github.com/actions/toolkit/blob/2b08dc18f261b9fdd978b70279b85cbef81af8bc/packages/cache/src/internal/config.ts#L19
|
||||
if v, ok := os.LookupEnv("ACTIONS_CACHE_SERVICE_V2"); ok {
|
||||
if b, err := strconv.ParseBool(v); err == nil && b {
|
||||
version = "2"
|
||||
}
|
||||
}
|
||||
}
|
||||
if _, ok := ci.Attrs["token"]; !ok {
|
||||
if v, ok := os.LookupEnv("ACTIONS_RUNTIME_TOKEN"); ok {
|
||||
ci.Attrs["token"] = v
|
||||
}
|
||||
}
|
||||
if _, ok := ci.Attrs["url_v2"]; !ok && version == "2" {
|
||||
// https://github.com/actions/toolkit/blob/2b08dc18f261b9fdd978b70279b85cbef81af8bc/packages/cache/src/internal/config.ts#L34-L35
|
||||
if v, ok := os.LookupEnv("ACTIONS_RESULTS_URL"); ok {
|
||||
ci.Attrs["url_v2"] = v
|
||||
}
|
||||
}
|
||||
if _, ok := ci.Attrs["url"]; !ok {
|
||||
// https://github.com/actions/toolkit/blob/2b08dc18f261b9fdd978b70279b85cbef81af8bc/packages/cache/src/internal/config.ts#L28-L33
|
||||
if v, ok := os.LookupEnv("ACTIONS_CACHE_URL"); ok {
|
||||
ci.Attrs["url"] = v
|
||||
} else if v, ok := os.LookupEnv("ACTIONS_RESULTS_URL"); ok {
|
||||
ci.Attrs["url"] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addAwsCredentials(ci *controllerapi.CacheOptionsEntry) {
|
||||
if ci.Type != "s3" {
|
||||
return
|
||||
}
|
||||
_, okAccessKeyID := ci.Attrs["access_key_id"]
|
||||
_, okSecretAccessKey := ci.Attrs["secret_access_key"]
|
||||
// If the user provides access_key_id, secret_access_key, do not override the session token.
|
||||
if okAccessKeyID && okSecretAccessKey {
|
||||
return
|
||||
}
|
||||
ctx := context.TODO()
|
||||
awsConfig, err := awsconfig.LoadDefaultConfig(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
credentials, err := awsConfig.Credentials.Retrieve(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !okAccessKeyID && credentials.AccessKeyID != "" {
|
||||
ci.Attrs["access_key_id"] = credentials.AccessKeyID
|
||||
}
|
||||
if !okSecretAccessKey && credentials.SecretAccessKey != "" {
|
||||
ci.Attrs["secret_access_key"] = credentials.SecretAccessKey
|
||||
}
|
||||
if _, ok := ci.Attrs["session_token"]; !ok && credentials.SessionToken != "" {
|
||||
ci.Attrs["session_token"] = credentials.SessionToken
|
||||
}
|
||||
}
|
||||
|
||||
func isActive(pb *controllerapi.CacheOptionsEntry) bool {
|
||||
// Always active if not gha.
|
||||
if pb.Type != "gha" {
|
||||
return true
|
||||
}
|
||||
return pb.Attrs["token"] != "" && (pb.Attrs["url"] != "" || pb.Attrs["url_v2"] != "")
|
||||
}
|
||||
|
|
|
@ -4,42 +4,10 @@ import (
|
|||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/buildx/controller/pb"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
func TestCacheOptions_DerivedVars(t *testing.T) {
|
||||
t.Setenv("ACTIONS_RUNTIME_TOKEN", "sensitive_token")
|
||||
t.Setenv("ACTIONS_CACHE_URL", "https://cache.github.com")
|
||||
t.Setenv("AWS_ACCESS_KEY_ID", "definitely_dont_look_here")
|
||||
t.Setenv("AWS_SECRET_ACCESS_KEY", "hackers_please_dont_steal")
|
||||
t.Setenv("AWS_SESSION_TOKEN", "not_a_mitm_attack")
|
||||
|
||||
cacheFrom, err := ParseCacheEntry([]string{"type=gha", "type=s3,region=us-west-2,bucket=my_bucket,name=my_image"})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []*pb.CacheOptionsEntry{
|
||||
{
|
||||
Type: "gha",
|
||||
Attrs: map[string]string{
|
||||
"token": "sensitive_token",
|
||||
"url": "https://cache.github.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "s3",
|
||||
Attrs: map[string]string{
|
||||
"region": "us-west-2",
|
||||
"bucket": "my_bucket",
|
||||
"name": "my_image",
|
||||
"access_key_id": "definitely_dont_look_here",
|
||||
"secret_access_key": "hackers_please_dont_steal",
|
||||
"session_token": "not_a_mitm_attack",
|
||||
},
|
||||
},
|
||||
}, cacheFrom.ToPB())
|
||||
}
|
||||
|
||||
func TestCacheOptions(t *testing.T) {
|
||||
t.Run("MarshalJSON", func(t *testing.T) {
|
||||
cache := CacheOptions{
|
||||
|
|
|
@ -1,17 +1,37 @@
|
|||
package buildflags
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
controllerapi "github.com/docker/buildx/controller/pb"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tonistiigi/go-csvvalue"
|
||||
)
|
||||
|
||||
const defaultCallFunc = "build"
|
||||
|
||||
func ParseCallFunc(str string) (*controllerapi.CallFunc, error) {
|
||||
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, " ")
|
||||
}
|
||||
|
||||
func ParseCallFunc(str string) (*CallFunc, error) {
|
||||
if str == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -20,7 +40,7 @@ func ParseCallFunc(str string) (*controllerapi.CallFunc, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f := &controllerapi.CallFunc{}
|
||||
f := &CallFunc{}
|
||||
for _, field := range fields {
|
||||
parts := strings.SplitN(field, "=", 2)
|
||||
if len(parts) == 2 {
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/containerd/platforms"
|
||||
controllerapi "github.com/docker/buildx/controller/pb"
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/moby/buildkit/exporter/containerimage/exptypes"
|
||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
|
@ -38,18 +37,6 @@ func (e Exports) Normalize() Exports {
|
|||
return removeDupes(e)
|
||||
}
|
||||
|
||||
func (e Exports) ToPB() []*controllerapi.ExportEntry {
|
||||
if len(e) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
entries := make([]*controllerapi.ExportEntry, len(e))
|
||||
for i, entry := range e {
|
||||
entries[i] = entry.ToPB()
|
||||
}
|
||||
return entries
|
||||
}
|
||||
|
||||
type ExportEntry struct {
|
||||
Type string `json:"type"`
|
||||
Attrs map[string]string `json:"attrs,omitempty"`
|
||||
|
@ -77,14 +64,6 @@ func (e *ExportEntry) String() string {
|
|||
return b.String()
|
||||
}
|
||||
|
||||
func (e *ExportEntry) ToPB() *controllerapi.ExportEntry {
|
||||
return &controllerapi.ExportEntry{
|
||||
Type: e.Type,
|
||||
Attrs: maps.Clone(e.Attrs),
|
||||
Destination: e.Destination,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *ExportEntry) MarshalJSON() ([]byte, error) {
|
||||
m := maps.Clone(e.Attrs)
|
||||
if m == nil {
|
||||
|
@ -164,12 +143,12 @@ func (e *ExportEntry) validate() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func ParseExports(inp []string) ([]*controllerapi.ExportEntry, error) {
|
||||
func ParseExports(inp []string) ([]*ExportEntry, error) {
|
||||
if len(inp) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
export := make(Exports, 0, len(inp))
|
||||
exports := make(Exports, 0, len(inp))
|
||||
for _, s := range inp {
|
||||
if s == "" {
|
||||
continue
|
||||
|
@ -179,9 +158,9 @@ func ParseExports(inp []string) ([]*controllerapi.ExportEntry, error) {
|
|||
if err := out.UnmarshalText([]byte(s)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
export = append(export, &out)
|
||||
exports = append(exports, &out)
|
||||
}
|
||||
return export.ToPB(), nil
|
||||
return exports, nil
|
||||
}
|
||||
|
||||
func ParseAnnotations(inp []string) (map[exptypes.AnnotationKey]string, error) {
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
controllerapi "github.com/docker/buildx/controller/pb"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tonistiigi/go-csvvalue"
|
||||
)
|
||||
|
@ -30,18 +29,6 @@ func (s Secrets) Normalize() Secrets {
|
|||
return removeSecretDupes(s)
|
||||
}
|
||||
|
||||
func (s Secrets) ToPB() []*controllerapi.Secret {
|
||||
if len(s) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
entries := make([]*controllerapi.Secret, len(s))
|
||||
for i, entry := range s {
|
||||
entries[i] = entry.ToPB()
|
||||
}
|
||||
return entries
|
||||
}
|
||||
|
||||
type Secret struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
FilePath string `json:"src,omitempty"`
|
||||
|
@ -66,14 +53,6 @@ func (s *Secret) String() string {
|
|||
return b.String()
|
||||
}
|
||||
|
||||
func (s *Secret) ToPB() *controllerapi.Secret {
|
||||
return &controllerapi.Secret{
|
||||
ID: s.ID,
|
||||
FilePath: s.FilePath,
|
||||
Env: s.Env,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Secret) UnmarshalJSON(data []byte) error {
|
||||
var v struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
|
@ -132,8 +111,8 @@ func (s *Secret) UnmarshalText(text []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func ParseSecretSpecs(sl []string) ([]*controllerapi.Secret, error) {
|
||||
fs := make([]*controllerapi.Secret, 0, len(sl))
|
||||
func ParseSecretSpecs(sl []string) (Secrets, error) {
|
||||
fs := make([]*Secret, 0, len(sl))
|
||||
for _, v := range sl {
|
||||
if v == "" {
|
||||
continue
|
||||
|
@ -148,12 +127,12 @@ func ParseSecretSpecs(sl []string) ([]*controllerapi.Secret, error) {
|
|||
return fs, nil
|
||||
}
|
||||
|
||||
func parseSecret(value string) (*controllerapi.Secret, error) {
|
||||
func parseSecret(value string) (*Secret, error) {
|
||||
var s Secret
|
||||
if err := s.UnmarshalText([]byte(value)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.ToPB(), nil
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
func removeSecretDupes(s []*Secret) []*Secret {
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"slices"
|
||||
"strings"
|
||||
|
||||
controllerapi "github.com/docker/buildx/controller/pb"
|
||||
"github.com/moby/buildkit/util/gitutil"
|
||||
)
|
||||
|
||||
|
@ -31,18 +30,6 @@ func (s SSHKeys) Normalize() SSHKeys {
|
|||
return removeSSHDupes(s)
|
||||
}
|
||||
|
||||
func (s SSHKeys) ToPB() []*controllerapi.SSH {
|
||||
if len(s) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
entries := make([]*controllerapi.SSH, len(s))
|
||||
for i, entry := range s {
|
||||
entries[i] = entry.ToPB()
|
||||
}
|
||||
return entries
|
||||
}
|
||||
|
||||
type SSH struct {
|
||||
ID string `json:"id,omitempty" cty:"id"`
|
||||
Paths []string `json:"paths,omitempty" cty:"paths"`
|
||||
|
@ -70,13 +57,6 @@ func (s *SSH) String() string {
|
|||
return b.String()
|
||||
}
|
||||
|
||||
func (s *SSH) ToPB() *controllerapi.SSH {
|
||||
return &controllerapi.SSH{
|
||||
ID: s.ID,
|
||||
Paths: s.Paths,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SSH) UnmarshalJSON(data []byte) error {
|
||||
var v struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
|
@ -103,8 +83,8 @@ func (s *SSH) UnmarshalText(text []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func ParseSSHSpecs(sl []string) ([]*controllerapi.SSH, error) {
|
||||
var outs []*controllerapi.SSH
|
||||
func ParseSSHSpecs(sl []string) ([]*SSH, error) {
|
||||
var outs []*SSH
|
||||
if len(sl) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -118,7 +98,7 @@ func ParseSSHSpecs(sl []string) ([]*controllerapi.SSH, error) {
|
|||
if err := out.UnmarshalText([]byte(s)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outs = append(outs, out.ToPB())
|
||||
outs = append(outs, &out)
|
||||
}
|
||||
return outs, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
package imagetools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/remotes/docker"
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/cli/cli/config/types"
|
||||
)
|
||||
|
||||
type authConfig struct {
|
||||
mu sync.Mutex
|
||||
authConfigCache map[string]authConfigCacheEntry
|
||||
cfg Auth
|
||||
}
|
||||
|
||||
type authConfigCacheEntry struct {
|
||||
Created time.Time
|
||||
Auth types.AuthConfig
|
||||
}
|
||||
|
||||
func newAuthConfig(a Auth) *authConfig {
|
||||
return &authConfig{
|
||||
authConfigCache: map[string]authConfigCacheEntry{},
|
||||
cfg: a,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *authConfig) credentials(host string) (string, string, error) {
|
||||
ac, err := a.authConfig(host)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if ac.IdentityToken != "" {
|
||||
return "", ac.IdentityToken, nil
|
||||
}
|
||||
return ac.Username, ac.Password, nil
|
||||
}
|
||||
|
||||
func (a *authConfig) authConfig(host string) (types.AuthConfig, error) {
|
||||
const defaultExpiration = 2 * time.Minute
|
||||
|
||||
if host == "registry-1.docker.io" {
|
||||
host = "https://index.docker.io/v1/"
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
if c, ok := a.authConfigCache[host]; ok && time.Since(c.Created) <= defaultExpiration {
|
||||
return c.Auth, nil
|
||||
}
|
||||
ac, err := a.cfg.GetAuthConfig(host)
|
||||
if err != nil {
|
||||
return types.AuthConfig{}, err
|
||||
}
|
||||
a.authConfigCache[host] = authConfigCacheEntry{
|
||||
Created: time.Now(),
|
||||
Auth: ac,
|
||||
}
|
||||
return ac, nil
|
||||
}
|
||||
|
||||
func RegistryAuthForRef(ref string, a Auth) (string, error) {
|
||||
if a == nil {
|
||||
return "", nil
|
||||
}
|
||||
r, err := parseRef(ref)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
host := reference.Domain(r)
|
||||
if host == "docker.io" {
|
||||
host = "https://index.docker.io/v1/"
|
||||
}
|
||||
ac, err := a.GetAuthConfig(host)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf, err := json.Marshal(ac)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.URLEncoding.EncodeToString(buf), nil
|
||||
}
|
||||
|
||||
type withBearerAuthorizer struct {
|
||||
docker.Authorizer
|
||||
AuthConfig *authConfig
|
||||
}
|
||||
|
||||
func (a *withBearerAuthorizer) Authorize(ctx context.Context, req *http.Request) error {
|
||||
ac, err := a.AuthConfig.authConfig(req.Host)
|
||||
if err == nil && ac.RegistryToken != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+ac.RegistryToken)
|
||||
return nil
|
||||
}
|
||||
return a.Authorizer.Authorize(ctx, req)
|
||||
}
|
|
@ -3,8 +3,6 @@ package imagetools
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
|
@ -36,8 +34,14 @@ type Resolver struct {
|
|||
}
|
||||
|
||||
func New(opt Opt) *Resolver {
|
||||
ac := newAuthConfig(opt.Auth)
|
||||
dockerAuth := docker.NewDockerAuthorizer(docker.WithAuthCreds(ac.credentials), docker.WithAuthClient(http.DefaultClient))
|
||||
auth := &withBearerAuthorizer{
|
||||
Authorizer: dockerAuth,
|
||||
AuthConfig: ac,
|
||||
}
|
||||
return &Resolver{
|
||||
auth: docker.NewDockerAuthorizer(docker.WithAuthCreds(toCredentialsFunc(opt.Auth)), docker.WithAuthClient(http.DefaultClient)),
|
||||
auth: auth,
|
||||
hosts: resolver.NewRegistryConfig(opt.RegistryConfig),
|
||||
buffer: contentutil.NewBuffer(),
|
||||
}
|
||||
|
@ -121,42 +125,3 @@ func parseRef(s string) (reference.Named, error) {
|
|||
ref = reference.TagNameOnly(ref)
|
||||
return ref, nil
|
||||
}
|
||||
|
||||
func toCredentialsFunc(a Auth) func(string) (string, string, error) {
|
||||
return func(host string) (string, string, error) {
|
||||
if host == "registry-1.docker.io" {
|
||||
host = "https://index.docker.io/v1/"
|
||||
}
|
||||
ac, err := a.GetAuthConfig(host)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if ac.IdentityToken != "" {
|
||||
return "", ac.IdentityToken, nil
|
||||
}
|
||||
return ac.Username, ac.Password, nil
|
||||
}
|
||||
}
|
||||
|
||||
func RegistryAuthForRef(ref string, a Auth) (string, error) {
|
||||
if a == nil {
|
||||
return "", nil
|
||||
}
|
||||
r, err := parseRef(ref)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
host := reference.Domain(r)
|
||||
if host == "docker.io" {
|
||||
host = "https://index.docker.io/v1/"
|
||||
}
|
||||
ac, err := a.GetAuthConfig(host)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf, err := json.Marshal(ac)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.URLEncoding.EncodeToString(buf), nil
|
||||
}
|
||||
|
|
|
@ -229,20 +229,24 @@ func copyToFunc(r io.Reader, wFunc func() (io.Writer, error)) error {
|
|||
buf := make([]byte, 4096)
|
||||
for {
|
||||
n, readErr := r.Read(buf)
|
||||
if readErr != nil && readErr != io.EOF {
|
||||
return readErr
|
||||
}
|
||||
w, err := wFunc()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if w != nil {
|
||||
if _, err := w.Write(buf[:n]); err != nil {
|
||||
logrus.WithError(err).Debugf("failed to copy")
|
||||
|
||||
if n > 0 {
|
||||
w, err := wFunc()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if w != nil {
|
||||
if _, err := w.Write(buf[:n]); err != nil {
|
||||
logrus.WithError(err).Debugf("failed to copy")
|
||||
}
|
||||
}
|
||||
}
|
||||
if readErr == io.EOF {
|
||||
return nil
|
||||
|
||||
if readErr != nil {
|
||||
if isReaderClosed(readErr) {
|
||||
return nil
|
||||
}
|
||||
return readErr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -255,3 +259,7 @@ type readerWithClose struct {
|
|||
func (r *readerWithClose) Close() error {
|
||||
return r.closeFunc()
|
||||
}
|
||||
|
||||
func isReaderClosed(err error) bool {
|
||||
return errors.Is(err, io.EOF) || errors.Is(err, io.ErrClosedPipe)
|
||||
}
|
||||
|
|
|
@ -133,7 +133,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -271,7 +272,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -409,7 +411,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -547,7 +550,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -664,7 +668,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -802,7 +807,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -940,7 +946,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -1078,7 +1085,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -1321,7 +1329,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -1459,7 +1468,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -1597,7 +1607,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -1756,7 +1767,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -1894,7 +1906,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -2032,7 +2045,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -2170,7 +2184,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -2308,7 +2323,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -2446,7 +2462,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -2584,7 +2601,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -3289,7 +3307,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -3355,7 +3374,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -3450,7 +3470,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -3545,7 +3566,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -3640,7 +3662,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -3735,7 +3758,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -3830,7 +3854,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -3925,7 +3950,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -4020,7 +4046,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -4115,7 +4142,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -4189,7 +4217,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -4284,7 +4313,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -4358,7 +4388,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -4453,7 +4484,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -4548,7 +4580,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -4664,7 +4697,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -4971,7 +5005,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -5045,7 +5080,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -5119,7 +5155,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -5193,7 +5230,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -5288,7 +5326,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -5362,7 +5401,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -5457,7 +5497,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -5552,7 +5593,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -5647,7 +5689,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -5742,7 +5785,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -5858,7 +5902,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -6165,7 +6210,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -6281,7 +6327,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -6588,7 +6635,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -6710,7 +6758,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -6805,7 +6854,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -6879,7 +6929,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -6953,7 +7004,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -7027,7 +7079,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -7122,7 +7175,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -7196,7 +7250,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -7270,7 +7325,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -7344,7 +7400,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -7652,7 +7709,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -7726,7 +7784,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -7833,7 +7892,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -7907,7 +7967,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -7981,7 +8042,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -8055,7 +8117,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -8129,7 +8192,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -8203,7 +8267,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -10399,7 +10464,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -10537,7 +10603,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -10654,7 +10721,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -10835,7 +10903,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -10930,7 +10999,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -11025,7 +11095,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -11120,6 +11191,7 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -134,7 +134,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -272,7 +273,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -410,7 +412,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -548,7 +551,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -665,7 +669,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -803,7 +808,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -941,7 +947,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1079,7 +1086,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1322,7 +1330,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1460,7 +1469,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1598,7 +1608,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1757,7 +1768,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1895,7 +1907,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -2033,7 +2046,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -2171,7 +2185,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -2309,7 +2324,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -2447,7 +2463,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -2585,7 +2602,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -3290,7 +3308,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -3356,7 +3375,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -3451,7 +3471,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -3546,7 +3567,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -3641,7 +3663,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -3736,7 +3759,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -3831,7 +3855,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -3926,7 +3951,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -4021,7 +4047,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -4116,7 +4143,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -4190,7 +4218,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -4285,7 +4314,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -4359,7 +4389,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -4454,7 +4485,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -4549,7 +4581,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -4665,7 +4698,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -4972,7 +5006,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -5046,7 +5081,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -5120,7 +5156,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -5194,7 +5231,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -5289,7 +5327,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -5363,7 +5402,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -5458,7 +5498,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -5553,7 +5594,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -5648,7 +5690,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -5743,7 +5786,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -5859,7 +5903,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -6166,7 +6211,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -6282,7 +6328,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -6589,7 +6636,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -6711,7 +6759,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -6806,7 +6855,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -6880,7 +6930,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -6954,7 +7005,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -7028,7 +7080,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -7123,7 +7176,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -7197,7 +7251,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -7271,7 +7326,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -7345,7 +7401,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -7653,7 +7710,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -7727,7 +7785,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -7834,7 +7893,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -7908,7 +7968,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -7982,7 +8043,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -8056,7 +8118,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -8130,7 +8193,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -8204,7 +8268,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -10400,7 +10465,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -10538,7 +10604,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -10655,7 +10722,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "semver:0.40.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -10729,7 +10797,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -10836,7 +10905,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/otel/sdk/tracer",
|
||||
"Version": "",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -10931,7 +11001,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -11026,7 +11097,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -11121,7 +11193,8 @@
|
|||
"InstrumentationLibrary": {
|
||||
"Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
|
||||
"Version": "0.45.0",
|
||||
"SchemaURL": ""
|
||||
"SchemaURL": "",
|
||||
"Attributes": null
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
|
@ -30,7 +30,7 @@ func TestParseSpanStubs(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
dtotel, err := os.ReadFile(otlpFixture)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(dtotel), string(dtSpanStubs))
|
||||
require.Equal(t, string(bytes.TrimSpace(dtotel)), string(dtSpanStubs))
|
||||
|
||||
exp, err := stdouttrace.New(stdouttrace.WithPrettyPrint())
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -16,12 +16,24 @@ import (
|
|||
"go.opentelemetry.io/otel/metric"
|
||||
)
|
||||
|
||||
type Printer struct {
|
||||
status chan *client.SolveStatus
|
||||
type printerState int
|
||||
|
||||
const (
|
||||
printerStateDone printerState = iota
|
||||
printerStateRunning
|
||||
printerStatePaused
|
||||
)
|
||||
|
||||
type Printer struct {
|
||||
out console.File
|
||||
mode progressui.DisplayMode
|
||||
opt *printerOpts
|
||||
|
||||
status chan *client.SolveStatus
|
||||
interrupt chan interruptRequest
|
||||
state printerState
|
||||
|
||||
ready chan struct{}
|
||||
done chan struct{}
|
||||
paused chan struct{}
|
||||
closeOnce sync.Once
|
||||
|
||||
err error
|
||||
|
@ -39,8 +51,8 @@ type Printer struct {
|
|||
func (p *Printer) Wait() error {
|
||||
p.closeOnce.Do(func() {
|
||||
close(p.status)
|
||||
<-p.done
|
||||
})
|
||||
<-p.done
|
||||
return p.err
|
||||
}
|
||||
|
||||
|
@ -54,13 +66,23 @@ func (p *Printer) IsDone() bool {
|
|||
}
|
||||
|
||||
func (p *Printer) Pause() error {
|
||||
p.paused = make(chan struct{})
|
||||
return p.Wait()
|
||||
done := make(chan struct{})
|
||||
p.interrupt <- interruptRequest{
|
||||
desiredState: printerStatePaused,
|
||||
done: done,
|
||||
}
|
||||
|
||||
// Need to wait for a response to confirm we have control
|
||||
// of the console output.
|
||||
<-done
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Printer) Unpause() {
|
||||
close(p.paused)
|
||||
<-p.ready
|
||||
func (p *Printer) Resume() {
|
||||
p.interrupt <- interruptRequest{
|
||||
desiredState: printerStateRunning,
|
||||
}
|
||||
// Do not care about waiting for a response.
|
||||
}
|
||||
|
||||
func (p *Printer) Write(s *client.SolveStatus) {
|
||||
|
@ -115,42 +137,115 @@ func NewPrinter(ctx context.Context, out console.File, mode progressui.DisplayMo
|
|||
}
|
||||
|
||||
pw := &Printer{
|
||||
ready: make(chan struct{}),
|
||||
metrics: opt.mw,
|
||||
out: out,
|
||||
mode: mode,
|
||||
opt: opt,
|
||||
status: make(chan *client.SolveStatus),
|
||||
interrupt: make(chan interruptRequest),
|
||||
state: printerStateRunning,
|
||||
done: make(chan struct{}),
|
||||
metrics: opt.mw,
|
||||
}
|
||||
go pw.run(ctx, d)
|
||||
|
||||
return pw, nil
|
||||
}
|
||||
|
||||
func (p *Printer) run(ctx context.Context, d progressui.Display) {
|
||||
defer close(p.done)
|
||||
defer close(p.interrupt)
|
||||
|
||||
var ss []*client.SolveStatus
|
||||
for {
|
||||
switch p.state {
|
||||
case printerStatePaused:
|
||||
ss, p.err = p.bufferDisplay(ctx, ss)
|
||||
case printerStateRunning:
|
||||
p.warnings, ss, p.err = p.updateDisplay(ctx, d, ss)
|
||||
if p.opt.onclose != nil {
|
||||
p.opt.onclose()
|
||||
}
|
||||
}
|
||||
|
||||
if p.state == printerStateDone {
|
||||
break
|
||||
}
|
||||
d, _ = p.newDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Printer) newDisplay() (progressui.Display, error) {
|
||||
return progressui.NewDisplay(p.out, p.mode, p.opt.displayOpts...)
|
||||
}
|
||||
|
||||
func (p *Printer) updateDisplay(ctx context.Context, d progressui.Display, ss []*client.SolveStatus) ([]client.VertexWarning, []*client.SolveStatus, error) {
|
||||
p.logMu.Lock()
|
||||
p.logSourceMap = map[digest.Digest]any{}
|
||||
p.logMu.Unlock()
|
||||
|
||||
resumeLogs := logutil.Pause(logrus.StandardLogger())
|
||||
defer resumeLogs()
|
||||
|
||||
interruptCh := make(chan interruptRequest, 1)
|
||||
ingress := make(chan *client.SolveStatus)
|
||||
|
||||
go func() {
|
||||
defer close(ingress)
|
||||
defer close(interruptCh)
|
||||
|
||||
for _, s := range ss {
|
||||
ingress <- s
|
||||
}
|
||||
|
||||
for {
|
||||
pw.status = make(chan *client.SolveStatus)
|
||||
pw.done = make(chan struct{})
|
||||
pw.closeOnce = sync.Once{}
|
||||
|
||||
pw.logMu.Lock()
|
||||
pw.logSourceMap = map[digest.Digest]any{}
|
||||
pw.logMu.Unlock()
|
||||
|
||||
resumeLogs := logutil.Pause(logrus.StandardLogger())
|
||||
close(pw.ready)
|
||||
// not using shared context to not disrupt display but let is finish reporting errors
|
||||
pw.warnings, pw.err = d.UpdateFrom(ctx, pw.status)
|
||||
resumeLogs()
|
||||
close(pw.done)
|
||||
|
||||
if opt.onclose != nil {
|
||||
opt.onclose()
|
||||
select {
|
||||
case s, ok := <-p.status:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
ingress <- s
|
||||
case req := <-p.interrupt:
|
||||
interruptCh <- req
|
||||
return
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
if pw.paused == nil {
|
||||
break
|
||||
}
|
||||
|
||||
pw.ready = make(chan struct{})
|
||||
<-pw.paused
|
||||
pw.paused = nil
|
||||
|
||||
d, _ = progressui.NewDisplay(out, mode, opt.displayOpts...)
|
||||
}
|
||||
}()
|
||||
<-pw.ready
|
||||
return pw, nil
|
||||
|
||||
warnings, err := d.UpdateFrom(context.Background(), ingress)
|
||||
if err == nil {
|
||||
err = context.Cause(ctx)
|
||||
}
|
||||
|
||||
interrupt := <-interruptCh
|
||||
p.state = interrupt.desiredState
|
||||
interrupt.close()
|
||||
return warnings, nil, err
|
||||
}
|
||||
|
||||
// bufferDisplay will buffer display updates from the status channel into a
|
||||
// slice.
|
||||
//
|
||||
// This method returns if either status gets closed or if an interrupt is received.
|
||||
func (p *Printer) bufferDisplay(ctx context.Context, ss []*client.SolveStatus) ([]*client.SolveStatus, error) {
|
||||
for {
|
||||
select {
|
||||
case s, ok := <-p.status:
|
||||
if !ok {
|
||||
p.state = printerStateDone
|
||||
return ss, nil
|
||||
}
|
||||
ss = append(ss, s)
|
||||
case req := <-p.interrupt:
|
||||
p.state = req.desiredState
|
||||
req.close()
|
||||
return ss, nil
|
||||
case <-ctx.Done():
|
||||
p.state = printerStateDone
|
||||
return nil, context.Cause(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Printer) WriteBuildRef(target string, ref string) {
|
||||
|
@ -221,3 +316,14 @@ func dedupWarnings(inp []client.VertexWarning) []client.VertexWarning {
|
|||
}
|
||||
return res
|
||||
}
|
||||
|
||||
type interruptRequest struct {
|
||||
desiredState printerState
|
||||
done chan<- struct{}
|
||||
}
|
||||
|
||||
func (req *interruptRequest) close() {
|
||||
if req.done != nil {
|
||||
close(req.done)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,93 +0,0 @@
|
|||
# go-fuzz-headers
|
||||
This repository contains various helper functions for go fuzzing. It is mostly used in combination with [go-fuzz](https://github.com/dvyukov/go-fuzz), but compatibility with fuzzing in the standard library will also be supported. Any coverage guided fuzzing engine that provides an array or slice of bytes can be used with go-fuzz-headers.
|
||||
|
||||
|
||||
## Usage
|
||||
Using go-fuzz-headers is easy. First create a new consumer with the bytes provided by the fuzzing engine:
|
||||
|
||||
```go
|
||||
import (
|
||||
fuzz "github.com/AdaLogics/go-fuzz-headers"
|
||||
)
|
||||
data := []byte{'R', 'a', 'n', 'd', 'o', 'm'}
|
||||
f := fuzz.NewConsumer(data)
|
||||
|
||||
```
|
||||
|
||||
This creates a `Consumer` that consumes the bytes of the input as it uses them to fuzz different types.
|
||||
|
||||
After that, `f` can be used to easily create fuzzed instances of different types. Below are some examples:
|
||||
|
||||
### Structs
|
||||
One of the most useful features of go-fuzz-headers is its ability to fill structs with the data provided by the fuzzing engine. This is done with a single line:
|
||||
```go
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
p := Person{}
|
||||
// Fill p with values based on the data provided by the fuzzing engine:
|
||||
err := f.GenerateStruct(&p)
|
||||
```
|
||||
|
||||
This includes nested structs too. In this example, the fuzz Consumer will also insert values in `p.BestFriend`:
|
||||
```go
|
||||
type PersonI struct {
|
||||
Name string
|
||||
Age int
|
||||
BestFriend PersonII
|
||||
}
|
||||
type PersonII struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
p := PersonI{}
|
||||
err := f.GenerateStruct(&p)
|
||||
```
|
||||
|
||||
If the consumer should insert values for unexported fields as well as exported, this can be enabled with:
|
||||
|
||||
```go
|
||||
f.AllowUnexportedFields()
|
||||
```
|
||||
|
||||
...and disabled with:
|
||||
|
||||
```go
|
||||
f.DisallowUnexportedFields()
|
||||
```
|
||||
|
||||
### Other types:
|
||||
|
||||
Other useful APIs:
|
||||
|
||||
```go
|
||||
createdString, err := f.GetString() // Gets a string
|
||||
createdInt, err := f.GetInt() // Gets an integer
|
||||
createdByte, err := f.GetByte() // Gets a byte
|
||||
createdBytes, err := f.GetBytes() // Gets a byte slice
|
||||
createdBool, err := f.GetBool() // Gets a boolean
|
||||
err := f.FuzzMap(target_map) // Fills a map
|
||||
createdTarBytes, err := f.TarBytes() // Gets bytes of a valid tar archive
|
||||
err := f.CreateFiles(inThisDir) // Fills inThisDir with files
|
||||
createdString, err := f.GetStringFrom("anyCharInThisString", ofThisLength) // Gets a string that consists of chars from "anyCharInThisString" and has the exact length "ofThisLength"
|
||||
```
|
||||
|
||||
Most APIs are added as they are needed.
|
||||
|
||||
## Projects that use go-fuzz-headers
|
||||
- [runC](https://github.com/opencontainers/runc)
|
||||
- [Istio](https://github.com/istio/istio)
|
||||
- [Vitess](https://github.com/vitessio/vitess)
|
||||
- [Containerd](https://github.com/containerd/containerd)
|
||||
|
||||
Feel free to add your own project to the list, if you use go-fuzz-headers to fuzz it.
|
||||
|
||||
|
||||
|
||||
|
||||
## Status
|
||||
The project is under development and will be updated regularly.
|
||||
|
||||
## References
|
||||
go-fuzz-headers' approach to fuzzing structs is strongly inspired by [gofuzz](https://github.com/google/gofuzz).
|
|
@ -1,960 +0,0 @@
|
|||
// Copyright 2023 The go-fuzz-headers Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gofuzzheaders
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
MaxTotalLen uint32 = 2000000
|
||||
maxDepth = 100
|
||||
)
|
||||
|
||||
func SetMaxTotalLen(newLen uint32) {
|
||||
MaxTotalLen = newLen
|
||||
}
|
||||
|
||||
type ConsumeFuzzer struct {
|
||||
data []byte
|
||||
dataTotal uint32
|
||||
CommandPart []byte
|
||||
RestOfArray []byte
|
||||
NumberOfCalls int
|
||||
position uint32
|
||||
fuzzUnexportedFields bool
|
||||
forceUTF8Strings bool
|
||||
curDepth int
|
||||
Funcs map[reflect.Type]reflect.Value
|
||||
}
|
||||
|
||||
func IsDivisibleBy(n int, divisibleby int) bool {
|
||||
return (n % divisibleby) == 0
|
||||
}
|
||||
|
||||
func NewConsumer(fuzzData []byte) *ConsumeFuzzer {
|
||||
return &ConsumeFuzzer{
|
||||
data: fuzzData,
|
||||
dataTotal: uint32(len(fuzzData)),
|
||||
Funcs: make(map[reflect.Type]reflect.Value),
|
||||
curDepth: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) Split(minCalls, maxCalls int) error {
|
||||
if f.dataTotal == 0 {
|
||||
return errors.New("could not split")
|
||||
}
|
||||
numberOfCalls := int(f.data[0])
|
||||
if numberOfCalls < minCalls || numberOfCalls > maxCalls {
|
||||
return errors.New("bad number of calls")
|
||||
}
|
||||
if int(f.dataTotal) < numberOfCalls+numberOfCalls+1 {
|
||||
return errors.New("length of data does not match required parameters")
|
||||
}
|
||||
|
||||
// Define part 2 and 3 of the data array
|
||||
commandPart := f.data[1 : numberOfCalls+1]
|
||||
restOfArray := f.data[numberOfCalls+1:]
|
||||
|
||||
// Just a small check. It is necessary
|
||||
if len(commandPart) != numberOfCalls {
|
||||
return errors.New("length of commandPart does not match number of calls")
|
||||
}
|
||||
|
||||
// Check if restOfArray is divisible by numberOfCalls
|
||||
if !IsDivisibleBy(len(restOfArray), numberOfCalls) {
|
||||
return errors.New("length of commandPart does not match number of calls")
|
||||
}
|
||||
f.CommandPart = commandPart
|
||||
f.RestOfArray = restOfArray
|
||||
f.NumberOfCalls = numberOfCalls
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) AllowUnexportedFields() {
|
||||
f.fuzzUnexportedFields = true
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) DisallowUnexportedFields() {
|
||||
f.fuzzUnexportedFields = false
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) AllowNonUTF8Strings() {
|
||||
f.forceUTF8Strings = false
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) DisallowNonUTF8Strings() {
|
||||
f.forceUTF8Strings = true
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) GenerateStruct(targetStruct interface{}) error {
|
||||
e := reflect.ValueOf(targetStruct).Elem()
|
||||
return f.fuzzStruct(e, false)
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) setCustom(v reflect.Value) error {
|
||||
// First: see if we have a fuzz function for it.
|
||||
doCustom, ok := f.Funcs[v.Type()]
|
||||
if !ok {
|
||||
return fmt.Errorf("could not find a custom function")
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Ptr:
|
||||
if v.IsNil() {
|
||||
if !v.CanSet() {
|
||||
return fmt.Errorf("could not use a custom function")
|
||||
}
|
||||
v.Set(reflect.New(v.Type().Elem()))
|
||||
}
|
||||
case reflect.Map:
|
||||
if v.IsNil() {
|
||||
if !v.CanSet() {
|
||||
return fmt.Errorf("could not use a custom function")
|
||||
}
|
||||
v.Set(reflect.MakeMap(v.Type()))
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("could not use a custom function")
|
||||
}
|
||||
|
||||
verr := doCustom.Call([]reflect.Value{v, reflect.ValueOf(Continue{
|
||||
F: f,
|
||||
})})
|
||||
|
||||
// check if we return an error
|
||||
if verr[0].IsNil() {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("could not use a custom function")
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) fuzzStruct(e reflect.Value, customFunctions bool) error {
|
||||
if f.curDepth >= maxDepth {
|
||||
// return err or nil here?
|
||||
return nil
|
||||
}
|
||||
f.curDepth++
|
||||
defer func() { f.curDepth-- }()
|
||||
|
||||
// We check if we should check for custom functions
|
||||
if customFunctions && e.IsValid() && e.CanAddr() {
|
||||
err := f.setCustom(e.Addr())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
switch e.Kind() {
|
||||
case reflect.Struct:
|
||||
for i := 0; i < e.NumField(); i++ {
|
||||
var v reflect.Value
|
||||
if !e.Field(i).CanSet() {
|
||||
if f.fuzzUnexportedFields {
|
||||
v = reflect.NewAt(e.Field(i).Type(), unsafe.Pointer(e.Field(i).UnsafeAddr())).Elem()
|
||||
}
|
||||
if err := f.fuzzStruct(v, customFunctions); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
v = e.Field(i)
|
||||
if err := f.fuzzStruct(v, customFunctions); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
case reflect.String:
|
||||
str, err := f.GetString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e.CanSet() {
|
||||
e.SetString(str)
|
||||
}
|
||||
case reflect.Slice:
|
||||
var maxElements uint32
|
||||
// Byte slices should not be restricted
|
||||
if e.Type().String() == "[]uint8" {
|
||||
maxElements = 10000000
|
||||
} else {
|
||||
maxElements = 50
|
||||
}
|
||||
|
||||
randQty, err := f.GetUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
numOfElements := randQty % maxElements
|
||||
if (f.dataTotal - f.position) < numOfElements {
|
||||
numOfElements = f.dataTotal - f.position
|
||||
}
|
||||
|
||||
uu := reflect.MakeSlice(e.Type(), int(numOfElements), int(numOfElements))
|
||||
|
||||
for i := 0; i < int(numOfElements); i++ {
|
||||
// If we have more than 10, then we can proceed with that.
|
||||
if err := f.fuzzStruct(uu.Index(i), customFunctions); err != nil {
|
||||
if i >= 10 {
|
||||
if e.CanSet() {
|
||||
e.Set(uu)
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if e.CanSet() {
|
||||
e.Set(uu)
|
||||
}
|
||||
case reflect.Uint:
|
||||
newInt, err := f.GetUint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e.CanSet() {
|
||||
e.SetUint(uint64(newInt))
|
||||
}
|
||||
case reflect.Uint16:
|
||||
newInt, err := f.GetUint16()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e.CanSet() {
|
||||
e.SetUint(uint64(newInt))
|
||||
}
|
||||
case reflect.Uint32:
|
||||
newInt, err := f.GetUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e.CanSet() {
|
||||
e.SetUint(uint64(newInt))
|
||||
}
|
||||
case reflect.Uint64:
|
||||
newInt, err := f.GetInt()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e.CanSet() {
|
||||
e.SetUint(uint64(newInt))
|
||||
}
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
newInt, err := f.GetInt()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e.CanSet() {
|
||||
e.SetInt(int64(newInt))
|
||||
}
|
||||
case reflect.Float32:
|
||||
newFloat, err := f.GetFloat32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e.CanSet() {
|
||||
e.SetFloat(float64(newFloat))
|
||||
}
|
||||
case reflect.Float64:
|
||||
newFloat, err := f.GetFloat64()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e.CanSet() {
|
||||
e.SetFloat(float64(newFloat))
|
||||
}
|
||||
case reflect.Map:
|
||||
if e.CanSet() {
|
||||
e.Set(reflect.MakeMap(e.Type()))
|
||||
const maxElements = 50
|
||||
randQty, err := f.GetInt()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
numOfElements := randQty % maxElements
|
||||
for i := 0; i < numOfElements; i++ {
|
||||
key := reflect.New(e.Type().Key()).Elem()
|
||||
if err := f.fuzzStruct(key, customFunctions); err != nil {
|
||||
return err
|
||||
}
|
||||
val := reflect.New(e.Type().Elem()).Elem()
|
||||
if err = f.fuzzStruct(val, customFunctions); err != nil {
|
||||
return err
|
||||
}
|
||||
e.SetMapIndex(key, val)
|
||||
}
|
||||
}
|
||||
case reflect.Ptr:
|
||||
if e.CanSet() {
|
||||
e.Set(reflect.New(e.Type().Elem()))
|
||||
if err := f.fuzzStruct(e.Elem(), customFunctions); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case reflect.Uint8:
|
||||
b, err := f.GetByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e.CanSet() {
|
||||
e.SetUint(uint64(b))
|
||||
}
|
||||
case reflect.Bool:
|
||||
b, err := f.GetBool()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e.CanSet() {
|
||||
e.SetBool(b)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) GetStringArray() (reflect.Value, error) {
|
||||
// The max size of the array:
|
||||
const max uint32 = 20
|
||||
|
||||
arraySize := f.position
|
||||
if arraySize > max {
|
||||
arraySize = max
|
||||
}
|
||||
stringArray := reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf("string")), int(arraySize), int(arraySize))
|
||||
if f.position+arraySize >= f.dataTotal {
|
||||
return stringArray, errors.New("could not make string array")
|
||||
}
|
||||
|
||||
for i := 0; i < int(arraySize); i++ {
|
||||
stringSize := uint32(f.data[f.position])
|
||||
if f.position+stringSize >= f.dataTotal {
|
||||
return stringArray, nil
|
||||
}
|
||||
stringToAppend := string(f.data[f.position : f.position+stringSize])
|
||||
strVal := reflect.ValueOf(stringToAppend)
|
||||
stringArray = reflect.Append(stringArray, strVal)
|
||||
f.position += stringSize
|
||||
}
|
||||
return stringArray, nil
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) GetInt() (int, error) {
|
||||
if f.position >= f.dataTotal {
|
||||
return 0, errors.New("not enough bytes to create int")
|
||||
}
|
||||
returnInt := int(f.data[f.position])
|
||||
f.position++
|
||||
return returnInt, nil
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) GetByte() (byte, error) {
|
||||
if f.position >= f.dataTotal {
|
||||
return 0x00, errors.New("not enough bytes to get byte")
|
||||
}
|
||||
returnByte := f.data[f.position]
|
||||
f.position++
|
||||
return returnByte, nil
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) GetNBytes(numberOfBytes int) ([]byte, error) {
|
||||
if f.position >= f.dataTotal {
|
||||
return nil, errors.New("not enough bytes to get byte")
|
||||
}
|
||||
returnBytes := make([]byte, 0, numberOfBytes)
|
||||
for i := 0; i < numberOfBytes; i++ {
|
||||
newByte, err := f.GetByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
returnBytes = append(returnBytes, newByte)
|
||||
}
|
||||
return returnBytes, nil
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) GetUint16() (uint16, error) {
|
||||
u16, err := f.GetNBytes(2)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
littleEndian, err := f.GetBool()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if littleEndian {
|
||||
return binary.LittleEndian.Uint16(u16), nil
|
||||
}
|
||||
return binary.BigEndian.Uint16(u16), nil
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) GetUint32() (uint32, error) {
|
||||
u32, err := f.GetNBytes(4)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return binary.BigEndian.Uint32(u32), nil
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) GetUint64() (uint64, error) {
|
||||
u64, err := f.GetNBytes(8)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
littleEndian, err := f.GetBool()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if littleEndian {
|
||||
return binary.LittleEndian.Uint64(u64), nil
|
||||
}
|
||||
return binary.BigEndian.Uint64(u64), nil
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) GetUint() (uint, error) {
|
||||
var zero uint
|
||||
size := int(unsafe.Sizeof(zero))
|
||||
if size == 8 {
|
||||
u64, err := f.GetUint64()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint(u64), nil
|
||||
}
|
||||
u32, err := f.GetUint32()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint(u32), nil
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) GetBytes() ([]byte, error) {
|
||||
var length uint32
|
||||
var err error
|
||||
length, err = f.GetUint32()
|
||||
if err != nil {
|
||||
return nil, errors.New("not enough bytes to create byte array")
|
||||
}
|
||||
|
||||
if length == 0 {
|
||||
length = 30
|
||||
}
|
||||
bytesLeft := f.dataTotal - f.position
|
||||
if bytesLeft <= 0 {
|
||||
return nil, errors.New("not enough bytes to create byte array")
|
||||
}
|
||||
|
||||
// If the length is the same as bytes left, we will not overflow
|
||||
// the remaining bytes.
|
||||
if length != bytesLeft {
|
||||
length = length % bytesLeft
|
||||
}
|
||||
byteBegin := f.position
|
||||
if byteBegin+length < byteBegin {
|
||||
return nil, errors.New("numbers overflow")
|
||||
}
|
||||
f.position = byteBegin + length
|
||||
return f.data[byteBegin:f.position], nil
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) GetString() (string, error) {
|
||||
if f.position >= f.dataTotal {
|
||||
return "nil", errors.New("not enough bytes to create string")
|
||||
}
|
||||
length, err := f.GetUint32()
|
||||
if err != nil {
|
||||
return "nil", errors.New("not enough bytes to create string")
|
||||
}
|
||||
if f.position > MaxTotalLen {
|
||||
return "nil", errors.New("created too large a string")
|
||||
}
|
||||
byteBegin := f.position
|
||||
if byteBegin >= f.dataTotal {
|
||||
return "nil", errors.New("not enough bytes to create string")
|
||||
}
|
||||
if byteBegin+length > f.dataTotal {
|
||||
return "nil", errors.New("not enough bytes to create string")
|
||||
}
|
||||
if byteBegin > byteBegin+length {
|
||||
return "nil", errors.New("numbers overflow")
|
||||
}
|
||||
f.position = byteBegin + length
|
||||
s := string(f.data[byteBegin:f.position])
|
||||
if f.forceUTF8Strings {
|
||||
s = strings.ToValidUTF8(s, "")
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) GetBool() (bool, error) {
|
||||
if f.position >= f.dataTotal {
|
||||
return false, errors.New("not enough bytes to create bool")
|
||||
}
|
||||
if IsDivisibleBy(int(f.data[f.position]), 2) {
|
||||
f.position++
|
||||
return true, nil
|
||||
} else {
|
||||
f.position++
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) FuzzMap(m interface{}) error {
|
||||
return f.GenerateStruct(m)
|
||||
}
|
||||
|
||||
func returnTarBytes(buf []byte) ([]byte, error) {
|
||||
return buf, nil
|
||||
// Count files
|
||||
var fileCounter int
|
||||
tr := tar.NewReader(bytes.NewReader(buf))
|
||||
for {
|
||||
_, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fileCounter++
|
||||
}
|
||||
if fileCounter >= 1 {
|
||||
return buf, nil
|
||||
}
|
||||
return nil, fmt.Errorf("not enough files were created\n")
|
||||
}
|
||||
|
||||
func setTarHeaderFormat(hdr *tar.Header, f *ConsumeFuzzer) error {
|
||||
ind, err := f.GetInt()
|
||||
if err != nil {
|
||||
hdr.Format = tar.FormatGNU
|
||||
//return nil
|
||||
}
|
||||
switch ind % 4 {
|
||||
case 0:
|
||||
hdr.Format = tar.FormatUnknown
|
||||
case 1:
|
||||
hdr.Format = tar.FormatUSTAR
|
||||
case 2:
|
||||
hdr.Format = tar.FormatPAX
|
||||
case 3:
|
||||
hdr.Format = tar.FormatGNU
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setTarHeaderTypeflag(hdr *tar.Header, f *ConsumeFuzzer) error {
|
||||
ind, err := f.GetInt()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch ind % 13 {
|
||||
case 0:
|
||||
hdr.Typeflag = tar.TypeReg
|
||||
case 1:
|
||||
hdr.Typeflag = tar.TypeLink
|
||||
linkname, err := f.GetString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hdr.Linkname = linkname
|
||||
case 2:
|
||||
hdr.Typeflag = tar.TypeSymlink
|
||||
linkname, err := f.GetString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hdr.Linkname = linkname
|
||||
case 3:
|
||||
hdr.Typeflag = tar.TypeChar
|
||||
case 4:
|
||||
hdr.Typeflag = tar.TypeBlock
|
||||
case 5:
|
||||
hdr.Typeflag = tar.TypeDir
|
||||
case 6:
|
||||
hdr.Typeflag = tar.TypeFifo
|
||||
case 7:
|
||||
hdr.Typeflag = tar.TypeCont
|
||||
case 8:
|
||||
hdr.Typeflag = tar.TypeXHeader
|
||||
case 9:
|
||||
hdr.Typeflag = tar.TypeXGlobalHeader
|
||||
case 10:
|
||||
hdr.Typeflag = tar.TypeGNUSparse
|
||||
case 11:
|
||||
hdr.Typeflag = tar.TypeGNULongName
|
||||
case 12:
|
||||
hdr.Typeflag = tar.TypeGNULongLink
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) createTarFileBody() ([]byte, error) {
|
||||
return f.GetBytes()
|
||||
/*length, err := f.GetUint32()
|
||||
if err != nil {
|
||||
return nil, errors.New("not enough bytes to create byte array")
|
||||
}
|
||||
|
||||
// A bit of optimization to attempt to create a file body
|
||||
// when we don't have as many bytes left as "length"
|
||||
remainingBytes := f.dataTotal - f.position
|
||||
if remainingBytes <= 0 {
|
||||
return nil, errors.New("created too large a string")
|
||||
}
|
||||
if f.position+length > MaxTotalLen {
|
||||
return nil, errors.New("created too large a string")
|
||||
}
|
||||
byteBegin := f.position
|
||||
if byteBegin >= f.dataTotal {
|
||||
return nil, errors.New("not enough bytes to create byte array")
|
||||
}
|
||||
if length == 0 {
|
||||
return nil, errors.New("zero-length is not supported")
|
||||
}
|
||||
if byteBegin+length >= f.dataTotal {
|
||||
return nil, errors.New("not enough bytes to create byte array")
|
||||
}
|
||||
if byteBegin+length < byteBegin {
|
||||
return nil, errors.New("numbers overflow")
|
||||
}
|
||||
f.position = byteBegin + length
|
||||
return f.data[byteBegin:f.position], nil*/
|
||||
}
|
||||
|
||||
// getTarFileName is similar to GetString(), but creates string based
|
||||
// on the length of f.data to reduce the likelihood of overflowing
|
||||
// f.data.
|
||||
func (f *ConsumeFuzzer) getTarFilename() (string, error) {
|
||||
return f.GetString()
|
||||
/*length, err := f.GetUint32()
|
||||
if err != nil {
|
||||
return "nil", errors.New("not enough bytes to create string")
|
||||
}
|
||||
|
||||
// A bit of optimization to attempt to create a file name
|
||||
// when we don't have as many bytes left as "length"
|
||||
remainingBytes := f.dataTotal - f.position
|
||||
if remainingBytes <= 0 {
|
||||
return "nil", errors.New("created too large a string")
|
||||
}
|
||||
if f.position > MaxTotalLen {
|
||||
return "nil", errors.New("created too large a string")
|
||||
}
|
||||
byteBegin := f.position
|
||||
if byteBegin >= f.dataTotal {
|
||||
return "nil", errors.New("not enough bytes to create string")
|
||||
}
|
||||
if byteBegin+length > f.dataTotal {
|
||||
return "nil", errors.New("not enough bytes to create string")
|
||||
}
|
||||
if byteBegin > byteBegin+length {
|
||||
return "nil", errors.New("numbers overflow")
|
||||
}
|
||||
f.position = byteBegin + length
|
||||
return string(f.data[byteBegin:f.position]), nil*/
|
||||
}
|
||||
|
||||
type TarFile struct {
|
||||
Hdr *tar.Header
|
||||
Body []byte
|
||||
}
|
||||
|
||||
// TarBytes returns valid bytes for a tar archive
|
||||
func (f *ConsumeFuzzer) TarBytes() ([]byte, error) {
|
||||
numberOfFiles, err := f.GetInt()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var tarFiles []*TarFile
|
||||
tarFiles = make([]*TarFile, 0)
|
||||
|
||||
const maxNoOfFiles = 100
|
||||
for i := 0; i < numberOfFiles%maxNoOfFiles; i++ {
|
||||
var filename string
|
||||
var filebody []byte
|
||||
var sec, nsec int
|
||||
var err error
|
||||
|
||||
filename, err = f.getTarFilename()
|
||||
if err != nil {
|
||||
var sb strings.Builder
|
||||
sb.WriteString("file-")
|
||||
sb.WriteString(strconv.Itoa(i))
|
||||
filename = sb.String()
|
||||
}
|
||||
filebody, err = f.createTarFileBody()
|
||||
if err != nil {
|
||||
var sb strings.Builder
|
||||
sb.WriteString("filebody-")
|
||||
sb.WriteString(strconv.Itoa(i))
|
||||
filebody = []byte(sb.String())
|
||||
}
|
||||
|
||||
sec, err = f.GetInt()
|
||||
if err != nil {
|
||||
sec = 1672531200 // beginning of 2023
|
||||
}
|
||||
nsec, err = f.GetInt()
|
||||
if err != nil {
|
||||
nsec = 1703980800 // end of 2023
|
||||
}
|
||||
|
||||
hdr := &tar.Header{
|
||||
Name: filename,
|
||||
Size: int64(len(filebody)),
|
||||
Mode: 0o600,
|
||||
ModTime: time.Unix(int64(sec), int64(nsec)),
|
||||
}
|
||||
if err := setTarHeaderTypeflag(hdr, f); err != nil {
|
||||
return []byte(""), err
|
||||
}
|
||||
if err := setTarHeaderFormat(hdr, f); err != nil {
|
||||
return []byte(""), err
|
||||
}
|
||||
tf := &TarFile{
|
||||
Hdr: hdr,
|
||||
Body: filebody,
|
||||
}
|
||||
tarFiles = append(tarFiles, tf)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
tw := tar.NewWriter(&buf)
|
||||
defer tw.Close()
|
||||
|
||||
for _, tf := range tarFiles {
|
||||
tw.WriteHeader(tf.Hdr)
|
||||
tw.Write(tf.Body)
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// This is similar to TarBytes, but it returns a series of
|
||||
// files instead of raw tar bytes. The advantage of this
|
||||
// api is that it is cheaper in terms of cpu power to
|
||||
// modify or check the files in the fuzzer with TarFiles()
|
||||
// because it avoids creating a tar reader.
|
||||
func (f *ConsumeFuzzer) TarFiles() ([]*TarFile, error) {
|
||||
numberOfFiles, err := f.GetInt()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var tarFiles []*TarFile
|
||||
tarFiles = make([]*TarFile, 0)
|
||||
|
||||
const maxNoOfFiles = 100
|
||||
for i := 0; i < numberOfFiles%maxNoOfFiles; i++ {
|
||||
filename, err := f.getTarFilename()
|
||||
if err != nil {
|
||||
return tarFiles, err
|
||||
}
|
||||
filebody, err := f.createTarFileBody()
|
||||
if err != nil {
|
||||
return tarFiles, err
|
||||
}
|
||||
|
||||
sec, err := f.GetInt()
|
||||
if err != nil {
|
||||
return tarFiles, err
|
||||
}
|
||||
nsec, err := f.GetInt()
|
||||
if err != nil {
|
||||
return tarFiles, err
|
||||
}
|
||||
|
||||
hdr := &tar.Header{
|
||||
Name: filename,
|
||||
Size: int64(len(filebody)),
|
||||
Mode: 0o600,
|
||||
ModTime: time.Unix(int64(sec), int64(nsec)),
|
||||
}
|
||||
if err := setTarHeaderTypeflag(hdr, f); err != nil {
|
||||
hdr.Typeflag = tar.TypeReg
|
||||
}
|
||||
if err := setTarHeaderFormat(hdr, f); err != nil {
|
||||
return tarFiles, err // should not happend
|
||||
}
|
||||
tf := &TarFile{
|
||||
Hdr: hdr,
|
||||
Body: filebody,
|
||||
}
|
||||
tarFiles = append(tarFiles, tf)
|
||||
}
|
||||
return tarFiles, nil
|
||||
}
|
||||
|
||||
// CreateFiles creates pseudo-random files in rootDir.
|
||||
// It creates subdirs and places the files there.
|
||||
// It is the callers responsibility to ensure that
|
||||
// rootDir exists.
|
||||
func (f *ConsumeFuzzer) CreateFiles(rootDir string) error {
|
||||
numberOfFiles, err := f.GetInt()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
maxNumberOfFiles := numberOfFiles % 4000 // This is completely arbitrary
|
||||
if maxNumberOfFiles == 0 {
|
||||
return errors.New("maxNumberOfFiles is nil")
|
||||
}
|
||||
|
||||
var noOfCreatedFiles int
|
||||
for i := 0; i < maxNumberOfFiles; i++ {
|
||||
// The file to create:
|
||||
fileName, err := f.GetString()
|
||||
if err != nil {
|
||||
if noOfCreatedFiles > 0 {
|
||||
// If files have been created, we don't return an error.
|
||||
break
|
||||
} else {
|
||||
return errors.New("could not get fileName")
|
||||
}
|
||||
}
|
||||
if strings.Contains(fileName, "..") || (len(fileName) > 0 && fileName[0] == 47) || strings.Contains(fileName, "\\") {
|
||||
continue
|
||||
}
|
||||
fullFilePath := filepath.Join(rootDir, fileName)
|
||||
|
||||
// Find the subdirectory of the file
|
||||
if subDir := filepath.Dir(fileName); subDir != "" && subDir != "." {
|
||||
// create the dir first; avoid going outside the root dir
|
||||
if strings.Contains(subDir, "../") || (len(subDir) > 0 && subDir[0] == 47) || strings.Contains(subDir, "\\") {
|
||||
continue
|
||||
}
|
||||
dirPath := filepath.Join(rootDir, subDir)
|
||||
if _, err := os.Stat(dirPath); os.IsNotExist(err) {
|
||||
err2 := os.MkdirAll(dirPath, 0o777)
|
||||
if err2 != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
fullFilePath = filepath.Join(dirPath, fileName)
|
||||
} else {
|
||||
// Create symlink
|
||||
createSymlink, err := f.GetBool()
|
||||
if err != nil {
|
||||
if noOfCreatedFiles > 0 {
|
||||
break
|
||||
} else {
|
||||
return errors.New("could not create the symlink")
|
||||
}
|
||||
}
|
||||
if createSymlink {
|
||||
symlinkTarget, err := f.GetString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.Symlink(symlinkTarget, fullFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// stop loop here, since a symlink needs no further action
|
||||
noOfCreatedFiles++
|
||||
continue
|
||||
}
|
||||
// We create a normal file
|
||||
fileContents, err := f.GetBytes()
|
||||
if err != nil {
|
||||
if noOfCreatedFiles > 0 {
|
||||
break
|
||||
} else {
|
||||
return errors.New("could not create the file")
|
||||
}
|
||||
}
|
||||
err = os.WriteFile(fullFilePath, fileContents, 0o666)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
noOfCreatedFiles++
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetStringFrom returns a string that can only consist of characters
|
||||
// included in possibleChars. It returns an error if the created string
|
||||
// does not have the specified length.
|
||||
func (f *ConsumeFuzzer) GetStringFrom(possibleChars string, length int) (string, error) {
|
||||
if (f.dataTotal - f.position) < uint32(length) {
|
||||
return "", errors.New("not enough bytes to create a string")
|
||||
}
|
||||
output := make([]byte, 0, length)
|
||||
for i := 0; i < length; i++ {
|
||||
charIndex, err := f.GetInt()
|
||||
if err != nil {
|
||||
return string(output), err
|
||||
}
|
||||
output = append(output, possibleChars[charIndex%len(possibleChars)])
|
||||
}
|
||||
return string(output), nil
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) GetRune() ([]rune, error) {
|
||||
stringToConvert, err := f.GetString()
|
||||
if err != nil {
|
||||
return []rune("nil"), err
|
||||
}
|
||||
return []rune(stringToConvert), nil
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) GetFloat32() (float32, error) {
|
||||
u32, err := f.GetNBytes(4)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
littleEndian, err := f.GetBool()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if littleEndian {
|
||||
u32LE := binary.LittleEndian.Uint32(u32)
|
||||
return math.Float32frombits(u32LE), nil
|
||||
}
|
||||
u32BE := binary.BigEndian.Uint32(u32)
|
||||
return math.Float32frombits(u32BE), nil
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) GetFloat64() (float64, error) {
|
||||
u64, err := f.GetNBytes(8)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
littleEndian, err := f.GetBool()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if littleEndian {
|
||||
u64LE := binary.LittleEndian.Uint64(u64)
|
||||
return math.Float64frombits(u64LE), nil
|
||||
}
|
||||
u64BE := binary.BigEndian.Uint64(u64)
|
||||
return math.Float64frombits(u64BE), nil
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) CreateSlice(targetSlice interface{}) error {
|
||||
return f.GenerateStruct(targetSlice)
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
// Copyright 2023 The go-fuzz-headers Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gofuzzheaders
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type Continue struct {
|
||||
F *ConsumeFuzzer
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) AddFuncs(fuzzFuncs []interface{}) {
|
||||
for i := range fuzzFuncs {
|
||||
v := reflect.ValueOf(fuzzFuncs[i])
|
||||
if v.Kind() != reflect.Func {
|
||||
panic("Need only funcs!")
|
||||
}
|
||||
t := v.Type()
|
||||
if t.NumIn() != 2 || t.NumOut() != 1 {
|
||||
fmt.Println(t.NumIn(), t.NumOut())
|
||||
|
||||
panic("Need 2 in and 1 out params. In must be the type. Out must be an error")
|
||||
}
|
||||
argT := t.In(0)
|
||||
switch argT.Kind() {
|
||||
case reflect.Ptr, reflect.Map:
|
||||
default:
|
||||
panic("fuzzFunc must take pointer or map type")
|
||||
}
|
||||
if t.In(1) != reflect.TypeOf(Continue{}) {
|
||||
panic("fuzzFunc's second parameter must be type Continue")
|
||||
}
|
||||
f.Funcs[argT] = v
|
||||
}
|
||||
}
|
||||
|
||||
func (f *ConsumeFuzzer) GenerateWithCustom(targetStruct interface{}) error {
|
||||
e := reflect.ValueOf(targetStruct).Elem()
|
||||
return f.fuzzStruct(e, true)
|
||||
}
|
||||
|
||||
func (c Continue) GenerateStruct(targetStruct interface{}) error {
|
||||
return c.F.GenerateStruct(targetStruct)
|
||||
}
|
||||
|
||||
func (c Continue) GenerateStructWithCustom(targetStruct interface{}) error {
|
||||
return c.F.GenerateWithCustom(targetStruct)
|
||||
}
|
|
@ -1,556 +0,0 @@
|
|||
// Copyright 2023 The go-fuzz-headers Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gofuzzheaders
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// returns a keyword by index
|
||||
func getKeyword(f *ConsumeFuzzer) (string, error) {
|
||||
index, err := f.GetInt()
|
||||
if err != nil {
|
||||
return keywords[0], err
|
||||
}
|
||||
for i, k := range keywords {
|
||||
if i == index {
|
||||
return k, nil
|
||||
}
|
||||
}
|
||||
return keywords[0], fmt.Errorf("could not get a kw")
|
||||
}
|
||||
|
||||
// Simple utility function to check if a string
|
||||
// slice contains a string.
|
||||
func containsString(s []string, e string) bool {
|
||||
for _, a := range s {
|
||||
if a == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// These keywords are used specifically for fuzzing Vitess
|
||||
var keywords = []string{
|
||||
"accessible", "action", "add", "after", "against", "algorithm",
|
||||
"all", "alter", "always", "analyze", "and", "as", "asc", "asensitive",
|
||||
"auto_increment", "avg_row_length", "before", "begin", "between",
|
||||
"bigint", "binary", "_binary", "_utf8mb4", "_utf8", "_latin1", "bit",
|
||||
"blob", "bool", "boolean", "both", "by", "call", "cancel", "cascade",
|
||||
"cascaded", "case", "cast", "channel", "change", "char", "character",
|
||||
"charset", "check", "checksum", "coalesce", "code", "collate", "collation",
|
||||
"column", "columns", "comment", "committed", "commit", "compact", "complete",
|
||||
"compressed", "compression", "condition", "connection", "constraint", "continue",
|
||||
"convert", "copy", "cume_dist", "substr", "substring", "create", "cross",
|
||||
"csv", "current_date", "current_time", "current_timestamp", "current_user",
|
||||
"cursor", "data", "database", "databases", "day", "day_hour", "day_microsecond",
|
||||
"day_minute", "day_second", "date", "datetime", "dec", "decimal", "declare",
|
||||
"default", "definer", "delay_key_write", "delayed", "delete", "dense_rank",
|
||||
"desc", "describe", "deterministic", "directory", "disable", "discard",
|
||||
"disk", "distinct", "distinctrow", "div", "double", "do", "drop", "dumpfile",
|
||||
"duplicate", "dynamic", "each", "else", "elseif", "empty", "enable",
|
||||
"enclosed", "encryption", "end", "enforced", "engine", "engines", "enum",
|
||||
"error", "escape", "escaped", "event", "exchange", "exclusive", "exists",
|
||||
"exit", "explain", "expansion", "export", "extended", "extract", "false",
|
||||
"fetch", "fields", "first", "first_value", "fixed", "float", "float4",
|
||||
"float8", "flush", "for", "force", "foreign", "format", "from", "full",
|
||||
"fulltext", "function", "general", "generated", "geometry", "geometrycollection",
|
||||
"get", "global", "gtid_executed", "grant", "group", "grouping", "groups",
|
||||
"group_concat", "having", "header", "high_priority", "hosts", "hour", "hour_microsecond",
|
||||
"hour_minute", "hour_second", "if", "ignore", "import", "in", "index", "indexes",
|
||||
"infile", "inout", "inner", "inplace", "insensitive", "insert", "insert_method",
|
||||
"int", "int1", "int2", "int3", "int4", "int8", "integer", "interval",
|
||||
"into", "io_after_gtids", "is", "isolation", "iterate", "invoker", "join",
|
||||
"json", "json_table", "key", "keys", "keyspaces", "key_block_size", "kill", "lag",
|
||||
"language", "last", "last_value", "last_insert_id", "lateral", "lead", "leading",
|
||||
"leave", "left", "less", "level", "like", "limit", "linear", "lines",
|
||||
"linestring", "load", "local", "localtime", "localtimestamp", "lock", "logs",
|
||||
"long", "longblob", "longtext", "loop", "low_priority", "manifest",
|
||||
"master_bind", "match", "max_rows", "maxvalue", "mediumblob", "mediumint",
|
||||
"mediumtext", "memory", "merge", "microsecond", "middleint", "min_rows", "minute",
|
||||
"minute_microsecond", "minute_second", "mod", "mode", "modify", "modifies",
|
||||
"multilinestring", "multipoint", "multipolygon", "month", "name",
|
||||
"names", "natural", "nchar", "next", "no", "none", "not", "no_write_to_binlog",
|
||||
"nth_value", "ntile", "null", "numeric", "of", "off", "offset", "on",
|
||||
"only", "open", "optimize", "optimizer_costs", "option", "optionally",
|
||||
"or", "order", "out", "outer", "outfile", "over", "overwrite", "pack_keys",
|
||||
"parser", "partition", "partitioning", "password", "percent_rank", "plugins",
|
||||
"point", "polygon", "precision", "primary", "privileges", "processlist",
|
||||
"procedure", "query", "quarter", "range", "rank", "read", "reads", "read_write",
|
||||
"real", "rebuild", "recursive", "redundant", "references", "regexp", "relay",
|
||||
"release", "remove", "rename", "reorganize", "repair", "repeat", "repeatable",
|
||||
"replace", "require", "resignal", "restrict", "return", "retry", "revert",
|
||||
"revoke", "right", "rlike", "rollback", "row", "row_format", "row_number",
|
||||
"rows", "s3", "savepoint", "schema", "schemas", "second", "second_microsecond",
|
||||
"security", "select", "sensitive", "separator", "sequence", "serializable",
|
||||
"session", "set", "share", "shared", "show", "signal", "signed", "slow",
|
||||
"smallint", "spatial", "specific", "sql", "sqlexception", "sqlstate",
|
||||
"sqlwarning", "sql_big_result", "sql_cache", "sql_calc_found_rows",
|
||||
"sql_no_cache", "sql_small_result", "ssl", "start", "starting",
|
||||
"stats_auto_recalc", "stats_persistent", "stats_sample_pages", "status",
|
||||
"storage", "stored", "straight_join", "stream", "system", "vstream",
|
||||
"table", "tables", "tablespace", "temporary", "temptable", "terminated",
|
||||
"text", "than", "then", "time", "timestamp", "timestampadd", "timestampdiff",
|
||||
"tinyblob", "tinyint", "tinytext", "to", "trailing", "transaction", "tree",
|
||||
"traditional", "trigger", "triggers", "true", "truncate", "uncommitted",
|
||||
"undefined", "undo", "union", "unique", "unlock", "unsigned", "update",
|
||||
"upgrade", "usage", "use", "user", "user_resources", "using", "utc_date",
|
||||
"utc_time", "utc_timestamp", "validation", "values", "variables", "varbinary",
|
||||
"varchar", "varcharacter", "varying", "vgtid_executed", "virtual", "vindex",
|
||||
"vindexes", "view", "vitess", "vitess_keyspaces", "vitess_metadata",
|
||||
"vitess_migration", "vitess_migrations", "vitess_replication_status",
|
||||
"vitess_shards", "vitess_tablets", "vschema", "warnings", "when",
|
||||
"where", "while", "window", "with", "without", "work", "write", "xor",
|
||||
"year", "year_month", "zerofill",
|
||||
}
|
||||
|
||||
// Keywords that could get an additional keyword
|
||||
var needCustomString = []string{
|
||||
"DISTINCTROW", "FROM", // Select keywords:
|
||||
"GROUP BY", "HAVING", "WINDOW",
|
||||
"FOR",
|
||||
"ORDER BY", "LIMIT",
|
||||
"INTO", "PARTITION", "AS", // Insert Keywords:
|
||||
"ON DUPLICATE KEY UPDATE",
|
||||
"WHERE", "LIMIT", // Delete keywords
|
||||
"INFILE", "INTO TABLE", "CHARACTER SET", // Load keywords
|
||||
"TERMINATED BY", "ENCLOSED BY",
|
||||
"ESCAPED BY", "STARTING BY",
|
||||
"TERMINATED BY", "STARTING BY",
|
||||
"IGNORE",
|
||||
"VALUE", "VALUES", // Replace tokens
|
||||
"SET", // Update tokens
|
||||
"ENGINE =", // Drop tokens
|
||||
"DEFINER =", "ON SCHEDULE", "RENAME TO", // Alter tokens
|
||||
"COMMENT", "DO", "INITIAL_SIZE = ", "OPTIONS",
|
||||
}
|
||||
|
||||
var alterTableTokens = [][]string{
|
||||
{"CUSTOM_FUZZ_STRING"},
|
||||
{"CUSTOM_ALTTER_TABLE_OPTIONS"},
|
||||
{"PARTITION_OPTIONS_FOR_ALTER_TABLE"},
|
||||
}
|
||||
|
||||
var alterTokens = [][]string{
|
||||
{
|
||||
"DATABASE", "SCHEMA", "DEFINER = ", "EVENT", "FUNCTION", "INSTANCE",
|
||||
"LOGFILE GROUP", "PROCEDURE", "SERVER",
|
||||
},
|
||||
{"CUSTOM_FUZZ_STRING"},
|
||||
{
|
||||
"ON SCHEDULE", "ON COMPLETION PRESERVE", "ON COMPLETION NOT PRESERVE",
|
||||
"ADD UNDOFILE", "OPTIONS",
|
||||
},
|
||||
{"RENAME TO", "INITIAL_SIZE = "},
|
||||
{"ENABLE", "DISABLE", "DISABLE ON SLAVE", "ENGINE"},
|
||||
{"COMMENT"},
|
||||
{"DO"},
|
||||
}
|
||||
|
||||
var setTokens = [][]string{
|
||||
{"CHARACTER SET", "CHARSET", "CUSTOM_FUZZ_STRING", "NAMES"},
|
||||
{"CUSTOM_FUZZ_STRING", "DEFAULT", "="},
|
||||
{"CUSTOM_FUZZ_STRING"},
|
||||
}
|
||||
|
||||
var dropTokens = [][]string{
|
||||
{"TEMPORARY", "UNDO"},
|
||||
{
|
||||
"DATABASE", "SCHEMA", "EVENT", "INDEX", "LOGFILE GROUP",
|
||||
"PROCEDURE", "FUNCTION", "SERVER", "SPATIAL REFERENCE SYSTEM",
|
||||
"TABLE", "TABLESPACE", "TRIGGER", "VIEW",
|
||||
},
|
||||
{"IF EXISTS"},
|
||||
{"CUSTOM_FUZZ_STRING"},
|
||||
{"ON", "ENGINE = ", "RESTRICT", "CASCADE"},
|
||||
}
|
||||
|
||||
var renameTokens = [][]string{
|
||||
{"TABLE"},
|
||||
{"CUSTOM_FUZZ_STRING"},
|
||||
{"TO"},
|
||||
{"CUSTOM_FUZZ_STRING"},
|
||||
}
|
||||
|
||||
var truncateTokens = [][]string{
|
||||
{"TABLE"},
|
||||
{"CUSTOM_FUZZ_STRING"},
|
||||
}
|
||||
|
||||
var createTokens = [][]string{
|
||||
{"OR REPLACE", "TEMPORARY", "UNDO"}, // For create spatial reference system
|
||||
{
|
||||
"UNIQUE", "FULLTEXT", "SPATIAL", "ALGORITHM = UNDEFINED", "ALGORITHM = MERGE",
|
||||
"ALGORITHM = TEMPTABLE",
|
||||
},
|
||||
{
|
||||
"DATABASE", "SCHEMA", "EVENT", "FUNCTION", "INDEX", "LOGFILE GROUP",
|
||||
"PROCEDURE", "SERVER", "SPATIAL REFERENCE SYSTEM", "TABLE", "TABLESPACE",
|
||||
"TRIGGER", "VIEW",
|
||||
},
|
||||
{"IF NOT EXISTS"},
|
||||
{"CUSTOM_FUZZ_STRING"},
|
||||
}
|
||||
|
||||
/*
|
||||
// For future use.
|
||||
var updateTokens = [][]string{
|
||||
{"LOW_PRIORITY"},
|
||||
{"IGNORE"},
|
||||
{"SET"},
|
||||
{"WHERE"},
|
||||
{"ORDER BY"},
|
||||
{"LIMIT"},
|
||||
}
|
||||
*/
|
||||
|
||||
var replaceTokens = [][]string{
|
||||
{"LOW_PRIORITY", "DELAYED"},
|
||||
{"INTO"},
|
||||
{"PARTITION"},
|
||||
{"CUSTOM_FUZZ_STRING"},
|
||||
{"VALUES", "VALUE"},
|
||||
}
|
||||
|
||||
var loadTokens = [][]string{
|
||||
{"DATA"},
|
||||
{"LOW_PRIORITY", "CONCURRENT", "LOCAL"},
|
||||
{"INFILE"},
|
||||
{"REPLACE", "IGNORE"},
|
||||
{"INTO TABLE"},
|
||||
{"PARTITION"},
|
||||
{"CHARACTER SET"},
|
||||
{"FIELDS", "COLUMNS"},
|
||||
{"TERMINATED BY"},
|
||||
{"OPTIONALLY"},
|
||||
{"ENCLOSED BY"},
|
||||
{"ESCAPED BY"},
|
||||
{"LINES"},
|
||||
{"STARTING BY"},
|
||||
{"TERMINATED BY"},
|
||||
{"IGNORE"},
|
||||
{"LINES", "ROWS"},
|
||||
{"CUSTOM_FUZZ_STRING"},
|
||||
}
|
||||
|
||||
// These Are everything that comes after "INSERT"
|
||||
var insertTokens = [][]string{
|
||||
{"LOW_PRIORITY", "DELAYED", "HIGH_PRIORITY", "IGNORE"},
|
||||
{"INTO"},
|
||||
{"PARTITION"},
|
||||
{"CUSTOM_FUZZ_STRING"},
|
||||
{"AS"},
|
||||
{"ON DUPLICATE KEY UPDATE"},
|
||||
}
|
||||
|
||||
// These are everything that comes after "SELECT"
|
||||
var selectTokens = [][]string{
|
||||
{"*", "CUSTOM_FUZZ_STRING", "DISTINCTROW"},
|
||||
{"HIGH_PRIORITY"},
|
||||
{"STRAIGHT_JOIN"},
|
||||
{"SQL_SMALL_RESULT", "SQL_BIG_RESULT", "SQL_BUFFER_RESULT"},
|
||||
{"SQL_NO_CACHE", "SQL_CALC_FOUND_ROWS"},
|
||||
{"CUSTOM_FUZZ_STRING"},
|
||||
{"FROM"},
|
||||
{"WHERE"},
|
||||
{"GROUP BY"},
|
||||
{"HAVING"},
|
||||
{"WINDOW"},
|
||||
{"ORDER BY"},
|
||||
{"LIMIT"},
|
||||
{"CUSTOM_FUZZ_STRING"},
|
||||
{"FOR"},
|
||||
}
|
||||
|
||||
// These are everything that comes after "DELETE"
|
||||
var deleteTokens = [][]string{
|
||||
{"LOW_PRIORITY", "QUICK", "IGNORE", "FROM", "AS"},
|
||||
{"PARTITION"},
|
||||
{"WHERE"},
|
||||
{"ORDER BY"},
|
||||
{"LIMIT"},
|
||||
}
|
||||
|
||||
var alter_table_options = []string{
|
||||
"ADD", "COLUMN", "FIRST", "AFTER", "INDEX", "KEY", "FULLTEXT", "SPATIAL",
|
||||
"CONSTRAINT", "UNIQUE", "FOREIGN KEY", "CHECK", "ENFORCED", "DROP", "ALTER",
|
||||
"NOT", "INPLACE", "COPY", "SET", "VISIBLE", "INVISIBLE", "DEFAULT", "CHANGE",
|
||||
"CHARACTER SET", "COLLATE", "DISABLE", "ENABLE", "KEYS", "TABLESPACE", "LOCK",
|
||||
"FORCE", "MODIFY", "SHARED", "EXCLUSIVE", "NONE", "ORDER BY", "RENAME COLUMN",
|
||||
"AS", "=", "ASC", "DESC", "WITH", "WITHOUT", "VALIDATION", "ADD PARTITION",
|
||||
"DROP PARTITION", "DISCARD PARTITION", "IMPORT PARTITION", "TRUNCATE PARTITION",
|
||||
"COALESCE PARTITION", "REORGANIZE PARTITION", "EXCHANGE PARTITION",
|
||||
"ANALYZE PARTITION", "CHECK PARTITION", "OPTIMIZE PARTITION", "REBUILD PARTITION",
|
||||
"REPAIR PARTITION", "REMOVE PARTITIONING", "USING", "BTREE", "HASH", "COMMENT",
|
||||
"KEY_BLOCK_SIZE", "WITH PARSER", "AUTOEXTEND_SIZE", "AUTO_INCREMENT", "AVG_ROW_LENGTH",
|
||||
"CHECKSUM", "INSERT_METHOD", "ROW_FORMAT", "DYNAMIC", "FIXED", "COMPRESSED", "REDUNDANT",
|
||||
"COMPACT", "SECONDARY_ENGINE_ATTRIBUTE", "STATS_AUTO_RECALC", "STATS_PERSISTENT",
|
||||
"STATS_SAMPLE_PAGES", "ZLIB", "LZ4", "ENGINE_ATTRIBUTE", "KEY_BLOCK_SIZE", "MAX_ROWS",
|
||||
"MIN_ROWS", "PACK_KEYS", "PASSWORD", "COMPRESSION", "CONNECTION", "DIRECTORY",
|
||||
"DELAY_KEY_WRITE", "ENCRYPTION", "STORAGE", "DISK", "MEMORY", "UNION",
|
||||
}
|
||||
|
||||
// Creates an 'alter table' statement. 'alter table' is an exception
|
||||
// in that it has its own function. The majority of statements
|
||||
// are created by 'createStmt()'.
|
||||
func createAlterTableStmt(f *ConsumeFuzzer) (string, error) {
|
||||
maxArgs, err := f.GetInt()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
maxArgs = maxArgs % 30
|
||||
if maxArgs == 0 {
|
||||
return "", fmt.Errorf("could not create alter table stmt")
|
||||
}
|
||||
|
||||
var stmt strings.Builder
|
||||
stmt.WriteString("ALTER TABLE ")
|
||||
for i := 0; i < maxArgs; i++ {
|
||||
// Calculate if we get existing token or custom string
|
||||
tokenType, err := f.GetInt()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if tokenType%4 == 1 {
|
||||
customString, err := f.GetString()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
stmt.WriteString(" " + customString)
|
||||
} else {
|
||||
tokenIndex, err := f.GetInt()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
stmt.WriteString(" " + alter_table_options[tokenIndex%len(alter_table_options)])
|
||||
}
|
||||
}
|
||||
return stmt.String(), nil
|
||||
}
|
||||
|
||||
func chooseToken(tokens []string, f *ConsumeFuzzer) (string, error) {
|
||||
index, err := f.GetInt()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var token strings.Builder
|
||||
token.WriteString(tokens[index%len(tokens)])
|
||||
if token.String() == "CUSTOM_FUZZ_STRING" {
|
||||
customFuzzString, err := f.GetString()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return customFuzzString, nil
|
||||
}
|
||||
|
||||
// Check if token requires an argument
|
||||
if containsString(needCustomString, token.String()) {
|
||||
customFuzzString, err := f.GetString()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
token.WriteString(" " + customFuzzString)
|
||||
}
|
||||
return token.String(), nil
|
||||
}
|
||||
|
||||
var stmtTypes = map[string][][]string{
|
||||
"DELETE": deleteTokens,
|
||||
"INSERT": insertTokens,
|
||||
"SELECT": selectTokens,
|
||||
"LOAD": loadTokens,
|
||||
"REPLACE": replaceTokens,
|
||||
"CREATE": createTokens,
|
||||
"DROP": dropTokens,
|
||||
"RENAME": renameTokens,
|
||||
"TRUNCATE": truncateTokens,
|
||||
"SET": setTokens,
|
||||
"ALTER": alterTokens,
|
||||
"ALTER TABLE": alterTableTokens, // ALTER TABLE has its own set of tokens
|
||||
}
|
||||
|
||||
var stmtTypeEnum = map[int]string{
|
||||
0: "DELETE",
|
||||
1: "INSERT",
|
||||
2: "SELECT",
|
||||
3: "LOAD",
|
||||
4: "REPLACE",
|
||||
5: "CREATE",
|
||||
6: "DROP",
|
||||
7: "RENAME",
|
||||
8: "TRUNCATE",
|
||||
9: "SET",
|
||||
10: "ALTER",
|
||||
11: "ALTER TABLE",
|
||||
}
|
||||
|
||||
func createStmt(f *ConsumeFuzzer) (string, error) {
|
||||
stmtIndex, err := f.GetInt()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
stmtIndex = stmtIndex % len(stmtTypes)
|
||||
|
||||
queryType := stmtTypeEnum[stmtIndex]
|
||||
tokens := stmtTypes[queryType]
|
||||
|
||||
// We have custom creator for ALTER TABLE
|
||||
if queryType == "ALTER TABLE" {
|
||||
query, err := createAlterTableStmt(f)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return query, nil
|
||||
}
|
||||
|
||||
// Here we are creating a query that is not
|
||||
// an 'alter table' query. For available
|
||||
// queries, see "stmtTypes"
|
||||
|
||||
// First specify the first query keyword:
|
||||
var query strings.Builder
|
||||
query.WriteString(queryType)
|
||||
|
||||
// Next create the args for the
|
||||
queryArgs, err := createStmtArgs(tokens, f)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
query.WriteString(" " + queryArgs)
|
||||
return query.String(), nil
|
||||
}
|
||||
|
||||
// Creates the arguments of a statements. In a select statement
|
||||
// that would be everything after "select".
|
||||
func createStmtArgs(tokenslice [][]string, f *ConsumeFuzzer) (string, error) {
|
||||
var query, token strings.Builder
|
||||
|
||||
// We go through the tokens in the tokenslice,
|
||||
// create the respective token and add it to
|
||||
// "query"
|
||||
for _, tokens := range tokenslice {
|
||||
// For extra randomization, the fuzzer can
|
||||
// choose to not include this token.
|
||||
includeThisToken, err := f.GetBool()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !includeThisToken {
|
||||
continue
|
||||
}
|
||||
|
||||
// There may be several tokens to choose from:
|
||||
if len(tokens) > 1 {
|
||||
chosenToken, err := chooseToken(tokens, f)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
query.WriteString(" " + chosenToken)
|
||||
} else {
|
||||
token.WriteString(tokens[0])
|
||||
|
||||
// In case the token is "CUSTOM_FUZZ_STRING"
|
||||
// we will then create a non-structured string
|
||||
if token.String() == "CUSTOM_FUZZ_STRING" {
|
||||
customFuzzString, err := f.GetString()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
query.WriteString(" " + customFuzzString)
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if token requires an argument.
|
||||
// Tokens that take an argument can be found
|
||||
// in 'needCustomString'. If so, we add a
|
||||
// non-structured string to the token.
|
||||
if containsString(needCustomString, token.String()) {
|
||||
customFuzzString, err := f.GetString()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
token.WriteString(fmt.Sprintf(" %s", customFuzzString))
|
||||
}
|
||||
query.WriteString(fmt.Sprintf(" %s", token.String()))
|
||||
}
|
||||
}
|
||||
return query.String(), nil
|
||||
}
|
||||
|
||||
// Creates a semi-structured query. It creates a string
|
||||
// that is a combination of the keywords and random strings.
|
||||
func createQuery(f *ConsumeFuzzer) (string, error) {
|
||||
queryLen, err := f.GetInt()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
maxLen := queryLen % 60
|
||||
if maxLen == 0 {
|
||||
return "", fmt.Errorf("could not create a query")
|
||||
}
|
||||
var query strings.Builder
|
||||
for i := 0; i < maxLen; i++ {
|
||||
// Get a new token:
|
||||
useKeyword, err := f.GetBool()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if useKeyword {
|
||||
keyword, err := getKeyword(f)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
query.WriteString(" " + keyword)
|
||||
} else {
|
||||
customString, err := f.GetString()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
query.WriteString(" " + customString)
|
||||
}
|
||||
}
|
||||
if query.String() == "" {
|
||||
return "", fmt.Errorf("could not create a query")
|
||||
}
|
||||
return query.String(), nil
|
||||
}
|
||||
|
||||
// GetSQLString is the API that users interact with.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// f := NewConsumer(data)
|
||||
// sqlString, err := f.GetSQLString()
|
||||
func (f *ConsumeFuzzer) GetSQLString() (string, error) {
|
||||
var query string
|
||||
veryStructured, err := f.GetBool()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if veryStructured {
|
||||
query, err = createStmt(f)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
query, err = createQuery(f)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return query, nil
|
||||
}
|
|
@ -176,7 +176,7 @@
|
|||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2013-2017 Docker, Inc.
|
||||
Copyright 2020 The Compose Specification Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
|
@ -401,7 +401,17 @@
|
|||
"type": "object",
|
||||
"description": "Provider-specific options.",
|
||||
"patternProperties": {
|
||||
"^.+$": {"type": ["string", "number", "null"]}
|
||||
"^.+$": {"oneOf": [
|
||||
{ "type": ["string", "number", "boolean"] },
|
||||
{ "type": "array", "items": {"type": ["string", "number", "boolean"]}}
|
||||
]}
|
||||
}
|
||||
},
|
||||
"configs": {
|
||||
"type": "object",
|
||||
"description": "Config files to pass to the provider.",
|
||||
"patternProperties": {
|
||||
"^.+$": {"type": ["string"]}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1229,8 +1229,8 @@ func deriveDeepCopy_12(dst, src []DeviceMapping) {
|
|||
func deriveDeepCopy_13(dst, src *ServiceProviderConfig) {
|
||||
dst.Type = src.Type
|
||||
if src.Options != nil {
|
||||
dst.Options = make(map[string]string, len(src.Options))
|
||||
deriveDeepCopy_4(dst.Options, src.Options)
|
||||
dst.Options = make(map[string][]string, len(src.Options))
|
||||
deriveDeepCopy_15(dst.Options, src.Options)
|
||||
} else {
|
||||
dst.Options = nil
|
||||
}
|
||||
|
|
|
@ -40,3 +40,27 @@ func (d *Options) DecodeMapstructure(value interface{}) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MultiOptions allow option to be repeated
|
||||
type MultiOptions map[string][]string
|
||||
|
||||
func (d *MultiOptions) DecodeMapstructure(value interface{}) error {
|
||||
switch v := value.(type) {
|
||||
case map[string]interface{}:
|
||||
m := make(map[string][]string)
|
||||
for key, e := range v {
|
||||
switch e := e.(type) {
|
||||
case []interface{}:
|
||||
for _, v := range e {
|
||||
m[key] = append(m[key], fmt.Sprint(v))
|
||||
}
|
||||
default:
|
||||
m[key] = append(m[key], fmt.Sprint(e))
|
||||
}
|
||||
}
|
||||
*d = m
|
||||
default:
|
||||
return fmt.Errorf("invalid type %T for options", value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -143,9 +143,9 @@ type ServiceConfig struct {
|
|||
}
|
||||
|
||||
type ServiceProviderConfig struct {
|
||||
Type string `yaml:"type,omitempty" json:"driver,omitempty"`
|
||||
Options Options `yaml:"options,omitempty" json:"options,omitempty"`
|
||||
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
|
||||
Type string `yaml:"type,omitempty" json:"driver,omitempty"`
|
||||
Options MultiOptions `yaml:"options,omitempty" json:"options,omitempty"`
|
||||
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
|
||||
}
|
||||
|
||||
// MarshalYAML makes ServiceConfig implement yaml.Marshaller
|
||||
|
|
|
@ -18,6 +18,7 @@ package validation
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/tree"
|
||||
|
@ -29,6 +30,7 @@ var checks = map[tree.Path]checkerFunc{
|
|||
"volumes.*": checkVolume,
|
||||
"configs.*": checkFileObject("file", "environment", "content"),
|
||||
"secrets.*": checkFileObject("file", "environment"),
|
||||
"services.*.ports.*": checkIPAddress,
|
||||
"services.*.develop.watch.*.path": checkPath,
|
||||
"services.*.deploy.resources.reservations.devices.*": checkDeviceRequest,
|
||||
"services.*.gpus.*": checkDeviceRequest,
|
||||
|
@ -105,3 +107,13 @@ func checkDeviceRequest(value any, p tree.Path) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkIPAddress(value any, p tree.Path) error {
|
||||
if v, ok := value.(map[string]any); ok {
|
||||
ip, ok := v["host_ip"]
|
||||
if ok && net.ParseIP(ip.(string)) == nil {
|
||||
return fmt.Errorf("%s: invalid ip address: %s", p, ip)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//go:build !darwin && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos
|
||||
// +build !darwin,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos
|
||||
//go:build !darwin && !freebsd && !linux && !netbsd && !openbsd && !windows && !zos
|
||||
// +build !darwin,!freebsd,!linux,!netbsd,!openbsd,!windows,!zos
|
||||
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
|
|
@ -31,6 +31,15 @@ func NewPty() (Console, string, error) {
|
|||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return NewPtyFromFile(f)
|
||||
}
|
||||
|
||||
// NewPtyFromFile creates a new pty pair, just like [NewPty] except that the
|
||||
// provided [os.File] is used as the master rather than automatically creating
|
||||
// a new master from /dev/ptmx. The ownership of [os.File] is passed to the
|
||||
// returned [Console], so the caller must be careful to not call Close on the
|
||||
// underlying file.
|
||||
func NewPtyFromFile(f File) (Console, string, error) {
|
||||
slave, err := ptsname(f)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
|
|
|
@ -18,7 +18,6 @@ package console
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
@ -30,12 +29,12 @@ const (
|
|||
|
||||
// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
|
||||
// unlockpt should be called before opening the slave side of a pty.
|
||||
func unlockpt(f *os.File) error {
|
||||
func unlockpt(f File) error {
|
||||
return unix.IoctlSetPointerInt(int(f.Fd()), unix.TIOCPTYUNLK, 0)
|
||||
}
|
||||
|
||||
// ptsname retrieves the name of the first available pts for the given master.
|
||||
func ptsname(f *os.File) (string, error) {
|
||||
func ptsname(f File) (string, error) {
|
||||
n, err := unix.IoctlGetInt(int(f.Fd()), unix.TIOCPTYGNAME)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
|
@ -21,7 +21,6 @@ package console
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
@ -39,7 +38,7 @@ const (
|
|||
|
||||
// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
|
||||
// unlockpt should be called before opening the slave side of a pty.
|
||||
func unlockpt(f *os.File) error {
|
||||
func unlockpt(f File) error {
|
||||
fd := C.int(f.Fd())
|
||||
if _, err := C.unlockpt(fd); err != nil {
|
||||
C.close(fd)
|
||||
|
@ -49,7 +48,7 @@ func unlockpt(f *os.File) error {
|
|||
}
|
||||
|
||||
// ptsname retrieves the name of the first available pts for the given master.
|
||||
func ptsname(f *os.File) (string, error) {
|
||||
func ptsname(f File) (string, error) {
|
||||
n, err := unix.IoctlGetInt(int(f.Fd()), unix.TIOCGPTN)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
|
@ -21,7 +21,6 @@ package console
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
@ -42,12 +41,12 @@ const (
|
|||
|
||||
// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
|
||||
// unlockpt should be called before opening the slave side of a pty.
|
||||
func unlockpt(f *os.File) error {
|
||||
func unlockpt(f File) error {
|
||||
panic("unlockpt() support requires cgo.")
|
||||
}
|
||||
|
||||
// ptsname retrieves the name of the first available pts for the given master.
|
||||
func ptsname(f *os.File) (string, error) {
|
||||
func ptsname(f File) (string, error) {
|
||||
n, err := unix.IoctlGetInt(int(f.Fd()), unix.TIOCGPTN)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
|
@ -18,7 +18,6 @@ package console
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
@ -31,7 +30,7 @@ const (
|
|||
|
||||
// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
|
||||
// unlockpt should be called before opening the slave side of a pty.
|
||||
func unlockpt(f *os.File) error {
|
||||
func unlockpt(f File) error {
|
||||
var u int32
|
||||
// XXX do not use unix.IoctlSetPointerInt here, see commit dbd69c59b81.
|
||||
if _, _, err := unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))); err != 0 {
|
||||
|
@ -41,7 +40,7 @@ func unlockpt(f *os.File) error {
|
|||
}
|
||||
|
||||
// ptsname retrieves the name of the first available pts for the given master.
|
||||
func ptsname(f *os.File) (string, error) {
|
||||
func ptsname(f File) (string, error) {
|
||||
var u uint32
|
||||
// XXX do not use unix.IoctlGetInt here, see commit dbd69c59b81.
|
||||
if _, _, err := unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.TIOCGPTN, uintptr(unsafe.Pointer(&u))); err != 0 {
|
||||
|
|
|
@ -18,7 +18,6 @@ package console
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
@ -31,12 +30,12 @@ const (
|
|||
// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
|
||||
// unlockpt should be called before opening the slave side of a pty.
|
||||
// This does not exist on NetBSD, it does not allocate controlling terminals on open
|
||||
func unlockpt(f *os.File) error {
|
||||
func unlockpt(f File) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ptsname retrieves the name of the first available pts for the given master.
|
||||
func ptsname(f *os.File) (string, error) {
|
||||
func ptsname(f File) (string, error) {
|
||||
ptm, err := unix.IoctlGetPtmget(int(f.Fd()), unix.TIOCPTSNAME)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue