Compare commits

..

24 Commits

Author SHA1 Message Date
Tõnis Tiigi
59582a88fc Merge pull request #2471 from tonistiigi/v0.14.1-picks
[v0.14] cherry picks for v0.14.1
2024-05-22 07:43:43 -07:00
Tonis Tiigi
a3b1fae96d driver: handle nil logger for bootstrap
resolveNode methods can call with nil logger. Although
the results should already be cached now in resolver
this makes the protection more explicit.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 035236a5ed)
2024-05-22 07:17:53 -07:00
Tonis Tiigi
6a84f43fba build: add cache to resolvedNode
Currently it is possible for boot() to be called
multiple times, resulting multiple slow requests to
establish connection (eg. multiple container inspects
for container driver).

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 99777eaf34)
2024-05-22 07:17:46 -07:00
Jonathan A. Sternberg
cf68b5b878 vendor: update buildx to latest docker/cli
This version of docker/cli has changes to remove compose-cli wrapper and
move all CLI metrics to OTEL.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
(cherry picked from commit 4fc4bc07ae)
2024-05-16 12:14:08 -05:00
Tonis Tiigi
3f1aaa68d5 build: fix multiple named contexts pointing to same bake target
Contexts using target: schema are replaced by input: pointing
to previous build result before build request is sent. Currently
this replacement did not work if multiple contexts pointed to
the same target name.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit f8c6a97edc)
2024-05-16 12:14:08 -05:00
jaihwan104
f6830f3b86 build: exit 1 when manifest merge failed
Signed-off-by: jaihwan104 <42341126+jaihwan104@users.noreply.github.com>
(cherry picked from commit f2823515db)
2024-05-16 12:13:59 -05:00
CrazyMax
f6e57cf5b5 build: don't generate metadata file when print flag is used
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
(cherry picked from commit ba264138d6)
2024-05-16 11:31:58 -05:00
Tonis Tiigi
b77648d5f8 build: avoid default load with --print
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit fbb0f9b424)
2024-05-16 11:29:59 -05:00
Tõnis Tiigi
171fcbeb69 Merge pull request #2417 from tonistiigi/update-buildkit-240417
vendor: update buildkit to 71f99c52a669
2024-04-17 10:02:29 -07:00
Tonis Tiigi
370a5aa127 update lint fallback image
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2024-04-17 09:18:52 -07:00
Tonis Tiigi
13653fb84d vendor: update buildkit to 71f99c52a669
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2024-04-17 08:21:11 -07:00
Tõnis Tiigi
1b16594f4a Merge pull request #2415 from igaskin/scheduler-name
feat: adding option to add scheduler name to kubernetes driver
2024-04-17 08:18:23 -07:00
Tõnis Tiigi
3905e8cf06 Merge pull request #2416 from crazy-max/print-internal
build: mark information requests as internal
2024-04-17 08:15:55 -07:00
CrazyMax
177b95c972 build: mark information requests as internal
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2024-04-17 16:56:43 +02:00
Isaac Gaskin
74fdbb5e7f feat: adding option to add scheduler name to kubernetes driver
this allows for custom scheduling of deployments

Signed-off-by: Isaac Gaskin <isaac.gaskin@circle.com>
2024-04-16 14:51:59 -07:00
Tõnis Tiigi
ac331d3569 Merge pull request #2401 from crazy-max/ci-k3s-update
ci: switch to reusable workflow to install k3s
2024-04-15 16:00:55 -07:00
Tõnis Tiigi
07c9b45bae Merge pull request #2408 from tonistiigi/print-statuscode
build: support statuscode response for print requests
2024-04-15 15:58:52 -07:00
Tõnis Tiigi
b91957444b Merge pull request #2406 from tonistiigi/print-lint-fallback
build: add fallback image for --print=lint
2024-04-15 15:58:34 -07:00
Tonis Tiigi
46c44c58ae build: support statuscode response for print requests
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2024-04-15 10:38:54 -07:00
CrazyMax
6aed54c35a Merge pull request #2405 from docker/dependabot/github_actions/peter-evans/create-pull-request-6.0.3
build(deps): bump peter-evans/create-pull-request from 6.0.2 to 6.0.3
2024-04-13 14:54:34 +02:00
Tonis Tiigi
126fe653c7 build: refactor print fallbacks to own function
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2024-04-12 17:09:43 -07:00
Tonis Tiigi
f0cbc95eaf build: add fallback image for --print=lint
Fallback to known supporting image if lint called
on old frontend.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2024-04-12 17:09:38 -07:00
dependabot[bot]
1a0f9fa96c build(deps): bump peter-evans/create-pull-request from 6.0.2 to 6.0.3
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 6.0.2 to 6.0.3.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](70a41aba78...c55203cfde)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-12 18:33:29 +00:00
CrazyMax
54a5c1ff93 ci: switch to reusable workflow to install k3s
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2024-04-11 10:15:37 +02:00
43 changed files with 658 additions and 478 deletions

View File

@@ -57,7 +57,7 @@ jobs:
VENDOR_MODULE: github.com/docker/buildx@${{ env.RELEASE_NAME }} VENDOR_MODULE: github.com/docker/buildx@${{ env.RELEASE_NAME }}
- -
name: Create PR on docs repo name: Create PR on docs repo
uses: peter-evans/create-pull-request@70a41aba780001da0a30141984ae2a0c95d8704e # v6.0.2 uses: peter-evans/create-pull-request@c55203cfde3e5c11a452d352b4393e68b85b4533 # v6.0.3
with: with:
token: ${{ secrets.GHPAT_DOCS_DISPATCH }} token: ${{ secrets.GHPAT_DOCS_DISPATCH }}
push-to-fork: docker-tools-robot/docker.github.io push-to-fork: docker-tools-robot/docker.github.io

View File

@@ -137,67 +137,9 @@ jobs:
- -
name: Install k3s name: Install k3s
if: matrix.driver == 'kubernetes' if: matrix.driver == 'kubernetes'
uses: actions/github-script@v7 uses: crazy-max/.github/.github/actions/install-k3s@fa6141aedf23596fb8bdcceab9cce8dadaa31bd9
with: with:
script: | version: ${{ env.K3S_VERSION }}
const fs = require('fs');
let wait = function(milliseconds) {
return new Promise((resolve, reject) => {
if (typeof(milliseconds) !== 'number') {
throw new Error('milleseconds not a number');
}
setTimeout(() => resolve("done!"), milliseconds)
});
}
try {
const kubeconfig="/tmp/buildkit-k3s/kubeconfig.yaml";
core.info(`storing kubeconfig in ${kubeconfig}`);
await exec.exec('docker', ["run", "-d",
"--privileged",
"--name=buildkit-k3s",
"-e", "K3S_KUBECONFIG_OUTPUT="+kubeconfig,
"-e", "K3S_KUBECONFIG_MODE=666",
"-v", "/tmp/buildkit-k3s:/tmp/buildkit-k3s",
"-p", "6443:6443",
"-p", "80:80",
"-p", "443:443",
"-p", "8080:8080",
"rancher/k3s:${{ env.K3S_VERSION }}", "server"
]);
await wait(10000);
core.exportVariable('KUBECONFIG', kubeconfig);
let nodeName;
for (let count = 1; count <= 5; count++) {
try {
const nodeNameOutput = await exec.getExecOutput("kubectl get nodes --no-headers -oname");
nodeName = nodeNameOutput.stdout
} catch (error) {
core.info(`Unable to resolve node name (${error.message}). Attempt ${count} of 5.`)
} finally {
if (nodeName) {
break;
}
await wait(5000);
}
}
if (!nodeName) {
throw new Error(`Unable to resolve node name after 5 attempts.`);
}
await exec.exec(`kubectl wait --for=condition=Ready ${nodeName}`);
} catch (error) {
core.setFailed(error.message);
}
-
name: Print KUBECONFIG
if: matrix.driver == 'kubernetes'
run: |
yq ${{ env.KUBECONFIG }}
- -
name: Launch remote buildkitd name: Launch remote buildkitd
if: matrix.driver == 'remote' if: matrix.driver == 'remote'

View File

@@ -53,7 +53,10 @@ var (
const ( const (
//nolint:gosec // G101: false-positive //nolint:gosec // G101: false-positive
printFallbackImage = "docker/dockerfile:1.5.2-labs@sha256:f2e91734a84c0922ff47aa4098ab775f1dfa932430d2888dd5cad5251fafdac4" printFallbackImage = "docker/dockerfile:1.5@sha256:dbbd5e059e8a07ff7ea6233b213b36aa516b4c53c645f1817a4dd18b83cbea56"
// https://github.com/moby/buildkit/commit/71f99c52a669dc0322b5ea57bc28a09c20427227
//nolint:gosec // G101: false-positive
printLintFallbackImage = "docker.io/docker/dockerfile-upstream@sha256:47663570b6cc49ed90dc6e3215090a366989ab934d12dc93856a8ae0d27a95e7"
) )
type Options struct { type Options struct {
@@ -88,8 +91,9 @@ type Options struct {
} }
type PrintFunc struct { type PrintFunc struct {
Name string Name string
Format string Format string
IgnoreStatus bool
} }
type Inputs struct { type Inputs struct {
@@ -400,25 +404,8 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[s
res, err := c.Solve(ctx, req) res, err := c.Solve(ctx, req)
if err != nil { if err != nil {
fallback := false req, ok := fallbackPrintError(err, req)
var reqErr *errdefs.UnsupportedSubrequestError if ok {
if errors.As(err, &reqErr) {
switch reqErr.Name {
case "frontend.outline", "frontend.targets":
fallback = true
default:
return nil, err
}
} else {
return nil, err
}
// buildkit v0.8 vendored in Docker 20.10 does not support typed errors
if strings.Contains(err.Error(), "unsupported request frontend.outline") || strings.Contains(err.Error(), "unsupported request frontend.targets") {
fallback = true
}
if fallback {
req.FrontendOpt["build-arg:BUILDKIT_SYNTAX"] = printFallbackImage
res2, err2 := c.Solve(ctx, req) res2, err2 := c.Solve(ctx, req)
if err2 != nil { if err2 != nil {
return nil, err return nil, err
@@ -468,7 +455,7 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[s
} else { } else {
rr, err = c.Build(ctx, *so, "buildx", buildFunc, ch) rr, err = c.Build(ctx, *so, "buildx", buildFunc, ch)
} }
if desktop.BuildBackendEnabled() && node.Driver.HistoryAPISupported(ctx) { if !so.Internal && desktop.BuildBackendEnabled() && node.Driver.HistoryAPISupported(ctx) {
if err != nil { if err != nil {
return &desktop.ErrorWithBuildRef{ return &desktop.ErrorWithBuildRef{
Ref: buildRef, Ref: buildRef,
@@ -554,7 +541,7 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[s
} }
if pushNames != "" { if pushNames != "" {
progress.Write(pw, fmt.Sprintf("merging manifest list %s", pushNames), func() error { err := progress.Write(pw, fmt.Sprintf("merging manifest list %s", pushNames), func() error {
descs := make([]specs.Descriptor, 0, len(res)) descs := make([]specs.Descriptor, 0, len(res))
for _, r := range res { for _, r := range res {
@@ -650,6 +637,9 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[s
} }
return nil return nil
}) })
if err != nil {
return err
}
} }
return nil return nil
}) })
@@ -794,11 +784,11 @@ func calculateChildTargets(reqs map[string][]*reqForNode, opt map[string]Options
} }
func waitContextDeps(ctx context.Context, index int, results *waitmap.Map, so *client.SolveOpt) error { func waitContextDeps(ctx context.Context, index int, results *waitmap.Map, so *client.SolveOpt) error {
m := map[string]string{} m := map[string][]string{}
for k, v := range so.FrontendAttrs { for k, v := range so.FrontendAttrs {
if strings.HasPrefix(k, "context:") && strings.HasPrefix(v, "target:") { if strings.HasPrefix(k, "context:") && strings.HasPrefix(v, "target:") {
target := resultKey(index, strings.TrimPrefix(v, "target:")) target := resultKey(index, strings.TrimPrefix(v, "target:"))
m[target] = k m[target] = append(m[target], k)
} }
} }
if len(m) == 0 { if len(m) == 0 {
@@ -813,7 +803,7 @@ func waitContextDeps(ctx context.Context, index int, results *waitmap.Map, so *c
return err return err
} }
for k, v := range m { for k, contexts := range m {
r, ok := res[k] r, ok := res[k]
if !ok { if !ok {
continue continue
@@ -828,19 +818,45 @@ func waitContextDeps(ctx context.Context, index int, results *waitmap.Map, so *c
if so.FrontendInputs == nil { if so.FrontendInputs == nil {
so.FrontendInputs = map[string]llb.State{} so.FrontendInputs = map[string]llb.State{}
} }
if len(rr.Refs) > 0 {
for platform, r := range rr.Refs { for _, v := range contexts {
st, err := r.ToState() if len(rr.Refs) > 0 {
for platform, r := range rr.Refs {
st, err := r.ToState()
if err != nil {
return err
}
so.FrontendInputs[k+"::"+platform] = st
so.FrontendAttrs[v+"::"+platform] = "input:" + k + "::" + platform
metadata := make(map[string][]byte)
if dt, ok := rr.Metadata[exptypes.ExporterImageConfigKey+"/"+platform]; ok {
metadata[exptypes.ExporterImageConfigKey] = dt
}
if dt, ok := rr.Metadata["containerimage.buildinfo/"+platform]; ok {
metadata["containerimage.buildinfo"] = dt
}
if len(metadata) > 0 {
dt, err := json.Marshal(metadata)
if err != nil {
return err
}
so.FrontendAttrs["input-metadata:"+k+"::"+platform] = string(dt)
}
}
delete(so.FrontendAttrs, v)
}
if rr.Ref != nil {
st, err := rr.Ref.ToState()
if err != nil { if err != nil {
return err return err
} }
so.FrontendInputs[k+"::"+platform] = st so.FrontendInputs[k] = st
so.FrontendAttrs[v+"::"+platform] = "input:" + k + "::" + platform so.FrontendAttrs[v] = "input:" + k
metadata := make(map[string][]byte) metadata := make(map[string][]byte)
if dt, ok := rr.Metadata[exptypes.ExporterImageConfigKey+"/"+platform]; ok { if dt, ok := rr.Metadata[exptypes.ExporterImageConfigKey]; ok {
metadata[exptypes.ExporterImageConfigKey] = dt metadata[exptypes.ExporterImageConfigKey] = dt
} }
if dt, ok := rr.Metadata["containerimage.buildinfo/"+platform]; ok { if dt, ok := rr.Metadata["containerimage.buildinfo"]; ok {
metadata["containerimage.buildinfo"] = dt metadata["containerimage.buildinfo"] = dt
} }
if len(metadata) > 0 { if len(metadata) > 0 {
@@ -848,37 +864,54 @@ func waitContextDeps(ctx context.Context, index int, results *waitmap.Map, so *c
if err != nil { if err != nil {
return err return err
} }
so.FrontendAttrs["input-metadata:"+k+"::"+platform] = string(dt) so.FrontendAttrs["input-metadata:"+k] = string(dt)
} }
} }
delete(so.FrontendAttrs, v)
}
if rr.Ref != nil {
st, err := rr.Ref.ToState()
if err != nil {
return err
}
so.FrontendInputs[k] = st
so.FrontendAttrs[v] = "input:" + k
metadata := make(map[string][]byte)
if dt, ok := rr.Metadata[exptypes.ExporterImageConfigKey]; ok {
metadata[exptypes.ExporterImageConfigKey] = dt
}
if dt, ok := rr.Metadata["containerimage.buildinfo"]; ok {
metadata["containerimage.buildinfo"] = dt
}
if len(metadata) > 0 {
dt, err := json.Marshal(metadata)
if err != nil {
return err
}
so.FrontendAttrs["input-metadata:"+k] = string(dt)
}
} }
} }
return nil return nil
} }
func fallbackPrintError(err error, req gateway.SolveRequest) (gateway.SolveRequest, bool) {
if _, ok := req.FrontendOpt["requestid"]; !ok {
return req, false
}
fallback := false
fallbackLint := false
var reqErr *errdefs.UnsupportedSubrequestError
if errors.As(err, &reqErr) {
switch reqErr.Name {
case "frontend.lint":
fallbackLint = true
fallthrough
case "frontend.outline", "frontend.targets":
fallback = true
default:
return req, false
}
}
// buildkit v0.8 vendored in Docker 20.10 does not support typed errors
for _, req := range []string{"frontend.outline", "frontend.targets", "frontend.lint"} {
if strings.Contains(err.Error(), "unsupported request "+req) {
fallback = true
}
if req == "frontend.lint" {
fallbackLint = true
}
}
if fallback {
req.FrontendOpt["build-arg:BUILDKIT_SYNTAX"] = printFallbackImage
if fallbackLint {
req.FrontendOpt["build-arg:BUILDKIT_SYNTAX"] = printLintFallbackImage
}
return req, true
}
return req, false
}
func noPrintFunc(opt map[string]Options) bool { func noPrintFunc(opt map[string]Options) bool {
for _, v := range opt { for _, v := range opt {
if v.PrintFunc != nil { if v.PrintFunc != nil {

View File

@@ -3,6 +3,7 @@ package build
import ( import (
"context" "context"
"fmt" "fmt"
"sync"
"github.com/containerd/containerd/platforms" "github.com/containerd/containerd/platforms"
"github.com/docker/buildx/builder" "github.com/docker/buildx/builder"
@@ -46,10 +47,22 @@ func (dp resolvedNode) BuildOpts(ctx context.Context) (gateway.BuildOpts, error)
type matchMaker func(specs.Platform) platforms.MatchComparer type matchMaker func(specs.Platform) platforms.MatchComparer
type cachedGroup[T any] struct {
g flightcontrol.Group[T]
cache map[int]T
cacheMu sync.Mutex
}
func newCachedGroup[T any]() cachedGroup[T] {
return cachedGroup[T]{
cache: map[int]T{},
}
}
type nodeResolver struct { type nodeResolver struct {
nodes []builder.Node nodes []builder.Node
clients flightcontrol.Group[*client.Client] clients cachedGroup[*client.Client]
opt flightcontrol.Group[gateway.BuildOpts] buildOpts cachedGroup[gateway.BuildOpts]
} }
func resolveDrivers(ctx context.Context, nodes []builder.Node, opt map[string]Options, pw progress.Writer) (map[string][]*resolvedNode, error) { func resolveDrivers(ctx context.Context, nodes []builder.Node, opt map[string]Options, pw progress.Writer) (map[string][]*resolvedNode, error) {
@@ -63,7 +76,9 @@ func resolveDrivers(ctx context.Context, nodes []builder.Node, opt map[string]Op
func newDriverResolver(nodes []builder.Node) *nodeResolver { func newDriverResolver(nodes []builder.Node) *nodeResolver {
r := &nodeResolver{ r := &nodeResolver{
nodes: nodes, nodes: nodes,
clients: newCachedGroup[*client.Client](),
buildOpts: newCachedGroup[gateway.BuildOpts](),
} }
return r return r
} }
@@ -179,6 +194,7 @@ func (r *nodeResolver) resolve(ctx context.Context, ps []specs.Platform, pw prog
resolver: r, resolver: r,
driverIndex: 0, driverIndex: 0,
}) })
nodeIdxs = append(nodeIdxs, 0)
} else { } else {
for i, idx := range nodeIdxs { for i, idx := range nodeIdxs {
node := &resolvedNode{ node := &resolvedNode{
@@ -237,11 +253,24 @@ func (r *nodeResolver) boot(ctx context.Context, idxs []int, pw progress.Writer)
for i, idx := range idxs { for i, idx := range idxs {
i, idx := i, idx i, idx := i, idx
eg.Go(func() error { eg.Go(func() error {
c, err := r.clients.Do(ctx, fmt.Sprint(idx), func(ctx context.Context) (*client.Client, error) { c, err := r.clients.g.Do(ctx, fmt.Sprint(idx), func(ctx context.Context) (*client.Client, error) {
if r.nodes[idx].Driver == nil { if r.nodes[idx].Driver == nil {
return nil, nil return nil, nil
} }
return driver.Boot(ctx, baseCtx, r.nodes[idx].Driver, pw) r.clients.cacheMu.Lock()
c, ok := r.clients.cache[idx]
r.clients.cacheMu.Unlock()
if ok {
return c, nil
}
c, err := driver.Boot(ctx, baseCtx, r.nodes[idx].Driver, pw)
if err != nil {
return nil, err
}
r.clients.cacheMu.Lock()
r.clients.cache[idx] = c
r.clients.cacheMu.Unlock()
return c, nil
}) })
if err != nil { if err != nil {
return err return err
@@ -272,14 +301,25 @@ func (r *nodeResolver) opts(ctx context.Context, idxs []int, pw progress.Writer)
continue continue
} }
eg.Go(func() error { eg.Go(func() error {
opt, err := r.opt.Do(ctx, fmt.Sprint(idx), func(ctx context.Context) (gateway.BuildOpts, error) { opt, err := r.buildOpts.g.Do(ctx, fmt.Sprint(idx), func(ctx context.Context) (gateway.BuildOpts, error) {
opt := gateway.BuildOpts{} r.buildOpts.cacheMu.Lock()
opt, ok := r.buildOpts.cache[idx]
r.buildOpts.cacheMu.Unlock()
if ok {
return opt, nil
}
_, err := c.Build(ctx, client.SolveOpt{ _, err := c.Build(ctx, client.SolveOpt{
Internal: true, Internal: true,
}, "buildx", func(ctx context.Context, c gateway.Client) (*gateway.Result, error) { }, "buildx", func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
opt = c.BuildOpts() opt = c.BuildOpts()
return nil, nil return nil, nil
}, nil) }, nil)
if err != nil {
return gateway.BuildOpts{}, err
}
r.buildOpts.cacheMu.Lock()
r.buildOpts.cache[idx] = opt
r.buildOpts.cacheMu.Unlock()
return opt, err return opt, err
}) })
if err != nil { if err != nil {

View File

@@ -162,7 +162,7 @@ func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt Op
case 1: case 1:
// valid // valid
case 0: case 0:
if !noDefaultLoad() { if !noDefaultLoad() && opt.PrintFunc == nil {
if nodeDriver.IsMobyDriver() { if nodeDriver.IsMobyDriver() {
// backwards compat for docker driver only: // backwards compat for docker driver only:
// this ensures the build results in a docker image. // this ensures the build results in a docker image.
@@ -354,6 +354,11 @@ func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt Op
so.FrontendAttrs["ulimit"] = ulimits so.FrontendAttrs["ulimit"] = ulimits
} }
// mark info request as internal
if opt.PrintFunc != nil {
so.Internal = true
}
return &so, releaseF, nil return &so, releaseF, nil
} }

View File

@@ -1,6 +1,7 @@
package main package main
import ( import (
"context"
"fmt" "fmt"
"os" "os"
@@ -15,6 +16,7 @@ import (
cliflags "github.com/docker/cli/cli/flags" cliflags "github.com/docker/cli/cli/flags"
"github.com/moby/buildkit/solver/errdefs" "github.com/moby/buildkit/solver/errdefs"
"github.com/moby/buildkit/util/stack" "github.com/moby/buildkit/util/stack"
"go.opentelemetry.io/otel"
//nolint:staticcheck // vendored dependencies may still use this //nolint:staticcheck // vendored dependencies may still use this
"github.com/containerd/containerd/pkg/seed" "github.com/containerd/containerd/pkg/seed"
@@ -38,10 +40,27 @@ func runStandalone(cmd *command.DockerCli) error {
if err := cmd.Initialize(cliflags.NewClientOptions()); err != nil { if err := cmd.Initialize(cliflags.NewClientOptions()); err != nil {
return err return err
} }
defer flushMetrics(cmd)
rootCmd := commands.NewRootCmd(os.Args[0], false, cmd) rootCmd := commands.NewRootCmd(os.Args[0], false, cmd)
return rootCmd.Execute() return rootCmd.Execute()
} }
// flushMetrics will manually flush metrics from the configured
// meter provider. This is needed when running in standalone mode
// because the meter provider is initialized by the cli library,
// but the mechanism for forcing it to report is not presently
// exposed and not invoked when run in standalone mode.
// There are plans to fix that in the next release, but this is
// needed temporarily until the API for this is more thorough.
func flushMetrics(cmd *command.DockerCli) {
if mp, ok := cmd.MeterProvider().(command.MeterProvider); ok {
if err := mp.ForceFlush(context.Background()); err != nil {
otel.Handle(err)
}
}
}
func runPlugin(cmd *command.DockerCli) error { func runPlugin(cmd *command.DockerCli) error {
rootCmd := commands.NewRootCmd("buildx", true, cmd) rootCmd := commands.NewRootCmd("buildx", true, cmd)
return plugin.RunPlugin(cmd, rootCmd, manager.Metadata{ return plugin.RunPlugin(cmd, rootCmd, manager.Metadata{

View File

@@ -122,27 +122,26 @@ func (o *buildOptions) toControllerOptions() (*controllerapi.BuildOptions, error
} }
opts := controllerapi.BuildOptions{ opts := controllerapi.BuildOptions{
Allow: o.allow, Allow: o.allow,
Annotations: o.annotations, Annotations: o.annotations,
BuildArgs: buildArgs, BuildArgs: buildArgs,
CgroupParent: o.cgroupParent, CgroupParent: o.cgroupParent,
ContextPath: o.contextPath, ContextPath: o.contextPath,
DockerfileName: o.dockerfileName, DockerfileName: o.dockerfileName,
ExtraHosts: o.extraHosts, ExtraHosts: o.extraHosts,
Labels: labels, Labels: labels,
NetworkMode: o.networkMode, NetworkMode: o.networkMode,
NoCacheFilter: o.noCacheFilter, NoCacheFilter: o.noCacheFilter,
Platforms: o.platforms, Platforms: o.platforms,
ShmSize: int64(o.shmSize), ShmSize: int64(o.shmSize),
Tags: o.tags, Tags: o.tags,
Target: o.target, Target: o.target,
Ulimits: dockerUlimitToControllerUlimit(o.ulimits), Ulimits: dockerUlimitToControllerUlimit(o.ulimits),
Builder: o.builder, Builder: o.builder,
NoCache: o.noCache, NoCache: o.noCache,
Pull: o.pull, Pull: o.pull,
ExportPush: o.exportPush, ExportPush: o.exportPush,
ExportLoad: o.exportLoad, ExportLoad: o.exportLoad,
WithProvenanceResponse: len(o.metadataFile) > 0,
} }
// TODO: extract env var parsing to a method easily usable by library consumers // TODO: extract env var parsing to a method easily usable by library consumers
@@ -207,6 +206,8 @@ func (o *buildOptions) toControllerOptions() (*controllerapi.BuildOptions, error
return nil, err return nil, err
} }
opts.WithProvenanceResponse = opts.PrintFunc == nil && len(o.metadataFile) > 0
return &opts, nil return &opts, nil
} }
@@ -268,8 +269,7 @@ func (o *buildOptionsHash) String() string {
} }
func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions) (err error) { func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions) (err error) {
mp := dockerCli.MeterProvider(ctx) mp := dockerCli.MeterProvider()
defer metricutil.Shutdown(ctx, mp)
ctx, end, err := tracing.TraceCurrentCommand(ctx, "build") ctx, end, err := tracing.TraceCurrentCommand(ctx, "build")
if err != nil { if err != nil {
@@ -365,15 +365,14 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
return errors.Wrap(err, "writing image ID file") return errors.Wrap(err, "writing image ID file")
} }
} }
if options.metadataFile != "" {
if err := writeMetadataFile(options.metadataFile, decodeExporterResponse(resp.ExporterResponse)); err != nil {
return err
}
}
if opts.PrintFunc != nil { if opts.PrintFunc != nil {
if err := printResult(opts.PrintFunc, resp.ExporterResponse); err != nil { if err := printResult(opts.PrintFunc, resp.ExporterResponse); err != nil {
return err return err
} }
} else if options.metadataFile != "" {
if err := writeMetadataFile(options.metadataFile, decodeExporterResponse(resp.ExporterResponse)); err != nil {
return err
}
} }
return nil return nil
} }
@@ -874,6 +873,11 @@ func printResult(f *controllerapi.PrintFunc, res map[string]string) error {
log.Printf("%s %+v", f, res) log.Printf("%s %+v", f, res)
} }
} }
if v, ok := res["result.statuscode"]; !f.IgnoreStatus && ok {
if n, err := strconv.Atoi(v); err == nil && n != 0 {
os.Exit(n)
}
}
return nil return nil
} }

View File

@@ -161,8 +161,9 @@ func RunBuild(ctx context.Context, dockerCli command.Cli, in controllerapi.Build
if in.PrintFunc != nil { if in.PrintFunc != nil {
opts.PrintFunc = &build.PrintFunc{ opts.PrintFunc = &build.PrintFunc{
Name: in.PrintFunc.Name, Name: in.PrintFunc.Name,
Format: in.PrintFunc.Format, Format: in.PrintFunc.Format,
IgnoreStatus: in.PrintFunc.IgnoreStatus,
} }
} }

View File

@@ -813,6 +813,7 @@ func (m *Secret) GetEnv() string {
type PrintFunc struct { type PrintFunc struct {
Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"` Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"`
Format string `protobuf:"bytes,2,opt,name=Format,proto3" json:"Format,omitempty"` Format string `protobuf:"bytes,2,opt,name=Format,proto3" json:"Format,omitempty"`
IgnoreStatus bool `protobuf:"varint,3,opt,name=IgnoreStatus,proto3" json:"IgnoreStatus,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
@@ -856,6 +857,13 @@ func (m *PrintFunc) GetFormat() string {
return "" return ""
} }
func (m *PrintFunc) GetIgnoreStatus() bool {
if m != nil {
return m.IgnoreStatus
}
return false
}
type InspectRequest struct { type InspectRequest struct {
Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
@@ -2086,129 +2094,130 @@ func init() {
func init() { proto.RegisterFile("controller.proto", fileDescriptor_ed7f10298fa1d90f) } func init() { proto.RegisterFile("controller.proto", fileDescriptor_ed7f10298fa1d90f) }
var fileDescriptor_ed7f10298fa1d90f = []byte{ var fileDescriptor_ed7f10298fa1d90f = []byte{
// 1946 bytes of a gzipped FileDescriptorProto // 1960 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x58, 0x5f, 0x53, 0x1b, 0xc9, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x58, 0x5f, 0x73, 0x1b, 0x49,
0x11, 0xcf, 0x4a, 0x42, 0x7f, 0x5a, 0x08, 0xe3, 0x09, 0x76, 0xc6, 0x6b, 0x9f, 0x8d, 0xd7, 0xf6, 0x11, 0x67, 0x25, 0x59, 0x7f, 0x5a, 0x96, 0xcf, 0x19, 0x9c, 0x30, 0xd9, 0xe4, 0x12, 0x67, 0x93,
0x45, 0x15, 0xa7, 0xc4, 0x1d, 0x17, 0x1f, 0xe7, 0xf3, 0x5d, 0x55, 0x40, 0xa0, 0xc0, 0x95, 0x0d, 0x1c, 0x2a, 0x42, 0xc9, 0x77, 0x3e, 0x72, 0xb9, 0x5c, 0xee, 0xaa, 0xb0, 0x65, 0x0b, 0xfb, 0x2a,
0xd4, 0x0a, 0xdb, 0x95, 0xa4, 0x2a, 0xae, 0x45, 0x1a, 0xc4, 0x16, 0xcb, 0x8e, 0xb2, 0x33, 0x12, 0xb1, 0x5d, 0x23, 0x27, 0x29, 0xb8, 0x2a, 0xae, 0x56, 0xd2, 0x58, 0xde, 0xd2, 0x6a, 0x47, 0xec,
0x28, 0x4f, 0x79, 0x48, 0xde, 0x52, 0xf9, 0x1e, 0xa9, 0x7c, 0x84, 0x3c, 0xe5, 0x2d, 0x1f, 0x27, 0x8c, 0x64, 0x8b, 0x27, 0x1e, 0xe0, 0x8d, 0xe2, 0x7b, 0x50, 0x7c, 0x04, 0x9e, 0x78, 0xe3, 0xe3,
0x1f, 0x21, 0x35, 0x3d, 0xb3, 0xab, 0x15, 0xd2, 0x0a, 0xc8, 0x3d, 0x69, 0xba, 0xf7, 0xd7, 0xdd, 0xf0, 0x11, 0xa8, 0xf9, 0xb3, 0xab, 0x5d, 0x4b, 0x2b, 0xdb, 0xf0, 0xa4, 0xe9, 0x9e, 0x5f, 0x77,
0xd3, 0x3d, 0x3d, 0xdd, 0x3d, 0x82, 0xe5, 0x0e, 0x0f, 0x65, 0xc4, 0x83, 0x80, 0x45, 0x8d, 0x7e, 0x4f, 0xf7, 0xf6, 0x74, 0xf7, 0x08, 0xd6, 0xbb, 0x2c, 0x10, 0x21, 0xf3, 0x7d, 0x1a, 0x36, 0x46,
0xc4, 0x25, 0x27, 0x2b, 0xc7, 0x03, 0x3f, 0xe8, 0x5e, 0x36, 0x52, 0x1f, 0x86, 0x5f, 0xda, 0x6f, 0x21, 0x13, 0x0c, 0x6d, 0x74, 0xc6, 0x9e, 0xdf, 0xbb, 0x6c, 0x24, 0x36, 0x26, 0x5f, 0xd8, 0x6f,
0x7a, 0xbe, 0x3c, 0x1d, 0x1c, 0x37, 0x3a, 0xfc, 0x7c, 0xed, 0x9c, 0x1f, 0x8f, 0xd6, 0x10, 0x75, 0xfa, 0x9e, 0x38, 0x1f, 0x77, 0x1a, 0x5d, 0x36, 0xdc, 0x1a, 0xb2, 0xce, 0x74, 0x4b, 0xa1, 0x06,
0xe6, 0xcb, 0x35, 0xaf, 0xef, 0xaf, 0x09, 0x16, 0x0d, 0xfd, 0x0e, 0x13, 0x6b, 0x46, 0x28, 0xfe, 0x9e, 0xd8, 0x72, 0x47, 0xde, 0x16, 0xa7, 0xe1, 0xc4, 0xeb, 0x52, 0xbe, 0x65, 0x84, 0xa2, 0x5f,
0xd5, 0x2a, 0xed, 0x57, 0x99, 0xc2, 0x82, 0x0f, 0xa2, 0x0e, 0xeb, 0xf3, 0xc0, 0xef, 0x8c, 0xd6, 0xad, 0xd2, 0x7e, 0x99, 0x29, 0xcc, 0xd9, 0x38, 0xec, 0xd2, 0x11, 0xf3, 0xbd, 0xee, 0x74, 0x6b,
0xfa, 0xc7, 0x6b, 0x7a, 0xa5, 0xc5, 0x9c, 0x3a, 0xac, 0xbc, 0xf5, 0x85, 0x3c, 0x8c, 0x78, 0x87, 0xd4, 0xd9, 0xd2, 0x2b, 0x2d, 0xe6, 0xd4, 0x61, 0xe3, 0xad, 0xc7, 0xc5, 0x49, 0xc8, 0xba, 0x94,
0x09, 0xc1, 0x84, 0xcb, 0xfe, 0x38, 0x60, 0x42, 0x92, 0x65, 0xc8, 0xbb, 0xec, 0x84, 0x5a, 0xab, 0x73, 0xca, 0x09, 0xfd, 0xc3, 0x98, 0x72, 0x81, 0xd6, 0x21, 0x4f, 0xe8, 0x19, 0xb6, 0x36, 0xad,
0x56, 0xbd, 0xe2, 0xaa, 0xa5, 0x73, 0x08, 0xf7, 0xae, 0x20, 0x45, 0x9f, 0x87, 0x82, 0x91, 0x0d, 0x7a, 0x85, 0xc8, 0xa5, 0x73, 0x02, 0x77, 0xaf, 0x20, 0xf9, 0x88, 0x05, 0x9c, 0xa2, 0x57, 0xb0,
0x58, 0xd8, 0x0b, 0x4f, 0xb8, 0xa0, 0xd6, 0x6a, 0xbe, 0x5e, 0x5d, 0x7f, 0xda, 0x98, 0xe5, 0x5c, 0x72, 0x18, 0x9c, 0x31, 0x8e, 0xad, 0xcd, 0x7c, 0xbd, 0xba, 0xfd, 0xa4, 0xb1, 0xc8, 0xb9, 0x86,
0xc3, 0xc8, 0x29, 0xa4, 0xab, 0xf1, 0x8e, 0x80, 0x6a, 0x8a, 0x4b, 0x1e, 0x41, 0x25, 0x26, 0xb7, 0x91, 0x93, 0x48, 0xa2, 0xf1, 0x0e, 0x87, 0x6a, 0x82, 0x8b, 0x1e, 0x42, 0x25, 0x22, 0xf7, 0x8c,
0x8d, 0xe1, 0x31, 0x83, 0xb4, 0x60, 0x71, 0x2f, 0x1c, 0xf2, 0x33, 0xd6, 0xe4, 0xe1, 0x89, 0xdf, 0xe1, 0x19, 0x03, 0xb5, 0x60, 0xf5, 0x30, 0x98, 0xb0, 0x01, 0x6d, 0xb2, 0xe0, 0xcc, 0xeb, 0xe3,
0xa3, 0xb9, 0x55, 0xab, 0x5e, 0x5d, 0x77, 0x66, 0x1b, 0x4b, 0x23, 0xdd, 0x09, 0x39, 0xe7, 0x07, 0xdc, 0xa6, 0x55, 0xaf, 0x6e, 0x3b, 0x8b, 0x8d, 0x25, 0x91, 0x24, 0x25, 0xe7, 0x7c, 0x0f, 0x78,
0xa0, 0xdb, 0xbe, 0xe8, 0xf0, 0x30, 0x64, 0x9d, 0xd8, 0x99, 0x4c, 0xa7, 0x27, 0xf7, 0x94, 0xbb, 0xcf, 0xe3, 0x5d, 0x16, 0x04, 0xb4, 0x1b, 0x39, 0x93, 0xe9, 0x74, 0xfa, 0x4c, 0xb9, 0x2b, 0x67,
0xb2, 0x27, 0xe7, 0x21, 0x3c, 0x98, 0xa1, 0x4b, 0x87, 0xc5, 0xf9, 0x03, 0x2c, 0x6e, 0xa9, 0xbd, 0x72, 0x1e, 0xc0, 0xfd, 0x05, 0xba, 0x74, 0x58, 0x9c, 0xdf, 0xc3, 0xea, 0xae, 0x3c, 0x5b, 0xb6,
0x65, 0x2b, 0xff, 0x0e, 0x4a, 0x07, 0x7d, 0xe9, 0xf3, 0x50, 0xcc, 0xf7, 0x06, 0xd5, 0x18, 0xa4, 0xf2, 0x6f, 0xa1, 0x74, 0x3c, 0x12, 0x1e, 0x0b, 0xf8, 0x72, 0x6f, 0x94, 0x1a, 0x83, 0x24, 0x91,
0x1b, 0x8b, 0x38, 0xff, 0x59, 0x34, 0x06, 0x0c, 0x83, 0xac, 0x42, 0xb5, 0xc9, 0x43, 0xc9, 0x2e, 0x88, 0xf3, 0xef, 0x55, 0x63, 0xc0, 0x30, 0xd0, 0x26, 0x54, 0x9b, 0x2c, 0x10, 0xf4, 0x52, 0x9c,
0xe5, 0xa1, 0x27, 0x4f, 0x8d, 0xa1, 0x34, 0x8b, 0x7c, 0x0e, 0x4b, 0xdb, 0xbc, 0x73, 0xc6, 0xa2, 0xb8, 0xe2, 0xdc, 0x18, 0x4a, 0xb2, 0xd0, 0x67, 0xb0, 0xb6, 0xc7, 0xba, 0x03, 0x1a, 0x9e, 0x79,
0x13, 0x3f, 0x60, 0xfb, 0xde, 0x39, 0x33, 0x2e, 0x5d, 0xe1, 0x92, 0xef, 0x95, 0xd7, 0x7e, 0x28, 0x3e, 0x3d, 0x72, 0x87, 0xd4, 0xb8, 0x74, 0x85, 0x8b, 0xbe, 0x93, 0x5e, 0x7b, 0x81, 0x68, 0x8d,
0x5b, 0x83, 0xb0, 0x43, 0xf3, 0xb8, 0xb5, 0x27, 0x59, 0xa7, 0x6a, 0x60, 0xee, 0x58, 0x82, 0xfc, 0x83, 0x2e, 0xce, 0xab, 0xa3, 0x3d, 0xce, 0xfa, 0xaa, 0x06, 0x46, 0x66, 0x12, 0xe8, 0x07, 0xa8,
0x1e, 0x6a, 0x4a, 0x4d, 0xd7, 0x98, 0x16, 0xb4, 0x80, 0x89, 0xf1, 0xea, 0x7a, 0xef, 0x1a, 0x13, 0x49, 0x35, 0x3d, 0x63, 0x9a, 0xe3, 0x82, 0x4a, 0x8c, 0x97, 0xd7, 0x7b, 0xd7, 0x48, 0xc9, 0xed,
0x72, 0x3b, 0xa1, 0x8c, 0x46, 0xee, 0xa4, 0x2e, 0xb2, 0x02, 0x0b, 0x9b, 0x41, 0xc0, 0x2f, 0xe8, 0x07, 0x22, 0x9c, 0x92, 0xb4, 0x2e, 0xb4, 0x01, 0x2b, 0x3b, 0xbe, 0xcf, 0x2e, 0xf0, 0xca, 0x66,
0xc2, 0x6a, 0xbe, 0x5e, 0x71, 0x35, 0x41, 0xbe, 0x86, 0xd2, 0xa6, 0x94, 0x4c, 0x48, 0x41, 0x8b, 0xbe, 0x5e, 0x21, 0x9a, 0x40, 0x5f, 0x41, 0x69, 0x47, 0x08, 0xca, 0x05, 0xc7, 0x45, 0x65, 0xec,
0x68, 0xec, 0xd1, 0x6c, 0x63, 0x1a, 0xe4, 0xc6, 0x60, 0x72, 0x00, 0x15, 0xb4, 0xbf, 0x19, 0xf5, 0xe1, 0x62, 0x63, 0x1a, 0x44, 0x22, 0x30, 0x3a, 0x86, 0x8a, 0xb2, 0xbf, 0x13, 0xf6, 0x39, 0x2e,
0x04, 0x2d, 0xa1, 0xe4, 0x97, 0x37, 0xd8, 0x66, 0x22, 0xa3, 0xb7, 0x38, 0xd6, 0x41, 0x76, 0xa0, 0x29, 0xc9, 0x2f, 0x6e, 0x70, 0xcc, 0x58, 0x46, 0x1f, 0x71, 0xa6, 0x03, 0xed, 0x43, 0xa5, 0xe9,
0xd2, 0xf4, 0x3a, 0xa7, 0xac, 0x15, 0xf1, 0x73, 0x5a, 0x46, 0x85, 0x3f, 0x9f, 0xad, 0x10, 0x61, 0x76, 0xcf, 0x69, 0x2b, 0x64, 0x43, 0x5c, 0x56, 0x0a, 0x7f, 0xbe, 0x58, 0xa1, 0x82, 0x19, 0x85,
0x46, 0xa1, 0x51, 0x93, 0x48, 0x92, 0x4d, 0x28, 0x21, 0x71, 0xc4, 0x69, 0xe5, 0x76, 0x4a, 0x62, 0x46, 0x4d, 0x2c, 0x89, 0x76, 0xa0, 0xa4, 0x88, 0x53, 0x86, 0x2b, 0xb7, 0x53, 0x12, 0xc9, 0x21,
0x39, 0xe2, 0xc0, 0x62, 0xb3, 0x17, 0xf1, 0x41, 0xff, 0xd0, 0x8b, 0x58, 0x28, 0x29, 0xe0, 0x51, 0x07, 0x56, 0x9b, 0xfd, 0x90, 0x8d, 0x47, 0x27, 0x6e, 0x48, 0x03, 0x81, 0x41, 0x7d, 0xea, 0x14,
0x4f, 0xf0, 0xc8, 0x1b, 0x28, 0xed, 0x5c, 0xf6, 0x79, 0x24, 0x05, 0xad, 0xce, 0xbb, 0xbc, 0x1a, 0x0f, 0xbd, 0x81, 0xd2, 0xfe, 0xe5, 0x88, 0x85, 0x82, 0xe3, 0xea, 0xb2, 0xcb, 0xab, 0x41, 0xc6,
0x64, 0x0c, 0x18, 0x09, 0xf2, 0x18, 0x60, 0xe7, 0x52, 0x46, 0xde, 0x2e, 0x57, 0x61, 0x5f, 0xc4, 0x80, 0x91, 0x40, 0x8f, 0x00, 0xf6, 0x2f, 0x45, 0xe8, 0x1e, 0x30, 0x19, 0xf6, 0x55, 0xf5, 0x39,
0xe3, 0x48, 0x71, 0x48, 0x0b, 0x8a, 0x6f, 0xbd, 0x63, 0x16, 0x08, 0x5a, 0x43, 0xdd, 0x8d, 0x1b, 0x12, 0x1c, 0xd4, 0x82, 0xe2, 0x5b, 0xb7, 0x43, 0x7d, 0x8e, 0x6b, 0x4a, 0x77, 0xe3, 0x06, 0x81,
0x04, 0x56, 0x0b, 0x68, 0x43, 0x46, 0x5a, 0xe5, 0xf5, 0x3e, 0x93, 0x17, 0x3c, 0x3a, 0x7b, 0xc7, 0xd5, 0x02, 0xda, 0x90, 0x91, 0x96, 0x79, 0x7d, 0x44, 0xc5, 0x05, 0x0b, 0x07, 0xef, 0x58, 0x8f,
0xbb, 0x8c, 0x2e, 0xe9, 0xbc, 0x4e, 0xb1, 0xc8, 0x73, 0xa8, 0xed, 0x73, 0x1d, 0x3c, 0x3f, 0x90, 0xe2, 0x35, 0x9d, 0xd7, 0x09, 0x16, 0x7a, 0x06, 0xb5, 0x23, 0xa6, 0x83, 0xe7, 0xf9, 0x82, 0x86,
0x2c, 0xa2, 0x77, 0x70, 0x33, 0x93, 0x4c, 0xbc, 0xcb, 0x81, 0x27, 0x4f, 0x78, 0x74, 0x2e, 0xe8, 0xf8, 0x13, 0x75, 0x98, 0x34, 0x53, 0xdd, 0x65, 0xdf, 0x15, 0x67, 0x2c, 0x1c, 0x72, 0xbc, 0xae,
0x32, 0x22, 0xc6, 0x0c, 0x95, 0x41, 0x6d, 0xd6, 0x89, 0x98, 0x14, 0xf4, 0xee, 0xbc, 0x0c, 0xd2, 0x10, 0x33, 0x86, 0xcc, 0xa0, 0x36, 0xed, 0x86, 0x54, 0x70, 0x7c, 0x67, 0x59, 0x06, 0x69, 0x10,
0x20, 0x37, 0x06, 0x13, 0x0a, 0xa5, 0xf6, 0xe9, 0x79, 0xdb, 0xff, 0x13, 0xa3, 0x64, 0xd5, 0xaa, 0x89, 0xc0, 0x08, 0x43, 0xa9, 0x7d, 0x3e, 0x6c, 0x7b, 0x7f, 0xa4, 0x18, 0x6d, 0x5a, 0xf5, 0x3c,
0xe7, 0xdd, 0x98, 0x24, 0x2f, 0x21, 0xdf, 0x6e, 0xef, 0xd2, 0x9f, 0xa2, 0xb6, 0x07, 0x19, 0xda, 0x89, 0x48, 0xf4, 0x02, 0xf2, 0xed, 0xf6, 0x01, 0xfe, 0xa9, 0xd2, 0x76, 0x3f, 0x43, 0x5b, 0xfb,
0xda, 0xbb, 0xae, 0x42, 0x11, 0x02, 0x85, 0x23, 0xaf, 0x27, 0xe8, 0x0a, 0xee, 0x0b, 0xd7, 0xe4, 0x80, 0x48, 0x14, 0x42, 0x50, 0x38, 0x75, 0xfb, 0x1c, 0x6f, 0xa8, 0x73, 0xa9, 0x35, 0xba, 0x07,
0x3e, 0x14, 0x8f, 0xbc, 0xa8, 0xc7, 0x24, 0xbd, 0x87, 0x3e, 0x1b, 0x8a, 0xbc, 0x86, 0xd2, 0xfb, 0xc5, 0x53, 0x37, 0xec, 0x53, 0x81, 0xef, 0x2a, 0x9f, 0x0d, 0x85, 0x5e, 0x43, 0xe9, 0xbd, 0xef,
0xc0, 0x3f, 0xf7, 0xa5, 0xa0, 0xf7, 0xe7, 0x5d, 0x4e, 0x0d, 0x3a, 0xe8, 0x4b, 0x37, 0xc6, 0xab, 0x0d, 0x3d, 0xc1, 0xf1, 0xbd, 0x65, 0x97, 0x53, 0x83, 0x8e, 0x47, 0x82, 0x44, 0x78, 0x79, 0x5a,
0xdd, 0x62, 0xbc, 0x59, 0x44, 0x7f, 0x86, 0x3a, 0x63, 0x52, 0x7d, 0x31, 0xe1, 0xa2, 0x74, 0xd5, 0x15, 0x6f, 0x1a, 0xe2, 0x9f, 0x29, 0x9d, 0x11, 0x29, 0x77, 0x4c, 0xb8, 0x30, 0xde, 0xb4, 0xea,
0xaa, 0x97, 0xdd, 0x98, 0x54, 0x5b, 0x3b, 0x1c, 0x04, 0x01, 0x7d, 0x80, 0x6c, 0x5c, 0xeb, 0xb3, 0x65, 0x12, 0x91, 0xf2, 0x68, 0x27, 0x63, 0xdf, 0xc7, 0xf7, 0x15, 0x5b, 0xad, 0xf5, 0xb7, 0x97,
0x57, 0x69, 0x70, 0x38, 0x10, 0xa7, 0xd4, 0xc6, 0x2f, 0x29, 0xce, 0xf8, 0xfb, 0x5b, 0xee, 0x75, 0x69, 0x70, 0x32, 0xe6, 0xe7, 0xd8, 0x56, 0x3b, 0x09, 0xce, 0x6c, 0xff, 0x2d, 0x73, 0x7b, 0xf8,
0xe9, 0xc3, 0xf4, 0x77, 0xc5, 0x21, 0x7b, 0xb0, 0xd8, 0xc6, 0xb6, 0x74, 0x88, 0xcd, 0x88, 0x3e, 0x41, 0x72, 0x5f, 0x72, 0xd0, 0x21, 0xac, 0xb6, 0x55, 0x5b, 0x3a, 0x51, 0xcd, 0x08, 0x3f, 0x54,
0x42, 0x3f, 0x5e, 0x34, 0x54, 0xe7, 0x6a, 0xc4, 0x9d, 0x4b, 0xf9, 0x90, 0x6e, 0x5e, 0x0d, 0x0d, 0x7e, 0x3c, 0x6f, 0xc8, 0xce, 0xd5, 0x88, 0x3a, 0x97, 0xf4, 0x21, 0xd9, 0xbc, 0x1a, 0x1a, 0x4c,
0x76, 0x27, 0x44, 0xe3, 0xba, 0xfa, 0xd9, 0xb8, 0xae, 0xda, 0x50, 0xfe, 0x8d, 0x4a, 0x72, 0xc5, 0x52, 0xa2, 0x51, 0x5d, 0xfd, 0x74, 0x56, 0x57, 0x6d, 0x28, 0xff, 0x46, 0x26, 0xb9, 0x64, 0x3f,
0x7e, 0x8c, 0xec, 0x84, 0x56, 0xc9, 0xb4, 0x19, 0x86, 0x5c, 0x7a, 0xba, 0xee, 0x3e, 0xc1, 0x70, 0x52, 0xec, 0x98, 0x96, 0xc9, 0xb4, 0x13, 0x04, 0x4c, 0xb8, 0xba, 0xee, 0x3e, 0x56, 0xe1, 0x4e,
0xa7, 0x59, 0xe4, 0x6b, 0xb8, 0xff, 0xd1, 0x97, 0xa7, 0x87, 0x11, 0x1f, 0xb2, 0xd0, 0x0b, 0x3b, 0xb2, 0xd0, 0x57, 0x70, 0xef, 0xa3, 0x27, 0xce, 0x4f, 0x42, 0x36, 0xa1, 0x81, 0x1b, 0x74, 0x69,
0x2c, 0xae, 0xe8, 0x74, 0x15, 0xdd, 0xc8, 0xf8, 0x6a, 0xff, 0x1a, 0xc8, 0x74, 0xf5, 0x52, 0xbb, 0x54, 0xd1, 0xf1, 0xa6, 0x72, 0x23, 0x63, 0xd7, 0xfe, 0x35, 0xa0, 0xf9, 0xea, 0x25, 0x4f, 0x37,
0x3b, 0x63, 0xa3, 0xb8, 0xea, 0x9f, 0xb1, 0x91, 0x2a, 0x60, 0x43, 0x2f, 0x18, 0xc4, 0xb5, 0x57, 0xa0, 0xd3, 0xa8, 0xea, 0x0f, 0xe8, 0x54, 0x16, 0xb0, 0x89, 0xeb, 0x8f, 0xa3, 0xda, 0xab, 0x89,
0x13, 0xdf, 0xe6, 0xbe, 0xb1, 0xec, 0xef, 0x60, 0x69, 0xb2, 0xb0, 0xdc, 0x4a, 0xfa, 0x35, 0x54, 0x6f, 0x72, 0x5f, 0x5b, 0xf6, 0xb7, 0xb0, 0x96, 0x2e, 0x2c, 0xb7, 0x92, 0x7e, 0x0d, 0xd5, 0xc4,
0x53, 0xb7, 0xe7, 0x36, 0xa2, 0xce, 0xbf, 0x2d, 0xa8, 0xa6, 0xae, 0x38, 0x26, 0xe3, 0xa8, 0xcf, 0xed, 0xb9, 0x8d, 0xa8, 0xf3, 0x2f, 0x0b, 0xaa, 0x89, 0x2b, 0xae, 0x92, 0x71, 0x3a, 0xa2, 0x46,
0x8c, 0x30, 0xae, 0xc9, 0x16, 0x2c, 0x6c, 0x4a, 0x19, 0xa9, 0x56, 0xa5, 0xf2, 0xf9, 0x97, 0xd7, 0x58, 0xad, 0xd1, 0x2e, 0xac, 0xec, 0x08, 0x11, 0xca, 0x56, 0x25, 0xf3, 0xf9, 0x97, 0xd7, 0x16,
0x16, 0x8a, 0x06, 0xc2, 0xf5, 0x55, 0xd6, 0xa2, 0x2a, 0xf8, 0xdb, 0x4c, 0x48, 0x3f, 0xc4, 0x50, 0x8a, 0x86, 0x82, 0xeb, 0xab, 0xac, 0x45, 0x65, 0xf0, 0xf7, 0x28, 0x17, 0x5e, 0xa0, 0x42, 0xad,
0x63, 0x67, 0xa9, 0xb8, 0x69, 0x96, 0xfd, 0x0d, 0xc0, 0x58, 0xec, 0x56, 0x3e, 0xfc, 0xd3, 0x82, 0x3a, 0x4b, 0x85, 0x24, 0x59, 0xf6, 0xd7, 0x00, 0x33, 0xb1, 0x5b, 0xf9, 0xf0, 0x0f, 0x0b, 0xee,
0xbb, 0x53, 0xd5, 0x70, 0xa6, 0x27, 0xbb, 0x93, 0x9e, 0xac, 0xdf, 0xb0, 0xb2, 0x4e, 0xfb, 0xf3, 0xcc, 0x55, 0xc3, 0x85, 0x9e, 0x1c, 0xa4, 0x3d, 0xd9, 0xbe, 0x61, 0x65, 0x9d, 0xf7, 0xe7, 0xff,
0x23, 0x76, 0xbb, 0x0f, 0x45, 0xdd, 0x82, 0x66, 0xee, 0xd0, 0x86, 0xf2, 0xb6, 0x2f, 0xbc, 0xe3, 0x38, 0xed, 0x11, 0x14, 0x75, 0x0b, 0x5a, 0x78, 0x42, 0x1b, 0xca, 0x7b, 0x1e, 0x77, 0x3b, 0x3e,
0x80, 0x75, 0x51, 0xb4, 0xec, 0x26, 0x34, 0xf6, 0x3f, 0xdc, 0xbd, 0x8e, 0x9e, 0x26, 0x1c, 0x5d, 0xed, 0x29, 0xd1, 0x32, 0x89, 0x69, 0xd5, 0xff, 0xd4, 0xe9, 0x75, 0xf4, 0x34, 0xe1, 0xe8, 0x5a,
0x6b, 0xc8, 0x12, 0xe4, 0x92, 0xd9, 0x29, 0xb7, 0xb7, 0xad, 0xc0, 0xaa, 0xf1, 0x6b, 0x57, 0x2b, 0x83, 0xd6, 0x20, 0x17, 0xcf, 0x4e, 0xb9, 0xc3, 0x3d, 0x09, 0x96, 0x8d, 0x5f, 0xbb, 0x5a, 0x21,
0xae, 0x26, 0x9c, 0x16, 0x14, 0x75, 0xf5, 0x9a, 0xc2, 0xdb, 0x50, 0x6e, 0xf9, 0x01, 0xc3, 0xf9, 0x9a, 0x70, 0x5a, 0x50, 0xd4, 0xd5, 0x6b, 0x0e, 0x6f, 0x43, 0xb9, 0xe5, 0xf9, 0x54, 0xcd, 0x0f,
0x41, 0xef, 0x39, 0xa1, 0x95, 0x7b, 0x3b, 0xe1, 0xd0, 0x98, 0x55, 0x4b, 0x67, 0x23, 0x35, 0x26, 0xfa, 0xcc, 0x31, 0x2d, 0xdd, 0xdb, 0x0f, 0x26, 0xc6, 0xac, 0x5c, 0x3a, 0x3f, 0x24, 0xc6, 0x04,
0x28, 0x3f, 0x70, 0xa2, 0x30, 0x7e, 0xe0, 0x1c, 0x71, 0x1f, 0x8a, 0x2d, 0x1e, 0x9d, 0x7b, 0xd2, 0xe9, 0x87, 0x9a, 0x28, 0x8c, 0x1f, 0x6a, 0x8e, 0xb8, 0x07, 0xc5, 0x16, 0x0b, 0x87, 0xae, 0x30,
0x28, 0x33, 0x94, 0xe3, 0xc0, 0xd2, 0x5e, 0x28, 0xfa, 0xac, 0x23, 0xb3, 0xc7, 0xcd, 0x03, 0xb8, 0xca, 0x0c, 0x25, 0x5b, 0xd3, 0x61, 0x3f, 0x60, 0x21, 0x6d, 0x0b, 0x57, 0x8c, 0xb5, 0x2b, 0x65,
0x93, 0x60, 0xcc, 0xa0, 0x99, 0x9a, 0x97, 0xac, 0xdb, 0xcf, 0x4b, 0xff, 0xb0, 0xa0, 0x92, 0x54, 0x92, 0xe2, 0x39, 0x0e, 0xac, 0x1d, 0x06, 0x7c, 0x44, 0xbb, 0x22, 0x7b, 0x24, 0x3d, 0x86, 0x4f,
0x44, 0xd2, 0x84, 0x22, 0x9e, 0x46, 0x3c, 0xb5, 0xbe, 0xbc, 0xa6, 0x84, 0x36, 0x3e, 0x20, 0xda, 0x62, 0x8c, 0x19, 0x46, 0x13, 0x33, 0x95, 0x75, 0xfb, 0x99, 0xea, 0xef, 0x16, 0x54, 0xe2, 0xaa,
0x74, 0x26, 0x2d, 0x6a, 0x7f, 0x84, 0x6a, 0x8a, 0x3d, 0x23, 0x01, 0xd6, 0xd3, 0x09, 0x90, 0xd9, 0x89, 0x9a, 0x50, 0x54, 0x5f, 0x2c, 0x9a, 0x6c, 0x5f, 0x5c, 0x53, 0x66, 0x1b, 0x1f, 0x14, 0xda,
0x52, 0xb4, 0x91, 0x74, 0x7a, 0x6c, 0x43, 0x51, 0x33, 0x67, 0x86, 0x95, 0x40, 0x61, 0xd7, 0x8b, 0x74, 0x2f, 0x2d, 0x6a, 0x7f, 0x84, 0x6a, 0x82, 0xbd, 0x20, 0x49, 0xb6, 0x93, 0x49, 0x92, 0xd9,
0x74, 0x6a, 0xe4, 0x5d, 0x5c, 0x2b, 0x5e, 0x9b, 0x9f, 0x48, 0x3c, 0x9e, 0xbc, 0x8b, 0x6b, 0xe7, 0x76, 0xb4, 0x91, 0x64, 0x0a, 0xed, 0x41, 0x51, 0x33, 0x17, 0x86, 0x1e, 0x41, 0xe1, 0xc0, 0x0d,
0x5f, 0x16, 0xd4, 0xcc, 0x08, 0x6a, 0x22, 0xc8, 0x60, 0x59, 0xdf, 0x50, 0x16, 0x25, 0x55, 0x4d, 0x75, 0xfa, 0xe4, 0x89, 0x5a, 0x4b, 0x5e, 0x9b, 0x9d, 0x09, 0x15, 0xee, 0x3c, 0x51, 0x6b, 0xe7,
0xfb, 0xff, 0x7a, 0x4e, 0x28, 0x63, 0x68, 0xe3, 0xaa, 0xac, 0x8e, 0xc6, 0x94, 0x4a, 0xbb, 0x09, 0x9f, 0x16, 0xd4, 0xcc, 0x98, 0x6a, 0x22, 0x48, 0x61, 0x5d, 0xdf, 0x62, 0x1a, 0xc6, 0x95, 0x4f,
0xf7, 0x66, 0x42, 0x6f, 0x75, 0x45, 0x5e, 0xc0, 0xdd, 0xf1, 0x70, 0x9d, 0x9d, 0x27, 0x2b, 0x40, 0xfb, 0xff, 0x7a, 0x49, 0x28, 0x23, 0x68, 0xe3, 0xaa, 0xac, 0x8e, 0xc6, 0x9c, 0x4a, 0xbb, 0x09,
0xd2, 0x30, 0x33, 0x7c, 0x3f, 0x81, 0xaa, 0x7a, 0xac, 0x64, 0x8b, 0x39, 0xb0, 0xa8, 0x01, 0x26, 0x77, 0x17, 0x42, 0x6f, 0x75, 0x8d, 0x9e, 0xc3, 0x9d, 0xd9, 0x00, 0x9e, 0x9d, 0x27, 0x1b, 0x80,
0x32, 0x04, 0x0a, 0x67, 0x6c, 0xa4, 0xb3, 0xa1, 0xe2, 0xe2, 0xda, 0xf9, 0xbb, 0xa5, 0xde, 0x1c, 0x92, 0x30, 0x33, 0xa0, 0x3f, 0x86, 0xaa, 0x7c, 0xd0, 0x64, 0x8b, 0x39, 0xb0, 0xaa, 0x01, 0x26,
0xfd, 0x81, 0x7c, 0xc7, 0x84, 0xf0, 0x7a, 0x2a, 0x01, 0x0b, 0x7b, 0xa1, 0x2f, 0x4d, 0xf6, 0x7d, 0x32, 0x08, 0x0a, 0x03, 0x3a, 0xd5, 0xd9, 0x50, 0x21, 0x6a, 0xed, 0xfc, 0xcd, 0x92, 0xef, 0x92,
0x9e, 0xf5, 0xf6, 0xe8, 0x0f, 0xa4, 0x82, 0x19, 0xa9, 0xdd, 0x9f, 0xb8, 0x28, 0x45, 0x36, 0xa0, 0xd1, 0x58, 0xbc, 0xa3, 0x9c, 0xbb, 0x7d, 0x99, 0x80, 0x85, 0xc3, 0xc0, 0x13, 0x26, 0xfb, 0x3e,
0xb0, 0xed, 0x49, 0xcf, 0xe4, 0x42, 0xc6, 0xa4, 0xa5, 0x10, 0x29, 0x41, 0x45, 0x6e, 0x95, 0xd4, 0xcb, 0x7a, 0x9f, 0x8c, 0xc6, 0x42, 0xc2, 0x8c, 0xd4, 0xc1, 0x4f, 0x88, 0x92, 0x42, 0xaf, 0xa0,
0x03, 0xab, 0x3f, 0x90, 0xce, 0x73, 0x58, 0xbe, 0xaa, 0x7d, 0x86, 0x6b, 0x5f, 0x41, 0x35, 0xa5, 0xb0, 0xe7, 0x0a, 0xd7, 0xe4, 0x42, 0xc6, 0x34, 0x26, 0x11, 0x09, 0x41, 0x49, 0xee, 0x96, 0xe4,
0x05, 0xef, 0xed, 0x41, 0x0b, 0x01, 0x65, 0x57, 0x2d, 0x95, 0xaf, 0xc9, 0x46, 0x16, 0xb5, 0x0d, 0x23, 0x6c, 0x34, 0x16, 0xce, 0x33, 0x58, 0xbf, 0xaa, 0x7d, 0x81, 0x6b, 0x5f, 0x42, 0x35, 0xa1,
0xe7, 0x0e, 0xd4, 0x50, 0x75, 0x12, 0xc1, 0x3f, 0xe7, 0xa0, 0x14, 0xab, 0xd8, 0x98, 0xf0, 0xfb, 0x45, 0xdd, 0xed, 0xe3, 0x96, 0x02, 0x94, 0x89, 0x5c, 0x4a, 0x5f, 0xe3, 0x83, 0xac, 0x6a, 0x1b,
0x69, 0x96, 0xdf, 0xd3, 0x2e, 0xbf, 0x82, 0x82, 0xaa, 0x1f, 0xc6, 0xe5, 0x8c, 0x31, 0xa5, 0xd5, 0xce, 0x27, 0x50, 0x53, 0xaa, 0xe3, 0x08, 0xfe, 0x29, 0x07, 0xa5, 0x48, 0xc5, 0xab, 0x94, 0xdf,
0x4d, 0x89, 0x29, 0x38, 0xf9, 0x1e, 0x8a, 0x2e, 0x13, 0x6a, 0xa4, 0xd2, 0x8f, 0x8f, 0x67, 0xb3, 0x4f, 0xb2, 0xfc, 0x9e, 0x77, 0xf9, 0x25, 0x14, 0x64, 0x8d, 0x31, 0x2e, 0x67, 0x8c, 0x32, 0xad,
0x05, 0x35, 0x66, 0x2c, 0x6c, 0x84, 0x94, 0x78, 0xdb, 0xef, 0x85, 0x5e, 0x40, 0x0b, 0xf3, 0xc4, 0x5e, 0x42, 0x4c, 0xc2, 0xd1, 0x77, 0x50, 0x24, 0x94, 0xcb, 0xb1, 0x4b, 0x3f, 0x50, 0x9e, 0x2e,
0x35, 0x26, 0x25, 0xae, 0x19, 0xe3, 0x70, 0xff, 0xd5, 0x82, 0xea, 0xdc, 0x50, 0xcf, 0x7f, 0x1e, 0x16, 0xd4, 0x98, 0x99, 0xb0, 0x11, 0x92, 0xe2, 0x6d, 0xaf, 0x1f, 0xb8, 0x3e, 0x2e, 0x2c, 0x13,
0x4e, 0x3d, 0x59, 0xf3, 0xff, 0xe7, 0x93, 0xf5, 0x2f, 0xb9, 0x49, 0x45, 0x38, 0x5d, 0xa9, 0xfb, 0xd7, 0x98, 0x84, 0xb8, 0x66, 0xcc, 0xc2, 0xfd, 0x17, 0x0b, 0xaa, 0x4b, 0x43, 0xbd, 0xfc, 0x09,
0xd4, 0xe7, 0x7e, 0x28, 0x4d, 0xca, 0xa6, 0x38, 0x6a, 0xa3, 0xcd, 0xf3, 0xae, 0x29, 0xfa, 0x6a, 0x39, 0xf7, 0xac, 0xcd, 0xff, 0x8f, 0xcf, 0xda, 0x3f, 0xe7, 0xd2, 0x8a, 0xd4, 0x04, 0x26, 0xef,
0xa9, 0xae, 0xd9, 0x3e, 0x57, 0xbc, 0x2a, 0xa6, 0x81, 0x26, 0xc6, 0x25, 0x3d, 0x6f, 0x4a, 0xba, 0xd3, 0x88, 0x79, 0x81, 0x30, 0x29, 0x9b, 0xe0, 0xc8, 0x83, 0x36, 0x87, 0x3d, 0xd3, 0x18, 0xe4,
0x4a, 0x8d, 0xf7, 0x82, 0x45, 0x18, 0xb8, 0x8a, 0x8b, 0x6b, 0x55, 0xc5, 0xf7, 0x39, 0x72, 0x17, 0x52, 0x5e, 0xb3, 0x23, 0x26, 0x79, 0x55, 0x95, 0x06, 0x9a, 0x98, 0x95, 0xfd, 0xbc, 0x29, 0xfb,
0x50, 0xd8, 0x50, 0x68, 0xe5, 0xa2, 0x4b, 0x8b, 0x3a, 0x1c, 0xcd, 0x8b, 0xd8, 0xca, 0x45, 0x97, 0x32, 0x35, 0xde, 0x73, 0x1a, 0xaa, 0xc0, 0x55, 0x88, 0x5a, 0xcb, 0x4a, 0x7f, 0xc4, 0x14, 0x77,
0x96, 0x12, 0x2b, 0x17, 0x68, 0xe5, 0x48, 0x8e, 0x68, 0x59, 0x27, 0xe0, 0x91, 0x1c, 0xa9, 0x36, 0x45, 0x09, 0x1b, 0x4a, 0x59, 0xb9, 0xe8, 0xe1, 0xa2, 0x0e, 0x47, 0xf3, 0x22, 0xb2, 0x72, 0xd1,
0xe3, 0xf2, 0x20, 0x38, 0xf6, 0x3a, 0x67, 0xb4, 0xa2, 0xfb, 0x5b, 0x4c, 0xab, 0x39, 0x54, 0xc5, 0xc3, 0xa5, 0xd8, 0xca, 0x85, 0xb2, 0x72, 0x2a, 0xa6, 0xb8, 0xac, 0x13, 0xf0, 0x54, 0x4c, 0x65,
0xdc, 0xf7, 0x02, 0x7c, 0xb1, 0x94, 0xdd, 0x98, 0x74, 0x36, 0xa1, 0x92, 0xa4, 0x8a, 0xea, 0x5c, 0x2b, 0x22, 0xcc, 0xf7, 0x3b, 0x6e, 0x77, 0x80, 0x2b, 0xba, 0x07, 0x46, 0xb4, 0x9c, 0x55, 0x65,
0xad, 0x2e, 0x1e, 0x45, 0xcd, 0xcd, 0xb5, 0xba, 0x71, 0x96, 0xe7, 0xa6, 0xb3, 0x3c, 0x9f, 0xca, 0xcc, 0x3d, 0xd7, 0x57, 0xaf, 0x9a, 0x32, 0x89, 0x48, 0x67, 0x07, 0x2a, 0x71, 0xaa, 0xc8, 0xee,
0xf2, 0x0d, 0xa8, 0x4d, 0x24, 0x8d, 0x02, 0xb9, 0xfc, 0x42, 0x18, 0x45, 0xb8, 0x56, 0xbc, 0x26, 0xd6, 0xea, 0xa9, 0x4f, 0x51, 0x23, 0xb9, 0x56, 0x2f, 0xca, 0xf2, 0xdc, 0x7c, 0x96, 0xe7, 0x13,
0x0f, 0xf4, 0x9b, 0xbc, 0xe6, 0xe2, 0xda, 0x79, 0x06, 0xb5, 0x89, 0x74, 0x99, 0x55, 0x97, 0x9d, 0x59, 0xfe, 0x0a, 0x6a, 0xa9, 0xa4, 0x91, 0x20, 0xc2, 0x2e, 0xb8, 0x51, 0xa4, 0xd6, 0x92, 0xd7,
0xa7, 0x50, 0x6b, 0x4b, 0x4f, 0x0e, 0xe6, 0xfc, 0x89, 0xf2, 0x5f, 0x0b, 0x96, 0x62, 0x8c, 0xa9, 0x64, 0xbe, 0x7e, 0xb7, 0xd7, 0x88, 0x5a, 0x3b, 0x4f, 0xa1, 0x96, 0x4a, 0x97, 0x45, 0x75, 0xd9,
0x3c, 0xbf, 0x82, 0xf2, 0x90, 0x45, 0x92, 0x5d, 0x26, 0xbd, 0x88, 0x4e, 0x8f, 0xc1, 0x1f, 0x10, 0x79, 0x02, 0x35, 0xdd, 0xe0, 0xb2, 0xcb, 0xce, 0x7f, 0x2c, 0x58, 0x8b, 0x30, 0xa6, 0xf2, 0xfc,
0xe1, 0x26, 0x48, 0xf2, 0x2d, 0x94, 0x05, 0xea, 0x61, 0xf1, 0x1c, 0xf3, 0x38, 0x4b, 0xca, 0xd8, 0x0a, 0xca, 0x13, 0x1a, 0x0a, 0x7a, 0x19, 0xf7, 0x22, 0x3c, 0x3f, 0x2a, 0x7f, 0x50, 0x08, 0x12,
0x4b, 0xf0, 0x64, 0x0d, 0x0a, 0x01, 0xef, 0x09, 0x3c, 0xf7, 0xea, 0xfa, 0xc3, 0x2c, 0xb9, 0xb7, 0x23, 0xd1, 0x37, 0x50, 0xe6, 0x4a, 0x0f, 0x8d, 0x66, 0x9d, 0x47, 0x59, 0x52, 0xc6, 0x5e, 0x8c,
0xbc, 0xe7, 0x22, 0x90, 0xbc, 0x81, 0xf2, 0x85, 0x17, 0x85, 0x7e, 0xd8, 0x8b, 0xdf, 0xf2, 0x4f, 0x47, 0x5b, 0x50, 0xf0, 0x59, 0x9f, 0xab, 0xef, 0x5e, 0xdd, 0x7e, 0x90, 0x25, 0xf7, 0x96, 0xf5,
0xb2, 0x84, 0x3e, 0x6a, 0x9c, 0x9b, 0x08, 0x38, 0x35, 0x75, 0x89, 0x4e, 0xb8, 0x89, 0x89, 0xf3, 0x89, 0x02, 0xa2, 0x37, 0x50, 0xbe, 0x70, 0xc3, 0xc0, 0x0b, 0xfa, 0xd1, 0x7b, 0xff, 0x71, 0x96,
0x5b, 0x95, 0xcb, 0x8a, 0x34, 0xee, 0xef, 0x41, 0x4d, 0xdf, 0x87, 0x0f, 0x2c, 0x12, 0x6a, 0x2a, 0xd0, 0x47, 0x8d, 0x23, 0xb1, 0x80, 0x53, 0x93, 0x97, 0xe8, 0x8c, 0x99, 0x98, 0x38, 0xbf, 0x95,
0xb4, 0xe6, 0xdd, 0xd9, 0xad, 0x34, 0xd4, 0x9d, 0x94, 0x74, 0x3e, 0x99, 0x76, 0x17, 0x33, 0x54, 0xb9, 0x2c, 0x49, 0xe3, 0xfe, 0x21, 0xd4, 0xf4, 0x7d, 0xf8, 0x40, 0x43, 0x2e, 0x27, 0x47, 0x6b,
0x2e, 0xf5, 0xbd, 0xce, 0x99, 0xd7, 0x8b, 0xcf, 0x29, 0x26, 0xd5, 0x97, 0xa1, 0xb1, 0xa7, 0xaf, 0xd9, 0x9d, 0xdd, 0x4d, 0x42, 0x49, 0x5a, 0xd2, 0xf9, 0xd1, 0xb4, 0xbb, 0x88, 0x21, 0x73, 0x69,
0x6d, 0x4c, 0xaa, 0xdc, 0x8c, 0xd8, 0xd0, 0x17, 0xe3, 0x01, 0x35, 0xa1, 0xd7, 0xff, 0x56, 0x02, 0xe4, 0x76, 0x07, 0x6e, 0x3f, 0xfa, 0x4e, 0x11, 0x29, 0x77, 0x26, 0xc6, 0x9e, 0xbe, 0xb6, 0x11,
0x68, 0x26, 0xfb, 0x21, 0x87, 0xb0, 0x80, 0xf6, 0x88, 0x33, 0xb7, 0x79, 0xa2, 0xdf, 0xf6, 0xb3, 0x29, 0x73, 0x33, 0xa4, 0x13, 0x8f, 0xcf, 0x86, 0xd8, 0x98, 0xde, 0xfe, 0x6b, 0x09, 0xa0, 0x19,
0x1b, 0x34, 0x58, 0xf2, 0x41, 0x25, 0x3f, 0x0e, 0x3d, 0xe4, 0x79, 0x56, 0x99, 0x48, 0xcf, 0x4d, 0x9f, 0x07, 0x9d, 0xc0, 0x8a, 0xb2, 0x87, 0x9c, 0xa5, 0xcd, 0x53, 0xf9, 0x6d, 0x3f, 0xbd, 0x41,
0xf6, 0x8b, 0x6b, 0x50, 0x46, 0xef, 0x7b, 0x28, 0xea, 0x2c, 0x20, 0x59, 0xb5, 0x30, 0x9d, 0xb7, 0x83, 0x45, 0x1f, 0x64, 0xf2, 0xab, 0xa1, 0x07, 0x3d, 0xcb, 0x2a, 0x13, 0xc9, 0xb9, 0xc9, 0x7e,
0xf6, 0xf3, 0xf9, 0x20, 0xad, 0xf4, 0x0b, 0x8b, 0xb8, 0xa6, 0x52, 0x12, 0x67, 0x4e, 0x2b, 0x34, 0x7e, 0x0d, 0xca, 0xe8, 0x7d, 0x0f, 0x45, 0x9d, 0x05, 0x28, 0xab, 0x16, 0x26, 0xf3, 0xd6, 0x7e,
0x37, 0x26, 0x2b, 0x00, 0x13, 0x5d, 0xa7, 0x6e, 0x91, 0x1f, 0xa0, 0xa8, 0x6b, 0x1d, 0xf9, 0x6c, 0xb6, 0x1c, 0xa4, 0x95, 0x7e, 0x6e, 0x21, 0x62, 0x2a, 0x25, 0x72, 0x96, 0xb4, 0x42, 0x73, 0x63,
0xb6, 0x40, 0xac, 0x6f, 0xfe, 0xe7, 0xba, 0xf5, 0x85, 0x45, 0xde, 0x41, 0x41, 0x35, 0x79, 0x92, 0xb2, 0x02, 0x90, 0xea, 0x3a, 0x75, 0x0b, 0x7d, 0x0f, 0x45, 0x5d, 0xeb, 0xd0, 0xa7, 0x8b, 0x05,
0xd1, 0xb1, 0x52, 0x13, 0x82, 0xed, 0xcc, 0x83, 0x98, 0x28, 0x7e, 0x02, 0x18, 0x8f, 0x1a, 0x24, 0x22, 0x7d, 0xcb, 0xb7, 0xeb, 0xd6, 0xe7, 0x16, 0x7a, 0x07, 0x05, 0xd9, 0xe4, 0x51, 0x46, 0xc7,
0xe3, 0x1f, 0x99, 0xa9, 0x99, 0xc5, 0xae, 0x5f, 0x0f, 0x34, 0x06, 0xde, 0xa9, 0x3e, 0x7b, 0xc2, 0x4a, 0x4c, 0x08, 0xb6, 0xb3, 0x0c, 0x62, 0xa2, 0xf8, 0x23, 0xc0, 0x6c, 0xd4, 0x40, 0x19, 0xff,
0x49, 0x66, 0x87, 0x4d, 0xae, 0x91, 0xed, 0xcc, 0x83, 0x18, 0x75, 0xa7, 0x50, 0x9b, 0xf8, 0xc7, 0xda, 0xcc, 0xcd, 0x2c, 0x76, 0xfd, 0x7a, 0xa0, 0x31, 0xf0, 0x4e, 0xf6, 0xd9, 0x33, 0x86, 0x32,
0x96, 0xfc, 0x22, 0xdb, 0xc9, 0xab, 0x7f, 0x00, 0xdb, 0x2f, 0x6f, 0x84, 0x35, 0x96, 0x64, 0x7a, 0x3b, 0x6c, 0x7c, 0x8d, 0x6c, 0x67, 0x19, 0xc4, 0xa8, 0x3b, 0x87, 0x5a, 0xea, 0x5f, 0x5d, 0xf4,
0x56, 0x33, 0x9f, 0x49, 0xe3, 0x3a, 0xbf, 0x27, 0xff, 0x7d, 0xb5, 0xd7, 0x6e, 0x8c, 0xd7, 0x56, 0x8b, 0x6c, 0x27, 0xaf, 0xfe, 0x49, 0x6c, 0xbf, 0xb8, 0x11, 0xd6, 0x58, 0x12, 0xc9, 0x59, 0xcd,
0xb7, 0x0a, 0xbf, 0xcb, 0xf5, 0x8f, 0x8f, 0x8b, 0xf8, 0x47, 0xf6, 0x57, 0xff, 0x0b, 0x00, 0x00, 0x6c, 0xa3, 0xc6, 0x75, 0x7e, 0xa7, 0xff, 0xa1, 0xb5, 0xb7, 0x6e, 0x8c, 0xd7, 0x56, 0x77, 0x0b,
0xff, 0xff, 0xf1, 0x59, 0xad, 0xb5, 0x66, 0x17, 0x00, 0x00, 0xbf, 0xcb, 0x8d, 0x3a, 0x9d, 0xa2, 0xfa, 0xb3, 0xfb, 0xcb, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff,
0xc1, 0x07, 0x8b, 0x2b, 0x8a, 0x17, 0x00, 0x00,
} }
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.

View File

@@ -112,8 +112,9 @@ message Secret {
} }
message PrintFunc { message PrintFunc {
string Name = 1; string Name = 1;
string Format = 2; string Format = 2;
bool IgnoreStatus = 3;
} }
message InspectRequest { message InspectRequest {

View File

@@ -83,6 +83,11 @@ func ParseBuilderName(name string) (string, error) {
func Boot(ctx, clientContext context.Context, d *DriverHandle, pw progress.Writer) (*client.Client, error) { func Boot(ctx, clientContext context.Context, d *DriverHandle, pw progress.Writer) (*client.Client, error) {
try := 0 try := 0
logger := discardLogger
if pw != nil {
logger = pw.Write
}
for { for {
info, err := d.Info(ctx) info, err := d.Info(ctx)
if err != nil { if err != nil {
@@ -93,7 +98,7 @@ func Boot(ctx, clientContext context.Context, d *DriverHandle, pw progress.Write
if try > 2 { if try > 2 {
return nil, errors.Errorf("failed to bootstrap %T driver in attempts", d) return nil, errors.Errorf("failed to bootstrap %T driver in attempts", d)
} }
if err := d.Bootstrap(ctx, pw.Write); err != nil { if err := d.Bootstrap(ctx, logger); err != nil {
return nil, err return nil, err
} }
} }
@@ -109,6 +114,8 @@ func Boot(ctx, clientContext context.Context, d *DriverHandle, pw progress.Write
} }
} }
func discardLogger(*client.SolveStatus) {}
func historyAPISupported(ctx context.Context, c *client.Client) bool { func historyAPISupported(ctx context.Context, c *client.Client) bool {
cl, err := c.ControlClient().ListenBuildHistory(ctx, &controlapi.BuildHistoryRequest{ cl, err := c.ControlClient().ListenBuildHistory(ctx, &controlapi.BuildHistoryRequest{
ActiveOnly: true, ActiveOnly: true,

View File

@@ -153,6 +153,8 @@ func (f *factory) processDriverOpts(deploymentName string, namespace string, cfg
if _, isImage := cfg.DriverOpts["image"]; !isImage { if _, isImage := cfg.DriverOpts["image"]; !isImage {
deploymentOpt.Image = bkimage.DefaultRootlessImage deploymentOpt.Image = bkimage.DefaultRootlessImage
} }
case "schedulername":
deploymentOpt.SchedulerName = v
case "serviceaccount": case "serviceaccount":
deploymentOpt.ServiceAccountName = v deploymentOpt.ServiceAccountName = v
case "nodeselector": case "nodeselector":

View File

@@ -20,6 +20,7 @@ type DeploymentOpt struct {
Image string Image string
Replicas int Replicas int
ServiceAccountName string ServiceAccountName string
SchedulerName string
// Qemu // Qemu
Qemu struct { Qemu struct {
@@ -107,6 +108,7 @@ func NewDeployment(opt *DeploymentOpt) (d *appsv1.Deployment, c []*corev1.Config
}, },
Spec: corev1.PodSpec{ Spec: corev1.PodSpec{
ServiceAccountName: opt.ServiceAccountName, ServiceAccountName: opt.ServiceAccountName,
SchedulerName: opt.SchedulerName,
Containers: []corev1.Container{ Containers: []corev1.Container{
{ {
Name: containerName, Name: containerName,

4
go.mod
View File

@@ -14,7 +14,7 @@ require (
github.com/containerd/typeurl/v2 v2.1.1 github.com/containerd/typeurl/v2 v2.1.1
github.com/creack/pty v1.1.18 github.com/creack/pty v1.1.18
github.com/distribution/reference v0.5.0 github.com/distribution/reference v0.5.0
github.com/docker/cli v26.0.1-0.20240410153731-b6c552212837+incompatible // v26.1.0-dev github.com/docker/cli v26.1.3+incompatible
github.com/docker/cli-docs-tool v0.7.0 github.com/docker/cli-docs-tool v0.7.0
github.com/docker/docker v26.0.0+incompatible github.com/docker/docker v26.0.0+incompatible
github.com/docker/go-units v0.5.0 github.com/docker/go-units v0.5.0
@@ -26,7 +26,7 @@ require (
github.com/hashicorp/go-cty-funcs v0.0.0-20230405223818-a090f58aa992 github.com/hashicorp/go-cty-funcs v0.0.0-20230405223818-a090f58aa992
github.com/hashicorp/hcl/v2 v2.20.1 github.com/hashicorp/hcl/v2 v2.20.1
github.com/in-toto/in-toto-golang v0.5.0 github.com/in-toto/in-toto-golang v0.5.0
github.com/moby/buildkit v0.13.0-rc3.0.20240411143343-549891b34890 // v0.14.0-dev github.com/moby/buildkit v0.13.0-rc3.0.20240417151852-71f99c52a669 // v0.14.0-dev
github.com/moby/sys/mountinfo v0.7.1 github.com/moby/sys/mountinfo v0.7.1
github.com/moby/sys/signal v0.7.0 github.com/moby/sys/signal v0.7.0
github.com/morikuni/aec v1.0.0 github.com/morikuni/aec v1.0.0

8
go.sum
View File

@@ -117,8 +117,8 @@ 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/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v26.0.1-0.20240410153731-b6c552212837+incompatible h1:KTmSJjZSQM+cpaczHecGsBNlgJtRccef/62pCOeiA9o= github.com/docker/cli v26.1.3+incompatible h1:bUpXT/N0kDE3VUHI2r5VMsYQgi38kYuoC0oL9yt3lqc=
github.com/docker/cli v26.0.1-0.20240410153731-b6c552212837+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v26.1.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli-docs-tool v0.7.0 h1:M2Da98Unz2kz3A5d4yeSGbhyOge2mfYSNjAFt01Rw0M= github.com/docker/cli-docs-tool v0.7.0 h1:M2Da98Unz2kz3A5d4yeSGbhyOge2mfYSNjAFt01Rw0M=
github.com/docker/cli-docs-tool v0.7.0/go.mod h1:zMjqTFCU361PRh8apiXzeAZ1Q/xupbIwTusYpzCXS/o= github.com/docker/cli-docs-tool v0.7.0/go.mod h1:zMjqTFCU361PRh8apiXzeAZ1Q/xupbIwTusYpzCXS/o=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
@@ -293,8 +293,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/buildkit v0.13.0-rc3.0.20240411143343-549891b34890 h1:ikD8s7J6fN9kme4Yq1nUKlpZhEnakiMEcN9XeKFZXbs= github.com/moby/buildkit v0.13.0-rc3.0.20240417151852-71f99c52a669 h1:DnnuoY7BDEXoW4qbDHBvWy2lCzus6AO4CJGaEq94e7M=
github.com/moby/buildkit v0.13.0-rc3.0.20240411143343-549891b34890/go.mod h1:iqJg3dy9wLt5maCeC8WBbDISnLvuSX+R9jVNzg2zACU= github.com/moby/buildkit v0.13.0-rc3.0.20240417151852-71f99c52a669/go.mod h1:iqJg3dy9wLt5maCeC8WBbDISnLvuSX+R9jVNzg2zACU=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= 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/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=

View File

@@ -2,6 +2,7 @@ package buildflags
import ( import (
"encoding/csv" "encoding/csv"
"strconv"
"strings" "strings"
controllerapi "github.com/docker/buildx/controller/pb" controllerapi "github.com/docker/buildx/controller/pb"
@@ -21,9 +22,16 @@ func ParsePrintFunc(str string) (*controllerapi.PrintFunc, error) {
for _, field := range fields { for _, field := range fields {
parts := strings.SplitN(field, "=", 2) parts := strings.SplitN(field, "=", 2)
if len(parts) == 2 { if len(parts) == 2 {
if parts[0] == "format" { switch parts[0] {
case "format":
f.Format = parts[1] f.Format = parts[1]
} else { case "ignorestatus":
v, err := strconv.ParseBool(parts[1])
if err != nil {
return nil, errors.Wrapf(err, "invalid ignorestatus print value: %s", parts[1])
}
f.IgnoreStatus = v
default:
return nil, errors.Errorf("invalid print field: %s", field) return nil, errors.Errorf("invalid print field: %s", field)
} }
} else { } else {

View File

@@ -1,11 +1,7 @@
package metricutil package metricutil
import ( import (
"context"
"github.com/docker/buildx/version" "github.com/docker/buildx/version"
"github.com/docker/cli/cli/command"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric"
) )
@@ -15,10 +11,3 @@ func Meter(mp metric.MeterProvider) metric.Meter {
return mp.Meter(version.Package, return mp.Meter(version.Package,
metric.WithInstrumentationVersion(version.Version)) metric.WithInstrumentationVersion(version.Version))
} }
// Shutdown invokes Shutdown on the MeterProvider and then reports any error to the OTEL handler.
func Shutdown(ctx context.Context, mp command.MeterProvider) {
if err := mp.Shutdown(ctx); err != nil {
otel.Handle(err)
}
}

View File

@@ -15,7 +15,7 @@ type Writer interface {
ClearLogSource(interface{}) ClearLogSource(interface{})
} }
func Write(w Writer, name string, f func() error) { func Write(w Writer, name string, f func() error) error {
dgst := digest.FromBytes([]byte(identity.NewID())) dgst := digest.FromBytes([]byte(identity.NewID()))
tm := time.Now() tm := time.Now()
@@ -40,6 +40,8 @@ func Write(w Writer, name string, f func() error) {
w.Write(&client.SolveStatus{ w.Write(&client.SolveStatus{
Vertexes: []*client.Vertex{&vtx2}, Vertexes: []*client.Vertex{&vtx2},
}) })
return err
} }
func WriteBuildRef(w Writer, target string, ref string) { func WriteBuildRef(w Writer, target string, ref string) {

View File

@@ -5,6 +5,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"strconv" "strconv"
"strings"
"text/template" "text/template"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -71,18 +72,18 @@ func TemplateReplaceArg(i int) string {
return fmt.Sprintf(hookTemplateArg, strconv.Itoa(i)) return fmt.Sprintf(hookTemplateArg, strconv.Itoa(i))
} }
func ParseTemplate(hookTemplate string, cmd *cobra.Command) (string, error) { func ParseTemplate(hookTemplate string, cmd *cobra.Command) ([]string, error) {
tmpl := template.New("").Funcs(commandFunctions) tmpl := template.New("").Funcs(commandFunctions)
tmpl, err := tmpl.Parse(hookTemplate) tmpl, err := tmpl.Parse(hookTemplate)
if err != nil { if err != nil {
return "", err return nil, err
} }
b := bytes.Buffer{} b := bytes.Buffer{}
err = tmpl.Execute(&b, cmd) err = tmpl.Execute(&b, cmd)
if err != nil { if err != nil {
return "", err return nil, err
} }
return b.String(), nil return strings.Split(b.String(), "\n"), nil
} }
var ErrHookTemplateParse = errors.New("failed to parse hook template") var ErrHookTemplateParse = errors.New("failed to parse hook template")

View File

@@ -14,31 +14,42 @@ import (
// that plugins declaring support for hooks get passed when // that plugins declaring support for hooks get passed when
// being invoked following a CLI command execution. // being invoked following a CLI command execution.
type HookPluginData struct { type HookPluginData struct {
RootCmd string // RootCmd is a string representing the matching hook configuration
Flags map[string]string // which is currently being invoked. If a hook for `docker context` is
// configured and the user executes `docker context ls`, the plugin will
// be invoked with `context`.
RootCmd string
Flags map[string]string
CommandError string
} }
// RunPluginHooks calls the hook subcommand for all present // RunCLICommandHooks is the entrypoint into the hooks execution flow after
// CLI plugins that declare support for hooks in their metadata // a main CLI command was executed. It calls the hook subcommand for all
// and parses/prints their responses. // present CLI plugins that declare support for hooks in their metadata and
func RunPluginHooks(dockerCli command.Cli, rootCmd, subCommand *cobra.Command, plugin string, args []string) error { // parses/prints their responses.
subCmdName := subCommand.Name() func RunCLICommandHooks(dockerCli command.Cli, rootCmd, subCommand *cobra.Command, cmdErrorMessage string) {
if plugin != "" { commandName := strings.TrimPrefix(subCommand.CommandPath(), rootCmd.Name()+" ")
subCmdName = plugin flags := getCommandFlags(subCommand)
}
var flags map[string]string runHooks(dockerCli, rootCmd, subCommand, commandName, flags, cmdErrorMessage)
if plugin == "" { }
flags = getCommandFlags(subCommand)
} else { // RunPluginHooks is the entrypoint for the hooks execution flow
flags = getNaiveFlags(args) // after a plugin command was just executed by the CLI.
} func RunPluginHooks(dockerCli command.Cli, rootCmd, subCommand *cobra.Command, args []string) {
nextSteps := invokeAndCollectHooks(dockerCli, rootCmd, subCommand, subCmdName, flags) commandName := strings.Join(args, " ")
flags := getNaiveFlags(args)
runHooks(dockerCli, rootCmd, subCommand, commandName, flags, "")
}
func runHooks(dockerCli command.Cli, rootCmd, subCommand *cobra.Command, invokedCommand string, flags map[string]string, cmdErrorMessage string) {
nextSteps := invokeAndCollectHooks(dockerCli, rootCmd, subCommand, invokedCommand, flags, cmdErrorMessage)
hooks.PrintNextSteps(dockerCli.Err(), nextSteps) hooks.PrintNextSteps(dockerCli.Err(), nextSteps)
return nil
} }
func invokeAndCollectHooks(dockerCli command.Cli, rootCmd, subCmd *cobra.Command, hookCmdName string, flags map[string]string) []string { func invokeAndCollectHooks(dockerCli command.Cli, rootCmd, subCmd *cobra.Command, subCmdStr string, flags map[string]string, cmdErrorMessage string) []string {
pluginsCfg := dockerCli.ConfigFile().Plugins pluginsCfg := dockerCli.ConfigFile().Plugins
if pluginsCfg == nil { if pluginsCfg == nil {
return nil return nil
@@ -46,7 +57,8 @@ func invokeAndCollectHooks(dockerCli command.Cli, rootCmd, subCmd *cobra.Command
nextSteps := make([]string, 0, len(pluginsCfg)) nextSteps := make([]string, 0, len(pluginsCfg))
for pluginName, cfg := range pluginsCfg { for pluginName, cfg := range pluginsCfg {
if !registersHook(cfg, hookCmdName) { match, ok := pluginMatch(cfg, subCmdStr)
if !ok {
continue continue
} }
@@ -55,7 +67,11 @@ func invokeAndCollectHooks(dockerCli command.Cli, rootCmd, subCmd *cobra.Command
continue continue
} }
hookReturn, err := p.RunHook(hookCmdName, flags) hookReturn, err := p.RunHook(HookPluginData{
RootCmd: match,
Flags: flags,
CommandError: cmdErrorMessage,
})
if err != nil { if err != nil {
// skip misbehaving plugins, but don't halt execution // skip misbehaving plugins, but don't halt execution
continue continue
@@ -76,23 +92,46 @@ func invokeAndCollectHooks(dockerCli command.Cli, rootCmd, subCmd *cobra.Command
if err != nil { if err != nil {
continue continue
} }
nextSteps = append(nextSteps, processedHook) nextSteps = append(nextSteps, processedHook...)
} }
return nextSteps return nextSteps
} }
func registersHook(pluginCfg map[string]string, subCmdName string) bool { // pluginMatch takes a plugin configuration and a string representing the
hookCmdStr, ok := pluginCfg["hooks"] // command being executed (such as 'image ls' the root 'docker' is omitted)
if !ok { // and, if the configuration includes a hook for the invoked command, returns
return false // the configured hook string.
func pluginMatch(pluginCfg map[string]string, subCmd string) (string, bool) {
configuredPluginHooks, ok := pluginCfg["hooks"]
if !ok || configuredPluginHooks == "" {
return "", false
} }
commands := strings.Split(hookCmdStr, ",")
commands := strings.Split(configuredPluginHooks, ",")
for _, hookCmd := range commands { for _, hookCmd := range commands {
if hookCmd == subCmdName { if hookMatch(hookCmd, subCmd) {
return true return hookCmd, true
} }
} }
return false
return "", false
}
func hookMatch(hookCmd, subCmd string) bool {
hookCmdTokens := strings.Split(hookCmd, " ")
subCmdTokens := strings.Split(subCmd, " ")
if len(hookCmdTokens) > len(subCmdTokens) {
return false
}
for i, v := range hookCmdTokens {
if v != subCmdTokens[i] {
return false
}
}
return true
} }
func getCommandFlags(cmd *cobra.Command) map[string]string { func getCommandFlags(cmd *cobra.Command) map[string]string {

View File

@@ -240,8 +240,7 @@ func PluginRunCommand(dockerCli command.Cli, name string, rootcmd *cobra.Command
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
cmd.Env = os.Environ() cmd.Env = append(cmd.Environ(), ReexecEnvvar+"="+os.Args[0])
cmd.Env = append(cmd.Env, ReexecEnvvar+"="+os.Args[0])
cmd.Env = appendPluginResourceAttributesEnvvar(cmd.Env, rootcmd, plugin) cmd.Env = appendPluginResourceAttributesEnvvar(cmd.Env, rootcmd, plugin)
return cmd, nil return cmd, nil

View File

@@ -2,6 +2,7 @@ package manager
import ( import (
"encoding/json" "encoding/json"
"os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"regexp" "regexp"
@@ -104,16 +105,16 @@ func newPlugin(c Candidate, cmds []*cobra.Command) (Plugin, error) {
// RunHook executes the plugin's hooks command // RunHook executes the plugin's hooks command
// and returns its unprocessed output. // and returns its unprocessed output.
func (p *Plugin) RunHook(cmdName string, flags map[string]string) ([]byte, error) { func (p *Plugin) RunHook(hookData HookPluginData) ([]byte, error) {
hDataBytes, err := json.Marshal(HookPluginData{ hDataBytes, err := json.Marshal(hookData)
RootCmd: cmdName,
Flags: flags,
})
if err != nil { if err != nil {
return nil, wrapAsPluginError(err, "failed to marshall hook data") return nil, wrapAsPluginError(err, "failed to marshall hook data")
} }
hookCmdOutput, err := exec.Command(p.Path, p.Name, HookSubcommandName, string(hDataBytes)).Output() pCmd := exec.Command(p.Path, p.Name, HookSubcommandName, string(hDataBytes))
pCmd.Env = os.Environ()
pCmd.Env = append(pCmd.Env, ReexecEnvvar+"="+os.Args[0])
hookCmdOutput, err := pCmd.Output()
if err != nil { if err != nil {
return nil, wrapAsPluginError(err, "failed to execute plugin hook subcommand") return nil, wrapAsPluginError(err, "failed to execute plugin hook subcommand")
} }

View File

@@ -52,6 +52,24 @@ func RunPlugin(dockerCli *command.DockerCli, plugin *cobra.Command, meta manager
opts = append(opts, withPluginClientConn(plugin.Name())) opts = append(opts, withPluginClientConn(plugin.Name()))
} }
err = tcmd.Initialize(opts...) err = tcmd.Initialize(opts...)
ogRunE := cmd.RunE
if ogRunE == nil {
ogRun := cmd.Run
// necessary because error will always be nil here
// see: https://github.com/golangci/golangci-lint/issues/1379
//nolint:unparam
ogRunE = func(cmd *cobra.Command, args []string) error {
ogRun(cmd, args)
return nil
}
cmd.Run = nil
}
cmd.RunE = func(cmd *cobra.Command, args []string) error {
stopInstrumentation := dockerCli.StartInstrumentation(cmd)
err := ogRunE(cmd, args)
stopInstrumentation(err)
return err
}
}) })
return err return err
} }

View File

@@ -273,6 +273,11 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...CLIOption)
return ResolveDefaultContext(cli.options, cli.contextStoreConfig) return ResolveDefaultContext(cli.options, cli.contextStoreConfig)
}, },
} }
// TODO(krissetto): pass ctx to the funcs instead of using this
cli.createGlobalMeterProvider(cli.baseCtx)
cli.createGlobalTracerProvider(cli.baseCtx)
return nil return nil
} }

View File

@@ -41,35 +41,25 @@ type TelemetryClient interface {
// each time this function is invoked. // each time this function is invoked.
Resource() *resource.Resource Resource() *resource.Resource
// TracerProvider returns a TracerProvider. This TracerProvider will be configured // TracerProvider returns the currently initialized TracerProvider. This TracerProvider will be configured
// with the default tracing components for a CLI program along with any options given // with the default tracing components for a CLI program
// for the SDK. TracerProvider() trace.TracerProvider
TracerProvider(ctx context.Context, opts ...sdktrace.TracerProviderOption) TracerProvider
// MeterProvider returns a MeterProvider. This MeterProvider will be configured // MeterProvider returns the currently initialized MeterProvider. This MeterProvider will be configured
// with the default metric components for a CLI program along with any options given // with the default metric components for a CLI program
// for the SDK. MeterProvider() metric.MeterProvider
MeterProvider(ctx context.Context, opts ...sdkmetric.Option) MeterProvider
} }
func (cli *DockerCli) Resource() *resource.Resource { func (cli *DockerCli) Resource() *resource.Resource {
return cli.res.Get() return cli.res.Get()
} }
func (cli *DockerCli) TracerProvider(ctx context.Context, opts ...sdktrace.TracerProviderOption) TracerProvider { func (cli *DockerCli) TracerProvider() trace.TracerProvider {
allOpts := make([]sdktrace.TracerProviderOption, 0, len(opts)+2) return otel.GetTracerProvider()
allOpts = append(allOpts, sdktrace.WithResource(cli.Resource()))
allOpts = append(allOpts, dockerSpanExporter(ctx, cli)...)
allOpts = append(allOpts, opts...)
return sdktrace.NewTracerProvider(allOpts...)
} }
func (cli *DockerCli) MeterProvider(ctx context.Context, opts ...sdkmetric.Option) MeterProvider { func (cli *DockerCli) MeterProvider() metric.MeterProvider {
allOpts := make([]sdkmetric.Option, 0, len(opts)+2) return otel.GetMeterProvider()
allOpts = append(allOpts, sdkmetric.WithResource(cli.Resource()))
allOpts = append(allOpts, dockerMetricExporter(ctx, cli)...)
allOpts = append(allOpts, opts...)
return sdkmetric.NewMeterProvider(allOpts...)
} }
// WithResourceOptions configures additional options for the default resource. The default // WithResourceOptions configures additional options for the default resource. The default
@@ -122,6 +112,28 @@ func (r *telemetryResource) init() {
r.opts = nil r.opts = nil
} }
// createGlobalMeterProvider creates a new MeterProvider from the initialized DockerCli struct
// with the given options and sets it as the global meter provider
func (cli *DockerCli) createGlobalMeterProvider(ctx context.Context, opts ...sdkmetric.Option) {
allOpts := make([]sdkmetric.Option, 0, len(opts)+2)
allOpts = append(allOpts, sdkmetric.WithResource(cli.Resource()))
allOpts = append(allOpts, dockerMetricExporter(ctx, cli)...)
allOpts = append(allOpts, opts...)
mp := sdkmetric.NewMeterProvider(allOpts...)
otel.SetMeterProvider(mp)
}
// createGlobalTracerProvider creates a new TracerProvider from the initialized DockerCli struct
// with the given options and sets it as the global tracer provider
func (cli *DockerCli) createGlobalTracerProvider(ctx context.Context, opts ...sdktrace.TracerProviderOption) {
allOpts := make([]sdktrace.TracerProviderOption, 0, len(opts)+2)
allOpts = append(allOpts, sdktrace.WithResource(cli.Resource()))
allOpts = append(allOpts, dockerSpanExporter(ctx, cli)...)
allOpts = append(allOpts, opts...)
tp := sdktrace.NewTracerProvider(allOpts...)
otel.SetTracerProvider(tp)
}
func defaultResourceOptions() []resource.Option { func defaultResourceOptions() []resource.Option {
return []resource.Option{ return []resource.Option{
resource.WithDetectors(serviceNameDetector{}), resource.WithDetectors(serviceNameDetector{}),
@@ -174,11 +186,6 @@ func newCLIReader(exp sdkmetric.Exporter) sdkmetric.Reader {
} }
func (r *cliReader) Shutdown(ctx context.Context) error { func (r *cliReader) Shutdown(ctx context.Context) error {
var rm metricdata.ResourceMetrics
if err := r.Reader.Collect(ctx, &rm); err != nil {
return err
}
// Place a pretty tight constraint on the actual reporting. // Place a pretty tight constraint on the actual reporting.
// We don't want CLI metrics to prevent the CLI from exiting // We don't want CLI metrics to prevent the CLI from exiting
// so if there's some kind of issue we need to abort pretty // so if there's some kind of issue we need to abort pretty
@@ -186,6 +193,15 @@ func (r *cliReader) Shutdown(ctx context.Context) error {
ctx, cancel := context.WithTimeout(ctx, exportTimeout) ctx, cancel := context.WithTimeout(ctx, exportTimeout)
defer cancel() defer cancel()
return r.ForceFlush(ctx)
}
func (r *cliReader) ForceFlush(ctx context.Context) error {
var rm metricdata.ResourceMetrics
if err := r.Reader.Collect(ctx, &rm); err != nil {
return err
}
return r.exporter.Export(ctx, &rm) return r.exporter.Export(ctx, &rm)
} }

View File

@@ -41,24 +41,20 @@ func dockerExporterOTLPEndpoint(cli Cli) (endpoint string, secure bool) {
otelCfg = m[otelContextFieldName] otelCfg = m[otelContextFieldName]
} }
if otelCfg == nil { if otelCfg != nil {
return "", false otelMap, ok := otelCfg.(map[string]any)
if !ok {
otel.Handle(errors.Errorf(
"unexpected type for field %q: %T (expected: %T)",
otelContextFieldName,
otelCfg,
otelMap,
))
}
// keys from https://opentelemetry.io/docs/concepts/sdk-configuration/otlp-exporter-configuration/
endpoint, _ = otelMap[otelExporterOTLPEndpoint].(string)
} }
otelMap, ok := otelCfg.(map[string]any)
if !ok {
otel.Handle(errors.Errorf(
"unexpected type for field %q: %T (expected: %T)",
otelContextFieldName,
otelCfg,
otelMap,
))
return "", false
}
// keys from https://opentelemetry.io/docs/concepts/sdk-configuration/otlp-exporter-configuration/
endpoint, _ = otelMap[otelExporterOTLPEndpoint].(string)
// Override with env var value if it exists AND IS SET // Override with env var value if it exists AND IS SET
// (ignore otel defaults for this override when the key exists but is empty) // (ignore otel defaults for this override when the key exists but is empty)
if override := os.Getenv(debugEnvVarPrefix + otelExporterOTLPEndpoint); override != "" { if override := os.Getenv(debugEnvVarPrefix + otelExporterOTLPEndpoint); override != "" {

View File

@@ -26,8 +26,7 @@ func BaseCommandAttributes(cmd *cobra.Command, streams Streams) []attribute.KeyV
// Note: this should be the last func to wrap/modify the PersistentRunE/RunE funcs before command execution. // Note: this should be the last func to wrap/modify the PersistentRunE/RunE funcs before command execution.
// //
// can also be used for spans! // can also be used for spans!
func (cli *DockerCli) InstrumentCobraCommands(cmd *cobra.Command, mp metric.MeterProvider) { func (cli *DockerCli) InstrumentCobraCommands(ctx context.Context, cmd *cobra.Command) {
meter := getDefaultMeter(mp)
// If PersistentPreRunE is nil, make it execute PersistentPreRun and return nil by default // If PersistentPreRunE is nil, make it execute PersistentPreRun and return nil by default
ogPersistentPreRunE := cmd.PersistentPreRunE ogPersistentPreRunE := cmd.PersistentPreRunE
if ogPersistentPreRunE == nil { if ogPersistentPreRunE == nil {
@@ -55,10 +54,9 @@ func (cli *DockerCli) InstrumentCobraCommands(cmd *cobra.Command, mp metric.Mete
} }
cmd.RunE = func(cmd *cobra.Command, args []string) error { cmd.RunE = func(cmd *cobra.Command, args []string) error {
// start the timer as the first step of every cobra command // start the timer as the first step of every cobra command
baseAttrs := BaseCommandAttributes(cmd, cli) stopInstrumentation := cli.StartInstrumentation(cmd)
stopCobraCmdTimer := startCobraCommandTimer(cmd, meter, baseAttrs)
cmdErr := ogRunE(cmd, args) cmdErr := ogRunE(cmd, args)
stopCobraCmdTimer(cmdErr) stopInstrumentation(cmdErr)
return cmdErr return cmdErr
} }
@@ -66,8 +64,17 @@ func (cli *DockerCli) InstrumentCobraCommands(cmd *cobra.Command, mp metric.Mete
} }
} }
func startCobraCommandTimer(cmd *cobra.Command, meter metric.Meter, attrs []attribute.KeyValue) func(err error) { // StartInstrumentation instruments CLI commands with the individual metrics and spans configured.
ctx := cmd.Context() // It's the main command OTel utility, and new command-related metrics should be added to it.
// It should be called immediately before command execution, and returns a stopInstrumentation function
// that must be called with the error resulting from the command execution.
func (cli *DockerCli) StartInstrumentation(cmd *cobra.Command) (stopInstrumentation func(error)) {
baseAttrs := BaseCommandAttributes(cmd, cli)
return startCobraCommandTimer(cli.MeterProvider(), baseAttrs)
}
func startCobraCommandTimer(mp metric.MeterProvider, attrs []attribute.KeyValue) func(err error) {
meter := getDefaultMeter(mp)
durationCounter, _ := meter.Float64Counter( durationCounter, _ := meter.Float64Counter(
"command.time", "command.time",
metric.WithDescription("Measures the duration of the cobra command"), metric.WithDescription("Measures the duration of the cobra command"),
@@ -76,12 +83,20 @@ func startCobraCommandTimer(cmd *cobra.Command, meter metric.Meter, attrs []attr
start := time.Now() start := time.Now()
return func(err error) { return func(err error) {
// Use a new context for the export so that the command being cancelled
// doesn't affect the metrics, and we get metrics for cancelled commands.
ctx, cancel := context.WithTimeout(context.Background(), exportTimeout)
defer cancel()
duration := float64(time.Since(start)) / float64(time.Millisecond) duration := float64(time.Since(start)) / float64(time.Millisecond)
cmdStatusAttrs := attributesFromError(err) cmdStatusAttrs := attributesFromError(err)
durationCounter.Add(ctx, duration, durationCounter.Add(ctx, duration,
metric.WithAttributes(attrs...), metric.WithAttributes(attrs...),
metric.WithAttributes(cmdStatusAttrs...), metric.WithAttributes(cmdStatusAttrs...),
) )
if mp, ok := mp.(MeterProvider); ok {
mp.ForceFlush(ctx)
}
} }
} }

View File

@@ -642,7 +642,7 @@ type fileActionState struct {
fa *FileAction fa *FileAction
} }
func (ms *marshalState) addInput(st *fileActionState, c *Constraints, o Output) (pb.InputIndex, error) { func (ms *marshalState) addInput(c *Constraints, o Output) (pb.InputIndex, error) {
inp, err := o.ToInput(ms.ctx, c) inp, err := o.ToInput(ms.ctx, c)
if err != nil { if err != nil {
return 0, err return 0, err
@@ -684,7 +684,7 @@ func (ms *marshalState) add(fa *FileAction, c *Constraints) (*fileActionState, e
} }
if source := fa.state.Output(); source != nil { if source := fa.state.Output(); source != nil {
inp, err := ms.addInput(st, c, source) inp, err := ms.addInput(c, source)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -700,7 +700,7 @@ func (ms *marshalState) add(fa *FileAction, c *Constraints) (*fileActionState, e
if a, ok := fa.action.(*fileActionCopy); ok { if a, ok := fa.action.(*fileActionCopy); ok {
if a.state != nil { if a.state != nil {
if out := a.state.Output(); out != nil { if out := a.state.Output(); out != nil {
inp, err := ms.addInput(st, c, out) inp, err := ms.addInput(c, out)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -269,7 +269,7 @@ func parseString(rest string, d *directives) (*Node, map[string]bool, error) {
} }
// parseJSON converts JSON arrays to an AST. // parseJSON converts JSON arrays to an AST.
func parseJSON(rest string, d *directives) (*Node, map[string]bool, error) { func parseJSON(rest string) (*Node, map[string]bool, error) {
rest = strings.TrimLeftFunc(rest, unicode.IsSpace) rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
if !strings.HasPrefix(rest, "[") { if !strings.HasPrefix(rest, "[") {
return nil, nil, errors.Errorf("Error parsing %q as a JSON array", rest) return nil, nil, errors.Errorf("Error parsing %q as a JSON array", rest)
@@ -307,7 +307,7 @@ func parseMaybeJSON(rest string, d *directives) (*Node, map[string]bool, error)
return nil, nil, nil return nil, nil, nil
} }
node, attrs, err := parseJSON(rest, d) node, attrs, err := parseJSON(rest)
if err == nil { if err == nil {
return node, attrs, nil return node, attrs, nil
@@ -325,7 +325,7 @@ func parseMaybeJSON(rest string, d *directives) (*Node, map[string]bool, error)
// so, passes to parseJSON; if not, attempts to parse it as a whitespace // so, passes to parseJSON; if not, attempts to parse it as a whitespace
// delimited string. // delimited string.
func parseMaybeJSONToList(rest string, d *directives) (*Node, map[string]bool, error) { func parseMaybeJSONToList(rest string, d *directives) (*Node, map[string]bool, error) {
node, attrs, err := parseJSON(rest, d) node, attrs, err := parseJSON(rest)
if err == nil { if err == nil {
return node, attrs, nil return node, attrs, nil

View File

@@ -6,13 +6,14 @@ import (
"fmt" "fmt"
"io" "io"
"sort" "sort"
"text/tabwriter"
"github.com/moby/buildkit/client/llb" "github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/frontend/dockerfile/parser" "github.com/moby/buildkit/frontend/dockerfile/parser"
"github.com/moby/buildkit/frontend/gateway/client" "github.com/moby/buildkit/frontend/gateway/client"
"github.com/moby/buildkit/frontend/subrequests" "github.com/moby/buildkit/frontend/subrequests"
"github.com/moby/buildkit/solver/errdefs"
"github.com/moby/buildkit/solver/pb" "github.com/moby/buildkit/solver/pb"
"github.com/pkg/errors"
) )
const RequestLint = "frontend.lint" const RequestLint = "frontend.lint"
@@ -26,16 +27,10 @@ var SubrequestLintDefinition = subrequests.Request{
Metadata: []subrequests.Named{ Metadata: []subrequests.Named{
{Name: "result.json"}, {Name: "result.json"},
{Name: "result.txt"}, {Name: "result.txt"},
{Name: "result.statuscode"},
}, },
} }
type Source struct {
Filename string `json:"fileName"`
Language string `json:"language"`
Definition *pb.Definition `json:"definition"`
Data []byte `json:"data"`
}
type Warning struct { type Warning struct {
RuleName string `json:"ruleName"` RuleName string `json:"ruleName"`
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
@@ -45,19 +40,19 @@ type Warning struct {
} }
type LintResults struct { type LintResults struct {
Warnings []Warning `json:"warnings"` Warnings []Warning `json:"warnings"`
Sources []Source `json:"sources"` Sources []*pb.SourceInfo `json:"sources"`
} }
func (results *LintResults) AddSource(sourceMap *llb.SourceMap) int { func (results *LintResults) AddSource(sourceMap *llb.SourceMap) int {
newSource := Source{ newSource := &pb.SourceInfo{
Filename: sourceMap.Filename, Filename: sourceMap.Filename,
Language: sourceMap.Language, Language: sourceMap.Language,
Definition: sourceMap.Definition.ToPB(), Definition: sourceMap.Definition.ToPB(),
Data: sourceMap.Data, Data: sourceMap.Data,
} }
for i, source := range results.Sources { for i, source := range results.Sources {
if sourceEqual(source, newSource) { if sourceInfoEqual(source, newSource) {
return i return i
} }
} }
@@ -92,13 +87,6 @@ func (results *LintResults) AddWarning(rulename, description, url, fmtmsg string
}) })
} }
func sourceEqual(a, b Source) bool {
if a.Filename != b.Filename || a.Language != b.Language {
return false
}
return bytes.Equal(a.Data, b.Data)
}
func (results *LintResults) ToResult() (*client.Result, error) { func (results *LintResults) ToResult() (*client.Result, error) {
res := client.NewResult() res := client.NewResult()
dt, err := json.MarshalIndent(results, "", " ") dt, err := json.MarshalIndent(results, "", " ")
@@ -113,51 +101,91 @@ func (results *LintResults) ToResult() (*client.Result, error) {
} }
res.AddMeta("result.txt", b.Bytes()) res.AddMeta("result.txt", b.Bytes())
status := 0
if len(results.Warnings) > 0 {
status = 1
}
res.AddMeta("result.statuscode", []byte(fmt.Sprintf("%d", status)))
res.AddMeta("version", []byte(SubrequestLintDefinition.Version)) res.AddMeta("version", []byte(SubrequestLintDefinition.Version))
return res, nil return res, nil
} }
func PrintLintViolations(dt []byte, w io.Writer) error { func (results *LintResults) validateWarnings() error {
var warnings LintResults for _, warning := range results.Warnings {
if int(warning.Location.SourceIndex) >= len(results.Sources) {
return errors.Errorf("sourceIndex is out of range")
}
if warning.Location.SourceIndex > 0 {
warningSource := results.Sources[warning.Location.SourceIndex]
if warningSource == nil {
return errors.Errorf("sourceIndex points to nil source")
}
}
}
return nil
}
if err := json.Unmarshal(dt, &warnings); err != nil { func PrintLintViolations(dt []byte, w io.Writer) error {
var results LintResults
if err := json.Unmarshal(dt, &results); err != nil {
return err return err
} }
// Here, we're grouping the warnings by rule name if err := results.validateWarnings(); err != nil {
lintWarnings := make(map[string][]Warning) return err
lintWarningRules := []string{}
for _, warning := range warnings.Warnings {
if _, ok := lintWarnings[warning.RuleName]; !ok {
lintWarningRules = append(lintWarningRules, warning.RuleName)
lintWarnings[warning.RuleName] = []Warning{}
}
lintWarnings[warning.RuleName] = append(lintWarnings[warning.RuleName], warning)
}
sort.Strings(lintWarningRules)
tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0)
for _, rule := range lintWarningRules {
fmt.Fprintf(tw, "Lint Rule %s\n", rule)
for _, warning := range lintWarnings[rule] {
source := warnings.Sources[warning.Location.SourceIndex]
sourceData := bytes.Split(source.Data, []byte("\n"))
firstRange := warning.Location.Ranges[0]
if firstRange.Start.Line != firstRange.End.Line {
fmt.Fprintf(tw, "\t%s:%d-%d\n", source.Filename, firstRange.Start.Line, firstRange.End.Line)
} else {
fmt.Fprintf(tw, "\t%s:%d\n", source.Filename, firstRange.Start.Line)
}
fmt.Fprintf(tw, "\t%s\n", warning.Detail)
for _, r := range warning.Location.Ranges {
for i := r.Start.Line; i <= r.End.Line; i++ {
fmt.Fprintf(tw, "\t%d\t|\t%s\n", i, sourceData[i-1])
}
}
fmt.Fprintln(tw)
}
fmt.Fprintln(tw)
} }
return tw.Flush() sort.Slice(results.Warnings, func(i, j int) bool {
warningI := results.Warnings[i]
warningJ := results.Warnings[j]
sourceIndexI := warningI.Location.SourceIndex
sourceIndexJ := warningJ.Location.SourceIndex
if sourceIndexI < 0 && sourceIndexJ < 0 {
return warningI.RuleName < warningJ.RuleName
} else if sourceIndexI < 0 || sourceIndexJ < 0 {
return sourceIndexI < 0
}
sourceInfoI := results.Sources[warningI.Location.SourceIndex]
sourceInfoJ := results.Sources[warningJ.Location.SourceIndex]
if sourceInfoI.Filename != sourceInfoJ.Filename {
return sourceInfoI.Filename < sourceInfoJ.Filename
}
if len(warningI.Location.Ranges) == 0 && len(warningJ.Location.Ranges) == 0 {
return warningI.RuleName < warningJ.RuleName
} else if len(warningI.Location.Ranges) == 0 || len(warningJ.Location.Ranges) == 0 {
return len(warningI.Location.Ranges) == 0
}
return warningI.Location.Ranges[0].Start.Line < warningJ.Location.Ranges[0].Start.Line
})
for _, warning := range results.Warnings {
fmt.Fprintf(w, "\n- %s\n%s\n", warning.Detail, warning.Description)
if warning.URL != "" {
fmt.Fprintf(w, "URL: %s\n", warning.URL)
}
if warning.Location.SourceIndex < 0 {
continue
}
sourceInfo := results.Sources[warning.Location.SourceIndex]
source := errdefs.Source{
Info: sourceInfo,
Ranges: warning.Location.Ranges,
}
err := source.Print(w)
if err != nil {
return err
}
}
return nil
}
func sourceInfoEqual(a, b *pb.SourceInfo) bool {
if a.Filename != b.Filename || a.Language != b.Language {
return false
}
return bytes.Equal(a.Data, b.Data)
} }

View File

@@ -11,6 +11,6 @@ func getFallbackAgentPath() (string, error) {
return "", errors.Errorf("make sure SSH_AUTH_SOCK is set") return "", errors.Errorf("make sure SSH_AUTH_SOCK is set")
} }
func getWindowsPipeDialer(path string) *socketDialer { func getWindowsPipeDialer(_ string) *socketDialer {
return nil return nil
} }

View File

@@ -37,8 +37,8 @@ type Upload struct {
cc Upload_PullClient cc Upload_PullClient
} }
func (u *Upload) WriteTo(w io.Writer) (int, error) { func (u *Upload) WriteTo(w io.Writer) (int64, error) {
n := 0 var n int64
for { for {
var bm BytesMessage var bm BytesMessage
if err := u.cc.RecvMsg(&bm); err != nil { if err := u.cc.RecvMsg(&bm); err != nil {
@@ -48,7 +48,7 @@ func (u *Upload) WriteTo(w io.Writer) (int, error) {
return n, errors.WithStack(err) return n, errors.WithStack(err)
} }
nn, err := w.Write(bm.Data) nn, err := w.Write(bm.Data)
n += nn n += int64(nn)
if err != nil { if err != nil {
return n, errors.WithStack(err) return n, errors.WithStack(err)
} }

View File

@@ -113,14 +113,14 @@ func (b *buffer) Writer(ctx context.Context, opts ...content.WriterOpt) (content
} }
func (b *buffer) ReaderAt(ctx context.Context, desc ocispecs.Descriptor) (content.ReaderAt, error) { func (b *buffer) ReaderAt(ctx context.Context, desc ocispecs.Descriptor) (content.ReaderAt, error) {
r, err := b.getBytesReader(ctx, desc.Digest) r, err := b.getBytesReader(desc.Digest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &readerAt{Reader: r, Closer: io.NopCloser(r), size: int64(r.Len())}, nil return &readerAt{Reader: r, Closer: io.NopCloser(r), size: int64(r.Len())}, nil
} }
func (b *buffer) getBytesReader(ctx context.Context, dgst digest.Digest) (*bytes.Reader, error) { func (b *buffer) getBytesReader(dgst digest.Digest) (*bytes.Reader, error) {
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()

View File

@@ -69,7 +69,7 @@ func ParseGitRef(ref string) (*GitRef, error) {
} else { } else {
remote, err = ParseURL(ref) remote, err = ParseURL(ref)
if errors.Is(err, ErrUnknownProtocol) { if errors.Is(err, ErrUnknownProtocol) {
remote, err = ParseURL("https://" + ref) return nil, err
} }
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -115,7 +115,7 @@ func Config(ctx context.Context, str string, resolver remotes.Resolver, cache Co
} }
if desc.MediaType == images.MediaTypeDockerSchema1Manifest { if desc.MediaType == images.MediaTypeDockerSchema1Manifest {
dgst, dt, err := readSchema1Config(ctx, ref.String(), desc, fetcher, cache) dgst, dt, err := readSchema1Config(ctx, desc, fetcher)
return dgst, dt, err return dgst, dt, err
} }

View File

@@ -14,7 +14,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
func readSchema1Config(ctx context.Context, ref string, desc ocispecs.Descriptor, fetcher remotes.Fetcher, cache ContentCache) (digest.Digest, []byte, error) { func readSchema1Config(ctx context.Context, desc ocispecs.Descriptor, fetcher remotes.Fetcher) (digest.Digest, []byte, error) {
rc, err := fetcher.Fetch(ctx, desc) rc, err := fetcher.Fetch(ctx, desc)
if err != nil { if err != nil {
return "", nil, err return "", nil, err

View File

@@ -155,7 +155,7 @@ func NewDisplay(out io.Writer, mode DisplayMode, opts ...DisplayOpt) (Display, e
case PlainMode: case PlainMode:
return newPlainDisplay(out, opts...), nil return newPlainDisplay(out, opts...), nil
case RawJSONMode: case RawJSONMode:
return newRawJSONDisplay(out, opts...), nil return newRawJSONDisplay(out), nil
case QuietMode: case QuietMode:
return newDiscardDisplay(), nil return newDiscardDisplay(), nil
default: default:
@@ -283,7 +283,7 @@ type rawJSONDisplay struct {
// newRawJSONDisplay creates a new Display that outputs an unbuffered // newRawJSONDisplay creates a new Display that outputs an unbuffered
// output of status update events. // output of status update events.
func newRawJSONDisplay(w io.Writer, opts ...DisplayOpt) Display { func newRawJSONDisplay(w io.Writer) Display {
enc := json.NewEncoder(w) enc := json.NewEncoder(w)
enc.SetIndent("", " ") enc.SetIndent("", " ")
return Display{ return Display{

View File

@@ -231,7 +231,7 @@ disabled_plugins = ["cri"]
"nsenter", "-U", "--preserve-credentials", "-m", "-t", fmt.Sprintf("%d", pid)}, "nsenter", "-U", "--preserve-credentials", "-m", "-t", fmt.Sprintf("%d", pid)},
append(buildkitdArgs, "--containerd-worker-snapshotter=native")...) append(buildkitdArgs, "--containerd-worker-snapshotter=native")...)
} }
buildkitdSock, stop, err := runBuildkitd(ctx, cfg, buildkitdArgs, cfg.Logs, c.UID, c.GID, c.ExtraEnv) buildkitdSock, stop, err := runBuildkitd(cfg, buildkitdArgs, cfg.Logs, c.UID, c.GID, c.ExtraEnv)
if err != nil { if err != nil {
integration.PrintLogs(cfg.Logs, log.Println) integration.PrintLogs(cfg.Logs, log.Println)
return nil, nil, err return nil, nil, err

View File

@@ -77,7 +77,7 @@ func (s *OCI) New(ctx context.Context, cfg *integration.BackendConfig) (integrat
if runtime.GOOS != "windows" && s.Snapshotter != "native" { if runtime.GOOS != "windows" && s.Snapshotter != "native" {
extraEnv = append(extraEnv, "BUILDKIT_DEBUG_FORCE_OVERLAY_DIFF=true") extraEnv = append(extraEnv, "BUILDKIT_DEBUG_FORCE_OVERLAY_DIFF=true")
} }
buildkitdSock, stop, err := runBuildkitd(ctx, cfg, buildkitdArgs, cfg.Logs, s.UID, s.GID, extraEnv) buildkitdSock, stop, err := runBuildkitd(cfg, buildkitdArgs, cfg.Logs, s.UID, s.GID, extraEnv)
if err != nil { if err != nil {
integration.PrintLogs(cfg.Logs, log.Println) integration.PrintLogs(cfg.Logs, log.Println)
return nil, nil, err return nil, nil, err

View File

@@ -2,7 +2,6 @@ package workers
import ( import (
"bytes" "bytes"
"context"
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
@@ -27,7 +26,6 @@ func (osp otelSocketPath) UpdateConfigFile(in string) string {
} }
func runBuildkitd( func runBuildkitd(
ctx context.Context,
conf *integration.BackendConfig, conf *integration.BackendConfig,
args []string, args []string,
logs map[string]*bytes.Buffer, logs map[string]*bytes.Buffer,

View File

@@ -35,11 +35,11 @@ func getContainerdDebugSock(tmpdir string) string {
} }
// no-op for parity with unix // no-op for parity with unix
func mountInfo(tmpdir string) error { func mountInfo(_ string) error {
return nil return nil
} }
func chown(name string, uid, gid int) error { func chown(_ string, _, _ int) error {
// Chown not supported on Windows // Chown not supported on Windows
return nil return nil
} }

View File

@@ -66,7 +66,7 @@ func (c *Connection) StartConnection(ctx context.Context) error {
c.disconnectedCh = make(chan bool, 1) c.disconnectedCh = make(chan bool, 1)
c.backgroundConnectionDoneCh = make(chan struct{}) c.backgroundConnectionDoneCh = make(chan struct{})
if err := c.connect(ctx); err == nil { if err := c.connect(); err == nil {
c.setStateConnected() c.setStateConnected()
} else { } else {
c.SetStateDisconnected(err) c.SetStateDisconnected(err)
@@ -148,7 +148,7 @@ func (c *Connection) indefiniteBackgroundConnection() {
// Normal scenario that we'll wait for // Normal scenario that we'll wait for
} }
if err := c.connect(context.Background()); err == nil { if err := c.connect(); err == nil {
c.setStateConnected() c.setStateConnected()
} else { } else {
// this code is unreachable in most cases // this code is unreachable in most cases
@@ -168,7 +168,7 @@ func (c *Connection) indefiniteBackgroundConnection() {
} }
} }
func (c *Connection) connect(ctx context.Context) error { func (c *Connection) connect() error {
c.newConnectionHandler(c.cc) c.newConnectionHandler(c.cc)
return nil return nil
} }

4
vendor/modules.txt vendored
View File

@@ -215,7 +215,7 @@ github.com/davecgh/go-spew/spew
# github.com/distribution/reference v0.5.0 # github.com/distribution/reference v0.5.0
## explicit; go 1.20 ## explicit; go 1.20
github.com/distribution/reference github.com/distribution/reference
# github.com/docker/cli v26.0.1-0.20240410153731-b6c552212837+incompatible # github.com/docker/cli v26.1.3+incompatible
## explicit ## explicit
github.com/docker/cli/cli github.com/docker/cli/cli
github.com/docker/cli/cli-plugins/hooks github.com/docker/cli/cli-plugins/hooks
@@ -510,7 +510,7 @@ github.com/mitchellh/mapstructure
# github.com/mitchellh/reflectwalk v1.0.2 # github.com/mitchellh/reflectwalk v1.0.2
## explicit ## explicit
github.com/mitchellh/reflectwalk github.com/mitchellh/reflectwalk
# github.com/moby/buildkit v0.13.0-rc3.0.20240411143343-549891b34890 # github.com/moby/buildkit v0.13.0-rc3.0.20240417151852-71f99c52a669
## explicit; go 1.21 ## explicit; go 1.21
github.com/moby/buildkit/api/services/control github.com/moby/buildkit/api/services/control
github.com/moby/buildkit/api/types github.com/moby/buildkit/api/types