From ca49048881872e4d051f54659a8f48d48e5aeab0 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Wed, 9 Oct 2024 11:24:10 +0200 Subject: [PATCH 01/23] buildkitd: cdi config Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- cmd/buildkitd/config/config.go | 7 +++++++ cmd/buildkitd/main.go | 27 +++++++++++++++++++++++++ docs/buildkitd.toml.md | 7 +++++++ go.mod | 2 +- util/appdefaults/appdefaults_unix.go | 1 + util/appdefaults/appdefaults_windows.go | 1 + 6 files changed, 44 insertions(+), 1 deletion(-) diff --git a/cmd/buildkitd/config/config.go b/cmd/buildkitd/config/config.go index 480f0450a443..1e104eb4cce7 100644 --- a/cmd/buildkitd/config/config.go +++ b/cmd/buildkitd/config/config.go @@ -23,6 +23,8 @@ type Config struct { OTEL OTELConfig `toml:"otel"` + CDI CDIConfig `toml:"cdi"` + Workers struct { OCI OCIConfig `toml:"oci"` Containerd ContainerdConfig `toml:"containerd"` @@ -74,6 +76,11 @@ type OTELConfig struct { SocketPath string `toml:"socketPath"` } +type CDIConfig struct { + Enabled *bool `toml:"enabled"` + SpecDirs []string `toml:"specDirs"` +} + type GCConfig struct { GC *bool `toml:"gc"` // Deprecated: use GCReservedSpace instead diff --git a/cmd/buildkitd/main.go b/cmd/buildkitd/main.go index cdfe9a948a0e..a407b234a2d6 100644 --- a/cmd/buildkitd/main.go +++ b/cmd/buildkitd/main.go @@ -74,6 +74,7 @@ import ( "google.golang.org/grpc/health" healthv1 "google.golang.org/grpc/health/grpc_health_v1" "google.golang.org/grpc/reflection" + "tags.cncf.io/container-device-interface/pkg/cdi" ) func init() { @@ -216,6 +217,14 @@ func main() { Name: "otel-socket-path", Usage: "OTEL collector trace socket path", }, + cli.BoolFlag{ + Name: "cdi-enabled", + Usage: "enables support of the Container Device Interface (CDI)", + }, + cli.StringSliceFlag{ + Name: "cdi-spec-dir", + Usage: "list of directories to scan for CDI spec files", + }, ) app.Flags = append(app.Flags, appFlags...) app.Flags = append(app.Flags, serviceFlags()...) @@ -281,6 +290,12 @@ func main() { } closers = append(closers, mp.Shutdown) + if cfg.CDI.Enabled != nil && *cfg.CDI.Enabled { + if err := cdi.Configure(cdi.WithSpecDirs(cfg.CDI.SpecDirs...)); err != nil { + return errors.Wrap(err, "failed to configure CDI registry") + } + } + statsHandler := tracing.ServerStatsHandler( otelgrpc.WithTracerProvider(tp), otelgrpc.WithMeterProvider(mp), @@ -537,6 +552,10 @@ func setDefaultConfig(cfg *config.Config) { if cfg.OTEL.SocketPath == "" { cfg.OTEL.SocketPath = appdefaults.TraceSocketPath(isRootlessConfig()) } + + if len(cfg.CDI.SpecDirs) == 0 { + cfg.CDI.SpecDirs = appdefaults.CDISpecDirs + } } // isRootlessConfig is true if we should be using the rootless config @@ -619,6 +638,14 @@ func applyMainFlags(c *cli.Context, cfg *config.Config) error { cfg.OTEL.SocketPath = c.String("otel-socket-path") } + if c.IsSet("cdi-enabled") { + cdiEnabled := c.Bool("cdi-enabled") + cfg.CDI.Enabled = &cdiEnabled + } + if c.IsSet("cdi-spec-dir") { + cfg.CDI.SpecDirs = c.StringSlice("cdi-spec-dir") + } + applyPlatformFlags(c) return nil diff --git a/docs/buildkitd.toml.md b/docs/buildkitd.toml.md index 71061c626868..70cbb7229914 100644 --- a/docs/buildkitd.toml.md +++ b/docs/buildkitd.toml.md @@ -46,6 +46,13 @@ insecure-entitlements = [ "network.host", "security.insecure" ] # OTEL collector trace socket path socketPath = "/run/buildkit/otel-grpc.sock" +[cdi] + # Enables support of the Container Device Interface (CDI). + enabled = true + # List of directories to scan for CDI spec files. For more details about CDI + # specification, please refer to https://github.com/cncf-tags/container-device-interface/blob/main/SPEC.md#cdi-json-specification + specDirs = ["/etc/cdi", "/var/run/cdi"] + # config for build history API that stores information about completed build commands [history] # maxAge is the maximum age of history entries to keep, in seconds. diff --git a/go.mod b/go.mod index fde31734ec8f..2bc7ab934095 100644 --- a/go.mod +++ b/go.mod @@ -107,6 +107,7 @@ require ( google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 google.golang.org/protobuf v1.35.2 kernel.org/pub/linux/libs/security/libcap/cap v1.2.73 + tags.cncf.io/container-device-interface v0.8.0 ) require ( @@ -183,7 +184,6 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect kernel.org/pub/linux/libs/security/libcap/psx v1.2.73 // indirect sigs.k8s.io/yaml v1.4.0 // indirect - tags.cncf.io/container-device-interface v0.8.0 // indirect tags.cncf.io/container-device-interface/specs-go v0.8.0 // indirect ) diff --git a/util/appdefaults/appdefaults_unix.go b/util/appdefaults/appdefaults_unix.go index cb1aa06d18a2..5742c4dea6e6 100644 --- a/util/appdefaults/appdefaults_unix.go +++ b/util/appdefaults/appdefaults_unix.go @@ -17,6 +17,7 @@ const ( var ( UserCNIConfigPath = filepath.Join(UserConfigDir(), "cni.json") + CDISpecDirs = []string{"/etc/buildkit/cdi"} ) // UserAddress typically returns /run/user/$UID/buildkit/buildkitd.sock diff --git a/util/appdefaults/appdefaults_windows.go b/util/appdefaults/appdefaults_windows.go index 323c8a98abf9..c2103887a341 100644 --- a/util/appdefaults/appdefaults_windows.go +++ b/util/appdefaults/appdefaults_windows.go @@ -18,6 +18,7 @@ var ( var ( UserCNIConfigPath = DefaultCNIConfigPath + CDISpecDirs []string ) func UserAddress() string { From 319bf56d8de963999ea7cea42f67245ede1ce3cb Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Wed, 9 Oct 2024 13:34:56 +0200 Subject: [PATCH 02/23] exec: cdi device support Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- client/client_test.go | 87 ++++- client/llb/exec.go | 22 ++ client/llb/meta.go | 28 ++ client/llb/state.go | 6 + executor/executor.go | 1 + executor/oci/spec.go | 6 + executor/oci/spec_darwin.go | 9 + executor/oci/spec_freebsd.go | 9 + executor/oci/spec_linux.go | 70 ++++ executor/oci/spec_windows.go | 8 + solver/llbsolver/ops/exec.go | 1 + solver/pb/caps.go | 7 + solver/pb/ops.pb.go | 565 +++++++++++++++------------ solver/pb/ops.proto | 8 + solver/pb/ops_vtproto.pb.go | 249 ++++++++++++ util/testutil/integration/run.go | 2 + util/testutil/integration/sandbox.go | 37 +- util/testutil/workers/oci.go | 8 +- 18 files changed, 859 insertions(+), 264 deletions(-) diff --git a/client/client_test.go b/client/client_test.go index fd183f2fb9c0..3df2fd8ae05d 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -274,6 +274,15 @@ func testIntegration(t *testing.T, funcs ...func(t *testing.T, sb integration.Sa "dns": bridgeDNSNetwork, }), ) + + integration.Run(t, integration.TestFuncs( + testCDI, + ), + mirrors, + integration.WithMatrix("cdi", map[string]interface{}{ + "enabled": enableCDI, + }), + ) } func newContainerd(cdAddress string) (*ctd.Client, error) { @@ -7436,8 +7445,8 @@ func testMergeOp(t *testing.T, sb integration.Sandbox) { File(llb.Mkfile("bar/D", 0644, []byte("D"))). File(llb.Mkfile("bar/E", 0755, nil)). File(llb.Mkfile("qaz", 0644, nil)), - // /foo from stateE is not here because it is deleted in stateB, which is part of a submerge of mergeD ) + // /foo from stateE is not here because it is deleted in stateB, which is part of a submerge of mergeD } func testMergeOpCacheInline(t *testing.T, sb integration.Sandbox) { @@ -10986,3 +10995,79 @@ func (w warningsListOutput) String() string { } return b.String() } + +type cdiEnabled struct{} + +func (*cdiEnabled) UpdateConfigFile(in string) string { + return in + ` +[cdi] +enabled = true +` +} + +var ( + enableCDI integration.ConfigUpdater = &cdiEnabled{} +) + +func testCDI(t *testing.T, sb integration.Sandbox) { + if sb.Rootless() { + t.SkipNow() + } + + integration.SkipOnPlatform(t, "windows") + c, err := New(sb.Context(), sb.Address()) + require.NoError(t, err) + defer c.Close() + + require.NoError(t, os.WriteFile(filepath.Join(sb.CDISpecDir(), "vendor1-device.yaml"), []byte(` +cdiVersion: "0.3.0" +kind: "vendor1.com/device" +devices: +- name: foo + containerEdits: + env: + - FOO=injected +`), 0600)) + require.NoError(t, os.WriteFile(filepath.Join(sb.CDISpecDir(), "vendor2-device.yaml"), []byte(` +cdiVersion: "0.3.0" +kind: "vendor2.com/device" +devices: +- name: bar + containerEdits: + env: + - BAR=injected +`), 0600)) + + busybox := llb.Image("busybox:latest") + st := llb.Scratch() + + run := func(cmd string, ro ...llb.RunOption) { + st = busybox.Run(append(ro, llb.Shlex(cmd), llb.Dir("/wd"))...).AddMount("/wd", st) + } + + run(`sh -c 'env|sort | tee foo.env'`, llb.AddCDIDevice("vendor1.com/device=foo")) + run(`sh -c 'env|sort | tee bar.env'`, llb.AddCDIDevice("vendor2.com/device=bar")) + + def, err := st.Marshal(sb.Context()) + require.NoError(t, err) + + destDir := t.TempDir() + + _, err = c.Solve(sb.Context(), def, SolveOpt{ + Exports: []ExportEntry{ + { + Type: ExporterLocal, + OutputDir: destDir, + }, + }, + }, nil) + require.NoError(t, err) + + dt, err := os.ReadFile(filepath.Join(destDir, "foo.env")) + require.NoError(t, err) + require.Contains(t, strings.TrimSpace(string(dt)), `FOO=injected`) + + dt2, err := os.ReadFile(filepath.Join(destDir, "bar.env")) + require.NoError(t, err) + require.Contains(t, strings.TrimSpace(string(dt2)), `BAR=injected`) +} diff --git a/client/llb/exec.go b/client/llb/exec.go index a10fb94a26d6..2a97f7e8e72e 100644 --- a/client/llb/exec.go +++ b/client/llb/exec.go @@ -266,6 +266,22 @@ func (e *ExecOp) Marshal(ctx context.Context, c *Constraints) (digest.Digest, [] Network: network, Security: security, } + + cdiDevices, err := getCDIDevice(e.base)(ctx, c) + if err != nil { + return "", nil, nil, nil, err + } + if len(cdiDevices) > 0 { + addCap(&e.constraints, pb.CapExecMetaCDI) + cd := make([]*pb.CDIDevice, len(cdiDevices)) + for i, d := range cdiDevices { + cd[i] = &pb.CDIDevice{ + Name: d.Name, + } + } + peo.CdiDevices = cd + } + if network != NetModeSandbox { addCap(&e.constraints, pb.CapExecMetaNetwork) } @@ -624,6 +640,12 @@ func AddUlimit(name UlimitName, soft int64, hard int64) RunOption { }) } +func AddCDIDevice(name string) RunOption { + return runOptionFunc(func(ei *ExecInfo) { + ei.State = ei.State.AddCDIDevice(name) + }) +} + func ValidExitCodes(codes ...int) RunOption { return runOptionFunc(func(ei *ExecInfo) { ei.State = validExitCodes(codes...)(ei.State) diff --git a/client/llb/meta.go b/client/llb/meta.go index 640be7478255..e7bbe39686d7 100644 --- a/client/llb/meta.go +++ b/client/llb/meta.go @@ -24,6 +24,7 @@ var ( keyExtraHost = contextKeyT("llb.exec.extrahost") keyHostname = contextKeyT("llb.exec.hostname") keyUlimit = contextKeyT("llb.exec.ulimit") + keyDevice = contextKeyT("llb.exec.device") keyCgroupParent = contextKeyT("llb.exec.cgroup.parent") keyUser = contextKeyT("llb.exec.user") keyValidExitCodes = contextKeyT("llb.exec.validexitcodes") @@ -305,6 +306,33 @@ func getUlimit(s State) func(context.Context, *Constraints) ([]*pb.Ulimit, error } } +func cdiDevice(name string) StateOption { + return func(s State) State { + return s.withValue(keyDevice, func(ctx context.Context, c *Constraints) (interface{}, error) { + v, err := getCDIDevice(s)(ctx, c) + if err != nil { + return nil, err + } + return append(v, &pb.CDIDevice{ + Name: name, + }), nil + }) + } +} + +func getCDIDevice(s State) func(context.Context, *Constraints) ([]*pb.CDIDevice, error) { + return func(ctx context.Context, c *Constraints) ([]*pb.CDIDevice, error) { + v, err := s.getValue(keyDevice)(ctx, c) + if err != nil { + return nil, err + } + if v != nil { + return v.([]*pb.CDIDevice), nil + } + return nil, nil + } +} + func cgroupParent(cp string) StateOption { return func(s State) State { return s.WithValue(keyCgroupParent, cp) diff --git a/client/llb/state.go b/client/llb/state.go index b52aaa244339..a18ba2a297c1 100644 --- a/client/llb/state.go +++ b/client/llb/state.go @@ -476,6 +476,12 @@ func (s State) AddUlimit(name UlimitName, soft int64, hard int64) State { return ulimit(name, soft, hard)(s) } +// AddCDIDevice sets the fully qualified CDI device name. +// https://github.com/cncf-tags/container-device-interface/blob/main/SPEC.md +func (s State) AddCDIDevice(name string) State { + return cdiDevice(name)(s) +} + // WithCgroupParent sets the parent cgroup for any containers created from this state. // This is useful when you want to apply resource constraints to a group of containers. // Cgroups are Linux specific and only applies to containers created from this state such as via `[State.Run]` diff --git a/executor/executor.go b/executor/executor.go index 9902392084b6..d35e18af6513 100644 --- a/executor/executor.go +++ b/executor/executor.go @@ -22,6 +22,7 @@ type Meta struct { ReadonlyRootFS bool ExtraHosts []HostIP Ulimit []*pb.Ulimit + CDIDevices []*pb.CDIDevice CgroupParent string NetMode pb.NetMode SecurityMode pb.SecurityMode diff --git a/executor/oci/spec.go b/executor/oci/spec.go index fe0b03c4a1c1..d3cdfaa4415b 100644 --- a/executor/oci/spec.go +++ b/executor/oci/spec.go @@ -110,6 +110,12 @@ func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mou return nil, nil, err } + if cdiOpts, err := generateCDIOpts(ctx, meta.CDIDevices); err == nil { + opts = append(opts, cdiOpts...) + } else { + return nil, nil, err + } + hostname := defaultHostname if meta.Hostname != "" { hostname = meta.Hostname diff --git a/executor/oci/spec_darwin.go b/executor/oci/spec_darwin.go index c2658ecb9e6b..3b4f15143650 100644 --- a/executor/oci/spec_darwin.go +++ b/executor/oci/spec_darwin.go @@ -1,6 +1,8 @@ package oci import ( + "context" + "github.com/containerd/containerd/v2/core/mount" "github.com/containerd/containerd/v2/pkg/oci" "github.com/containerd/continuity/fs" @@ -62,3 +64,10 @@ func sub(m mount.Mount, subPath string) (mount.Mount, func() error, error) { m.Source = src return m, func() error { return nil }, nil } + +func generateCDIOpts(ctx context.Context, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) { + if len(devices) == 0 { + return nil, nil + } + return nil, errors.New("no support for CDI on Darwin") +} diff --git a/executor/oci/spec_freebsd.go b/executor/oci/spec_freebsd.go index 73cbe7ec7917..a9ac00aa4e7d 100644 --- a/executor/oci/spec_freebsd.go +++ b/executor/oci/spec_freebsd.go @@ -1,6 +1,8 @@ package oci import ( + "context" + "github.com/containerd/containerd/v2/core/mount" "github.com/containerd/containerd/v2/pkg/oci" "github.com/containerd/continuity/fs" @@ -70,3 +72,10 @@ func sub(m mount.Mount, subPath string) (mount.Mount, func() error, error) { m.Source = src return m, func() error { return nil }, nil } + +func generateCDIOpts(ctx context.Context, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) { + if len(devices) == 0 { + return nil, nil + } + return nil, errors.New("no support for CDI on FreeBSD") +} diff --git a/executor/oci/spec_linux.go b/executor/oci/spec_linux.go index 9ee59fc85a7d..3c01b3c5d501 100644 --- a/executor/oci/spec_linux.go +++ b/executor/oci/spec_linux.go @@ -14,16 +14,20 @@ import ( "github.com/containerd/containerd/v2/pkg/oci" cdseccomp "github.com/containerd/containerd/v2/pkg/seccomp" "github.com/containerd/continuity/fs" + "github.com/containerd/log" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/profiles/seccomp" "github.com/moby/buildkit/snapshot" "github.com/moby/buildkit/solver/pb" + "github.com/moby/buildkit/util/bklog" "github.com/moby/buildkit/util/entitlements/security" specs "github.com/opencontainers/runtime-spec/specs-go" selinux "github.com/opencontainers/selinux/go-selinux" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "golang.org/x/sys/unix" + "tags.cncf.io/container-device-interface/pkg/cdi" + "tags.cncf.io/container-device-interface/pkg/parser" ) var ( @@ -148,6 +152,72 @@ func generateRlimitOpts(ulimits []*pb.Ulimit) ([]oci.SpecOpts, error) { }, nil } +// genereateCDIOptions creates the OCI runtime spec options for injecting CDI +// devices. Two options are returned: The first ensures that the CDI registry +// is initialized with refresh disabled, and the second injects the devices +// into the container. +func generateCDIOpts(ctx context.Context, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) { + if len(devices) == 0 { + return nil, nil + } + var dd []string + for _, d := range devices { + if d == nil { + continue + } + if _, _, _, err := parser.ParseQualifiedName(d.Name); err != nil { + return nil, errors.Wrapf(err, "invalid CDI device name %s", d.Name) + } + dd = append(dd, d.Name) + } + + // withStaticCDIRegistry inits the CDI registry and disables auto-refresh. + // This is used from the `run` command to avoid creating a registry with + // auto-refresh enabled. It also provides a way to override the CDI spec + // file paths if required. + withStaticCDIRegistry := func() oci.SpecOpts { + return func(ctx context.Context, _ oci.Client, _ *containers.Container, s *oci.Spec) error { + _ = cdi.Configure(cdi.WithAutoRefresh(false)) + if err := cdi.Refresh(); err != nil { + // We don't consider registry refresh failure a fatal error. + // For instance, a dynamically generated invalid CDI Spec file + // for any particular vendor shouldn't prevent injection of + // devices of different vendors. CDI itself knows better, and + // it will fail the injection if necessary. + bklog.G(ctx).Warnf("CDI registry refresh failed: %v", err) + } + return nil + } + } + + // withCDIDevices injects the requested CDI devices into the OCI specification. + // FIXME: Use oci.WithCDIDevices once we switch to containerd 2.0. + withCDIDevices := func(devices ...string) oci.SpecOpts { + return func(ctx context.Context, _ oci.Client, c *containers.Container, s *specs.Spec) error { + if len(devices) == 0 { + return nil + } + if err := cdi.Refresh(); err != nil { + log.G(ctx).Warnf("CDI registry refresh failed: %v", err) + } + bklog.G(ctx).Debugf("Injecting CDI devices %v", devices) + if _, err := cdi.InjectDevices(s, devices...); err != nil { + return errors.Wrapf(err, "CDI device injection failed") + } + // One crucial thing to keep in mind is that CDI device injection + // might add OCI Spec environment variables, hooks, and mounts as + // well. Therefore, it is important that none of the corresponding + // OCI Spec fields are reset up in the call stack once we return. + return nil + } + } + + return []oci.SpecOpts{ + withStaticCDIRegistry(), + withCDIDevices(dd...), + }, nil +} + // withDefaultProfile sets the default seccomp profile to the spec. // Note: must follow the setting of process capabilities func withDefaultProfile() oci.SpecOpts { diff --git a/executor/oci/spec_windows.go b/executor/oci/spec_windows.go index d3059c3034f4..5d94fd12da19 100644 --- a/executor/oci/spec_windows.go +++ b/executor/oci/spec_windows.go @@ -110,3 +110,11 @@ func sub(m mount.Mount, subPath string) (mount.Mount, func() error, error) { m.Source = src return m, func() error { return nil }, nil } + +func generateCDIOpts(ctx context.Context, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) { + if len(devices) == 0 { + return nil, nil + } + // https://github.com/cncf-tags/container-device-interface/issues/28 + return nil, errors.New("no support for CDI on Windows") +} diff --git a/solver/llbsolver/ops/exec.go b/solver/llbsolver/ops/exec.go index 11184ae74d61..8a68ced9909c 100644 --- a/solver/llbsolver/ops/exec.go +++ b/solver/llbsolver/ops/exec.go @@ -432,6 +432,7 @@ func (e *ExecOp) Exec(ctx context.Context, g session.Group, inputs []solver.Resu ReadonlyRootFS: p.ReadonlyRootFS, ExtraHosts: extraHosts, Ulimit: e.op.Meta.Ulimit, + CDIDevices: e.op.CdiDevices, CgroupParent: e.op.Meta.CgroupParent, NetMode: e.op.Network, SecurityMode: e.op.Security, diff --git a/solver/pb/caps.go b/solver/pb/caps.go index b7a802499814..173791e4dd6f 100644 --- a/solver/pb/caps.go +++ b/solver/pb/caps.go @@ -47,6 +47,7 @@ const ( CapExecMetaSecurityDeviceWhitelistV1 apicaps.CapID = "exec.meta.security.devices.v1" CapExecMetaSetsDefaultPath apicaps.CapID = "exec.meta.setsdefaultpath" CapExecMetaUlimit apicaps.CapID = "exec.meta.ulimit" + CapExecMetaCDI apicaps.CapID = "exec.meta.cdi" CapExecMetaRemoveMountStubsRecursive apicaps.CapID = "exec.meta.removemountstubs.recursive" CapExecMountBind apicaps.CapID = "exec.mount.bind" CapExecMountBindReadWriteNoOutput apicaps.CapID = "exec.mount.bind.readwrite-nooutput" @@ -294,6 +295,12 @@ func init() { Status: apicaps.CapStatusExperimental, }) + Caps.Init(apicaps.Cap{ + ID: CapExecMetaCDI, + Enabled: true, + Status: apicaps.CapStatusExperimental, + }) + Caps.Init(apicaps.Cap{ ID: CapExecMountBind, Enabled: true, diff --git a/solver/pb/ops.pb.go b/solver/pb/ops.pb.go index 6975da4cee07..19c13b92e80f 100644 --- a/solver/pb/ops.pb.go +++ b/solver/pb/ops.pb.go @@ -579,11 +579,12 @@ type ExecOp struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Meta *Meta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` - Mounts []*Mount `protobuf:"bytes,2,rep,name=mounts,proto3" json:"mounts,omitempty"` - Network NetMode `protobuf:"varint,3,opt,name=network,proto3,enum=pb.NetMode" json:"network,omitempty"` - Security SecurityMode `protobuf:"varint,4,opt,name=security,proto3,enum=pb.SecurityMode" json:"security,omitempty"` - Secretenv []*SecretEnv `protobuf:"bytes,5,rep,name=secretenv,proto3" json:"secretenv,omitempty"` + Meta *Meta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + Mounts []*Mount `protobuf:"bytes,2,rep,name=mounts,proto3" json:"mounts,omitempty"` + Network NetMode `protobuf:"varint,3,opt,name=network,proto3,enum=pb.NetMode" json:"network,omitempty"` + Security SecurityMode `protobuf:"varint,4,opt,name=security,proto3,enum=pb.SecurityMode" json:"security,omitempty"` + Secretenv []*SecretEnv `protobuf:"bytes,5,rep,name=secretenv,proto3" json:"secretenv,omitempty"` + CdiDevices []*CDIDevice `protobuf:"bytes,6,rep,name=cdiDevices,proto3" json:"cdiDevices,omitempty"` } func (x *ExecOp) Reset() { @@ -651,6 +652,13 @@ func (x *ExecOp) GetSecretenv() []*SecretEnv { return nil } +func (x *ExecOp) GetCdiDevices() []*CDIDevice { + if x != nil { + return x.CdiDevices + } + return nil +} + // Meta is a set of arguments for ExecOp. // Meta is unrelated to LLB metadata. // FIXME: rename (ExecContext? ExecArgs?) @@ -955,6 +963,54 @@ func (x *SecretEnv) GetOptional() bool { return false } +// CDIDevice specifies a CDI device information. +type CDIDevice struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Fully qualified CDI device name (e.g., vendor.com/gpu=gpudevice1) + // https://github.com/cncf-tags/container-device-interface/blob/main/SPEC.md + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *CDIDevice) Reset() { + *x = CDIDevice{} + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CDIDevice) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CDIDevice) ProtoMessage() {} + +func (x *CDIDevice) ProtoReflect() protoreflect.Message { + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CDIDevice.ProtoReflect.Descriptor instead. +func (*CDIDevice) Descriptor() ([]byte, []int) { + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{8} +} + +func (x *CDIDevice) GetName() string { + if x != nil { + return x.Name + } + return "" +} + // Mount specifies how to mount an input Op as a filesystem. type Mount struct { state protoimpl.MessageState @@ -977,7 +1033,7 @@ type Mount struct { func (x *Mount) Reset() { *x = Mount{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[8] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -989,7 +1045,7 @@ func (x *Mount) String() string { func (*Mount) ProtoMessage() {} func (x *Mount) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[8] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1002,7 +1058,7 @@ func (x *Mount) ProtoReflect() protoreflect.Message { // Deprecated: Use Mount.ProtoReflect.Descriptor instead. func (*Mount) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{8} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{9} } func (x *Mount) GetInput() int64 { @@ -1101,7 +1157,7 @@ type TmpfsOpt struct { func (x *TmpfsOpt) Reset() { *x = TmpfsOpt{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[9] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1113,7 +1169,7 @@ func (x *TmpfsOpt) String() string { func (*TmpfsOpt) ProtoMessage() {} func (x *TmpfsOpt) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[9] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1126,7 +1182,7 @@ func (x *TmpfsOpt) ProtoReflect() protoreflect.Message { // Deprecated: Use TmpfsOpt.ProtoReflect.Descriptor instead. func (*TmpfsOpt) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{9} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{10} } func (x *TmpfsOpt) GetSize() int64 { @@ -1150,7 +1206,7 @@ type CacheOpt struct { func (x *CacheOpt) Reset() { *x = CacheOpt{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[10] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1162,7 +1218,7 @@ func (x *CacheOpt) String() string { func (*CacheOpt) ProtoMessage() {} func (x *CacheOpt) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[10] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1175,7 +1231,7 @@ func (x *CacheOpt) ProtoReflect() protoreflect.Message { // Deprecated: Use CacheOpt.ProtoReflect.Descriptor instead. func (*CacheOpt) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{10} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{11} } func (x *CacheOpt) GetID() string { @@ -1213,7 +1269,7 @@ type SecretOpt struct { func (x *SecretOpt) Reset() { *x = SecretOpt{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[11] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1225,7 +1281,7 @@ func (x *SecretOpt) String() string { func (*SecretOpt) ProtoMessage() {} func (x *SecretOpt) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[11] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1238,7 +1294,7 @@ func (x *SecretOpt) ProtoReflect() protoreflect.Message { // Deprecated: Use SecretOpt.ProtoReflect.Descriptor instead. func (*SecretOpt) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{11} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{12} } func (x *SecretOpt) GetID() string { @@ -1297,7 +1353,7 @@ type SSHOpt struct { func (x *SSHOpt) Reset() { *x = SSHOpt{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[12] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1309,7 +1365,7 @@ func (x *SSHOpt) String() string { func (*SSHOpt) ProtoMessage() {} func (x *SSHOpt) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[12] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1322,7 +1378,7 @@ func (x *SSHOpt) ProtoReflect() protoreflect.Message { // Deprecated: Use SSHOpt.ProtoReflect.Descriptor instead. func (*SSHOpt) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{12} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{13} } func (x *SSHOpt) GetID() string { @@ -1375,7 +1431,7 @@ type SourceOp struct { func (x *SourceOp) Reset() { *x = SourceOp{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[13] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1387,7 +1443,7 @@ func (x *SourceOp) String() string { func (*SourceOp) ProtoMessage() {} func (x *SourceOp) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[13] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1400,7 +1456,7 @@ func (x *SourceOp) ProtoReflect() protoreflect.Message { // Deprecated: Use SourceOp.ProtoReflect.Descriptor instead. func (*SourceOp) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{13} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{14} } func (x *SourceOp) GetIdentifier() string { @@ -1432,7 +1488,7 @@ type BuildOp struct { func (x *BuildOp) Reset() { *x = BuildOp{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[14] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1444,7 +1500,7 @@ func (x *BuildOp) String() string { func (*BuildOp) ProtoMessage() {} func (x *BuildOp) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[14] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1457,7 +1513,7 @@ func (x *BuildOp) ProtoReflect() protoreflect.Message { // Deprecated: Use BuildOp.ProtoReflect.Descriptor instead. func (*BuildOp) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{14} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{15} } func (x *BuildOp) GetBuilder() int64 { @@ -1499,7 +1555,7 @@ type BuildInput struct { func (x *BuildInput) Reset() { *x = BuildInput{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[15] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1511,7 +1567,7 @@ func (x *BuildInput) String() string { func (*BuildInput) ProtoMessage() {} func (x *BuildInput) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[15] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1524,7 +1580,7 @@ func (x *BuildInput) ProtoReflect() protoreflect.Message { // Deprecated: Use BuildInput.ProtoReflect.Descriptor instead. func (*BuildInput) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{15} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{16} } func (x *BuildInput) GetInput() int64 { @@ -1553,7 +1609,7 @@ type OpMetadata struct { func (x *OpMetadata) Reset() { *x = OpMetadata{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[16] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1565,7 +1621,7 @@ func (x *OpMetadata) String() string { func (*OpMetadata) ProtoMessage() {} func (x *OpMetadata) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[16] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1578,7 +1634,7 @@ func (x *OpMetadata) ProtoReflect() protoreflect.Message { // Deprecated: Use OpMetadata.ProtoReflect.Descriptor instead. func (*OpMetadata) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{16} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{17} } func (x *OpMetadata) GetIgnoreCache() bool { @@ -1628,7 +1684,7 @@ type Source struct { func (x *Source) Reset() { *x = Source{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[17] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1640,7 +1696,7 @@ func (x *Source) String() string { func (*Source) ProtoMessage() {} func (x *Source) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[17] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1653,7 +1709,7 @@ func (x *Source) ProtoReflect() protoreflect.Message { // Deprecated: Use Source.ProtoReflect.Descriptor instead. func (*Source) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{17} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{18} } func (x *Source) GetLocations() map[string]*Locations { @@ -1681,7 +1737,7 @@ type Locations struct { func (x *Locations) Reset() { *x = Locations{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[18] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1693,7 +1749,7 @@ func (x *Locations) String() string { func (*Locations) ProtoMessage() {} func (x *Locations) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[18] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1706,7 +1762,7 @@ func (x *Locations) ProtoReflect() protoreflect.Message { // Deprecated: Use Locations.ProtoReflect.Descriptor instead. func (*Locations) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{18} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{19} } func (x *Locations) GetLocations() []*Location { @@ -1730,7 +1786,7 @@ type SourceInfo struct { func (x *SourceInfo) Reset() { *x = SourceInfo{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[19] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1742,7 +1798,7 @@ func (x *SourceInfo) String() string { func (*SourceInfo) ProtoMessage() {} func (x *SourceInfo) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[19] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1755,7 +1811,7 @@ func (x *SourceInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use SourceInfo.ProtoReflect.Descriptor instead. func (*SourceInfo) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{19} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{20} } func (x *SourceInfo) GetFilename() string { @@ -1798,7 +1854,7 @@ type Location struct { func (x *Location) Reset() { *x = Location{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[20] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1810,7 +1866,7 @@ func (x *Location) String() string { func (*Location) ProtoMessage() {} func (x *Location) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[20] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1823,7 +1879,7 @@ func (x *Location) ProtoReflect() protoreflect.Message { // Deprecated: Use Location.ProtoReflect.Descriptor instead. func (*Location) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{20} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{21} } func (x *Location) GetSourceIndex() int32 { @@ -1852,7 +1908,7 @@ type Range struct { func (x *Range) Reset() { *x = Range{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[21] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1864,7 +1920,7 @@ func (x *Range) String() string { func (*Range) ProtoMessage() {} func (x *Range) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[21] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1877,7 +1933,7 @@ func (x *Range) ProtoReflect() protoreflect.Message { // Deprecated: Use Range.ProtoReflect.Descriptor instead. func (*Range) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{21} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{22} } func (x *Range) GetStart() *Position { @@ -1906,7 +1962,7 @@ type Position struct { func (x *Position) Reset() { *x = Position{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[22] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1918,7 +1974,7 @@ func (x *Position) String() string { func (*Position) ProtoMessage() {} func (x *Position) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[22] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1931,7 +1987,7 @@ func (x *Position) ProtoReflect() protoreflect.Message { // Deprecated: Use Position.ProtoReflect.Descriptor instead. func (*Position) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{22} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{23} } func (x *Position) GetLine() int32 { @@ -1958,7 +2014,7 @@ type ExportCache struct { func (x *ExportCache) Reset() { *x = ExportCache{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[23] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1970,7 +2026,7 @@ func (x *ExportCache) String() string { func (*ExportCache) ProtoMessage() {} func (x *ExportCache) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[23] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1983,7 +2039,7 @@ func (x *ExportCache) ProtoReflect() protoreflect.Message { // Deprecated: Use ExportCache.ProtoReflect.Descriptor instead. func (*ExportCache) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{23} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{24} } func (x *ExportCache) GetValue() bool { @@ -2005,7 +2061,7 @@ type ProgressGroup struct { func (x *ProgressGroup) Reset() { *x = ProgressGroup{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[24] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2017,7 +2073,7 @@ func (x *ProgressGroup) String() string { func (*ProgressGroup) ProtoMessage() {} func (x *ProgressGroup) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[24] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2030,7 +2086,7 @@ func (x *ProgressGroup) ProtoReflect() protoreflect.Message { // Deprecated: Use ProgressGroup.ProtoReflect.Descriptor instead. func (*ProgressGroup) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{24} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{25} } func (x *ProgressGroup) GetId() string { @@ -2068,7 +2124,7 @@ type ProxyEnv struct { func (x *ProxyEnv) Reset() { *x = ProxyEnv{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[25] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2080,7 +2136,7 @@ func (x *ProxyEnv) String() string { func (*ProxyEnv) ProtoMessage() {} func (x *ProxyEnv) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[25] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2093,7 +2149,7 @@ func (x *ProxyEnv) ProtoReflect() protoreflect.Message { // Deprecated: Use ProxyEnv.ProtoReflect.Descriptor instead. func (*ProxyEnv) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{25} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{26} } func (x *ProxyEnv) GetHttpProxy() string { @@ -2142,7 +2198,7 @@ type WorkerConstraints struct { func (x *WorkerConstraints) Reset() { *x = WorkerConstraints{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[26] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2154,7 +2210,7 @@ func (x *WorkerConstraints) String() string { func (*WorkerConstraints) ProtoMessage() {} func (x *WorkerConstraints) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[26] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[27] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2167,7 +2223,7 @@ func (x *WorkerConstraints) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkerConstraints.ProtoReflect.Descriptor instead. func (*WorkerConstraints) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{26} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{27} } func (x *WorkerConstraints) GetFilter() []string { @@ -2194,7 +2250,7 @@ type Definition struct { func (x *Definition) Reset() { *x = Definition{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[27] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2206,7 +2262,7 @@ func (x *Definition) String() string { func (*Definition) ProtoMessage() {} func (x *Definition) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[27] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[28] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2219,7 +2275,7 @@ func (x *Definition) ProtoReflect() protoreflect.Message { // Deprecated: Use Definition.ProtoReflect.Descriptor instead. func (*Definition) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{27} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{28} } func (x *Definition) GetDef() [][]byte { @@ -2253,7 +2309,7 @@ type FileOp struct { func (x *FileOp) Reset() { *x = FileOp{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[28] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2265,7 +2321,7 @@ func (x *FileOp) String() string { func (*FileOp) ProtoMessage() {} func (x *FileOp) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[28] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[29] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2278,7 +2334,7 @@ func (x *FileOp) ProtoReflect() protoreflect.Message { // Deprecated: Use FileOp.ProtoReflect.Descriptor instead. func (*FileOp) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{28} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{29} } func (x *FileOp) GetActions() []*FileAction { @@ -2309,7 +2365,7 @@ type FileAction struct { func (x *FileAction) Reset() { *x = FileAction{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[29] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2321,7 +2377,7 @@ func (x *FileAction) String() string { func (*FileAction) ProtoMessage() {} func (x *FileAction) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[29] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[30] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2334,7 +2390,7 @@ func (x *FileAction) ProtoReflect() protoreflect.Message { // Deprecated: Use FileAction.ProtoReflect.Descriptor instead. func (*FileAction) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{29} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{30} } func (x *FileAction) GetInput() int64 { @@ -2478,7 +2534,7 @@ type FileActionCopy struct { func (x *FileActionCopy) Reset() { *x = FileActionCopy{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[30] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2490,7 +2546,7 @@ func (x *FileActionCopy) String() string { func (*FileActionCopy) ProtoMessage() {} func (x *FileActionCopy) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[30] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[31] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2503,7 +2559,7 @@ func (x *FileActionCopy) ProtoReflect() protoreflect.Message { // Deprecated: Use FileActionCopy.ProtoReflect.Descriptor instead. func (*FileActionCopy) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{30} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{31} } func (x *FileActionCopy) GetSrc() string { @@ -2630,7 +2686,7 @@ type FileActionMkFile struct { func (x *FileActionMkFile) Reset() { *x = FileActionMkFile{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[31] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2642,7 +2698,7 @@ func (x *FileActionMkFile) String() string { func (*FileActionMkFile) ProtoMessage() {} func (x *FileActionMkFile) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[31] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[32] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2655,7 +2711,7 @@ func (x *FileActionMkFile) ProtoReflect() protoreflect.Message { // Deprecated: Use FileActionMkFile.ProtoReflect.Descriptor instead. func (*FileActionMkFile) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{31} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{32} } func (x *FileActionMkFile) GetPath() string { @@ -2710,7 +2766,7 @@ type FileActionSymlink struct { func (x *FileActionSymlink) Reset() { *x = FileActionSymlink{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[32] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2722,7 +2778,7 @@ func (x *FileActionSymlink) String() string { func (*FileActionSymlink) ProtoMessage() {} func (x *FileActionSymlink) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[32] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[33] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2735,7 +2791,7 @@ func (x *FileActionSymlink) ProtoReflect() protoreflect.Message { // Deprecated: Use FileActionSymlink.ProtoReflect.Descriptor instead. func (*FileActionSymlink) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{32} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{33} } func (x *FileActionSymlink) GetOldpath() string { @@ -2785,7 +2841,7 @@ type FileActionMkDir struct { func (x *FileActionMkDir) Reset() { *x = FileActionMkDir{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[33] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2797,7 +2853,7 @@ func (x *FileActionMkDir) String() string { func (*FileActionMkDir) ProtoMessage() {} func (x *FileActionMkDir) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[33] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[34] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2810,7 +2866,7 @@ func (x *FileActionMkDir) ProtoReflect() protoreflect.Message { // Deprecated: Use FileActionMkDir.ProtoReflect.Descriptor instead. func (*FileActionMkDir) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{33} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{34} } func (x *FileActionMkDir) GetPath() string { @@ -2863,7 +2919,7 @@ type FileActionRm struct { func (x *FileActionRm) Reset() { *x = FileActionRm{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[34] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2875,7 +2931,7 @@ func (x *FileActionRm) String() string { func (*FileActionRm) ProtoMessage() {} func (x *FileActionRm) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[34] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[35] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2888,7 +2944,7 @@ func (x *FileActionRm) ProtoReflect() protoreflect.Message { // Deprecated: Use FileActionRm.ProtoReflect.Descriptor instead. func (*FileActionRm) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{34} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{35} } func (x *FileActionRm) GetPath() string { @@ -2923,7 +2979,7 @@ type ChownOpt struct { func (x *ChownOpt) Reset() { *x = ChownOpt{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[35] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2935,7 +2991,7 @@ func (x *ChownOpt) String() string { func (*ChownOpt) ProtoMessage() {} func (x *ChownOpt) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[35] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[36] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2948,7 +3004,7 @@ func (x *ChownOpt) ProtoReflect() protoreflect.Message { // Deprecated: Use ChownOpt.ProtoReflect.Descriptor instead. func (*ChownOpt) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{35} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{36} } func (x *ChownOpt) GetUser() *UserOpt { @@ -2981,7 +3037,7 @@ type UserOpt struct { func (x *UserOpt) Reset() { *x = UserOpt{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[36] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2993,7 +3049,7 @@ func (x *UserOpt) String() string { func (*UserOpt) ProtoMessage() {} func (x *UserOpt) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[36] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[37] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3006,7 +3062,7 @@ func (x *UserOpt) ProtoReflect() protoreflect.Message { // Deprecated: Use UserOpt.ProtoReflect.Descriptor instead. func (*UserOpt) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{36} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{37} } func (m *UserOpt) GetUser() isUserOpt_User { @@ -3057,7 +3113,7 @@ type NamedUserOpt struct { func (x *NamedUserOpt) Reset() { *x = NamedUserOpt{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[37] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3069,7 +3125,7 @@ func (x *NamedUserOpt) String() string { func (*NamedUserOpt) ProtoMessage() {} func (x *NamedUserOpt) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[37] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[38] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3082,7 +3138,7 @@ func (x *NamedUserOpt) ProtoReflect() protoreflect.Message { // Deprecated: Use NamedUserOpt.ProtoReflect.Descriptor instead. func (*NamedUserOpt) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{37} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{38} } func (x *NamedUserOpt) GetName() string { @@ -3109,7 +3165,7 @@ type MergeInput struct { func (x *MergeInput) Reset() { *x = MergeInput{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[38] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3121,7 +3177,7 @@ func (x *MergeInput) String() string { func (*MergeInput) ProtoMessage() {} func (x *MergeInput) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[38] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[39] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3134,7 +3190,7 @@ func (x *MergeInput) ProtoReflect() protoreflect.Message { // Deprecated: Use MergeInput.ProtoReflect.Descriptor instead. func (*MergeInput) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{38} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{39} } func (x *MergeInput) GetInput() int64 { @@ -3154,7 +3210,7 @@ type MergeOp struct { func (x *MergeOp) Reset() { *x = MergeOp{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[39] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3166,7 +3222,7 @@ func (x *MergeOp) String() string { func (*MergeOp) ProtoMessage() {} func (x *MergeOp) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[39] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[40] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3179,7 +3235,7 @@ func (x *MergeOp) ProtoReflect() protoreflect.Message { // Deprecated: Use MergeOp.ProtoReflect.Descriptor instead. func (*MergeOp) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{39} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{40} } func (x *MergeOp) GetInputs() []*MergeInput { @@ -3199,7 +3255,7 @@ type LowerDiffInput struct { func (x *LowerDiffInput) Reset() { *x = LowerDiffInput{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[40] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3211,7 +3267,7 @@ func (x *LowerDiffInput) String() string { func (*LowerDiffInput) ProtoMessage() {} func (x *LowerDiffInput) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[40] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[41] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3224,7 +3280,7 @@ func (x *LowerDiffInput) ProtoReflect() protoreflect.Message { // Deprecated: Use LowerDiffInput.ProtoReflect.Descriptor instead. func (*LowerDiffInput) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{40} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{41} } func (x *LowerDiffInput) GetInput() int64 { @@ -3244,7 +3300,7 @@ type UpperDiffInput struct { func (x *UpperDiffInput) Reset() { *x = UpperDiffInput{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[41] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3256,7 +3312,7 @@ func (x *UpperDiffInput) String() string { func (*UpperDiffInput) ProtoMessage() {} func (x *UpperDiffInput) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[41] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[42] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3269,7 +3325,7 @@ func (x *UpperDiffInput) ProtoReflect() protoreflect.Message { // Deprecated: Use UpperDiffInput.ProtoReflect.Descriptor instead. func (*UpperDiffInput) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{41} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{42} } func (x *UpperDiffInput) GetInput() int64 { @@ -3290,7 +3346,7 @@ type DiffOp struct { func (x *DiffOp) Reset() { *x = DiffOp{} - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[42] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3302,7 +3358,7 @@ func (x *DiffOp) String() string { func (*DiffOp) ProtoMessage() {} func (x *DiffOp) ProtoReflect() protoreflect.Message { - mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[42] + mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[43] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3315,7 +3371,7 @@ func (x *DiffOp) ProtoReflect() protoreflect.Message { // Deprecated: Use DiffOp.ProtoReflect.Descriptor instead. func (*DiffOp) Descriptor() ([]byte, []int) { - return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{42} + return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{43} } func (x *DiffOp) GetLower() *LowerDiffInput { @@ -3373,7 +3429,7 @@ var file_github_com_moby_buildkit_solver_pb_ops_proto_rawDesc = []byte{ 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0x35, 0x0a, 0x05, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0xcb, 0x01, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0xfa, 0x01, 0x0a, 0x06, 0x45, 0x78, 0x65, 0x63, 0x4f, 0x70, 0x12, 0x1c, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x06, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x73, @@ -3386,43 +3442,48 @@ var file_github_com_moby_buildkit_solver_pb_ops_proto_rawDesc = []byte{ 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x08, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x2b, 0x0a, 0x09, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x76, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x45, 0x6e, 0x76, - 0x52, 0x09, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x76, 0x22, 0xf3, 0x02, 0x0a, 0x04, - 0x4d, 0x65, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x77, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x63, 0x77, 0x64, 0x12, 0x12, 0x0a, 0x04, - 0x75, 0x73, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, - 0x12, 0x29, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x65, 0x6e, 0x76, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x45, 0x6e, - 0x76, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x45, 0x6e, 0x76, 0x12, 0x2a, 0x0a, 0x0a, 0x65, - 0x78, 0x74, 0x72, 0x61, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x0a, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x49, 0x50, 0x52, 0x0a, 0x65, 0x78, 0x74, - 0x72, 0x61, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x75, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x09, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, - 0x06, 0x75, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x19, 0x72, + 0x52, 0x09, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x76, 0x12, 0x2d, 0x0a, 0x0a, 0x63, + 0x64, 0x69, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x44, 0x49, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x0a, + 0x63, 0x64, 0x69, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0xf3, 0x02, 0x0a, 0x04, 0x4d, + 0x65, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x77, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x63, 0x77, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x75, + 0x73, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, + 0x29, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x65, 0x6e, 0x76, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x45, 0x6e, 0x76, + 0x52, 0x08, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x45, 0x6e, 0x76, 0x12, 0x2a, 0x0a, 0x0a, 0x65, 0x78, + 0x74, 0x72, 0x61, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, + 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x49, 0x50, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x72, + 0x61, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x75, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x09, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x06, + 0x75, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x19, 0x72, 0x65, + 0x6d, 0x6f, 0x76, 0x65, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x74, 0x75, 0x62, 0x73, 0x52, 0x65, + 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x74, 0x75, 0x62, 0x73, 0x52, - 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, - 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x74, 0x75, 0x62, 0x73, - 0x52, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x45, 0x78, 0x69, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, - 0x05, 0x52, 0x0e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x45, 0x78, 0x69, 0x74, 0x43, 0x6f, 0x64, 0x65, - 0x73, 0x22, 0x2c, 0x0a, 0x06, 0x48, 0x6f, 0x73, 0x74, 0x49, 0x50, 0x12, 0x12, 0x0a, 0x04, 0x48, - 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, - 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x22, - 0x44, 0x0a, 0x06, 0x55, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x53, 0x6f, 0x66, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x53, 0x6f, 0x66, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x48, 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x04, 0x48, 0x61, 0x72, 0x64, 0x22, 0x4b, 0x0a, 0x09, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x45, - 0x6e, 0x76, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, - 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x61, 0x6c, 0x22, 0xaa, 0x03, 0x0a, 0x05, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, + 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x45, 0x78, 0x69, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x05, + 0x52, 0x0e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x45, 0x78, 0x69, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x73, + 0x22, 0x2c, 0x0a, 0x06, 0x48, 0x6f, 0x73, 0x74, 0x49, 0x50, 0x12, 0x12, 0x0a, 0x04, 0x48, 0x6f, + 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x0e, + 0x0a, 0x02, 0x49, 0x50, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x22, 0x44, + 0x0a, 0x06, 0x55, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x53, 0x6f, 0x66, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x53, 0x6f, 0x66, 0x74, + 0x12, 0x12, 0x0a, 0x04, 0x48, 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, + 0x48, 0x61, 0x72, 0x64, 0x22, 0x4b, 0x0a, 0x09, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x45, 0x6e, + 0x76, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, + 0x44, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, + 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, + 0x6c, 0x22, 0x1f, 0x0a, 0x09, 0x43, 0x44, 0x49, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x22, 0xaa, 0x03, 0x0a, 0x05, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x12, @@ -3756,7 +3817,7 @@ func file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP() []byte { } var file_github_com_moby_buildkit_solver_pb_ops_proto_enumTypes = make([]protoimpl.EnumInfo, 5) -var file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes = make([]protoimpl.MessageInfo, 50) +var file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes = make([]protoimpl.MessageInfo, 51) var file_github_com_moby_buildkit_solver_pb_ops_proto_goTypes = []any{ (NetMode)(0), // 0: pb.NetMode (SecurityMode)(0), // 1: pb.SecurityMode @@ -3771,115 +3832,117 @@ var file_github_com_moby_buildkit_solver_pb_ops_proto_goTypes = []any{ (*HostIP)(nil), // 10: pb.HostIP (*Ulimit)(nil), // 11: pb.Ulimit (*SecretEnv)(nil), // 12: pb.SecretEnv - (*Mount)(nil), // 13: pb.Mount - (*TmpfsOpt)(nil), // 14: pb.TmpfsOpt - (*CacheOpt)(nil), // 15: pb.CacheOpt - (*SecretOpt)(nil), // 16: pb.SecretOpt - (*SSHOpt)(nil), // 17: pb.SSHOpt - (*SourceOp)(nil), // 18: pb.SourceOp - (*BuildOp)(nil), // 19: pb.BuildOp - (*BuildInput)(nil), // 20: pb.BuildInput - (*OpMetadata)(nil), // 21: pb.OpMetadata - (*Source)(nil), // 22: pb.Source - (*Locations)(nil), // 23: pb.Locations - (*SourceInfo)(nil), // 24: pb.SourceInfo - (*Location)(nil), // 25: pb.Location - (*Range)(nil), // 26: pb.Range - (*Position)(nil), // 27: pb.Position - (*ExportCache)(nil), // 28: pb.ExportCache - (*ProgressGroup)(nil), // 29: pb.ProgressGroup - (*ProxyEnv)(nil), // 30: pb.ProxyEnv - (*WorkerConstraints)(nil), // 31: pb.WorkerConstraints - (*Definition)(nil), // 32: pb.Definition - (*FileOp)(nil), // 33: pb.FileOp - (*FileAction)(nil), // 34: pb.FileAction - (*FileActionCopy)(nil), // 35: pb.FileActionCopy - (*FileActionMkFile)(nil), // 36: pb.FileActionMkFile - (*FileActionSymlink)(nil), // 37: pb.FileActionSymlink - (*FileActionMkDir)(nil), // 38: pb.FileActionMkDir - (*FileActionRm)(nil), // 39: pb.FileActionRm - (*ChownOpt)(nil), // 40: pb.ChownOpt - (*UserOpt)(nil), // 41: pb.UserOpt - (*NamedUserOpt)(nil), // 42: pb.NamedUserOpt - (*MergeInput)(nil), // 43: pb.MergeInput - (*MergeOp)(nil), // 44: pb.MergeOp - (*LowerDiffInput)(nil), // 45: pb.LowerDiffInput - (*UpperDiffInput)(nil), // 46: pb.UpperDiffInput - (*DiffOp)(nil), // 47: pb.DiffOp - nil, // 48: pb.SourceOp.AttrsEntry - nil, // 49: pb.BuildOp.InputsEntry - nil, // 50: pb.BuildOp.AttrsEntry - nil, // 51: pb.OpMetadata.DescriptionEntry - nil, // 52: pb.OpMetadata.CapsEntry - nil, // 53: pb.Source.LocationsEntry - nil, // 54: pb.Definition.MetadataEntry + (*CDIDevice)(nil), // 13: pb.CDIDevice + (*Mount)(nil), // 14: pb.Mount + (*TmpfsOpt)(nil), // 15: pb.TmpfsOpt + (*CacheOpt)(nil), // 16: pb.CacheOpt + (*SecretOpt)(nil), // 17: pb.SecretOpt + (*SSHOpt)(nil), // 18: pb.SSHOpt + (*SourceOp)(nil), // 19: pb.SourceOp + (*BuildOp)(nil), // 20: pb.BuildOp + (*BuildInput)(nil), // 21: pb.BuildInput + (*OpMetadata)(nil), // 22: pb.OpMetadata + (*Source)(nil), // 23: pb.Source + (*Locations)(nil), // 24: pb.Locations + (*SourceInfo)(nil), // 25: pb.SourceInfo + (*Location)(nil), // 26: pb.Location + (*Range)(nil), // 27: pb.Range + (*Position)(nil), // 28: pb.Position + (*ExportCache)(nil), // 29: pb.ExportCache + (*ProgressGroup)(nil), // 30: pb.ProgressGroup + (*ProxyEnv)(nil), // 31: pb.ProxyEnv + (*WorkerConstraints)(nil), // 32: pb.WorkerConstraints + (*Definition)(nil), // 33: pb.Definition + (*FileOp)(nil), // 34: pb.FileOp + (*FileAction)(nil), // 35: pb.FileAction + (*FileActionCopy)(nil), // 36: pb.FileActionCopy + (*FileActionMkFile)(nil), // 37: pb.FileActionMkFile + (*FileActionSymlink)(nil), // 38: pb.FileActionSymlink + (*FileActionMkDir)(nil), // 39: pb.FileActionMkDir + (*FileActionRm)(nil), // 40: pb.FileActionRm + (*ChownOpt)(nil), // 41: pb.ChownOpt + (*UserOpt)(nil), // 42: pb.UserOpt + (*NamedUserOpt)(nil), // 43: pb.NamedUserOpt + (*MergeInput)(nil), // 44: pb.MergeInput + (*MergeOp)(nil), // 45: pb.MergeOp + (*LowerDiffInput)(nil), // 46: pb.LowerDiffInput + (*UpperDiffInput)(nil), // 47: pb.UpperDiffInput + (*DiffOp)(nil), // 48: pb.DiffOp + nil, // 49: pb.SourceOp.AttrsEntry + nil, // 50: pb.BuildOp.InputsEntry + nil, // 51: pb.BuildOp.AttrsEntry + nil, // 52: pb.OpMetadata.DescriptionEntry + nil, // 53: pb.OpMetadata.CapsEntry + nil, // 54: pb.Source.LocationsEntry + nil, // 55: pb.Definition.MetadataEntry } var file_github_com_moby_buildkit_solver_pb_ops_proto_depIdxs = []int32{ 7, // 0: pb.Op.inputs:type_name -> pb.Input 8, // 1: pb.Op.exec:type_name -> pb.ExecOp - 18, // 2: pb.Op.source:type_name -> pb.SourceOp - 33, // 3: pb.Op.file:type_name -> pb.FileOp - 19, // 4: pb.Op.build:type_name -> pb.BuildOp - 44, // 5: pb.Op.merge:type_name -> pb.MergeOp - 47, // 6: pb.Op.diff:type_name -> pb.DiffOp + 19, // 2: pb.Op.source:type_name -> pb.SourceOp + 34, // 3: pb.Op.file:type_name -> pb.FileOp + 20, // 4: pb.Op.build:type_name -> pb.BuildOp + 45, // 5: pb.Op.merge:type_name -> pb.MergeOp + 48, // 6: pb.Op.diff:type_name -> pb.DiffOp 6, // 7: pb.Op.platform:type_name -> pb.Platform - 31, // 8: pb.Op.constraints:type_name -> pb.WorkerConstraints + 32, // 8: pb.Op.constraints:type_name -> pb.WorkerConstraints 9, // 9: pb.ExecOp.meta:type_name -> pb.Meta - 13, // 10: pb.ExecOp.mounts:type_name -> pb.Mount + 14, // 10: pb.ExecOp.mounts:type_name -> pb.Mount 0, // 11: pb.ExecOp.network:type_name -> pb.NetMode 1, // 12: pb.ExecOp.security:type_name -> pb.SecurityMode 12, // 13: pb.ExecOp.secretenv:type_name -> pb.SecretEnv - 30, // 14: pb.Meta.proxy_env:type_name -> pb.ProxyEnv - 10, // 15: pb.Meta.extraHosts:type_name -> pb.HostIP - 11, // 16: pb.Meta.ulimit:type_name -> pb.Ulimit - 2, // 17: pb.Mount.mountType:type_name -> pb.MountType - 14, // 18: pb.Mount.TmpfsOpt:type_name -> pb.TmpfsOpt - 15, // 19: pb.Mount.cacheOpt:type_name -> pb.CacheOpt - 16, // 20: pb.Mount.secretOpt:type_name -> pb.SecretOpt - 17, // 21: pb.Mount.SSHOpt:type_name -> pb.SSHOpt - 3, // 22: pb.Mount.contentCache:type_name -> pb.MountContentCache - 4, // 23: pb.CacheOpt.sharing:type_name -> pb.CacheSharingOpt - 48, // 24: pb.SourceOp.attrs:type_name -> pb.SourceOp.AttrsEntry - 49, // 25: pb.BuildOp.inputs:type_name -> pb.BuildOp.InputsEntry - 32, // 26: pb.BuildOp.def:type_name -> pb.Definition - 50, // 27: pb.BuildOp.attrs:type_name -> pb.BuildOp.AttrsEntry - 51, // 28: pb.OpMetadata.description:type_name -> pb.OpMetadata.DescriptionEntry - 28, // 29: pb.OpMetadata.export_cache:type_name -> pb.ExportCache - 52, // 30: pb.OpMetadata.caps:type_name -> pb.OpMetadata.CapsEntry - 29, // 31: pb.OpMetadata.progress_group:type_name -> pb.ProgressGroup - 53, // 32: pb.Source.locations:type_name -> pb.Source.LocationsEntry - 24, // 33: pb.Source.infos:type_name -> pb.SourceInfo - 25, // 34: pb.Locations.locations:type_name -> pb.Location - 32, // 35: pb.SourceInfo.definition:type_name -> pb.Definition - 26, // 36: pb.Location.ranges:type_name -> pb.Range - 27, // 37: pb.Range.start:type_name -> pb.Position - 27, // 38: pb.Range.end:type_name -> pb.Position - 54, // 39: pb.Definition.metadata:type_name -> pb.Definition.MetadataEntry - 22, // 40: pb.Definition.Source:type_name -> pb.Source - 34, // 41: pb.FileOp.actions:type_name -> pb.FileAction - 35, // 42: pb.FileAction.copy:type_name -> pb.FileActionCopy - 36, // 43: pb.FileAction.mkfile:type_name -> pb.FileActionMkFile - 38, // 44: pb.FileAction.mkdir:type_name -> pb.FileActionMkDir - 39, // 45: pb.FileAction.rm:type_name -> pb.FileActionRm - 37, // 46: pb.FileAction.symlink:type_name -> pb.FileActionSymlink - 40, // 47: pb.FileActionCopy.owner:type_name -> pb.ChownOpt - 40, // 48: pb.FileActionMkFile.owner:type_name -> pb.ChownOpt - 40, // 49: pb.FileActionSymlink.owner:type_name -> pb.ChownOpt - 40, // 50: pb.FileActionMkDir.owner:type_name -> pb.ChownOpt - 41, // 51: pb.ChownOpt.user:type_name -> pb.UserOpt - 41, // 52: pb.ChownOpt.group:type_name -> pb.UserOpt - 42, // 53: pb.UserOpt.byName:type_name -> pb.NamedUserOpt - 43, // 54: pb.MergeOp.inputs:type_name -> pb.MergeInput - 45, // 55: pb.DiffOp.lower:type_name -> pb.LowerDiffInput - 46, // 56: pb.DiffOp.upper:type_name -> pb.UpperDiffInput - 20, // 57: pb.BuildOp.InputsEntry.value:type_name -> pb.BuildInput - 23, // 58: pb.Source.LocationsEntry.value:type_name -> pb.Locations - 21, // 59: pb.Definition.MetadataEntry.value:type_name -> pb.OpMetadata - 60, // [60:60] is the sub-list for method output_type - 60, // [60:60] is the sub-list for method input_type - 60, // [60:60] is the sub-list for extension type_name - 60, // [60:60] is the sub-list for extension extendee - 0, // [0:60] is the sub-list for field type_name + 13, // 14: pb.ExecOp.cdiDevices:type_name -> pb.CDIDevice + 31, // 15: pb.Meta.proxy_env:type_name -> pb.ProxyEnv + 10, // 16: pb.Meta.extraHosts:type_name -> pb.HostIP + 11, // 17: pb.Meta.ulimit:type_name -> pb.Ulimit + 2, // 18: pb.Mount.mountType:type_name -> pb.MountType + 15, // 19: pb.Mount.TmpfsOpt:type_name -> pb.TmpfsOpt + 16, // 20: pb.Mount.cacheOpt:type_name -> pb.CacheOpt + 17, // 21: pb.Mount.secretOpt:type_name -> pb.SecretOpt + 18, // 22: pb.Mount.SSHOpt:type_name -> pb.SSHOpt + 3, // 23: pb.Mount.contentCache:type_name -> pb.MountContentCache + 4, // 24: pb.CacheOpt.sharing:type_name -> pb.CacheSharingOpt + 49, // 25: pb.SourceOp.attrs:type_name -> pb.SourceOp.AttrsEntry + 50, // 26: pb.BuildOp.inputs:type_name -> pb.BuildOp.InputsEntry + 33, // 27: pb.BuildOp.def:type_name -> pb.Definition + 51, // 28: pb.BuildOp.attrs:type_name -> pb.BuildOp.AttrsEntry + 52, // 29: pb.OpMetadata.description:type_name -> pb.OpMetadata.DescriptionEntry + 29, // 30: pb.OpMetadata.export_cache:type_name -> pb.ExportCache + 53, // 31: pb.OpMetadata.caps:type_name -> pb.OpMetadata.CapsEntry + 30, // 32: pb.OpMetadata.progress_group:type_name -> pb.ProgressGroup + 54, // 33: pb.Source.locations:type_name -> pb.Source.LocationsEntry + 25, // 34: pb.Source.infos:type_name -> pb.SourceInfo + 26, // 35: pb.Locations.locations:type_name -> pb.Location + 33, // 36: pb.SourceInfo.definition:type_name -> pb.Definition + 27, // 37: pb.Location.ranges:type_name -> pb.Range + 28, // 38: pb.Range.start:type_name -> pb.Position + 28, // 39: pb.Range.end:type_name -> pb.Position + 55, // 40: pb.Definition.metadata:type_name -> pb.Definition.MetadataEntry + 23, // 41: pb.Definition.Source:type_name -> pb.Source + 35, // 42: pb.FileOp.actions:type_name -> pb.FileAction + 36, // 43: pb.FileAction.copy:type_name -> pb.FileActionCopy + 37, // 44: pb.FileAction.mkfile:type_name -> pb.FileActionMkFile + 39, // 45: pb.FileAction.mkdir:type_name -> pb.FileActionMkDir + 40, // 46: pb.FileAction.rm:type_name -> pb.FileActionRm + 38, // 47: pb.FileAction.symlink:type_name -> pb.FileActionSymlink + 41, // 48: pb.FileActionCopy.owner:type_name -> pb.ChownOpt + 41, // 49: pb.FileActionMkFile.owner:type_name -> pb.ChownOpt + 41, // 50: pb.FileActionSymlink.owner:type_name -> pb.ChownOpt + 41, // 51: pb.FileActionMkDir.owner:type_name -> pb.ChownOpt + 42, // 52: pb.ChownOpt.user:type_name -> pb.UserOpt + 42, // 53: pb.ChownOpt.group:type_name -> pb.UserOpt + 43, // 54: pb.UserOpt.byName:type_name -> pb.NamedUserOpt + 44, // 55: pb.MergeOp.inputs:type_name -> pb.MergeInput + 46, // 56: pb.DiffOp.lower:type_name -> pb.LowerDiffInput + 47, // 57: pb.DiffOp.upper:type_name -> pb.UpperDiffInput + 21, // 58: pb.BuildOp.InputsEntry.value:type_name -> pb.BuildInput + 24, // 59: pb.Source.LocationsEntry.value:type_name -> pb.Locations + 22, // 60: pb.Definition.MetadataEntry.value:type_name -> pb.OpMetadata + 61, // [61:61] is the sub-list for method output_type + 61, // [61:61] is the sub-list for method input_type + 61, // [61:61] is the sub-list for extension type_name + 61, // [61:61] is the sub-list for extension extendee + 0, // [0:61] is the sub-list for field type_name } func init() { file_github_com_moby_buildkit_solver_pb_ops_proto_init() } @@ -3895,14 +3958,14 @@ func file_github_com_moby_buildkit_solver_pb_ops_proto_init() { (*Op_Merge)(nil), (*Op_Diff)(nil), } - file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[29].OneofWrappers = []any{ + file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[30].OneofWrappers = []any{ (*FileAction_Copy)(nil), (*FileAction_Mkfile)(nil), (*FileAction_Mkdir)(nil), (*FileAction_Rm)(nil), (*FileAction_Symlink)(nil), } - file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[36].OneofWrappers = []any{ + file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[37].OneofWrappers = []any{ (*UserOpt_ByName)(nil), (*UserOpt_ByID)(nil), } @@ -3912,7 +3975,7 @@ func file_github_com_moby_buildkit_solver_pb_ops_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_github_com_moby_buildkit_solver_pb_ops_proto_rawDesc, NumEnums: 5, - NumMessages: 50, + NumMessages: 51, NumExtensions: 0, NumServices: 0, }, diff --git a/solver/pb/ops.proto b/solver/pb/ops.proto index 5ff388299cef..eeafec12f0a2 100644 --- a/solver/pb/ops.proto +++ b/solver/pb/ops.proto @@ -47,6 +47,7 @@ message ExecOp { NetMode network = 3; SecurityMode security = 4; repeated SecretEnv secretenv = 5; + repeated CDIDevice cdiDevices = 6; } // Meta is a set of arguments for ExecOp. @@ -95,6 +96,13 @@ message SecretEnv { bool optional = 3; } +// CDIDevice specifies a CDI device information. +message CDIDevice { + // Fully qualified CDI device name (e.g., vendor.com/gpu=gpudevice1) + // https://github.com/cncf-tags/container-device-interface/blob/main/SPEC.md + string name = 1; +} + // Mount specifies how to mount an input Op as a filesystem. message Mount { int64 input = 1; diff --git a/solver/pb/ops_vtproto.pb.go b/solver/pb/ops_vtproto.pb.go index 9d71812423f7..16df7e6ae446 100644 --- a/solver/pb/ops_vtproto.pb.go +++ b/solver/pb/ops_vtproto.pb.go @@ -166,6 +166,13 @@ func (m *ExecOp) CloneVT() *ExecOp { } r.Secretenv = tmpContainer } + if rhs := m.CdiDevices; rhs != nil { + tmpContainer := make([]*CDIDevice, len(rhs)) + for k, v := range rhs { + tmpContainer[k] = v.CloneVT() + } + r.CdiDevices = tmpContainer + } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -284,6 +291,23 @@ func (m *SecretEnv) CloneMessageVT() proto.Message { return m.CloneVT() } +func (m *CDIDevice) CloneVT() *CDIDevice { + if m == nil { + return (*CDIDevice)(nil) + } + r := new(CDIDevice) + r.Name = m.Name + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *CDIDevice) CloneMessageVT() proto.Message { + return m.CloneVT() +} + func (m *Mount) CloneVT() *Mount { if m == nil { return (*Mount)(nil) @@ -1429,6 +1453,23 @@ func (this *ExecOp) EqualVT(that *ExecOp) bool { } } } + if len(this.CdiDevices) != len(that.CdiDevices) { + return false + } + for i, vx := range this.CdiDevices { + vy := that.CdiDevices[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &CDIDevice{} + } + if q == nil { + q = &CDIDevice{} + } + if !p.EqualVT(q) { + return false + } + } + } return string(this.unknownFields) == string(that.unknownFields) } @@ -1606,6 +1647,25 @@ func (this *SecretEnv) EqualMessageVT(thatMsg proto.Message) bool { } return this.EqualVT(that) } +func (this *CDIDevice) EqualVT(that *CDIDevice) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *CDIDevice) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*CDIDevice) + if !ok { + return false + } + return this.EqualVT(that) +} func (this *Mount) EqualVT(that *Mount) bool { if this == that { return true @@ -3220,6 +3280,18 @@ func (m *ExecOp) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.CdiDevices) > 0 { + for iNdEx := len(m.CdiDevices) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.CdiDevices[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x32 + } + } if len(m.Secretenv) > 0 { for iNdEx := len(m.Secretenv) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Secretenv[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) @@ -3565,6 +3637,46 @@ func (m *SecretEnv) MarshalToSizedBufferVT(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *CDIDevice) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CDIDevice) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *CDIDevice) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *Mount) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil @@ -6023,6 +6135,12 @@ func (m *ExecOp) SizeVT() (n int) { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } + if len(m.CdiDevices) > 0 { + for _, e := range m.CdiDevices { + l = e.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + } n += len(m.unknownFields) return n } @@ -6150,6 +6268,20 @@ func (m *SecretEnv) SizeVT() (n int) { return n } +func (m *CDIDevice) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + func (m *Mount) SizeVT() (n int) { if m == nil { return 0 @@ -7936,6 +8068,40 @@ func (m *ExecOp) UnmarshalVT(dAtA []byte) error { return err } iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CdiDevices", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CdiDevices = append(m.CdiDevices, &CDIDevice{}) + if err := m.CdiDevices[len(m.CdiDevices)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) @@ -8772,6 +8938,89 @@ func (m *SecretEnv) UnmarshalVT(dAtA []byte) error { } return nil } +func (m *CDIDevice) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CDIDevice: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CDIDevice: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *Mount) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/util/testutil/integration/run.go b/util/testutil/integration/run.go index 4dbbcf229c9b..2c08102914ac 100644 --- a/util/testutil/integration/run.go +++ b/util/testutil/integration/run.go @@ -60,12 +60,14 @@ type Sandbox interface { NewRegistry() (string, error) Value(string) interface{} // chosen matrix value Name() string + CDISpecDir() string } // BackendConfig is used to configure backends created by a worker. type BackendConfig struct { Logs map[string]*bytes.Buffer DaemonConfig []ConfigUpdater + CDISpecDir string } type Worker interface { diff --git a/util/testutil/integration/sandbox.go b/util/testutil/integration/sandbox.go index f28fbf1765dc..76c5e110b577 100644 --- a/util/testutil/integration/sandbox.go +++ b/util/testutil/integration/sandbox.go @@ -27,11 +27,12 @@ const maxSandboxTimeout = 5 * time.Minute type sandbox struct { Backend - logs map[string]*bytes.Buffer - cleanup *MultiCloser - mv matrixValue - ctx context.Context - name string + logs map[string]*bytes.Buffer + cleanup *MultiCloser + mv matrixValue + ctx context.Context + cdiSpecDir string + name string } func (sb *sandbox) Name() string { @@ -42,6 +43,10 @@ func (sb *sandbox) Context() context.Context { return sb.ctx } +func (sb *sandbox) CDISpecDir() string { + return sb.cdiSpecDir +} + func (sb *sandbox) Logs() map[string]*bytes.Buffer { return sb.logs } @@ -110,6 +115,15 @@ func newSandbox(ctx context.Context, t *testing.T, w Worker, mirror string, mv m } }() + cdiSpecDir, err := os.MkdirTemp("", "buildkit-integration-cdi") + if err != nil { + return nil, nil, errors.Wrap(err, "cannot create cdi spec dir") + } + deferF.Append(func() error { + return os.RemoveAll(cdiSpecDir) + }) + cfg.CDISpecDir = cdiSpecDir + b, closer, err := w.New(ctx, cfg) if err != nil { return nil, nil, errors.Wrap(err, "creating worker") @@ -139,12 +153,13 @@ func newSandbox(ctx context.Context, t *testing.T, w Worker, mirror string, mv m }() return &sandbox{ - Backend: b, - logs: cfg.Logs, - cleanup: deferF, - mv: mv, - ctx: ctx, - name: w.Name(), + Backend: b, + logs: cfg.Logs, + cleanup: deferF, + mv: mv, + ctx: ctx, + cdiSpecDir: cfg.CDISpecDir, + name: w.Name(), }, cl, nil } diff --git a/util/testutil/workers/oci.go b/util/testutil/workers/oci.go index 54eefd130d99..5810a040dbe2 100644 --- a/util/testutil/workers/oci.go +++ b/util/testutil/workers/oci.go @@ -47,7 +47,13 @@ func (s *OCI) New(ctx context.Context, cfg *integration.BackendConfig) (integrat return nil, nil, err } // Include use of --oci-worker-labels to trigger https://github.com/moby/buildkit/pull/603 - buildkitdArgs := []string{"buildkitd", "--oci-worker=true", "--containerd-worker=false", "--oci-worker-gc=false", "--oci-worker-labels=org.mobyproject.buildkit.worker.sandbox=true"} + buildkitdArgs := []string{"buildkitd", + "--oci-worker=true", + "--containerd-worker=false", + "--oci-worker-gc=false", + "--oci-worker-labels=org.mobyproject.buildkit.worker.sandbox=true", + "--cdi-spec-dir=" + cfg.CDISpecDir, + } if s.Snapshotter != "" { buildkitdArgs = append(buildkitdArgs, From 0fe26ed106aa3df5c37f7ec1815fed0fa32b1f90 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Wed, 9 Oct 2024 16:14:26 +0200 Subject: [PATCH 03/23] dockerfile: device support with frontend attribute Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- frontend/dockerfile/dockerfile2llb/convert.go | 9 +++ .../dockerfile/dockerfile_rundevice_test.go | 76 +++++++++++++++++++ frontend/dockerfile/dockerfile_test.go | 18 +++++ frontend/dockerui/attr.go | 23 ++++++ frontend/dockerui/config.go | 8 ++ 5 files changed, 134 insertions(+) create mode 100644 frontend/dockerfile/dockerfile_rundevice_test.go diff --git a/frontend/dockerfile/dockerfile2llb/convert.go b/frontend/dockerfile/dockerfile2llb/convert.go index c084e3991ee3..f1bb6c72689b 100644 --- a/frontend/dockerfile/dockerfile2llb/convert.go +++ b/frontend/dockerfile/dockerfile2llb/convert.go @@ -724,6 +724,7 @@ func toDispatchState(ctx context.Context, dt []byte, opt ConvertOpt) (*dispatchS extraHosts: opt.ExtraHosts, shmSize: opt.ShmSize, ulimit: opt.Ulimits, + devices: opt.Devices, cgroupParent: opt.CgroupParent, llbCaps: opt.LLBCaps, sourceMap: opt.SourceMap, @@ -859,6 +860,7 @@ type dispatchOpt struct { extraHosts []llb.HostIP shmSize int64 ulimit []*pb.Ulimit + devices []*pb.CDIDevice cgroupParent string llbCaps *apicaps.CapSet sourceMap *llb.SourceMap @@ -1303,6 +1305,13 @@ func dispatchRun(d *dispatchState, c *instructions.RunCommand, proxy *llb.ProxyE } } + // TODO: use entitlements and put this on labs + if dopt.llbCaps != nil && dopt.llbCaps.Supports(pb.CapExecMetaCDI) == nil { + for _, u := range dopt.devices { + opt = append(opt, llb.AddCDIDevice(u.Name)) + } + } + shlex := *dopt.shlex shlex.RawQuotes = true shlex.SkipUnsetEnv = true diff --git a/frontend/dockerfile/dockerfile_rundevice_test.go b/frontend/dockerfile/dockerfile_rundevice_test.go new file mode 100644 index 000000000000..acf77e5eb960 --- /dev/null +++ b/frontend/dockerfile/dockerfile_rundevice_test.go @@ -0,0 +1,76 @@ +package dockerfile + +import ( + "os" + "path/filepath" + "testing" + + "github.com/containerd/continuity/fs/fstest" + "github.com/moby/buildkit/client" + "github.com/moby/buildkit/frontend/dockerui" + "github.com/moby/buildkit/util/testutil/integration" + "github.com/stretchr/testify/require" + "github.com/tonistiigi/fsutil" +) + +var deviceTests = integration.TestFuncs( + testDeviceEnv, +) + +func testDeviceEnv(t *testing.T, sb integration.Sandbox) { + if sb.Rootless() { + t.SkipNow() + } + + integration.SkipOnPlatform(t, "windows") + f := getFrontend(t, sb) + + require.NoError(t, os.WriteFile(filepath.Join(sb.CDISpecDir(), "vendor1-device.yaml"), []byte(` +cdiVersion: "0.3.0" +kind: "vendor1.com/device" +devices: +- name: foo + containerEdits: + env: + - FOO=injected +`), 0600)) + + dockerfile := []byte(` +FROM busybox AS base +RUN env|sort | tee foo.env +FROM scratch +COPY --from=base /foo.env / +`) + + dir := integration.Tmpdir( + t, + fstest.CreateFile("Dockerfile", dockerfile, 0600), + ) + + c, err := client.New(sb.Context(), sb.Address()) + require.NoError(t, err) + defer c.Close() + + destDir := t.TempDir() + + _, err = f.Solve(sb.Context(), c, client.SolveOpt{ + FrontendAttrs: map[string]string{ + "device": "vendor1.com/device=foo", + }, + LocalMounts: map[string]fsutil.FS{ + dockerui.DefaultLocalNameDockerfile: dir, + dockerui.DefaultLocalNameContext: dir, + }, + Exports: []client.ExportEntry{ + { + Type: client.ExporterLocal, + OutputDir: destDir, + }, + }, + }, nil) + require.NoError(t, err) + + dt, err := os.ReadFile(filepath.Join(destDir, "foo.env")) + require.NoError(t, err) + require.Contains(t, string(dt), `FOO=injected`) +} diff --git a/frontend/dockerfile/dockerfile_test.go b/frontend/dockerfile/dockerfile_test.go index c1195dbbb503..2879ec3b62a5 100644 --- a/frontend/dockerfile/dockerfile_test.go +++ b/frontend/dockerfile/dockerfile_test.go @@ -301,6 +301,11 @@ func TestIntegration(t *testing.T) { "granted": networkHostGranted, "denied": networkHostDenied, }))...) + + integration.Run(t, deviceTests, append(opts, + integration.WithMatrix("cdi", map[string]interface{}{ + "enabled": enableCDI, + }))...) } func testEmptyStringArgInEnv(t *testing.T, sb integration.Sandbox) { @@ -9897,6 +9902,19 @@ var ( networkHostDenied integration.ConfigUpdater = &networkModeSandbox{} ) +type cdiEnabled struct{} + +func (*cdiEnabled) UpdateConfigFile(in string) string { + return in + ` +[cdi] +enabled = true +` +} + +var ( + enableCDI integration.ConfigUpdater = &cdiEnabled{} +) + func fixedWriteCloser(wc io.WriteCloser) filesync.FileOutputFunc { return func(map[string]string) (io.WriteCloser, error) { return wc, nil diff --git a/frontend/dockerui/attr.go b/frontend/dockerui/attr.go index 259100b46381..8a0697383418 100644 --- a/frontend/dockerui/attr.go +++ b/frontend/dockerui/attr.go @@ -1,6 +1,7 @@ package dockerui import ( + "encoding/csv" "net" "strconv" "strings" @@ -13,6 +14,7 @@ import ( ocispecs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/tonistiigi/go-csvvalue" + "tags.cncf.io/container-device-interface/pkg/parser" ) func parsePlatforms(v string) ([]ocispecs.Platform, error) { @@ -97,6 +99,27 @@ func parseUlimits(v string) ([]*pb.Ulimit, error) { return out, nil } +func parseDevices(v string) ([]*pb.CDIDevice, error) { + if v == "" { + return nil, nil + } + out := make([]*pb.CDIDevice, 0) + csvReader := csv.NewReader(strings.NewReader(v)) + names, err := csvReader.Read() + if err != nil { + return nil, err + } + for _, name := range names { + if _, _, _, err := parser.ParseQualifiedName(name); err != nil { + return nil, errors.Wrapf(err, "invalid CDI device name %q", name) + } + out = append(out, &pb.CDIDevice{ + Name: name, + }) + } + return out, nil +} + func parseNetMode(v string) (pb.NetMode, error) { if v == "" { return llb.NetModeSandbox, nil diff --git a/frontend/dockerui/config.go b/frontend/dockerui/config.go index 368b57080c65..6c957f29a609 100644 --- a/frontend/dockerui/config.go +++ b/frontend/dockerui/config.go @@ -40,6 +40,7 @@ const ( keyShmSize = "shm-size" keyTargetPlatform = "platform" keyUlimit = "ulimit" + keyDevice = "device" keyCacheFrom = "cache-from" // for registry only. deprecated in favor of keyCacheImports keyCacheImports = "cache-imports" // JSON representation of []CacheOptionsEntry @@ -66,6 +67,7 @@ type Config struct { ShmSize int64 Target string Ulimits []*pb.Ulimit + Devices []*pb.CDIDevice LinterConfig *linter.Config CacheImports []client.CacheOptionsEntry @@ -187,6 +189,12 @@ func (bc *Client) init() error { } bc.Ulimits = ulimits + devices, err := parseDevices(opts[keyDevice]) + if err != nil { + return errors.Wrap(err, "failed to parse device") + } + bc.Devices = devices + defaultNetMode, err := parseNetMode(opts[keyForceNetwork]) if err != nil { return err From f5c38aac46e57609808beb9348a09280232ce353 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Wed, 9 Oct 2024 16:37:16 +0200 Subject: [PATCH 04/23] dockerfile: device flag support Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- frontend/dockerfile/dockerfile2llb/convert.go | 7 ++- .../dockerfile2llb/convert_rundevice.go | 17 ++++++ .../dockerfile/dockerfile_rundevice_test.go | 56 +++++++++++++++++++ .../instructions/commands_rundevice.go | 49 ++++++++++++++++ 4 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 frontend/dockerfile/dockerfile2llb/convert_rundevice.go create mode 100644 frontend/dockerfile/instructions/commands_rundevice.go diff --git a/frontend/dockerfile/dockerfile2llb/convert.go b/frontend/dockerfile/dockerfile2llb/convert.go index f1bb6c72689b..5aae74203633 100644 --- a/frontend/dockerfile/dockerfile2llb/convert.go +++ b/frontend/dockerfile/dockerfile2llb/convert.go @@ -1305,11 +1305,16 @@ func dispatchRun(d *dispatchState, c *instructions.RunCommand, proxy *llb.ProxyE } } - // TODO: use entitlements and put this on labs + // TODO: use entitlements if dopt.llbCaps != nil && dopt.llbCaps.Supports(pb.CapExecMetaCDI) == nil { for _, u := range dopt.devices { opt = append(opt, llb.AddCDIDevice(u.Name)) } + runDevices, err := dispatchRunDevices(c) + if err != nil { + return err + } + opt = append(opt, runDevices...) } shlex := *dopt.shlex diff --git a/frontend/dockerfile/dockerfile2llb/convert_rundevice.go b/frontend/dockerfile/dockerfile2llb/convert_rundevice.go new file mode 100644 index 000000000000..df21a3492861 --- /dev/null +++ b/frontend/dockerfile/dockerfile2llb/convert_rundevice.go @@ -0,0 +1,17 @@ +package dockerfile2llb + +// TODO: move in labs with dfrundevice tag + +import ( + "github.com/moby/buildkit/client/llb" + "github.com/moby/buildkit/frontend/dockerfile/instructions" +) + +func dispatchRunDevices(c *instructions.RunCommand) ([]llb.RunOption, error) { + var out []llb.RunOption + devices := instructions.GetDevices(c) + for _, device := range devices { + out = append(out, llb.AddCDIDevice(device)) + } + return out, nil +} diff --git a/frontend/dockerfile/dockerfile_rundevice_test.go b/frontend/dockerfile/dockerfile_rundevice_test.go index acf77e5eb960..2c9882e1a40d 100644 --- a/frontend/dockerfile/dockerfile_rundevice_test.go +++ b/frontend/dockerfile/dockerfile_rundevice_test.go @@ -15,6 +15,7 @@ import ( var deviceTests = integration.TestFuncs( testDeviceEnv, + testDeviceRunEnv, ) func testDeviceEnv(t *testing.T, sb integration.Sandbox) { @@ -74,3 +75,58 @@ COPY --from=base /foo.env / require.NoError(t, err) require.Contains(t, string(dt), `FOO=injected`) } + +func testDeviceRunEnv(t *testing.T, sb integration.Sandbox) { + if sb.Rootless() { + t.SkipNow() + } + + integration.SkipOnPlatform(t, "windows") + f := getFrontend(t, sb) + + require.NoError(t, os.WriteFile(filepath.Join(sb.CDISpecDir(), "vendor1-device.yaml"), []byte(` +cdiVersion: "0.3.0" +kind: "vendor1.com/device" +devices: +- name: foo + containerEdits: + env: + - FOO=injected +`), 0600)) + + dockerfile := []byte(` +FROM busybox AS base +RUN --device=vendor1.com/device=foo env|sort | tee foo.env +FROM scratch +COPY --from=base /foo.env / +`) + + dir := integration.Tmpdir( + t, + fstest.CreateFile("Dockerfile", dockerfile, 0600), + ) + + c, err := client.New(sb.Context(), sb.Address()) + require.NoError(t, err) + defer c.Close() + + destDir := t.TempDir() + + _, err = f.Solve(sb.Context(), c, client.SolveOpt{ + LocalMounts: map[string]fsutil.FS{ + dockerui.DefaultLocalNameDockerfile: dir, + dockerui.DefaultLocalNameContext: dir, + }, + Exports: []client.ExportEntry{ + { + Type: client.ExporterLocal, + OutputDir: destDir, + }, + }, + }, nil) + require.NoError(t, err) + + dt, err := os.ReadFile(filepath.Join(destDir, "foo.env")) + require.NoError(t, err) + require.Contains(t, string(dt), `FOO=injected`) +} diff --git a/frontend/dockerfile/instructions/commands_rundevice.go b/frontend/dockerfile/instructions/commands_rundevice.go new file mode 100644 index 000000000000..17977ef18fe2 --- /dev/null +++ b/frontend/dockerfile/instructions/commands_rundevice.go @@ -0,0 +1,49 @@ +package instructions + +import ( + "github.com/pkg/errors" +) + +var deviceKey = "dockerfile/run/device" + +func init() { + parseRunPreHooks = append(parseRunPreHooks, runDevicePreHook) + parseRunPostHooks = append(parseRunPostHooks, runDevicePostHook) +} + +func runDevicePreHook(cmd *RunCommand, req parseRequest) error { + st := &deviceState{} + st.flag = req.flags.AddStrings("device") + cmd.setExternalValue(deviceKey, st) + return nil +} + +func runDevicePostHook(cmd *RunCommand, req parseRequest) error { + return setDeviceState(cmd) +} + +func setDeviceState(cmd *RunCommand) error { + st := getDeviceState(cmd) + if st == nil { + return errors.Errorf("no device state") + } + st.names = st.flag.StringValues + return nil +} + +func getDeviceState(cmd *RunCommand) *deviceState { + v := cmd.getExternalValue(deviceKey) + if v == nil { + return nil + } + return v.(*deviceState) +} + +func GetDevices(cmd *RunCommand) []string { + return getDeviceState(cmd).names +} + +type deviceState struct { + flag *Flag + names []string +} From 33f21640c3c90d9d7fec7196b9dc9f4ea0a3120b Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Wed, 9 Oct 2024 18:31:44 +0200 Subject: [PATCH 05/23] buildkitd: update default cdi spec dirs Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- docs/buildkitd.toml.md | 2 +- util/appdefaults/appdefaults_unix.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/buildkitd.toml.md b/docs/buildkitd.toml.md index 70cbb7229914..345199c7ee6b 100644 --- a/docs/buildkitd.toml.md +++ b/docs/buildkitd.toml.md @@ -51,7 +51,7 @@ insecure-entitlements = [ "network.host", "security.insecure" ] enabled = true # List of directories to scan for CDI spec files. For more details about CDI # specification, please refer to https://github.com/cncf-tags/container-device-interface/blob/main/SPEC.md#cdi-json-specification - specDirs = ["/etc/cdi", "/var/run/cdi"] + specDirs = ["/etc/cdi", "/var/run/cdi", "/etc/buildkit/cdi"] # config for build history API that stores information about completed build commands [history] diff --git a/util/appdefaults/appdefaults_unix.go b/util/appdefaults/appdefaults_unix.go index 5742c4dea6e6..a351a7944e85 100644 --- a/util/appdefaults/appdefaults_unix.go +++ b/util/appdefaults/appdefaults_unix.go @@ -17,7 +17,7 @@ const ( var ( UserCNIConfigPath = filepath.Join(UserConfigDir(), "cni.json") - CDISpecDirs = []string{"/etc/buildkit/cdi"} + CDISpecDirs = []string{"/etc/cdi", "/var/run/cdi", "/etc/buildkit/cdi"} ) // UserAddress typically returns /run/user/$UID/buildkit/buildkitd.sock From 17f46d161c458c4d92927a67fa26787ccc9b494d Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Wed, 9 Oct 2024 18:59:08 +0200 Subject: [PATCH 06/23] worker: cdi manager Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- cmd/buildkitd/main.go | 7 ------- cmd/buildkitd/main_oci_worker.go | 12 ++++++++++++ worker/base/worker.go | 6 ++++++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/cmd/buildkitd/main.go b/cmd/buildkitd/main.go index a407b234a2d6..2c585a049acb 100644 --- a/cmd/buildkitd/main.go +++ b/cmd/buildkitd/main.go @@ -74,7 +74,6 @@ import ( "google.golang.org/grpc/health" healthv1 "google.golang.org/grpc/health/grpc_health_v1" "google.golang.org/grpc/reflection" - "tags.cncf.io/container-device-interface/pkg/cdi" ) func init() { @@ -290,12 +289,6 @@ func main() { } closers = append(closers, mp.Shutdown) - if cfg.CDI.Enabled != nil && *cfg.CDI.Enabled { - if err := cdi.Configure(cdi.WithSpecDirs(cfg.CDI.SpecDirs...)); err != nil { - return errors.Wrap(err, "failed to configure CDI registry") - } - } - statsHandler := tracing.ServerStatsHandler( otelgrpc.WithTracerProvider(tp), otelgrpc.WithMeterProvider(mp), diff --git a/cmd/buildkitd/main_oci_worker.go b/cmd/buildkitd/main_oci_worker.go index a8a27ed4f61a..3704d88f2071 100644 --- a/cmd/buildkitd/main_oci_worker.go +++ b/cmd/buildkitd/main_oci_worker.go @@ -48,6 +48,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/backoff" "google.golang.org/grpc/credentials/insecure" + "tags.cncf.io/container-device-interface/pkg/cdi" ) func init() { @@ -323,6 +324,17 @@ func ociWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([]worker opt.BuildkitVersion = getBuildkitVersion() opt.RegistryHosts = hosts + if common.config.CDI.Enabled != nil && *common.config.CDI.Enabled { + if len(common.config.CDI.SpecDirs) == 0 { + return nil, errors.New("No CDI specification directories specified") + } + cdiCache, err := cdi.NewCache(cdi.WithSpecDirs(common.config.CDI.SpecDirs...)) + if err != nil { + return nil, errors.Wrapf(err, "CDI registry initialization failure") + } + opt.CDIManager = cdiCache + } + if platformsStr := cfg.Platforms; len(platformsStr) != 0 { platforms, err := parsePlatforms(platformsStr) if err != nil { diff --git a/worker/base/worker.go b/worker/base/worker.go index ffe78f6ec6b1..ca3ff8279879 100644 --- a/worker/base/worker.go +++ b/worker/base/worker.go @@ -52,6 +52,7 @@ import ( "github.com/pkg/errors" "golang.org/x/sync/errgroup" "golang.org/x/sync/semaphore" + "tags.cncf.io/container-device-interface/pkg/cdi" ) const labelCreatedAt = "buildkit/createdat" @@ -82,6 +83,7 @@ type WorkerOpt struct { MetadataStore *metadata.Store MountPoolRoot string ResourceMonitor *resources.Monitor + CDIManager *cdi.Cache } // Worker is a local worker instance with dedicated snapshotter, cache, and so on. @@ -245,6 +247,10 @@ func (w *Worker) LeaseManager() *leaseutil.Manager { return w.WorkerOpt.LeaseManager } +func (w *Worker) CDIManager() *cdi.Cache { + return w.WorkerOpt.CDIManager +} + func (w *Worker) ID() string { return w.WorkerOpt.ID } From 9b39b0d82cddbf7682cf6d3d254840546f8c9da2 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Wed, 9 Oct 2024 15:41:21 -0700 Subject: [PATCH 07/23] allow listing devices Signed-off-by: Tonis Tiigi --- api/types/worker.pb.go | 171 +++++++++--- api/types/worker.proto | 7 + api/types/worker_vtproto.pb.go | 459 +++++++++++++++++++++++++++++++++ client/info.go | 18 ++ client/workers.go | 2 + cmd/buildctl/debug/workers.go | 12 + control/control.go | 16 ++ worker/worker.go | 2 + 8 files changed, 647 insertions(+), 40 deletions(-) diff --git a/api/types/worker.pb.go b/api/types/worker.pb.go index c4065f639176..3d95d0e80855 100644 --- a/api/types/worker.pb.go +++ b/api/types/worker.pb.go @@ -31,6 +31,7 @@ type WorkerRecord struct { Platforms []*pb.Platform `protobuf:"bytes,3,rep,name=platforms,proto3" json:"platforms,omitempty"` GCPolicy []*GCPolicy `protobuf:"bytes,4,rep,name=GCPolicy,proto3" json:"GCPolicy,omitempty"` BuildkitVersion *BuildkitVersion `protobuf:"bytes,5,opt,name=BuildkitVersion,proto3" json:"BuildkitVersion,omitempty"` + CDIDevices []*CDIDevice `protobuf:"bytes,6,rep,name=CDIDevices,proto3" json:"CDIDevices,omitempty"` } func (x *WorkerRecord) Reset() { @@ -98,6 +99,13 @@ func (x *WorkerRecord) GetBuildkitVersion() *BuildkitVersion { return nil } +func (x *WorkerRecord) GetCDIDevices() []*CDIDevice { + if x != nil { + return x.CDIDevices + } + return nil +} + type GCPolicy struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -245,6 +253,67 @@ func (x *BuildkitVersion) GetRevision() string { return "" } +type CDIDevice struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"` + AutoAllow bool `protobuf:"varint,2,opt,name=AutoAllow,proto3" json:"AutoAllow,omitempty"` + Annotations map[string]string `protobuf:"bytes,3,rep,name=Annotations,proto3" json:"Annotations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *CDIDevice) Reset() { + *x = CDIDevice{} + mi := &file_github_com_moby_buildkit_api_types_worker_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CDIDevice) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CDIDevice) ProtoMessage() {} + +func (x *CDIDevice) ProtoReflect() protoreflect.Message { + mi := &file_github_com_moby_buildkit_api_types_worker_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CDIDevice.ProtoReflect.Descriptor instead. +func (*CDIDevice) Descriptor() ([]byte, []int) { + return file_github_com_moby_buildkit_api_types_worker_proto_rawDescGZIP(), []int{3} +} + +func (x *CDIDevice) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *CDIDevice) GetAutoAllow() bool { + if x != nil { + return x.AutoAllow + } + return false +} + +func (x *CDIDevice) GetAnnotations() map[string]string { + if x != nil { + return x.Annotations + } + return nil +} + var File_github_com_moby_buildkit_api_types_worker_proto protoreflect.FileDescriptor var file_github_com_moby_buildkit_api_types_worker_proto_rawDesc = []byte{ @@ -255,7 +324,7 @@ var file_github_com_moby_buildkit_api_types_worker_proto_rawDesc = []byte{ 0x2e, 0x76, 0x31, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x1a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x6f, 0x62, 0x79, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x6b, 0x69, 0x74, 0x2f, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x62, 0x2f, 0x6f, 0x70, - 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe0, 0x02, 0x0a, 0x0c, 0x57, 0x6f, 0x72, 0x6b, + 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa3, 0x03, 0x0a, 0x0c, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x48, 0x0a, 0x06, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x6d, 0x6f, 0x62, 0x79, 0x2e, @@ -273,34 +342,52 @@ var file_github_com_moby_buildkit_api_types_worker_proto_rawDesc = []byte{ 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6d, 0x6f, 0x62, 0x79, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x6b, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x6b, 0x69, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0f, - 0x42, 0x75, 0x69, 0x6c, 0x64, 0x6b, 0x69, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x1a, - 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc8, 0x01, 0x0a, 0x08, 0x47, - 0x43, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x12, 0x22, 0x0a, 0x0c, 0x6b, 0x65, 0x65, - 0x70, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0c, 0x6b, 0x65, 0x65, 0x70, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, - 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, - 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x64, 0x53, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, - 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x53, 0x70, 0x61, 0x63, 0x65, 0x12, 0x22, 0x0a, - 0x0c, 0x6d, 0x61, 0x78, 0x55, 0x73, 0x65, 0x64, 0x53, 0x70, 0x61, 0x63, 0x65, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x55, 0x73, 0x65, 0x64, 0x53, 0x70, 0x61, 0x63, - 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x6d, 0x69, 0x6e, 0x46, 0x72, 0x65, 0x65, 0x53, 0x70, 0x61, 0x63, - 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x46, 0x72, 0x65, 0x65, - 0x53, 0x70, 0x61, 0x63, 0x65, 0x22, 0x61, 0x0a, 0x0f, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x6b, 0x69, - 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x63, 0x6b, - 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, - 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, - 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x6f, 0x62, 0x79, 0x2f, 0x62, 0x75, 0x69, 0x6c, - 0x64, 0x6b, 0x69, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x3b, 0x6d, - 0x6f, 0x62, 0x79, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x6b, 0x69, 0x74, 0x5f, 0x76, 0x31, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x42, 0x75, 0x69, 0x6c, 0x64, 0x6b, 0x69, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x41, 0x0a, 0x0a, 0x43, 0x44, 0x49, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x06, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6d, 0x6f, 0x62, 0x79, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x6b, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x43, 0x44, 0x49, + 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x0a, 0x43, 0x44, 0x49, 0x44, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc8, 0x01, + 0x0a, 0x08, 0x47, 0x43, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, + 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x12, 0x22, 0x0a, 0x0c, + 0x6b, 0x65, 0x65, 0x70, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0c, 0x6b, 0x65, 0x65, 0x70, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x18, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x72, 0x65, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x53, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x53, 0x70, 0x61, 0x63, 0x65, + 0x12, 0x22, 0x0a, 0x0c, 0x6d, 0x61, 0x78, 0x55, 0x73, 0x65, 0x64, 0x53, 0x70, 0x61, 0x63, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x55, 0x73, 0x65, 0x64, 0x53, + 0x70, 0x61, 0x63, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x6d, 0x69, 0x6e, 0x46, 0x72, 0x65, 0x65, 0x53, + 0x70, 0x61, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x46, + 0x72, 0x65, 0x65, 0x53, 0x70, 0x61, 0x63, 0x65, 0x22, 0x61, 0x0a, 0x0f, 0x42, 0x75, 0x69, 0x6c, + 0x64, 0x6b, 0x69, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, + 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, + 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xd3, 0x01, 0x0a, 0x09, + 0x43, 0x44, 0x49, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, + 0x09, 0x41, 0x75, 0x74, 0x6f, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x09, 0x41, 0x75, 0x74, 0x6f, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x12, 0x54, 0x0a, 0x0b, 0x41, + 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x32, 0x2e, 0x6d, 0x6f, 0x62, 0x79, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x6b, 0x69, 0x74, + 0x2e, 0x76, 0x31, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x43, 0x44, 0x49, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x6d, 0x6f, 0x62, 0x79, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x6b, 0x69, 0x74, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x3b, 0x6d, 0x6f, 0x62, 0x79, 0x5f, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x6b, 0x69, 0x74, 0x5f, 0x76, 0x31, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -315,24 +402,28 @@ func file_github_com_moby_buildkit_api_types_worker_proto_rawDescGZIP() []byte { return file_github_com_moby_buildkit_api_types_worker_proto_rawDescData } -var file_github_com_moby_buildkit_api_types_worker_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_github_com_moby_buildkit_api_types_worker_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_github_com_moby_buildkit_api_types_worker_proto_goTypes = []any{ (*WorkerRecord)(nil), // 0: moby.buildkit.v1.types.WorkerRecord (*GCPolicy)(nil), // 1: moby.buildkit.v1.types.GCPolicy (*BuildkitVersion)(nil), // 2: moby.buildkit.v1.types.BuildkitVersion - nil, // 3: moby.buildkit.v1.types.WorkerRecord.LabelsEntry - (*pb.Platform)(nil), // 4: pb.Platform + (*CDIDevice)(nil), // 3: moby.buildkit.v1.types.CDIDevice + nil, // 4: moby.buildkit.v1.types.WorkerRecord.LabelsEntry + nil, // 5: moby.buildkit.v1.types.CDIDevice.AnnotationsEntry + (*pb.Platform)(nil), // 6: pb.Platform } var file_github_com_moby_buildkit_api_types_worker_proto_depIdxs = []int32{ - 3, // 0: moby.buildkit.v1.types.WorkerRecord.Labels:type_name -> moby.buildkit.v1.types.WorkerRecord.LabelsEntry - 4, // 1: moby.buildkit.v1.types.WorkerRecord.platforms:type_name -> pb.Platform + 4, // 0: moby.buildkit.v1.types.WorkerRecord.Labels:type_name -> moby.buildkit.v1.types.WorkerRecord.LabelsEntry + 6, // 1: moby.buildkit.v1.types.WorkerRecord.platforms:type_name -> pb.Platform 1, // 2: moby.buildkit.v1.types.WorkerRecord.GCPolicy:type_name -> moby.buildkit.v1.types.GCPolicy 2, // 3: moby.buildkit.v1.types.WorkerRecord.BuildkitVersion:type_name -> moby.buildkit.v1.types.BuildkitVersion - 4, // [4:4] is the sub-list for method output_type - 4, // [4:4] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 3, // 4: moby.buildkit.v1.types.WorkerRecord.CDIDevices:type_name -> moby.buildkit.v1.types.CDIDevice + 5, // 5: moby.buildkit.v1.types.CDIDevice.Annotations:type_name -> moby.buildkit.v1.types.CDIDevice.AnnotationsEntry + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_github_com_moby_buildkit_api_types_worker_proto_init() } @@ -346,7 +437,7 @@ func file_github_com_moby_buildkit_api_types_worker_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_github_com_moby_buildkit_api_types_worker_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 6, NumExtensions: 0, NumServices: 0, }, diff --git a/api/types/worker.proto b/api/types/worker.proto index 51a92d6b6cc2..81a0d8c4cda8 100644 --- a/api/types/worker.proto +++ b/api/types/worker.proto @@ -12,6 +12,7 @@ message WorkerRecord { repeated pb.Platform platforms = 3; repeated GCPolicy GCPolicy = 4; BuildkitVersion BuildkitVersion = 5; + repeated CDIDevice CDIDevices = 6; } message GCPolicy { @@ -30,3 +31,9 @@ message BuildkitVersion { string version = 2; string revision = 3; } + +message CDIDevice { + string Name = 1; + bool AutoAllow = 2; + map Annotations = 3; +} \ No newline at end of file diff --git a/api/types/worker_vtproto.pb.go b/api/types/worker_vtproto.pb.go index 62ed2afe1e8f..016412ac0d8f 100644 --- a/api/types/worker_vtproto.pb.go +++ b/api/types/worker_vtproto.pb.go @@ -48,6 +48,13 @@ func (m *WorkerRecord) CloneVT() *WorkerRecord { } r.GCPolicy = tmpContainer } + if rhs := m.CDIDevices; rhs != nil { + tmpContainer := make([]*CDIDevice, len(rhs)) + for k, v := range rhs { + tmpContainer[k] = v.CloneVT() + } + r.CDIDevices = tmpContainer + } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -104,6 +111,31 @@ func (m *BuildkitVersion) CloneMessageVT() proto.Message { return m.CloneVT() } +func (m *CDIDevice) CloneVT() *CDIDevice { + if m == nil { + return (*CDIDevice)(nil) + } + r := new(CDIDevice) + r.Name = m.Name + r.AutoAllow = m.AutoAllow + if rhs := m.Annotations; rhs != nil { + tmpContainer := make(map[string]string, len(rhs)) + for k, v := range rhs { + tmpContainer[k] = v + } + r.Annotations = tmpContainer + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *CDIDevice) CloneMessageVT() proto.Message { + return m.CloneVT() +} + func (this *WorkerRecord) EqualVT(that *WorkerRecord) bool { if this == that { return true @@ -162,6 +194,23 @@ func (this *WorkerRecord) EqualVT(that *WorkerRecord) bool { if !this.BuildkitVersion.EqualVT(that.BuildkitVersion) { return false } + if len(this.CDIDevices) != len(that.CDIDevices) { + return false + } + for i, vx := range this.CDIDevices { + vy := that.CDIDevices[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &CDIDevice{} + } + if q == nil { + q = &CDIDevice{} + } + if !p.EqualVT(q) { + return false + } + } + } return string(this.unknownFields) == string(that.unknownFields) } @@ -237,6 +286,40 @@ func (this *BuildkitVersion) EqualMessageVT(thatMsg proto.Message) bool { } return this.EqualVT(that) } +func (this *CDIDevice) EqualVT(that *CDIDevice) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.AutoAllow != that.AutoAllow { + return false + } + if len(this.Annotations) != len(that.Annotations) { + return false + } + for i, vx := range this.Annotations { + vy, ok := that.Annotations[i] + if !ok { + return false + } + if vx != vy { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *CDIDevice) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*CDIDevice) + if !ok { + return false + } + return this.EqualVT(that) +} func (m *WorkerRecord) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil @@ -267,6 +350,18 @@ func (m *WorkerRecord) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.CDIDevices) > 0 { + for iNdEx := len(m.CDIDevices) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.CDIDevices[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x32 + } + } if m.BuildkitVersion != nil { size, err := m.BuildkitVersion.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { @@ -456,6 +551,75 @@ func (m *BuildkitVersion) MarshalToSizedBufferVT(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *CDIDevice) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CDIDevice) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *CDIDevice) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Annotations) > 0 { + for k := range m.Annotations { + v := m.Annotations[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a + } + } + if m.AutoAllow { + i-- + if m.AutoAllow { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *WorkerRecord) SizeVT() (n int) { if m == nil { return 0 @@ -490,6 +654,12 @@ func (m *WorkerRecord) SizeVT() (n int) { l = m.BuildkitVersion.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } + if len(m.CDIDevices) > 0 { + for _, e := range m.CDIDevices { + l = e.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + } n += len(m.unknownFields) return n } @@ -547,6 +717,31 @@ func (m *BuildkitVersion) SizeVT() (n int) { return n } +func (m *CDIDevice) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.AutoAllow { + n += 2 + } + if len(m.Annotations) > 0 { + for k, v := range m.Annotations { + _ = k + _ = v + mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protohelpers.SizeOfVarint(uint64(len(v))) + n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) + } + } + n += len(m.unknownFields) + return n +} + func (m *WorkerRecord) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -839,6 +1034,40 @@ func (m *WorkerRecord) UnmarshalVT(dAtA []byte) error { return err } iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CDIDevices", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CDIDevices = append(m.CDIDevices, &CDIDevice{}) + if err := m.CDIDevices[len(m.CDIDevices)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) @@ -1187,3 +1416,233 @@ func (m *BuildkitVersion) UnmarshalVT(dAtA []byte) error { } return nil } +func (m *CDIDevice) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CDIDevice: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CDIDevice: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AutoAllow", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AutoAllow = bool(v != 0) + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Annotations", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Annotations == nil { + m.Annotations = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protohelpers.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protohelpers.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protohelpers.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protohelpers.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Annotations[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} diff --git a/client/info.go b/client/info.go index d5bdbcec8968..0aa95bb9537d 100644 --- a/client/info.go +++ b/client/info.go @@ -18,6 +18,12 @@ type BuildkitVersion struct { Revision string `json:"revision"` } +type CDIDevice struct { + Name string `json:"name"` + AutoAllow bool `json:"autoAllow"` + Annotations map[string]string `json:"annotations"` +} + func (c *Client) Info(ctx context.Context) (*Info, error) { res, err := c.ControlClient().Info(ctx, &controlapi.InfoRequest{}) if err != nil { @@ -38,3 +44,15 @@ func fromAPIBuildkitVersion(in *apitypes.BuildkitVersion) BuildkitVersion { Revision: in.Revision, } } + +func fromAPICDIDevices(in []*apitypes.CDIDevice) []CDIDevice { + var out []CDIDevice + for _, d := range in { + out = append(out, CDIDevice{ + Name: d.Name, + AutoAllow: d.AutoAllow, + Annotations: d.Annotations, + }) + } + return out +} diff --git a/client/workers.go b/client/workers.go index 10b0cbf85992..a55581751499 100644 --- a/client/workers.go +++ b/client/workers.go @@ -18,6 +18,7 @@ type WorkerInfo struct { Platforms []ocispecs.Platform `json:"platforms"` GCPolicy []PruneInfo `json:"gcPolicy"` BuildkitVersion BuildkitVersion `json:"buildkitVersion"` + CDIDevices []CDIDevice `json:"cdiDevices"` } // ListWorkers lists all active workers @@ -42,6 +43,7 @@ func (c *Client) ListWorkers(ctx context.Context, opts ...ListWorkersOption) ([] Platforms: pb.ToSpecPlatforms(w.Platforms), GCPolicy: fromAPIGCPolicy(w.GCPolicy), BuildkitVersion: fromAPIBuildkitVersion(w.BuildkitVersion), + CDIDevices: fromAPICDIDevices(w.CDIDevices), }) } diff --git a/cmd/buildctl/debug/workers.go b/cmd/buildctl/debug/workers.go index 42703e971f9d..0778d5620e02 100644 --- a/cmd/buildctl/debug/workers.go +++ b/cmd/buildctl/debug/workers.go @@ -82,6 +82,18 @@ func printWorkersVerbose(tw *tabwriter.Writer, winfo []*client.WorkerInfo) { v := wi.Labels[k] fmt.Fprintf(tw, "\t%s:\t%s\n", k, v) } + if len(wi.CDIDevices) > 0 { + fmt.Fprint(tw, "Devices:\n") + for _, d := range wi.CDIDevices { + fmt.Fprintf(tw, "\tName:\t%s\n", d.Name) + fmt.Fprintf(tw, "\tAutoAllow:\t%v\n", d.AutoAllow) + for _, k := range sortedKeys(d.Annotations) { + v := d.Annotations[k] + fmt.Fprintf(tw, "\t\t%s:\t%s\n", k, v) + } + } + fmt.Fprint(tw, "\n") + } for i, rule := range wi.GCPolicy { fmt.Fprintf(tw, "GC Policy rule#%d:\n", i) fmt.Fprintf(tw, "\tAll:\t%v\n", rule.All) diff --git a/control/control.go b/control/control.go index 592775d9d015..0ac12e8fead0 100644 --- a/control/control.go +++ b/control/control.go @@ -54,6 +54,7 @@ import ( "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" + "tags.cncf.io/container-device-interface/pkg/cdi" ) type Opt struct { @@ -586,6 +587,7 @@ func (c *Controller) ListWorkers(ctx context.Context, r *controlapi.ListWorkersR Platforms: pb.PlatformsFromSpec(w.Platforms(true)), GCPolicy: toPBGCPolicy(w.GCPolicy()), BuildkitVersion: toPBBuildkitVersion(w.BuildkitVersion()), + CDIDevices: toPBCDIDevices(w.CDIManager()), }) } return resp, nil @@ -684,6 +686,20 @@ func toPBBuildkitVersion(in client.BuildkitVersion) *apitypes.BuildkitVersion { } } +func toPBCDIDevices(manager *cdi.Cache) []*apitypes.CDIDevice { + devs := manager.ListDevices() + out := make([]*apitypes.CDIDevice, 0, len(devs)) + for _, dev := range devs { + spec := manager.GetDevice(dev).GetSpec() + out = append(out, &apitypes.CDIDevice{ + Name: dev, + AutoAllow: true, // TODO + Annotations: spec.Annotations, + }) + } + return out +} + func findDuplicateCacheOptions(cacheOpts []*controlapi.CacheOptionsEntry) ([]*controlapi.CacheOptionsEntry, error) { seen := map[string]*controlapi.CacheOptionsEntry{} duplicate := map[string]struct{}{} diff --git a/worker/worker.go b/worker/worker.go index ca5292e9eced..182eaebee174 100644 --- a/worker/worker.go +++ b/worker/worker.go @@ -16,6 +16,7 @@ import ( "github.com/moby/buildkit/solver/pb" "github.com/moby/buildkit/util/leaseutil" ocispecs "github.com/opencontainers/image-spec/specs-go/v1" + "tags.cncf.io/container-device-interface/pkg/cdi" ) type Worker interface { @@ -41,6 +42,7 @@ type Worker interface { CacheManager() cache.Manager LeaseManager() *leaseutil.Manager GarbageCollect(context.Context) error + CDIManager() *cdi.Cache } type Infos interface { From 0f24e350b78d374182d805e5b7191446d7f81486 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Wed, 9 Oct 2024 20:17:24 -0700 Subject: [PATCH 08/23] check CDI device error on startup Signed-off-by: Tonis Tiigi --- cmd/buildkitd/main_oci_worker.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/buildkitd/main_oci_worker.go b/cmd/buildkitd/main_oci_worker.go index 3704d88f2071..76487c3334b3 100644 --- a/cmd/buildkitd/main_oci_worker.go +++ b/cmd/buildkitd/main_oci_worker.go @@ -332,6 +332,9 @@ func ociWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([]worker if err != nil { return nil, errors.Wrapf(err, "CDI registry initialization failure") } + if err := cdiCache.Refresh(); err != nil { + return nil, errors.Wrapf(err, "CDI registry initialization failure") + } opt.CDIManager = cdiCache } From 3adcf53d1a7afbd7281bd5cf404c1afde979fcdb Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Wed, 9 Oct 2024 20:22:24 -0700 Subject: [PATCH 09/23] enable CDI by default for buildkitd Access should be managed by entitlements checks Signed-off-by: Tonis Tiigi --- cmd/buildkitd/config/config.go | 2 +- cmd/buildkitd/main.go | 2 +- cmd/buildkitd/main_oci_worker.go | 2 +- control/control.go | 3 +++ executor/oci/spec_linux.go | 1 + 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cmd/buildkitd/config/config.go b/cmd/buildkitd/config/config.go index 1e104eb4cce7..381effcdc9b7 100644 --- a/cmd/buildkitd/config/config.go +++ b/cmd/buildkitd/config/config.go @@ -77,7 +77,7 @@ type OTELConfig struct { } type CDIConfig struct { - Enabled *bool `toml:"enabled"` + Disabled *bool `toml:"disabled"` SpecDirs []string `toml:"specDirs"` } diff --git a/cmd/buildkitd/main.go b/cmd/buildkitd/main.go index 2c585a049acb..9106f569cbef 100644 --- a/cmd/buildkitd/main.go +++ b/cmd/buildkitd/main.go @@ -633,7 +633,7 @@ func applyMainFlags(c *cli.Context, cfg *config.Config) error { if c.IsSet("cdi-enabled") { cdiEnabled := c.Bool("cdi-enabled") - cfg.CDI.Enabled = &cdiEnabled + cfg.CDI.Disabled = &cdiEnabled } if c.IsSet("cdi-spec-dir") { cfg.CDI.SpecDirs = c.StringSlice("cdi-spec-dir") diff --git a/cmd/buildkitd/main_oci_worker.go b/cmd/buildkitd/main_oci_worker.go index 76487c3334b3..5451a0088428 100644 --- a/cmd/buildkitd/main_oci_worker.go +++ b/cmd/buildkitd/main_oci_worker.go @@ -324,7 +324,7 @@ func ociWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([]worker opt.BuildkitVersion = getBuildkitVersion() opt.RegistryHosts = hosts - if common.config.CDI.Enabled != nil && *common.config.CDI.Enabled { + if common.config.CDI.Disabled == nil || !*common.config.CDI.Disabled { if len(common.config.CDI.SpecDirs) == 0 { return nil, errors.New("No CDI specification directories specified") } diff --git a/control/control.go b/control/control.go index 0ac12e8fead0..a93ee5b7653d 100644 --- a/control/control.go +++ b/control/control.go @@ -687,6 +687,9 @@ func toPBBuildkitVersion(in client.BuildkitVersion) *apitypes.BuildkitVersion { } func toPBCDIDevices(manager *cdi.Cache) []*apitypes.CDIDevice { + if manager == nil { + return nil + } devs := manager.ListDevices() out := make([]*apitypes.CDIDevice, 0, len(devs)) for _, dev := range devs { diff --git a/executor/oci/spec_linux.go b/executor/oci/spec_linux.go index 3c01b3c5d501..44025f9ae081 100644 --- a/executor/oci/spec_linux.go +++ b/executor/oci/spec_linux.go @@ -178,6 +178,7 @@ func generateCDIOpts(ctx context.Context, devices []*pb.CDIDevice) ([]oci.SpecOp withStaticCDIRegistry := func() oci.SpecOpts { return func(ctx context.Context, _ oci.Client, _ *containers.Container, s *oci.Spec) error { _ = cdi.Configure(cdi.WithAutoRefresh(false)) + // TODO: this should use worker CDIManager if err := cdi.Refresh(); err != nil { // We don't consider registry refresh failure a fatal error. // For instance, a dynamically generated invalid CDI Spec file From f7f17747037b5d517e33c95bf294bcb50c54f3c2 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Mon, 3 Feb 2025 16:28:58 +0100 Subject: [PATCH 10/23] cdi: fixes since enabled by default Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- client/client_test.go | 20 +------------------- cmd/buildkitd/main.go | 10 +++++----- docs/buildkitd.toml.md | 4 ++-- frontend/dockerfile/dockerfile_test.go | 18 +----------------- 4 files changed, 9 insertions(+), 43 deletions(-) diff --git a/client/client_test.go b/client/client_test.go index 3df2fd8ae05d..2d025ea71980 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -277,12 +277,7 @@ func testIntegration(t *testing.T, funcs ...func(t *testing.T, sb integration.Sa integration.Run(t, integration.TestFuncs( testCDI, - ), - mirrors, - integration.WithMatrix("cdi", map[string]interface{}{ - "enabled": enableCDI, - }), - ) + ), mirrors) } func newContainerd(cdAddress string) (*ctd.Client, error) { @@ -10996,19 +10991,6 @@ func (w warningsListOutput) String() string { return b.String() } -type cdiEnabled struct{} - -func (*cdiEnabled) UpdateConfigFile(in string) string { - return in + ` -[cdi] -enabled = true -` -} - -var ( - enableCDI integration.ConfigUpdater = &cdiEnabled{} -) - func testCDI(t *testing.T, sb integration.Sandbox) { if sb.Rootless() { t.SkipNow() diff --git a/cmd/buildkitd/main.go b/cmd/buildkitd/main.go index 9106f569cbef..43949e26f3fb 100644 --- a/cmd/buildkitd/main.go +++ b/cmd/buildkitd/main.go @@ -217,8 +217,8 @@ func main() { Usage: "OTEL collector trace socket path", }, cli.BoolFlag{ - Name: "cdi-enabled", - Usage: "enables support of the Container Device Interface (CDI)", + Name: "cdi-disabled", + Usage: "disables support of the Container Device Interface (CDI)", }, cli.StringSliceFlag{ Name: "cdi-spec-dir", @@ -631,9 +631,9 @@ func applyMainFlags(c *cli.Context, cfg *config.Config) error { cfg.OTEL.SocketPath = c.String("otel-socket-path") } - if c.IsSet("cdi-enabled") { - cdiEnabled := c.Bool("cdi-enabled") - cfg.CDI.Disabled = &cdiEnabled + if c.IsSet("cdi-disabled") { + cdiDisabled := c.Bool("cdi-disabled") + cfg.CDI.Disabled = &cdiDisabled } if c.IsSet("cdi-spec-dir") { cfg.CDI.SpecDirs = c.StringSlice("cdi-spec-dir") diff --git a/docs/buildkitd.toml.md b/docs/buildkitd.toml.md index 345199c7ee6b..ec314b9f08ba 100644 --- a/docs/buildkitd.toml.md +++ b/docs/buildkitd.toml.md @@ -47,8 +47,8 @@ insecure-entitlements = [ "network.host", "security.insecure" ] socketPath = "/run/buildkit/otel-grpc.sock" [cdi] - # Enables support of the Container Device Interface (CDI). - enabled = true + # Disables support of the Container Device Interface (CDI). + disabled = true # List of directories to scan for CDI spec files. For more details about CDI # specification, please refer to https://github.com/cncf-tags/container-device-interface/blob/main/SPEC.md#cdi-json-specification specDirs = ["/etc/cdi", "/var/run/cdi", "/etc/buildkit/cdi"] diff --git a/frontend/dockerfile/dockerfile_test.go b/frontend/dockerfile/dockerfile_test.go index 2879ec3b62a5..78da4d950d71 100644 --- a/frontend/dockerfile/dockerfile_test.go +++ b/frontend/dockerfile/dockerfile_test.go @@ -302,10 +302,7 @@ func TestIntegration(t *testing.T) { "denied": networkHostDenied, }))...) - integration.Run(t, deviceTests, append(opts, - integration.WithMatrix("cdi", map[string]interface{}{ - "enabled": enableCDI, - }))...) + integration.Run(t, deviceTests, opts...) } func testEmptyStringArgInEnv(t *testing.T, sb integration.Sandbox) { @@ -9902,19 +9899,6 @@ var ( networkHostDenied integration.ConfigUpdater = &networkModeSandbox{} ) -type cdiEnabled struct{} - -func (*cdiEnabled) UpdateConfigFile(in string) string { - return in + ` -[cdi] -enabled = true -` -} - -var ( - enableCDI integration.ConfigUpdater = &cdiEnabled{} -) - func fixedWriteCloser(wc io.WriteCloser) filesync.FileOutputFunc { return func(map[string]string) (io.WriteCloser, error) { return wc, nil From d69cc70521e62d746a300e84c3cd0f129eb0f730 Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Tue, 4 Feb 2025 12:12:27 +0100 Subject: [PATCH 11/23] cdi: use worker cdi manager when generating devices oci spec Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- cmd/buildkitd/main.go | 28 +++++++++++++++ cmd/buildkitd/main_containerd_worker.go | 6 ++++ cmd/buildkitd/main_oci_worker.go | 22 ++++-------- executor/containerdexecutor/executor.go | 4 +++ executor/containerdexecutor/executor_unix.go | 2 +- .../containerdexecutor/executor_windows.go | 2 +- executor/oci/spec.go | 17 +++++---- executor/oci/spec_darwin.go | 5 ++- executor/oci/spec_freebsd.go | 5 ++- executor/oci/spec_linux.go | 36 +++---------------- executor/oci/spec_windows.go | 3 +- executor/runcexecutor/executor.go | 6 +++- util/testutil/workers/containerd.go | 1 + worker/containerd/containerd.go | 4 +++ worker/runc/runc.go | 5 ++- worker/runc/runc_test.go | 2 +- 16 files changed, 82 insertions(+), 66 deletions(-) diff --git a/cmd/buildkitd/main.go b/cmd/buildkitd/main.go index 43949e26f3fb..77d48620f7a8 100644 --- a/cmd/buildkitd/main.go +++ b/cmd/buildkitd/main.go @@ -74,6 +74,7 @@ import ( "google.golang.org/grpc/health" healthv1 "google.golang.org/grpc/health/grpc_health_v1" "google.golang.org/grpc/reflection" + "tags.cncf.io/container-device-interface/pkg/cdi" ) func init() { @@ -1044,3 +1045,30 @@ func newMeterProvider(ctx context.Context) (*sdkmetric.MeterProvider, error) { } return sdkmetric.NewMeterProvider(opts...), nil } + +// getCDIManager returns a new CDI registry with disabled auto-refresh. +func getCDIManager(disabled *bool, specDirs []string) (*cdi.Cache, error) { + if disabled != nil && *disabled { + return nil, nil + } + if len(specDirs) == 0 { + return nil, errors.New("No CDI specification directories specified") + } + cdiCache, err := func() (*cdi.Cache, error) { + cdiCache, err := cdi.NewCache( + cdi.WithSpecDirs(specDirs...), + cdi.WithAutoRefresh(false), + ) + if err != nil { + return nil, err + } + if err := cdiCache.Refresh(); err != nil { + return nil, err + } + return cdiCache, nil + }() + if err != nil { + return nil, errors.Wrapf(err, "CDI registry initialization failure") + } + return cdiCache, nil +} diff --git a/cmd/buildkitd/main_containerd_worker.go b/cmd/buildkitd/main_containerd_worker.go index d07bc99ec2bf..60139e4f2d77 100644 --- a/cmd/buildkitd/main_containerd_worker.go +++ b/cmd/buildkitd/main_containerd_worker.go @@ -282,6 +282,11 @@ func containerdWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([ dns := getDNSConfig(common.config.DNS) + cdiManager, err := getCDIManager(common.config.CDI.Disabled, common.config.CDI.SpecDirs) + if err != nil { + return nil, err + } + nc := netproviders.Opt{ Mode: common.config.Workers.Containerd.NetworkConfig.Mode, CNI: cniprovider.Opt{ @@ -339,6 +344,7 @@ func containerdWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([ ParallelismSem: parallelismSem, TraceSocket: common.traceSocket, Runtime: runtime, + CDIManager: cdiManager, } opt, err := containerd.NewWorkerOpt(workerOpts, ctd.WithTimeout(60*time.Second)) diff --git a/cmd/buildkitd/main_oci_worker.go b/cmd/buildkitd/main_oci_worker.go index 5451a0088428..ee1ed9eb962f 100644 --- a/cmd/buildkitd/main_oci_worker.go +++ b/cmd/buildkitd/main_oci_worker.go @@ -48,7 +48,6 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/backoff" "google.golang.org/grpc/credentials/insecure" - "tags.cncf.io/container-device-interface/pkg/cdi" ) func init() { @@ -299,6 +298,11 @@ func ociWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([]worker dns := getDNSConfig(common.config.DNS) + cdiManager, err := getCDIManager(common.config.CDI.Disabled, common.config.CDI.SpecDirs) + if err != nil { + return nil, err + } + nc := netproviders.Opt{ Mode: common.config.Workers.OCI.NetworkConfig.Mode, CNI: cniprovider.Opt{ @@ -316,7 +320,7 @@ func ociWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([]worker parallelismSem = semaphore.NewWeighted(int64(cfg.MaxParallelism)) } - opt, err := runc.NewWorkerOpt(common.config.Root, snFactory, cfg.Rootless, processMode, cfg.Labels, idmapping, nc, dns, cfg.Binary, cfg.ApparmorProfile, cfg.SELinux, parallelismSem, common.traceSocket, cfg.DefaultCgroupParent) + opt, err := runc.NewWorkerOpt(common.config.Root, snFactory, cfg.Rootless, processMode, cfg.Labels, idmapping, nc, dns, cfg.Binary, cfg.ApparmorProfile, cfg.SELinux, parallelismSem, common.traceSocket, cfg.DefaultCgroupParent, cdiManager) if err != nil { return nil, err } @@ -324,20 +328,6 @@ func ociWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([]worker opt.BuildkitVersion = getBuildkitVersion() opt.RegistryHosts = hosts - if common.config.CDI.Disabled == nil || !*common.config.CDI.Disabled { - if len(common.config.CDI.SpecDirs) == 0 { - return nil, errors.New("No CDI specification directories specified") - } - cdiCache, err := cdi.NewCache(cdi.WithSpecDirs(common.config.CDI.SpecDirs...)) - if err != nil { - return nil, errors.Wrapf(err, "CDI registry initialization failure") - } - if err := cdiCache.Refresh(); err != nil { - return nil, errors.Wrapf(err, "CDI registry initialization failure") - } - opt.CDIManager = cdiCache - } - if platformsStr := cfg.Platforms; len(platformsStr) != 0 { platforms, err := parsePlatforms(platformsStr) if err != nil { diff --git a/executor/containerdexecutor/executor.go b/executor/containerdexecutor/executor.go index 4022de0a6dfe..8c9097aad5cc 100644 --- a/executor/containerdexecutor/executor.go +++ b/executor/containerdexecutor/executor.go @@ -13,6 +13,7 @@ import ( "github.com/moby/buildkit/util/bklog" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" + "tags.cncf.io/container-device-interface/pkg/cdi" ctd "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/core/mount" @@ -40,6 +41,7 @@ type containerdExecutor struct { traceSocket string rootless bool runtime *RuntimeInfo + cdiManager *cdi.Cache } // OnCreateRuntimer provides an alternative to OCI hooks for applying network @@ -72,6 +74,7 @@ type ExecutorOptions struct { TraceSocket string Rootless bool Runtime *RuntimeInfo + CDIManager *cdi.Cache } // New creates a new executor backed by connection to containerd API @@ -92,6 +95,7 @@ func New(executorOpts ExecutorOptions) executor.Executor { traceSocket: executorOpts.TraceSocket, rootless: executorOpts.Rootless, runtime: executorOpts.Runtime, + cdiManager: executorOpts.CDIManager, } } diff --git a/executor/containerdexecutor/executor_unix.go b/executor/containerdexecutor/executor_unix.go index 10b6fababecf..2233909cdff5 100644 --- a/executor/containerdexecutor/executor_unix.go +++ b/executor/containerdexecutor/executor_unix.go @@ -145,7 +145,7 @@ func (w *containerdExecutor) createOCISpec(ctx context.Context, id, resolvConf, } processMode := oci.ProcessSandbox // FIXME(AkihiroSuda) - spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, namespace, w.cgroupParent, processMode, nil, w.apparmorProfile, w.selinux, w.traceSocket, opts...) + spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, namespace, w.cgroupParent, processMode, nil, w.apparmorProfile, w.selinux, w.traceSocket, w.cdiManager, opts...) if err != nil { releaseAll() return nil, nil, err diff --git a/executor/containerdexecutor/executor_windows.go b/executor/containerdexecutor/executor_windows.go index 14f0cf444caa..b09114deb432 100644 --- a/executor/containerdexecutor/executor_windows.go +++ b/executor/containerdexecutor/executor_windows.go @@ -88,7 +88,7 @@ func (w *containerdExecutor) createOCISpec(ctx context.Context, id, _, _ string, } processMode := oci.ProcessSandbox // FIXME(AkihiroSuda) - spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, "", "", namespace, "", processMode, nil, "", false, w.traceSocket, opts...) + spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, "", "", namespace, "", processMode, nil, "", false, w.traceSocket, nil, opts...) if err != nil { releaseAll() return nil, nil, err diff --git a/executor/oci/spec.go b/executor/oci/spec.go index d3cdfaa4415b..08f92e64789b 100644 --- a/executor/oci/spec.go +++ b/executor/oci/spec.go @@ -24,6 +24,7 @@ import ( specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/selinux/go-selinux" "github.com/pkg/errors" + "tags.cncf.io/container-device-interface/pkg/cdi" ) // ProcessMode configures PID namespaces @@ -59,7 +60,7 @@ func (pm ProcessMode) String() string { // GenerateSpec generates spec using containerd functionality. // opts are ignored for s.Process, s.Hostname, and s.Mounts . -func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mount, id, resolvConf, hostsFile string, namespace network.Namespace, cgroupParent string, processMode ProcessMode, idmap *idtools.IdentityMapping, apparmorProfile string, selinuxB bool, tracingSocket string, opts ...oci.SpecOpts) (*specs.Spec, func(), error) { +func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mount, id, resolvConf, hostsFile string, namespace network.Namespace, cgroupParent string, processMode ProcessMode, idmap *idtools.IdentityMapping, apparmorProfile string, selinuxB bool, tracingSocket string, cdiManager *cdi.Cache, opts ...oci.SpecOpts) (*specs.Spec, func(), error) { c := &containers.Container{ ID: id, } @@ -110,12 +111,6 @@ func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mou return nil, nil, err } - if cdiOpts, err := generateCDIOpts(ctx, meta.CDIDevices); err == nil { - opts = append(opts, cdiOpts...) - } else { - return nil, nil, err - } - hostname := defaultHostname if meta.Hostname != "" { hostname = meta.Hostname @@ -135,6 +130,14 @@ func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mou oci.WithHostname(hostname), ) + if cdiManager != nil { + if cdiOpts, err := generateCDIOpts(cdiManager, meta.CDIDevices); err == nil { + opts = append(opts, cdiOpts...) + } else { + return nil, nil, err + } + } + s, err := oci.GenerateSpec(ctx, nil, c, opts...) if err != nil { return nil, nil, errors.WithStack(err) diff --git a/executor/oci/spec_darwin.go b/executor/oci/spec_darwin.go index 3b4f15143650..bfc642289720 100644 --- a/executor/oci/spec_darwin.go +++ b/executor/oci/spec_darwin.go @@ -1,8 +1,6 @@ package oci import ( - "context" - "github.com/containerd/containerd/v2/core/mount" "github.com/containerd/containerd/v2/pkg/oci" "github.com/containerd/continuity/fs" @@ -10,6 +8,7 @@ import ( "github.com/moby/buildkit/solver/pb" "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" + "tags.cncf.io/container-device-interface/pkg/cdi" ) func withProcessArgs(args ...string) oci.SpecOpts { @@ -65,7 +64,7 @@ func sub(m mount.Mount, subPath string) (mount.Mount, func() error, error) { return m, func() error { return nil }, nil } -func generateCDIOpts(ctx context.Context, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) { +func generateCDIOpts(_ *cdi.Cache, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) { if len(devices) == 0 { return nil, nil } diff --git a/executor/oci/spec_freebsd.go b/executor/oci/spec_freebsd.go index a9ac00aa4e7d..a30a18165db5 100644 --- a/executor/oci/spec_freebsd.go +++ b/executor/oci/spec_freebsd.go @@ -1,8 +1,6 @@ package oci import ( - "context" - "github.com/containerd/containerd/v2/core/mount" "github.com/containerd/containerd/v2/pkg/oci" "github.com/containerd/continuity/fs" @@ -10,6 +8,7 @@ import ( "github.com/moby/buildkit/solver/pb" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" + "tags.cncf.io/container-device-interface/pkg/cdi" ) func withProcessArgs(args ...string) oci.SpecOpts { @@ -73,7 +72,7 @@ func sub(m mount.Mount, subPath string) (mount.Mount, func() error, error) { return m, func() error { return nil }, nil } -func generateCDIOpts(ctx context.Context, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) { +func generateCDIOpts(_ *cdi.Cache, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) { if len(devices) == 0 { return nil, nil } diff --git a/executor/oci/spec_linux.go b/executor/oci/spec_linux.go index 44025f9ae081..6187a291cf5b 100644 --- a/executor/oci/spec_linux.go +++ b/executor/oci/spec_linux.go @@ -14,7 +14,6 @@ import ( "github.com/containerd/containerd/v2/pkg/oci" cdseccomp "github.com/containerd/containerd/v2/pkg/seccomp" "github.com/containerd/continuity/fs" - "github.com/containerd/log" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/profiles/seccomp" "github.com/moby/buildkit/snapshot" @@ -153,10 +152,8 @@ func generateRlimitOpts(ulimits []*pb.Ulimit) ([]oci.SpecOpts, error) { } // genereateCDIOptions creates the OCI runtime spec options for injecting CDI -// devices. Two options are returned: The first ensures that the CDI registry -// is initialized with refresh disabled, and the second injects the devices -// into the container. -func generateCDIOpts(ctx context.Context, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) { +// devices. +func generateCDIOpts(manager *cdi.Cache, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) { if len(devices) == 0 { return nil, nil } @@ -171,38 +168,16 @@ func generateCDIOpts(ctx context.Context, devices []*pb.CDIDevice) ([]oci.SpecOp dd = append(dd, d.Name) } - // withStaticCDIRegistry inits the CDI registry and disables auto-refresh. - // This is used from the `run` command to avoid creating a registry with - // auto-refresh enabled. It also provides a way to override the CDI spec - // file paths if required. - withStaticCDIRegistry := func() oci.SpecOpts { - return func(ctx context.Context, _ oci.Client, _ *containers.Container, s *oci.Spec) error { - _ = cdi.Configure(cdi.WithAutoRefresh(false)) - // TODO: this should use worker CDIManager - if err := cdi.Refresh(); err != nil { - // We don't consider registry refresh failure a fatal error. - // For instance, a dynamically generated invalid CDI Spec file - // for any particular vendor shouldn't prevent injection of - // devices of different vendors. CDI itself knows better, and - // it will fail the injection if necessary. - bklog.G(ctx).Warnf("CDI registry refresh failed: %v", err) - } - return nil - } - } - - // withCDIDevices injects the requested CDI devices into the OCI specification. - // FIXME: Use oci.WithCDIDevices once we switch to containerd 2.0. withCDIDevices := func(devices ...string) oci.SpecOpts { return func(ctx context.Context, _ oci.Client, c *containers.Container, s *specs.Spec) error { if len(devices) == 0 { return nil } - if err := cdi.Refresh(); err != nil { - log.G(ctx).Warnf("CDI registry refresh failed: %v", err) + if err := manager.Refresh(); err != nil { + bklog.G(ctx).Warnf("CDI registry refresh failed: %v", err) } bklog.G(ctx).Debugf("Injecting CDI devices %v", devices) - if _, err := cdi.InjectDevices(s, devices...); err != nil { + if _, err := manager.InjectDevices(s, devices...); err != nil { return errors.Wrapf(err, "CDI device injection failed") } // One crucial thing to keep in mind is that CDI device injection @@ -214,7 +189,6 @@ func generateCDIOpts(ctx context.Context, devices []*pb.CDIDevice) ([]oci.SpecOp } return []oci.SpecOpts{ - withStaticCDIRegistry(), withCDIDevices(dd...), }, nil } diff --git a/executor/oci/spec_windows.go b/executor/oci/spec_windows.go index 5d94fd12da19..8b75803be22e 100644 --- a/executor/oci/spec_windows.go +++ b/executor/oci/spec_windows.go @@ -17,6 +17,7 @@ import ( "github.com/moby/buildkit/solver/pb" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" + "tags.cncf.io/container-device-interface/pkg/cdi" ) const ( @@ -111,7 +112,7 @@ func sub(m mount.Mount, subPath string) (mount.Mount, func() error, error) { return m, func() error { return nil }, nil } -func generateCDIOpts(ctx context.Context, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) { +func generateCDIOpts(_ *cdi.Cache, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) { if len(devices) == 0 { return nil, nil } diff --git a/executor/runcexecutor/executor.go b/executor/runcexecutor/executor.go index f284c90cd1dc..5308bf9a4df8 100644 --- a/executor/runcexecutor/executor.go +++ b/executor/runcexecutor/executor.go @@ -18,6 +18,7 @@ import ( "github.com/moby/buildkit/util/bklog" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" + "tags.cncf.io/container-device-interface/pkg/cdi" "github.com/containerd/containerd/v2/core/mount" containerdoci "github.com/containerd/containerd/v2/pkg/oci" @@ -57,6 +58,7 @@ type Opt struct { SELinux bool TracingSocket string ResourceMonitor *resources.Monitor + CDIManager *cdi.Cache } var defaultCommandCandidates = []string{"buildkit-runc", "runc"} @@ -78,6 +80,7 @@ type runcExecutor struct { selinux bool tracingSocket string resmon *resources.Monitor + cdiManager *cdi.Cache } func New(opt Opt, networkProviders map[pb.NetMode]network.Provider) (executor.Executor, error) { @@ -144,6 +147,7 @@ func New(opt Opt, networkProviders map[pb.NetMode]network.Provider) (executor.Ex selinux: opt.SELinux, tracingSocket: opt.TracingSocket, resmon: opt.ResourceMonitor, + cdiManager: opt.CDIManager, } return w, nil } @@ -267,7 +271,7 @@ func (w *runcExecutor) Run(ctx context.Context, id string, root executor.Mount, } } - spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, namespace, w.cgroupParent, w.processMode, w.idmap, w.apparmorProfile, w.selinux, w.tracingSocket, opts...) + spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, namespace, w.cgroupParent, w.processMode, w.idmap, w.apparmorProfile, w.selinux, w.tracingSocket, w.cdiManager, opts...) if err != nil { return nil, err } diff --git a/util/testutil/workers/containerd.go b/util/testutil/workers/containerd.go index d73e5dd89a41..b0c9e918b8a9 100644 --- a/util/testutil/workers/containerd.go +++ b/util/testutil/workers/containerd.go @@ -212,6 +212,7 @@ disabled_plugins = ["io.containerd.grpc.v1.cri"] "--containerd-worker=true", "--containerd-worker-addr", address, "--containerd-worker-labels=org.mobyproject.buildkit.worker.sandbox=true", // Include use of --containerd-worker-labels to trigger https://github.com/moby/buildkit/pull/603 + "--cdi-spec-dir=" + cfg.CDISpecDir, } buildkitdArgs = applyBuildkitdPlatformFlags(buildkitdArgs) buildkitdArgs = append(buildkitdArgs, snBuildkitdArgs...) diff --git a/worker/containerd/containerd.go b/worker/containerd/containerd.go index 31f5e01b0341..de30fdc95768 100644 --- a/worker/containerd/containerd.go +++ b/worker/containerd/containerd.go @@ -24,6 +24,7 @@ import ( ocispecs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "golang.org/x/sync/semaphore" + "tags.cncf.io/container-device-interface/pkg/cdi" ) type RuntimeInfo = containerdexecutor.RuntimeInfo @@ -43,6 +44,7 @@ type WorkerOptions struct { ParallelismSem *semaphore.Weighted TraceSocket string Runtime *RuntimeInfo + CDIManager *cdi.Cache } // NewWorkerOpt creates a WorkerOpt. @@ -162,6 +164,7 @@ func newContainerd(client *ctd.Client, workerOpts WorkerOptions) (base.WorkerOpt TraceSocket: workerOpts.TraceSocket, Rootless: workerOpts.Rootless, Runtime: workerOpts.Runtime, + CDIManager: workerOpts.CDIManager, NetworkProviders: np, } @@ -182,6 +185,7 @@ func newContainerd(client *ctd.Client, workerOpts WorkerOptions) (base.WorkerOpt GarbageCollect: gc, ParallelismSem: workerOpts.ParallelismSem, MountPoolRoot: filepath.Join(root, "cachemounts"), + CDIManager: workerOpts.CDIManager, } return opt, nil } diff --git a/worker/runc/runc.go b/worker/runc/runc.go index bd39247d17f3..95bf49312b74 100644 --- a/worker/runc/runc.go +++ b/worker/runc/runc.go @@ -29,6 +29,7 @@ import ( ocispecs "github.com/opencontainers/image-spec/specs-go/v1" bolt "go.etcd.io/bbolt" "golang.org/x/sync/semaphore" + "tags.cncf.io/container-device-interface/pkg/cdi" ) // SnapshotterFactory instantiates a snapshotter @@ -38,7 +39,7 @@ type SnapshotterFactory struct { } // NewWorkerOpt creates a WorkerOpt. -func NewWorkerOpt(root string, snFactory SnapshotterFactory, rootless bool, processMode oci.ProcessMode, labels map[string]string, idmap *idtools.IdentityMapping, nopt netproviders.Opt, dns *oci.DNSConfig, binary, apparmorProfile string, selinux bool, parallelismSem *semaphore.Weighted, traceSocket, defaultCgroupParent string) (base.WorkerOpt, error) { +func NewWorkerOpt(root string, snFactory SnapshotterFactory, rootless bool, processMode oci.ProcessMode, labels map[string]string, idmap *idtools.IdentityMapping, nopt netproviders.Opt, dns *oci.DNSConfig, binary, apparmorProfile string, selinux bool, parallelismSem *semaphore.Weighted, traceSocket, defaultCgroupParent string, cdiManager *cdi.Cache) (base.WorkerOpt, error) { var opt base.WorkerOpt name := "runc-" + snFactory.Name root = filepath.Join(root, name) @@ -78,6 +79,7 @@ func NewWorkerOpt(root string, snFactory SnapshotterFactory, rootless bool, proc TracingSocket: traceSocket, DefaultCgroupParent: defaultCgroupParent, ResourceMonitor: rm, + CDIManager: cdiManager, }, np) if err != nil { return opt, err @@ -166,6 +168,7 @@ func NewWorkerOpt(root string, snFactory SnapshotterFactory, rootless bool, proc ParallelismSem: parallelismSem, MountPoolRoot: filepath.Join(root, "cachemounts"), ResourceMonitor: rm, + CDIManager: cdiManager, } return opt, nil } diff --git a/worker/runc/runc_test.go b/worker/runc/runc_test.go index ed0835046e64..ea58b90d47f0 100644 --- a/worker/runc/runc_test.go +++ b/worker/runc/runc_test.go @@ -37,7 +37,7 @@ func newWorkerOpt(t *testing.T, processMode oci.ProcessMode) base.WorkerOpt { }, } rootless := false - workerOpt, err := NewWorkerOpt(tmpdir, snFactory, rootless, processMode, nil, nil, netproviders.Opt{Mode: "host"}, nil, "", "", false, nil, "", "") + workerOpt, err := NewWorkerOpt(tmpdir, snFactory, rootless, processMode, nil, nil, netproviders.Opt{Mode: "host"}, nil, "", "", false, nil, "", "", nil) require.NoError(t, err) return workerOpt From 943c3ad1e09af2cbfe67681cc60d7bcfeae2d615 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Mon, 3 Feb 2025 17:55:53 +0100 Subject: [PATCH 12/23] dockerfile: move cdi run device to labs Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- .../dockerfile/dockerfile2llb/convert_norundevice.go | 12 ++++++++++++ .../dockerfile/dockerfile2llb/convert_rundevice.go | 4 ++-- frontend/dockerfile/dockerfile_rundevice_test.go | 12 ++++++++---- frontend/dockerfile/dockerfile_test.go | 2 -- frontend/dockerfile/release/labs/tags | 2 +- 5 files changed, 23 insertions(+), 9 deletions(-) create mode 100644 frontend/dockerfile/dockerfile2llb/convert_norundevice.go diff --git a/frontend/dockerfile/dockerfile2llb/convert_norundevice.go b/frontend/dockerfile/dockerfile2llb/convert_norundevice.go new file mode 100644 index 000000000000..97a2aeef912a --- /dev/null +++ b/frontend/dockerfile/dockerfile2llb/convert_norundevice.go @@ -0,0 +1,12 @@ +//go:build !dfrundevice + +package dockerfile2llb + +import ( + "github.com/moby/buildkit/client/llb" + "github.com/moby/buildkit/frontend/dockerfile/instructions" +) + +func dispatchRunDevices(_ *instructions.RunCommand) ([]llb.RunOption, error) { + return nil, nil +} diff --git a/frontend/dockerfile/dockerfile2llb/convert_rundevice.go b/frontend/dockerfile/dockerfile2llb/convert_rundevice.go index df21a3492861..e326fb765fcc 100644 --- a/frontend/dockerfile/dockerfile2llb/convert_rundevice.go +++ b/frontend/dockerfile/dockerfile2llb/convert_rundevice.go @@ -1,6 +1,6 @@ -package dockerfile2llb +//go:build dfrundevice -// TODO: move in labs with dfrundevice tag +package dockerfile2llb import ( "github.com/moby/buildkit/client/llb" diff --git a/frontend/dockerfile/dockerfile_rundevice_test.go b/frontend/dockerfile/dockerfile_rundevice_test.go index 2c9882e1a40d..1046e5ce4de2 100644 --- a/frontend/dockerfile/dockerfile_rundevice_test.go +++ b/frontend/dockerfile/dockerfile_rundevice_test.go @@ -1,3 +1,5 @@ +//go:build dfrundevice + package dockerfile import ( @@ -13,10 +15,12 @@ import ( "github.com/tonistiigi/fsutil" ) -var deviceTests = integration.TestFuncs( - testDeviceEnv, - testDeviceRunEnv, -) +func init() { + allTests = append(allTests, integration.TestFuncs( + testDeviceEnv, + testDeviceRunEnv, + )...) +} func testDeviceEnv(t *testing.T, sb integration.Sandbox) { if sb.Rootless() { diff --git a/frontend/dockerfile/dockerfile_test.go b/frontend/dockerfile/dockerfile_test.go index 78da4d950d71..c1195dbbb503 100644 --- a/frontend/dockerfile/dockerfile_test.go +++ b/frontend/dockerfile/dockerfile_test.go @@ -301,8 +301,6 @@ func TestIntegration(t *testing.T) { "granted": networkHostGranted, "denied": networkHostDenied, }))...) - - integration.Run(t, deviceTests, opts...) } func testEmptyStringArgInEnv(t *testing.T, sb integration.Sandbox) { diff --git a/frontend/dockerfile/release/labs/tags b/frontend/dockerfile/release/labs/tags index 1ebdc80f9956..efaea6dab6da 100644 --- a/frontend/dockerfile/release/labs/tags +++ b/frontend/dockerfile/release/labs/tags @@ -1 +1 @@ -dfrunsecurity dfparents dfexcludepatterns dfcopychmodnonoctal +dfrunsecurity dfparents dfexcludepatterns dfcopychmodnonoctal dfrundevice From 6667434ec4da58adec25b361d166f7a2349ceed4 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Wed, 5 Feb 2025 13:15:43 +0100 Subject: [PATCH 13/23] cdi: support optional devices Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- client/client_test.go | 5 +- client/llb/exec.go | 62 +- client/llb/meta.go | 28 - client/llb/state.go | 7 +- executor/oci/spec_linux.go | 48 +- frontend/dockerfile/dockerfile2llb/convert.go | 11 +- .../dockerfile2llb/convert_rundevice.go | 2 +- solver/pb/ops.pb.go | 641 +++++++++--------- solver/pb/ops.proto | 2 + solver/pb/ops_vtproto.pb.go | 37 + 10 files changed, 454 insertions(+), 389 deletions(-) diff --git a/client/client_test.go b/client/client_test.go index 2d025ea71980..b13ab645a82e 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -11027,8 +11027,9 @@ devices: st = busybox.Run(append(ro, llb.Shlex(cmd), llb.Dir("/wd"))...).AddMount("/wd", st) } - run(`sh -c 'env|sort | tee foo.env'`, llb.AddCDIDevice("vendor1.com/device=foo")) - run(`sh -c 'env|sort | tee bar.env'`, llb.AddCDIDevice("vendor2.com/device=bar")) + run(`sh -c 'env|sort | tee foo.env'`, llb.AddCDIDevice(llb.CDIDeviceName("vendor1.com/device=foo"))) + run(`sh -c 'env|sort | tee bar.env'`, llb.AddCDIDevice(llb.CDIDeviceName("vendor2.com/device=bar"))) + run(`ls`, llb.AddCDIDevice(llb.CDIDeviceName("vendor3.com/device=baz"), llb.CDIDeviceOptional)) def, err := st.Marshal(sb.Context()) require.NoError(t, err) diff --git a/client/llb/exec.go b/client/llb/exec.go index 2a97f7e8e72e..ba66e7332c2d 100644 --- a/client/llb/exec.go +++ b/client/llb/exec.go @@ -60,6 +60,7 @@ type ExecOp struct { isValidated bool secrets []SecretInfo ssh []SSHInfo + cdiDevices []CDIDeviceInfo } func (e *ExecOp) AddMount(target string, source Output, opt ...MountOption) Output { @@ -267,21 +268,6 @@ func (e *ExecOp) Marshal(ctx context.Context, c *Constraints) (digest.Digest, [] Security: security, } - cdiDevices, err := getCDIDevice(e.base)(ctx, c) - if err != nil { - return "", nil, nil, nil, err - } - if len(cdiDevices) > 0 { - addCap(&e.constraints, pb.CapExecMetaCDI) - cd := make([]*pb.CDIDevice, len(cdiDevices)) - for i, d := range cdiDevices { - cd[i] = &pb.CDIDevice{ - Name: d.Name, - } - } - peo.CdiDevices = cd - } - if network != NetModeSandbox { addCap(&e.constraints, pb.CapExecMetaNetwork) } @@ -337,6 +323,18 @@ func (e *ExecOp) Marshal(ctx context.Context, c *Constraints) (digest.Digest, [] addCap(&e.constraints, pb.CapExecMountSSH) } + if len(e.cdiDevices) > 0 { + addCap(&e.constraints, pb.CapExecMetaCDI) + cd := make([]*pb.CDIDevice, len(e.cdiDevices)) + for i, d := range e.cdiDevices { + cd[i] = &pb.CDIDevice{ + Name: d.Name, + Optional: d.Optional, + } + } + peo.CdiDevices = cd + } + if e.constraints.Platform == nil { p, err := getPlatform(e.base)(ctx, c) if err != nil { @@ -640,12 +638,41 @@ func AddUlimit(name UlimitName, soft int64, hard int64) RunOption { }) } -func AddCDIDevice(name string) RunOption { +func AddCDIDevice(opts ...CDIDeviceOption) RunOption { return runOptionFunc(func(ei *ExecInfo) { - ei.State = ei.State.AddCDIDevice(name) + c := &CDIDeviceInfo{} + for _, opt := range opts { + opt.SetCDIDeviceOption(c) + } + ei.CDIDevices = append(ei.CDIDevices, *c) + }) +} + +type CDIDeviceOption interface { + SetCDIDeviceOption(*CDIDeviceInfo) +} + +type cdiDeviceOptionFunc func(*CDIDeviceInfo) + +func (fn cdiDeviceOptionFunc) SetCDIDeviceOption(ci *CDIDeviceInfo) { + fn(ci) +} + +func CDIDeviceName(name string) CDIDeviceOption { + return cdiDeviceOptionFunc(func(ci *CDIDeviceInfo) { + ci.Name = name }) } +var CDIDeviceOptional = cdiDeviceOptionFunc(func(ci *CDIDeviceInfo) { + ci.Optional = true +}) + +type CDIDeviceInfo struct { + Name string + Optional bool +} + func ValidExitCodes(codes ...int) RunOption { return runOptionFunc(func(ei *ExecInfo) { ei.State = validExitCodes(codes...)(ei.State) @@ -837,6 +864,7 @@ type ExecInfo struct { ProxyEnv *ProxyEnv Secrets []SecretInfo SSH []SSHInfo + CDIDevices []CDIDeviceInfo } type MountInfo struct { diff --git a/client/llb/meta.go b/client/llb/meta.go index e7bbe39686d7..640be7478255 100644 --- a/client/llb/meta.go +++ b/client/llb/meta.go @@ -24,7 +24,6 @@ var ( keyExtraHost = contextKeyT("llb.exec.extrahost") keyHostname = contextKeyT("llb.exec.hostname") keyUlimit = contextKeyT("llb.exec.ulimit") - keyDevice = contextKeyT("llb.exec.device") keyCgroupParent = contextKeyT("llb.exec.cgroup.parent") keyUser = contextKeyT("llb.exec.user") keyValidExitCodes = contextKeyT("llb.exec.validexitcodes") @@ -306,33 +305,6 @@ func getUlimit(s State) func(context.Context, *Constraints) ([]*pb.Ulimit, error } } -func cdiDevice(name string) StateOption { - return func(s State) State { - return s.withValue(keyDevice, func(ctx context.Context, c *Constraints) (interface{}, error) { - v, err := getCDIDevice(s)(ctx, c) - if err != nil { - return nil, err - } - return append(v, &pb.CDIDevice{ - Name: name, - }), nil - }) - } -} - -func getCDIDevice(s State) func(context.Context, *Constraints) ([]*pb.CDIDevice, error) { - return func(ctx context.Context, c *Constraints) ([]*pb.CDIDevice, error) { - v, err := s.getValue(keyDevice)(ctx, c) - if err != nil { - return nil, err - } - if v != nil { - return v.([]*pb.CDIDevice), nil - } - return nil, nil - } -} - func cgroupParent(cp string) StateOption { return func(s State) State { return s.WithValue(keyCgroupParent, cp) diff --git a/client/llb/state.go b/client/llb/state.go index a18ba2a297c1..5743a31e389a 100644 --- a/client/llb/state.go +++ b/client/llb/state.go @@ -295,6 +295,7 @@ func (s State) Run(ro ...RunOption) ExecState { } exec.secrets = ei.Secrets exec.ssh = ei.SSH + exec.cdiDevices = ei.CDIDevices return ExecState{ State: s.WithOutput(exec.Output()), @@ -476,12 +477,6 @@ func (s State) AddUlimit(name UlimitName, soft int64, hard int64) State { return ulimit(name, soft, hard)(s) } -// AddCDIDevice sets the fully qualified CDI device name. -// https://github.com/cncf-tags/container-device-interface/blob/main/SPEC.md -func (s State) AddCDIDevice(name string) State { - return cdiDevice(name)(s) -} - // WithCgroupParent sets the parent cgroup for any containers created from this state. // This is useful when you want to apply resource constraints to a group of containers. // Cgroups are Linux specific and only applies to containers created from this state such as via `[State.Run]` diff --git a/executor/oci/spec_linux.go b/executor/oci/spec_linux.go index 6187a291cf5b..2b4e3c78e7c4 100644 --- a/executor/oci/spec_linux.go +++ b/executor/oci/spec_linux.go @@ -157,29 +157,43 @@ func generateCDIOpts(manager *cdi.Cache, devices []*pb.CDIDevice) ([]oci.SpecOpt if len(devices) == 0 { return nil, nil } - var dd []string - for _, d := range devices { - if d == nil { - continue - } - if _, _, _, err := parser.ParseQualifiedName(d.Name); err != nil { - return nil, errors.Wrapf(err, "invalid CDI device name %s", d.Name) - } - dd = append(dd, d.Name) - } - withCDIDevices := func(devices ...string) oci.SpecOpts { + withCDIDevices := func(devices []*pb.CDIDevice) oci.SpecOpts { return func(ctx context.Context, _ oci.Client, c *containers.Container, s *specs.Spec) error { - if len(devices) == 0 { - return nil - } if err := manager.Refresh(); err != nil { bklog.G(ctx).Warnf("CDI registry refresh failed: %v", err) } - bklog.G(ctx).Debugf("Injecting CDI devices %v", devices) - if _, err := manager.InjectDevices(s, devices...); err != nil { + + registeredDevices := manager.ListDevices() + isDeviceRegistered := func(device *pb.CDIDevice) bool { + for _, name := range registeredDevices { + if device.Name == name { + return true + } + } + return false + } + + var dd []string + for _, d := range devices { + if d == nil { + continue + } + if _, _, _, err := parser.ParseQualifiedName(d.Name); err != nil { + return errors.Wrapf(err, "invalid CDI device name %s", d.Name) + } + if !isDeviceRegistered(d) && d.Optional { + bklog.G(ctx).Warnf("Optional CDI device %q is not registered", d.Name) + continue + } + dd = append(dd, d.Name) + } + + bklog.G(ctx).Debugf("Injecting CDI devices %v", dd) + if _, err := manager.InjectDevices(s, dd...); err != nil { return errors.Wrapf(err, "CDI device injection failed") } + // One crucial thing to keep in mind is that CDI device injection // might add OCI Spec environment variables, hooks, and mounts as // well. Therefore, it is important that none of the corresponding @@ -189,7 +203,7 @@ func generateCDIOpts(manager *cdi.Cache, devices []*pb.CDIDevice) ([]oci.SpecOpt } return []oci.SpecOpts{ - withCDIDevices(dd...), + withCDIDevices(devices), }, nil } diff --git a/frontend/dockerfile/dockerfile2llb/convert.go b/frontend/dockerfile/dockerfile2llb/convert.go index 5aae74203633..8c99d8babc48 100644 --- a/frontend/dockerfile/dockerfile2llb/convert.go +++ b/frontend/dockerfile/dockerfile2llb/convert.go @@ -1305,10 +1305,15 @@ func dispatchRun(d *dispatchState, c *instructions.RunCommand, proxy *llb.ProxyE } } - // TODO: use entitlements if dopt.llbCaps != nil && dopt.llbCaps.Supports(pb.CapExecMetaCDI) == nil { - for _, u := range dopt.devices { - opt = append(opt, llb.AddCDIDevice(u.Name)) + for _, device := range dopt.devices { + deviceOpts := []llb.CDIDeviceOption{ + llb.CDIDeviceName(device.Name), + } + if device.Optional { + deviceOpts = append(deviceOpts, llb.CDIDeviceOptional) + } + opt = append(opt, llb.AddCDIDevice(deviceOpts...)) } runDevices, err := dispatchRunDevices(c) if err != nil { diff --git a/frontend/dockerfile/dockerfile2llb/convert_rundevice.go b/frontend/dockerfile/dockerfile2llb/convert_rundevice.go index e326fb765fcc..5f6b222def65 100644 --- a/frontend/dockerfile/dockerfile2llb/convert_rundevice.go +++ b/frontend/dockerfile/dockerfile2llb/convert_rundevice.go @@ -11,7 +11,7 @@ func dispatchRunDevices(c *instructions.RunCommand) ([]llb.RunOption, error) { var out []llb.RunOption devices := instructions.GetDevices(c) for _, device := range devices { - out = append(out, llb.AddCDIDevice(device)) + out = append(out, llb.AddCDIDevice(llb.CDIDeviceName(device), llb.CDIDeviceOptional)) } return out, nil } diff --git a/solver/pb/ops.pb.go b/solver/pb/ops.pb.go index 19c13b92e80f..4acac63c24d0 100644 --- a/solver/pb/ops.pb.go +++ b/solver/pb/ops.pb.go @@ -972,6 +972,8 @@ type CDIDevice struct { // Fully qualified CDI device name (e.g., vendor.com/gpu=gpudevice1) // https://github.com/cncf-tags/container-device-interface/blob/main/SPEC.md Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // Optional defines if CDI device is required. + Optional bool `protobuf:"varint,2,opt,name=optional,proto3" json:"optional,omitempty"` } func (x *CDIDevice) Reset() { @@ -1011,6 +1013,13 @@ func (x *CDIDevice) GetName() string { return "" } +func (x *CDIDevice) GetOptional() bool { + if x != nil { + return x.Optional + } + return false +} + // Mount specifies how to mount an input Op as a filesystem. type Mount struct { state protoimpl.MessageState @@ -3481,327 +3490,329 @@ var file_github_com_moby_buildkit_solver_pb_ops_proto_rawDesc = []byte{ 0x44, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, - 0x6c, 0x22, 0x1f, 0x0a, 0x09, 0x43, 0x44, 0x49, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x12, + 0x6c, 0x22, 0x3b, 0x0a, 0x09, 0x43, 0x44, 0x49, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x22, 0xaa, 0x03, 0x0a, 0x05, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, - 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x69, 0x6e, 0x70, - 0x75, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x12, - 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, - 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, - 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, - 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x12, 0x2b, 0x0a, 0x09, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x4d, - 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x28, 0x0a, 0x08, 0x54, 0x6d, 0x70, 0x66, 0x73, 0x4f, 0x70, 0x74, 0x18, - 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x6d, 0x70, 0x66, 0x73, - 0x4f, 0x70, 0x74, 0x52, 0x08, 0x54, 0x6d, 0x70, 0x66, 0x73, 0x4f, 0x70, 0x74, 0x12, 0x28, 0x0a, - 0x08, 0x63, 0x61, 0x63, 0x68, 0x65, 0x4f, 0x70, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x61, 0x63, 0x68, 0x65, 0x4f, 0x70, 0x74, 0x52, 0x08, 0x63, - 0x61, 0x63, 0x68, 0x65, 0x4f, 0x70, 0x74, 0x12, 0x2b, 0x0a, 0x09, 0x73, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x4f, 0x70, 0x74, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x62, 0x2e, - 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x52, 0x09, 0x73, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x4f, 0x70, 0x74, 0x12, 0x22, 0x0a, 0x06, 0x53, 0x53, 0x48, 0x4f, 0x70, 0x74, 0x18, 0x16, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x53, 0x48, 0x4f, 0x70, 0x74, - 0x52, 0x06, 0x53, 0x53, 0x48, 0x4f, 0x70, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x49, 0x44, 0x18, 0x17, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x49, 0x44, 0x12, 0x39, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x43, - 0x61, 0x63, 0x68, 0x65, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x70, 0x62, 0x2e, - 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x43, 0x61, 0x63, 0x68, - 0x65, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x43, 0x61, 0x63, 0x68, 0x65, 0x22, - 0x1e, 0x0a, 0x08, 0x54, 0x6d, 0x70, 0x66, 0x73, 0x4f, 0x70, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, - 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, - 0x49, 0x0a, 0x08, 0x43, 0x61, 0x63, 0x68, 0x65, 0x4f, 0x70, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x49, - 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x2d, 0x0a, 0x07, 0x73, - 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x70, - 0x62, 0x2e, 0x43, 0x61, 0x63, 0x68, 0x65, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4f, 0x70, - 0x74, 0x52, 0x07, 0x73, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x6f, 0x0a, 0x09, 0x53, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x69, 0x64, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x67, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6d, - 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, - 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x22, 0x6c, 0x0a, 0x06, 0x53, - 0x53, 0x48, 0x4f, 0x70, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x69, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x67, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x1a, 0x0a, - 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x22, 0x93, 0x01, 0x0a, 0x08, 0x53, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x4f, 0x70, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x2d, 0x0a, 0x05, 0x61, 0x74, 0x74, 0x72, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x4f, 0x70, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, - 0x61, 0x74, 0x74, 0x72, 0x73, 0x1a, 0x38, 0x0a, 0x0a, 0x41, 0x74, 0x74, 0x72, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, - 0xa9, 0x02, 0x0a, 0x07, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4f, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x62, 0x75, - 0x69, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x2f, 0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x62, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, - 0x4f, 0x70, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, - 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x20, 0x0a, 0x03, 0x64, 0x65, 0x66, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x64, 0x65, 0x66, 0x12, 0x2c, 0x0a, 0x05, 0x61, 0x74, 0x74, 0x72, - 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x62, 0x2e, 0x42, 0x75, 0x69, - 0x6c, 0x64, 0x4f, 0x70, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x05, 0x61, 0x74, 0x74, 0x72, 0x73, 0x1a, 0x49, 0x0a, 0x0b, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x42, 0x75, 0x69, 0x6c, - 0x64, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x1a, 0x38, 0x0a, 0x0a, 0x41, 0x74, 0x74, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x22, 0xaa, + 0x03, 0x0a, 0x05, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x1a, + 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, + 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x16, + 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, + 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, + 0x6c, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, + 0x6c, 0x79, 0x12, 0x2b, 0x0a, 0x09, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x4d, 0x6f, 0x75, 0x6e, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x28, 0x0a, 0x08, 0x54, 0x6d, 0x70, 0x66, 0x73, 0x4f, 0x70, 0x74, 0x18, 0x13, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x6d, 0x70, 0x66, 0x73, 0x4f, 0x70, 0x74, 0x52, + 0x08, 0x54, 0x6d, 0x70, 0x66, 0x73, 0x4f, 0x70, 0x74, 0x12, 0x28, 0x0a, 0x08, 0x63, 0x61, 0x63, + 0x68, 0x65, 0x4f, 0x70, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, + 0x2e, 0x43, 0x61, 0x63, 0x68, 0x65, 0x4f, 0x70, 0x74, 0x52, 0x08, 0x63, 0x61, 0x63, 0x68, 0x65, + 0x4f, 0x70, 0x74, 0x12, 0x2b, 0x0a, 0x09, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4f, 0x70, 0x74, + 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x4f, 0x70, 0x74, 0x52, 0x09, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4f, 0x70, 0x74, + 0x12, 0x22, 0x0a, 0x06, 0x53, 0x53, 0x48, 0x4f, 0x70, 0x74, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x53, 0x48, 0x4f, 0x70, 0x74, 0x52, 0x06, 0x53, 0x53, + 0x48, 0x4f, 0x70, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x49, 0x44, + 0x18, 0x17, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x49, 0x44, + 0x12, 0x39, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x43, 0x61, 0x63, 0x68, 0x65, + 0x18, 0x18, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x4d, 0x6f, 0x75, 0x6e, + 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x43, 0x61, 0x63, 0x68, 0x65, 0x52, 0x0c, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x43, 0x61, 0x63, 0x68, 0x65, 0x22, 0x1e, 0x0a, 0x08, 0x54, + 0x6d, 0x70, 0x66, 0x73, 0x4f, 0x70, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x49, 0x0a, 0x08, 0x43, + 0x61, 0x63, 0x68, 0x65, 0x4f, 0x70, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x2d, 0x0a, 0x07, 0x73, 0x68, 0x61, 0x72, 0x69, + 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x61, + 0x63, 0x68, 0x65, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4f, 0x70, 0x74, 0x52, 0x07, 0x73, + 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x6f, 0x0a, 0x09, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, + 0x4f, 0x70, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x49, 0x44, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x03, 0x67, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x22, 0x6c, 0x0a, 0x06, 0x53, 0x53, 0x48, 0x4f, 0x70, + 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, + 0x44, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, + 0x75, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x03, 0x67, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x22, 0x93, 0x01, 0x0a, 0x08, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x4f, 0x70, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, + 0x65, 0x72, 0x12, 0x2d, 0x0a, 0x05, 0x61, 0x74, 0x74, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4f, 0x70, 0x2e, + 0x41, 0x74, 0x74, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x61, 0x74, 0x74, 0x72, + 0x73, 0x1a, 0x38, 0x0a, 0x0a, 0x41, 0x74, 0x74, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x22, 0x0a, 0x0a, 0x42, - 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x70, - 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x22, - 0x87, 0x03, 0x0a, 0x0a, 0x4f, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x21, - 0x0a, 0x0c, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x5f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x43, 0x61, 0x63, 0x68, - 0x65, 0x12, 0x41, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x62, 0x2e, 0x4f, 0x70, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x0c, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x63, - 0x61, 0x63, 0x68, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x62, 0x2e, - 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x61, 0x63, 0x68, 0x65, 0x52, 0x0b, 0x65, 0x78, 0x70, - 0x6f, 0x72, 0x74, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x2c, 0x0a, 0x04, 0x63, 0x61, 0x70, 0x73, - 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x62, 0x2e, 0x4f, 0x70, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x61, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x04, 0x63, 0x61, 0x70, 0x73, 0x12, 0x38, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, - 0x73, 0x73, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, - 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x1a, 0x3e, 0x0a, 0x10, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x1a, 0x37, 0x0a, 0x09, 0x43, 0x61, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xb4, 0x01, 0x0a, 0x06, 0x53, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x12, 0x37, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x24, 0x0a, - 0x05, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, - 0x62, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x69, 0x6e, - 0x66, 0x6f, 0x73, 0x1a, 0x4b, 0x0a, 0x0e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x22, 0x37, 0x0a, 0x09, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2a, 0x0a, - 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, - 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x88, 0x01, 0x0a, 0x0a, 0x53, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2e, 0x0a, 0x0a, 0x64, 0x65, 0x66, 0x69, - 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, - 0x62, 0x2e, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x64, 0x65, - 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67, - 0x75, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, - 0x75, 0x61, 0x67, 0x65, 0x22, 0x4f, 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, - 0x65, 0x78, 0x12, 0x21, 0x0a, 0x06, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x06, 0x72, - 0x61, 0x6e, 0x67, 0x65, 0x73, 0x22, 0x4b, 0x0a, 0x05, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, - 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, - 0x70, 0x62, 0x2e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x65, - 0x6e, 0x64, 0x22, 0x3c, 0x0a, 0x08, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, - 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x6c, 0x69, - 0x6e, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, - 0x22, 0x23, 0x0a, 0x0b, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, - 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x47, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, - 0x73, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x65, - 0x61, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x77, 0x65, 0x61, 0x6b, 0x22, 0x9f, - 0x01, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x45, 0x6e, 0x76, 0x12, 0x1d, 0x0a, 0x0a, 0x68, - 0x74, 0x74, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x68, 0x74, 0x74, 0x70, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x74, - 0x74, 0x70, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x68, 0x74, 0x74, 0x70, 0x73, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x66, - 0x74, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x66, 0x74, 0x70, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x6f, 0x5f, 0x70, - 0x72, 0x6f, 0x78, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x6f, 0x50, 0x72, - 0x6f, 0x78, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x6c, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x78, 0x79, - 0x22, 0x2b, 0x0a, 0x11, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, - 0x61, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0xc9, 0x01, - 0x0a, 0x0a, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, - 0x64, 0x65, 0x66, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x03, 0x64, 0x65, 0x66, 0x12, 0x38, - 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1c, 0x2e, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, - 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x06, 0x53, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x52, 0x06, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0x4b, 0x0a, 0x0d, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, - 0x2e, 0x70, 0x62, 0x2e, 0x4f, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x32, 0x0a, 0x06, 0x46, 0x69, 0x6c, - 0x65, 0x4f, 0x70, 0x12, 0x28, 0x0a, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xca, 0x02, - 0x0a, 0x0a, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, - 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x69, 0x6e, 0x70, - 0x75, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x49, - 0x6e, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x73, 0x65, 0x63, 0x6f, - 0x6e, 0x64, 0x61, 0x72, 0x79, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, - 0x75, 0x74, 0x12, 0x28, 0x0a, 0x04, 0x63, 0x6f, 0x70, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x43, 0x6f, 0x70, 0x79, 0x48, 0x00, 0x52, 0x04, 0x63, 0x6f, 0x70, 0x79, 0x12, 0x2e, 0x0a, 0x06, - 0x6d, 0x6b, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, - 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6b, 0x46, 0x69, - 0x6c, 0x65, 0x48, 0x00, 0x52, 0x06, 0x6d, 0x6b, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x2b, 0x0a, 0x05, - 0x6d, 0x6b, 0x64, 0x69, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x62, - 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6b, 0x44, 0x69, 0x72, - 0x48, 0x00, 0x52, 0x05, 0x6d, 0x6b, 0x64, 0x69, 0x72, 0x12, 0x22, 0x0a, 0x02, 0x72, 0x6d, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x41, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x6d, 0x48, 0x00, 0x52, 0x02, 0x72, 0x6d, 0x12, 0x31, 0x0a, - 0x07, 0x73, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, - 0x2e, 0x70, 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x79, - 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x48, 0x00, 0x52, 0x07, 0x73, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, - 0x42, 0x08, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xde, 0x04, 0x0a, 0x0e, 0x46, - 0x69, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x70, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x73, 0x72, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x72, 0x63, 0x12, - 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, - 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x68, 0x6f, 0x77, 0x6e, 0x4f, 0x70, 0x74, - 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x66, - 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x53, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0d, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x53, 0x79, 0x6d, 0x6c, 0x69, 0x6e, - 0x6b, 0x12, 0x28, 0x0a, 0x0f, 0x64, 0x69, 0x72, 0x43, 0x6f, 0x70, 0x79, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x69, 0x72, 0x43, - 0x6f, 0x70, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x4a, 0x0a, 0x20, 0x61, - 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x55, 0x6e, 0x70, 0x61, 0x63, 0x6b, 0x44, 0x6f, 0x63, 0x6b, - 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x20, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x55, 0x6e, - 0x70, 0x61, 0x63, 0x6b, 0x44, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x74, - 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x44, 0x65, 0x73, 0x74, 0x50, 0x61, 0x74, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x73, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, - 0x24, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x57, 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x57, 0x69, 0x6c, - 0x64, 0x63, 0x61, 0x72, 0x64, 0x12, 0x2e, 0x0a, 0x12, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x57, 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x12, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x57, 0x69, 0x6c, - 0x64, 0x63, 0x61, 0x72, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x70, - 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x69, - 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x73, 0x12, 0x29, - 0x0a, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, - 0x6e, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, - 0x65, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x73, 0x12, 0x46, 0x0a, 0x1e, 0x61, 0x6c, 0x77, - 0x61, 0x79, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, - 0x6e, 0x67, 0x44, 0x65, 0x73, 0x74, 0x50, 0x61, 0x74, 0x68, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x1e, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, - 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x73, 0x74, 0x50, 0x61, 0x74, 0x68, - 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x72, 0x18, 0x0f, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x72, 0x22, 0x90, 0x01, 0x0a, 0x10, - 0x46, 0x69, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6b, 0x46, 0x69, 0x6c, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x05, - 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, - 0x2e, 0x43, 0x68, 0x6f, 0x77, 0x6e, 0x4f, 0x70, 0x74, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, - 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x89, - 0x01, 0x0a, 0x11, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x79, 0x6d, - 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x6c, 0x64, 0x70, 0x61, 0x74, 0x68, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x6c, 0x64, 0x70, 0x61, 0x74, 0x68, 0x12, 0x18, - 0x0a, 0x07, 0x6e, 0x65, 0x77, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x6e, 0x65, 0x77, 0x70, 0x61, 0x74, 0x68, 0x12, 0x22, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, - 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x68, 0x6f, - 0x77, 0x6e, 0x4f, 0x70, 0x74, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, - 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x9d, 0x01, 0x0a, 0x0f, 0x46, - 0x69, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6b, 0x44, 0x69, 0x72, 0x12, 0x12, - 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, - 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x61, 0x6b, 0x65, 0x50, 0x61, - 0x72, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6d, 0x61, 0x6b, - 0x65, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x22, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xa9, 0x02, 0x0a, 0x07, + 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4f, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, + 0x72, 0x12, 0x2f, 0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x62, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4f, 0x70, 0x2e, 0x49, + 0x6e, 0x70, 0x75, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x69, 0x6e, 0x70, 0x75, + 0x74, 0x73, 0x12, 0x20, 0x0a, 0x03, 0x64, 0x65, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x03, 0x64, 0x65, 0x66, 0x12, 0x2c, 0x0a, 0x05, 0x61, 0x74, 0x74, 0x72, 0x73, 0x18, 0x04, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x62, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4f, 0x70, + 0x2e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x61, 0x74, 0x74, + 0x72, 0x73, 0x1a, 0x49, 0x0a, 0x0b, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x70, + 0x75, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x38, 0x0a, + 0x0a, 0x41, 0x74, 0x74, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x22, 0x0a, 0x0a, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x22, 0x87, 0x03, 0x0a, 0x0a, + 0x4f, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x67, + 0x6e, 0x6f, 0x72, 0x65, 0x5f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0b, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x41, 0x0a, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x62, 0x2e, 0x4f, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x32, 0x0a, 0x0c, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x63, 0x61, 0x63, 0x68, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x78, 0x70, 0x6f, + 0x72, 0x74, 0x43, 0x61, 0x63, 0x68, 0x65, 0x52, 0x0b, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, + 0x61, 0x63, 0x68, 0x65, 0x12, 0x2c, 0x0a, 0x04, 0x63, 0x61, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x62, 0x2e, 0x4f, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x43, 0x61, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x63, 0x61, + 0x70, 0x73, 0x12, 0x38, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x62, 0x2e, + 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x0d, 0x70, + 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x1a, 0x3e, 0x0a, 0x10, + 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x37, 0x0a, 0x09, + 0x43, 0x61, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xb4, 0x01, 0x0a, 0x06, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x12, 0x37, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, + 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, + 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x24, 0x0a, 0x05, 0x69, 0x6e, 0x66, + 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x1a, + 0x4b, 0x0a, 0x0e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x37, 0x0a, 0x09, + 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2a, 0x0a, 0x09, 0x6c, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, + 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x88, 0x01, 0x0a, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, + 0x64, 0x61, 0x74, 0x61, 0x12, 0x2e, 0x0a, 0x0a, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x44, 0x65, + 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, + 0x22, 0x4f, 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x21, + 0x0a, 0x06, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, + 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x06, 0x72, 0x61, 0x6e, 0x67, 0x65, + 0x73, 0x22, 0x4b, 0x0a, 0x05, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x0a, 0x05, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x50, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x1e, + 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, + 0x2e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x3c, + 0x0a, 0x08, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, + 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x1c, + 0x0a, 0x09, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x09, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x22, 0x23, 0x0a, 0x0b, + 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x22, 0x47, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x65, 0x61, 0x6b, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x77, 0x65, 0x61, 0x6b, 0x22, 0x9f, 0x01, 0x0a, 0x08, 0x50, + 0x72, 0x6f, 0x78, 0x79, 0x45, 0x6e, 0x76, 0x12, 0x1d, 0x0a, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x5f, + 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x68, 0x74, 0x74, + 0x70, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x74, 0x74, 0x70, 0x73, 0x5f, + 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x68, 0x74, 0x74, + 0x70, 0x73, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x74, 0x70, 0x5f, 0x70, + 0x72, 0x6f, 0x78, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x74, 0x70, 0x50, + 0x72, 0x6f, 0x78, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x6f, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x6f, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, + 0x1b, 0x0a, 0x09, 0x61, 0x6c, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x61, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x22, 0x2b, 0x0a, 0x11, + 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, + 0x73, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0xc9, 0x01, 0x0a, 0x0a, 0x44, 0x65, + 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x65, 0x66, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x03, 0x64, 0x65, 0x66, 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, + 0x62, 0x2e, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x06, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x52, 0x06, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0x4b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, + 0x4f, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x32, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x65, 0x4f, 0x70, 0x12, + 0x28, 0x0a, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xca, 0x02, 0x0a, 0x0a, 0x46, 0x69, + 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x26, + 0x0a, 0x0e, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x49, 0x6e, 0x70, 0x75, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, + 0x79, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x28, + 0x0a, 0x04, 0x63, 0x6f, 0x70, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, + 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x70, 0x79, + 0x48, 0x00, 0x52, 0x04, 0x63, 0x6f, 0x70, 0x79, 0x12, 0x2e, 0x0a, 0x06, 0x6d, 0x6b, 0x66, 0x69, + 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x46, 0x69, + 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x48, 0x00, + 0x52, 0x06, 0x6d, 0x6b, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x6d, 0x6b, 0x64, 0x69, + 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x46, 0x69, 0x6c, + 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6b, 0x44, 0x69, 0x72, 0x48, 0x00, 0x52, 0x05, + 0x6d, 0x6b, 0x64, 0x69, 0x72, 0x12, 0x22, 0x0a, 0x02, 0x72, 0x6d, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x6d, 0x48, 0x00, 0x52, 0x02, 0x72, 0x6d, 0x12, 0x31, 0x0a, 0x07, 0x73, 0x79, 0x6d, + 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x62, 0x2e, + 0x46, 0x69, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x79, 0x6d, 0x6c, 0x69, 0x6e, + 0x6b, 0x48, 0x00, 0x52, 0x07, 0x73, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x42, 0x08, 0x0a, 0x06, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xde, 0x04, 0x0a, 0x0e, 0x46, 0x69, 0x6c, 0x65, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x70, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x72, 0x63, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x72, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x64, + 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, + 0x22, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, + 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x68, 0x6f, 0x77, 0x6e, 0x4f, 0x70, 0x74, 0x52, 0x05, 0x6f, 0x77, + 0x6e, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, + 0x77, 0x53, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, + 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x53, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x28, 0x0a, + 0x0f, 0x64, 0x69, 0x72, 0x43, 0x6f, 0x70, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x69, 0x72, 0x43, 0x6f, 0x70, 0x79, 0x43, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x4a, 0x0a, 0x20, 0x61, 0x74, 0x74, 0x65, 0x6d, + 0x70, 0x74, 0x55, 0x6e, 0x70, 0x61, 0x63, 0x6b, 0x44, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x43, 0x6f, + 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x20, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x55, 0x6e, 0x70, 0x61, 0x63, 0x6b, + 0x44, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x69, 0x6c, + 0x69, 0x74, 0x79, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x73, + 0x74, 0x50, 0x61, 0x74, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x44, 0x65, 0x73, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x24, 0x0a, 0x0d, 0x61, + 0x6c, 0x6c, 0x6f, 0x77, 0x57, 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x57, 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, + 0x64, 0x12, 0x2e, 0x0a, 0x12, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x57, + 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x61, + 0x6c, 0x6c, 0x6f, 0x77, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x57, 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, + 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, + 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x74, 0x65, + 0x72, 0x6e, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x78, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x73, 0x18, 0x0d, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x50, 0x61, 0x74, + 0x74, 0x65, 0x72, 0x6e, 0x73, 0x12, 0x46, 0x0a, 0x1e, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x52, + 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x65, + 0x73, 0x74, 0x50, 0x61, 0x74, 0x68, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1e, 0x61, + 0x6c, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x45, 0x78, 0x69, 0x73, + 0x74, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x73, 0x74, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x18, 0x0a, + 0x07, 0x6d, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x72, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x6d, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x72, 0x22, 0x90, 0x01, 0x0a, 0x10, 0x46, 0x69, 0x6c, 0x65, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, + 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, + 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x68, 0x6f, 0x77, 0x6e, 0x4f, 0x70, 0x74, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x6e, 0x0a, 0x0c, 0x46, 0x69, - 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, - 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x24, - 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4e, 0x6f, 0x74, 0x46, - 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x57, 0x69, 0x6c, - 0x64, 0x63, 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c, - 0x6f, 0x77, 0x57, 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x22, 0x4e, 0x0a, 0x08, 0x43, 0x68, - 0x6f, 0x77, 0x6e, 0x4f, 0x70, 0x74, 0x12, 0x1f, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4f, 0x70, - 0x74, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, - 0x4f, 0x70, 0x74, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x22, 0x53, 0x0a, 0x07, 0x55, 0x73, - 0x65, 0x72, 0x4f, 0x70, 0x74, 0x12, 0x2a, 0x0a, 0x06, 0x62, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, - 0x55, 0x73, 0x65, 0x72, 0x4f, 0x70, 0x74, 0x48, 0x00, 0x52, 0x06, 0x62, 0x79, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x14, 0x0a, 0x04, 0x62, 0x79, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x48, - 0x00, 0x52, 0x04, 0x62, 0x79, 0x49, 0x44, 0x42, 0x06, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, - 0x38, 0x0a, 0x0c, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x4f, 0x70, 0x74, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x22, 0x22, 0x0a, 0x0a, 0x4d, 0x65, 0x72, - 0x67, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x22, 0x31, 0x0a, - 0x07, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x4f, 0x70, 0x12, 0x26, 0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75, - 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x4d, 0x65, - 0x72, 0x67, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, - 0x22, 0x26, 0x0a, 0x0e, 0x4c, 0x6f, 0x77, 0x65, 0x72, 0x44, 0x69, 0x66, 0x66, 0x49, 0x6e, 0x70, - 0x75, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x22, 0x26, 0x0a, 0x0e, 0x55, 0x70, 0x70, 0x65, - 0x72, 0x44, 0x69, 0x66, 0x66, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, - 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, - 0x22, 0x5c, 0x0a, 0x06, 0x44, 0x69, 0x66, 0x66, 0x4f, 0x70, 0x12, 0x28, 0x0a, 0x05, 0x6c, 0x6f, - 0x77, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x4c, - 0x6f, 0x77, 0x65, 0x72, 0x44, 0x69, 0x66, 0x66, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x05, 0x6c, - 0x6f, 0x77, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x05, 0x75, 0x70, 0x70, 0x65, 0x72, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x70, 0x70, 0x65, 0x72, 0x44, 0x69, - 0x66, 0x66, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x05, 0x75, 0x70, 0x70, 0x65, 0x72, 0x2a, 0x28, - 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x4e, 0x53, - 0x45, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x4f, 0x53, 0x54, 0x10, 0x01, 0x12, 0x08, - 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x02, 0x2a, 0x29, 0x0a, 0x0c, 0x53, 0x65, 0x63, 0x75, - 0x72, 0x69, 0x74, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x41, 0x4e, 0x44, - 0x42, 0x4f, 0x58, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x49, 0x4e, 0x53, 0x45, 0x43, 0x55, 0x52, - 0x45, 0x10, 0x01, 0x2a, 0x40, 0x0a, 0x09, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x08, 0x0a, 0x04, 0x42, 0x49, 0x4e, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x45, - 0x43, 0x52, 0x45, 0x54, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x53, 0x53, 0x48, 0x10, 0x02, 0x12, - 0x09, 0x0a, 0x05, 0x43, 0x41, 0x43, 0x48, 0x45, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x4d, - 0x50, 0x46, 0x53, 0x10, 0x04, 0x2a, 0x31, 0x0a, 0x11, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, - 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4e, 0x10, 0x01, 0x12, - 0x07, 0x0a, 0x03, 0x4f, 0x46, 0x46, 0x10, 0x02, 0x2a, 0x36, 0x0a, 0x0f, 0x43, 0x61, 0x63, 0x68, - 0x65, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4f, 0x70, 0x74, 0x12, 0x0a, 0x0a, 0x06, 0x53, - 0x48, 0x41, 0x52, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x52, 0x49, 0x56, 0x41, - 0x54, 0x45, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x4f, 0x43, 0x4b, 0x45, 0x44, 0x10, 0x02, - 0x42, 0x24, 0x5a, 0x22, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, - 0x6f, 0x62, 0x79, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x6b, 0x69, 0x74, 0x2f, 0x73, 0x6f, 0x6c, - 0x76, 0x65, 0x72, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x89, 0x01, 0x0a, 0x11, 0x46, + 0x69, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, + 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x6c, 0x64, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x6f, 0x6c, 0x64, 0x70, 0x61, 0x74, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, + 0x77, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x77, + 0x70, 0x61, 0x74, 0x68, 0x12, 0x22, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x68, 0x6f, 0x77, 0x6e, 0x4f, 0x70, + 0x74, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x9d, 0x01, 0x0a, 0x0f, 0x46, 0x69, 0x6c, 0x65, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6b, 0x44, 0x69, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, + 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, + 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x6d, 0x6f, + 0x64, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x61, 0x6b, 0x65, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6d, 0x61, 0x6b, 0x65, 0x50, 0x61, 0x72, + 0x65, 0x6e, 0x74, 0x73, 0x12, 0x22, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x68, 0x6f, 0x77, 0x6e, 0x4f, 0x70, + 0x74, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x6e, 0x0a, 0x0c, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x6c, + 0x6c, 0x6f, 0x77, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, + 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x57, 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x57, 0x69, + 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x22, 0x4e, 0x0a, 0x08, 0x43, 0x68, 0x6f, 0x77, 0x6e, 0x4f, + 0x70, 0x74, 0x12, 0x1f, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0b, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4f, 0x70, 0x74, 0x52, 0x04, 0x75, + 0x73, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4f, 0x70, 0x74, 0x52, + 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x22, 0x53, 0x0a, 0x07, 0x55, 0x73, 0x65, 0x72, 0x4f, 0x70, + 0x74, 0x12, 0x2a, 0x0a, 0x06, 0x62, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, + 0x4f, 0x70, 0x74, 0x48, 0x00, 0x52, 0x06, 0x62, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, + 0x04, 0x62, 0x79, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x04, 0x62, + 0x79, 0x49, 0x44, 0x42, 0x06, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x38, 0x0a, 0x0c, 0x4e, + 0x61, 0x6d, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x4f, 0x70, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, + 0x69, 0x6e, 0x70, 0x75, 0x74, 0x22, 0x22, 0x0a, 0x0a, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x49, 0x6e, + 0x70, 0x75, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x22, 0x31, 0x0a, 0x07, 0x4d, 0x65, 0x72, + 0x67, 0x65, 0x4f, 0x70, 0x12, 0x26, 0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x49, + 0x6e, 0x70, 0x75, 0x74, 0x52, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x22, 0x26, 0x0a, 0x0e, + 0x4c, 0x6f, 0x77, 0x65, 0x72, 0x44, 0x69, 0x66, 0x66, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x14, + 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x69, + 0x6e, 0x70, 0x75, 0x74, 0x22, 0x26, 0x0a, 0x0e, 0x55, 0x70, 0x70, 0x65, 0x72, 0x44, 0x69, 0x66, + 0x66, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x22, 0x5c, 0x0a, 0x06, + 0x44, 0x69, 0x66, 0x66, 0x4f, 0x70, 0x12, 0x28, 0x0a, 0x05, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x77, 0x65, 0x72, + 0x44, 0x69, 0x66, 0x66, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x05, 0x6c, 0x6f, 0x77, 0x65, 0x72, + 0x12, 0x28, 0x0a, 0x05, 0x75, 0x70, 0x70, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x70, 0x70, 0x65, 0x72, 0x44, 0x69, 0x66, 0x66, 0x49, 0x6e, + 0x70, 0x75, 0x74, 0x52, 0x05, 0x75, 0x70, 0x70, 0x65, 0x72, 0x2a, 0x28, 0x0a, 0x07, 0x4e, 0x65, + 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x4e, 0x53, 0x45, 0x54, 0x10, 0x00, + 0x12, 0x08, 0x0a, 0x04, 0x48, 0x4f, 0x53, 0x54, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, + 0x4e, 0x45, 0x10, 0x02, 0x2a, 0x29, 0x0a, 0x0c, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, + 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x41, 0x4e, 0x44, 0x42, 0x4f, 0x58, 0x10, + 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x49, 0x4e, 0x53, 0x45, 0x43, 0x55, 0x52, 0x45, 0x10, 0x01, 0x2a, + 0x40, 0x0a, 0x09, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, + 0x42, 0x49, 0x4e, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x45, 0x43, 0x52, 0x45, 0x54, + 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x53, 0x53, 0x48, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x43, + 0x41, 0x43, 0x48, 0x45, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x4d, 0x50, 0x46, 0x53, 0x10, + 0x04, 0x2a, 0x31, 0x0a, 0x11, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, + 0x54, 0x10, 0x00, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x4f, + 0x46, 0x46, 0x10, 0x02, 0x2a, 0x36, 0x0a, 0x0f, 0x43, 0x61, 0x63, 0x68, 0x65, 0x53, 0x68, 0x61, + 0x72, 0x69, 0x6e, 0x67, 0x4f, 0x70, 0x74, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x48, 0x41, 0x52, 0x45, + 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x10, 0x01, + 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x4f, 0x43, 0x4b, 0x45, 0x44, 0x10, 0x02, 0x42, 0x24, 0x5a, 0x22, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x6f, 0x62, 0x79, 0x2f, + 0x62, 0x75, 0x69, 0x6c, 0x64, 0x6b, 0x69, 0x74, 0x2f, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x2f, + 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/solver/pb/ops.proto b/solver/pb/ops.proto index eeafec12f0a2..90b69560e005 100644 --- a/solver/pb/ops.proto +++ b/solver/pb/ops.proto @@ -101,6 +101,8 @@ message CDIDevice { // Fully qualified CDI device name (e.g., vendor.com/gpu=gpudevice1) // https://github.com/cncf-tags/container-device-interface/blob/main/SPEC.md string name = 1; + // Optional defines if CDI device is required. + bool optional = 2; } // Mount specifies how to mount an input Op as a filesystem. diff --git a/solver/pb/ops_vtproto.pb.go b/solver/pb/ops_vtproto.pb.go index 16df7e6ae446..1a6c89c3690f 100644 --- a/solver/pb/ops_vtproto.pb.go +++ b/solver/pb/ops_vtproto.pb.go @@ -297,6 +297,7 @@ func (m *CDIDevice) CloneVT() *CDIDevice { } r := new(CDIDevice) r.Name = m.Name + r.Optional = m.Optional if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -1656,6 +1657,9 @@ func (this *CDIDevice) EqualVT(that *CDIDevice) bool { if this.Name != that.Name { return false } + if this.Optional != that.Optional { + return false + } return string(this.unknownFields) == string(that.unknownFields) } @@ -3667,6 +3671,16 @@ func (m *CDIDevice) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if m.Optional { + i-- + if m.Optional { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) @@ -6278,6 +6292,9 @@ func (m *CDIDevice) SizeVT() (n int) { if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } + if m.Optional { + n += 2 + } n += len(m.unknownFields) return n } @@ -8999,6 +9016,26 @@ func (m *CDIDevice) UnmarshalVT(dAtA []byte) error { } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Optional", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Optional = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) From 3cb3f686cd0817a1ad2ebba0cb62bb38bde0ec4d Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Wed, 5 Feb 2025 17:09:11 +0100 Subject: [PATCH 14/23] dockerfile: support optional cdi devices Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- .../dockerfile2llb/convert_rundevice.go | 11 ++- .../dockerfile/dockerfile_rundevice_test.go | 7 +- .../instructions/commands_rundevice.go | 94 +++++++++++++++++-- .../instructions/commands_rundevice_test.go | 74 +++++++++++++++ frontend/dockerui/attr.go | 22 ++--- frontend/dockerui/config.go | 6 +- 6 files changed, 185 insertions(+), 29 deletions(-) create mode 100644 frontend/dockerfile/instructions/commands_rundevice_test.go diff --git a/frontend/dockerfile/dockerfile2llb/convert_rundevice.go b/frontend/dockerfile/dockerfile2llb/convert_rundevice.go index 5f6b222def65..72b44755a420 100644 --- a/frontend/dockerfile/dockerfile2llb/convert_rundevice.go +++ b/frontend/dockerfile/dockerfile2llb/convert_rundevice.go @@ -9,9 +9,14 @@ import ( func dispatchRunDevices(c *instructions.RunCommand) ([]llb.RunOption, error) { var out []llb.RunOption - devices := instructions.GetDevices(c) - for _, device := range devices { - out = append(out, llb.AddCDIDevice(llb.CDIDeviceName(device), llb.CDIDeviceOptional)) + for _, device := range instructions.GetDevices(c) { + deviceOpts := []llb.CDIDeviceOption{ + llb.CDIDeviceName(device.Name), + } + if !device.Required { + deviceOpts = append(deviceOpts, llb.CDIDeviceOptional) + } + out = append(out, llb.AddCDIDevice(deviceOpts...)) } return out, nil } diff --git a/frontend/dockerfile/dockerfile_rundevice_test.go b/frontend/dockerfile/dockerfile_rundevice_test.go index 1046e5ce4de2..92b2322f8832 100644 --- a/frontend/dockerfile/dockerfile_rundevice_test.go +++ b/frontend/dockerfile/dockerfile_rundevice_test.go @@ -60,7 +60,8 @@ COPY --from=base /foo.env / _, err = f.Solve(sb.Context(), c, client.SolveOpt{ FrontendAttrs: map[string]string{ - "device": "vendor1.com/device=foo", + "device:0": "vendor1.com/device=foo,required", + "device:1": "vendor2.com/device=bar", }, LocalMounts: map[string]fsutil.FS{ dockerui.DefaultLocalNameDockerfile: dir, @@ -100,7 +101,9 @@ devices: dockerfile := []byte(` FROM busybox AS base -RUN --device=vendor1.com/device=foo env|sort | tee foo.env +RUN --device=vendor1.com/device=foo,required \ + --device=vendor2.com/device=bar \ + env|sort | tee foo.env FROM scratch COPY --from=base /foo.env / `) diff --git a/frontend/dockerfile/instructions/commands_rundevice.go b/frontend/dockerfile/instructions/commands_rundevice.go index 17977ef18fe2..975c95454978 100644 --- a/frontend/dockerfile/instructions/commands_rundevice.go +++ b/frontend/dockerfile/instructions/commands_rundevice.go @@ -1,10 +1,16 @@ package instructions import ( + "strconv" + "strings" + + "github.com/moby/buildkit/util/suggest" "github.com/pkg/errors" + "github.com/tonistiigi/go-csvvalue" + "tags.cncf.io/container-device-interface/pkg/parser" ) -var deviceKey = "dockerfile/run/device" +var devicesKey = "dockerfile/run/devices" func init() { parseRunPreHooks = append(parseRunPreHooks, runDevicePreHook) @@ -14,7 +20,7 @@ func init() { func runDevicePreHook(cmd *RunCommand, req parseRequest) error { st := &deviceState{} st.flag = req.flags.AddStrings("device") - cmd.setExternalValue(deviceKey, st) + cmd.setExternalValue(devicesKey, st) return nil } @@ -27,23 +33,95 @@ func setDeviceState(cmd *RunCommand) error { if st == nil { return errors.Errorf("no device state") } - st.names = st.flag.StringValues + devices := make([]*Device, len(st.flag.StringValues)) + for i, str := range st.flag.StringValues { + d, err := ParseDevice(str) + if err != nil { + return err + } + devices[i] = d + } + st.devices = devices return nil } func getDeviceState(cmd *RunCommand) *deviceState { - v := cmd.getExternalValue(deviceKey) + v := cmd.getExternalValue(devicesKey) if v == nil { return nil } return v.(*deviceState) } -func GetDevices(cmd *RunCommand) []string { - return getDeviceState(cmd).names +func GetDevices(cmd *RunCommand) []*Device { + return getDeviceState(cmd).devices } type deviceState struct { - flag *Flag - names []string + flag *Flag + devices []*Device +} + +type Device struct { + Name string + Required bool +} + +func ParseDevice(val string) (*Device, error) { + fields, err := csvvalue.Fields(val, nil) + if err != nil { + return nil, errors.Wrap(err, "failed to parse csv devices") + } + + d := &Device{} + + for i, field := range fields { + // check if the first field is a valid device name + var firstFieldErr error + if i == 0 { + if _, _, _, firstFieldErr = parser.ParseQualifiedName(field); firstFieldErr == nil { + d.Name = field + continue + } + } + + key, value, ok := strings.Cut(field, "=") + key = strings.ToLower(key) + + if !ok { + if len(fields) == 1 && firstFieldErr != nil { + return nil, errors.Wrapf(firstFieldErr, "invalid device name %s", field) + } + switch key { + case "required": + d.Required = true + continue + default: + // any other option requires a value. + return nil, errors.Errorf("invalid field '%s' must be a key=value pair", field) + } + } + + switch key { + case "name": + if d.Name != "" { + return nil, errors.Errorf("device name already set to %s", d.Name) + } + d.Name = value + case "required": + d.Required, err = strconv.ParseBool(value) + if err != nil { + return nil, errors.Errorf("invalid value for %s: %s", key, value) + } + default: + allKeys := []string{"name", "required"} + return nil, suggest.WrapError(errors.Errorf("unexpected key '%s' in '%s'", key, field), key, allKeys, true) + } + } + + if _, _, _, err := parser.ParseQualifiedName(d.Name); err != nil { + return nil, errors.Wrapf(err, "invalid device name %s", d.Name) + } + + return d, nil } diff --git a/frontend/dockerfile/instructions/commands_rundevice_test.go b/frontend/dockerfile/instructions/commands_rundevice_test.go new file mode 100644 index 000000000000..92d103cbd847 --- /dev/null +++ b/frontend/dockerfile/instructions/commands_rundevice_test.go @@ -0,0 +1,74 @@ +package instructions + +import ( + "testing" + + "github.com/pkg/errors" + "github.com/stretchr/testify/require" +) + +func TestParseDevice(t *testing.T) { + cases := []struct { + input string + expected *Device + expectedErr error + }{ + { + input: "vendor1.com/device=foo", + expected: &Device{Name: "vendor1.com/device=foo", Required: false}, + expectedErr: nil, + }, + { + input: "vendor1.com/device=foo,required", + expected: &Device{Name: "vendor1.com/device=foo", Required: true}, + expectedErr: nil, + }, + { + input: "vendor1.com/device=foo,required=true", + expected: &Device{Name: "vendor1.com/device=foo", Required: true}, + expectedErr: nil, + }, + { + input: "vendor1.com/device=foo,required=false", + expected: &Device{Name: "vendor1.com/device=foo", Required: false}, + expectedErr: nil, + }, + { + input: "name=vendor1.com/device=foo", + expected: &Device{Name: "vendor1.com/device=foo", Required: false}, + expectedErr: nil, + }, + { + input: "name=vendor1.com/device=foo,required", + expected: &Device{Name: "vendor1.com/device=foo", Required: true}, + expectedErr: nil, + }, + { + input: "vendor1.com/device=foo,name=vendor2.com/device=bar", + expected: nil, + expectedErr: errors.New("device name already set to vendor1.com/device=foo"), + }, + { + input: "invalid-device-name", + expected: nil, + expectedErr: errors.New(`invalid device name invalid-device-name: unqualified device "invalid-device-name", missing vendor`), + }, + { + input: "name=invalid-device-name", + expected: nil, + expectedErr: errors.New(`invalid device name invalid-device-name: unqualified device "invalid-device-name", missing vendor`), + }, + } + for _, tt := range cases { + t.Run(tt.input, func(t *testing.T) { + result, err := ParseDevice(tt.input) + if tt.expectedErr != nil { + require.Error(t, err) + require.EqualError(t, err, tt.expectedErr.Error()) + } else { + require.NoError(t, err) + require.Equal(t, tt.expected, result) + } + }) + } +} diff --git a/frontend/dockerui/attr.go b/frontend/dockerui/attr.go index 8a0697383418..5c055372e770 100644 --- a/frontend/dockerui/attr.go +++ b/frontend/dockerui/attr.go @@ -1,7 +1,6 @@ package dockerui import ( - "encoding/csv" "net" "strconv" "strings" @@ -10,11 +9,11 @@ import ( "github.com/containerd/platforms" "github.com/docker/go-units" "github.com/moby/buildkit/client/llb" + "github.com/moby/buildkit/frontend/dockerfile/instructions" "github.com/moby/buildkit/solver/pb" ocispecs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/tonistiigi/go-csvvalue" - "tags.cncf.io/container-device-interface/pkg/parser" ) func parsePlatforms(v string) ([]ocispecs.Platform, error) { @@ -99,22 +98,19 @@ func parseUlimits(v string) ([]*pb.Ulimit, error) { return out, nil } -func parseDevices(v string) ([]*pb.CDIDevice, error) { - if v == "" { +func parseDevices(v map[string]string) ([]*pb.CDIDevice, error) { + if v == nil { return nil, nil } out := make([]*pb.CDIDevice, 0) - csvReader := csv.NewReader(strings.NewReader(v)) - names, err := csvReader.Read() - if err != nil { - return nil, err - } - for _, name := range names { - if _, _, _, err := parser.ParseQualifiedName(name); err != nil { - return nil, errors.Wrapf(err, "invalid CDI device name %q", name) + for _, attrs := range v { + device, err := instructions.ParseDevice(attrs) + if err != nil { + return nil, err } out = append(out, &pb.CDIDevice{ - Name: name, + Name: device.Name, + Optional: !device.Required, }) } return out, nil diff --git a/frontend/dockerui/config.go b/frontend/dockerui/config.go index 6c957f29a609..5769e94914cb 100644 --- a/frontend/dockerui/config.go +++ b/frontend/dockerui/config.go @@ -28,6 +28,7 @@ const ( buildArgPrefix = "build-arg:" labelPrefix = "label:" localSessionIDPrefix = "local-sessionid:" + devicePrefix = "device:" keyTarget = "target" keyCgroupParent = "cgroup-parent" @@ -40,7 +41,6 @@ const ( keyShmSize = "shm-size" keyTargetPlatform = "platform" keyUlimit = "ulimit" - keyDevice = "device" keyCacheFrom = "cache-from" // for registry only. deprecated in favor of keyCacheImports keyCacheImports = "cache-imports" // JSON representation of []CacheOptionsEntry @@ -189,9 +189,9 @@ func (bc *Client) init() error { } bc.Ulimits = ulimits - devices, err := parseDevices(opts[keyDevice]) + devices, err := parseDevices(filter(opts, devicePrefix)) if err != nil { - return errors.Wrap(err, "failed to parse device") + return errors.Wrap(err, "failed to parse devices") } bc.Devices = devices From f61e01c14caa2a531410a5445f089873e48756f0 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Sun, 9 Feb 2025 21:38:58 -0800 Subject: [PATCH 15/23] llbsolver: on-demand CDI devices with automatic setup Signed-off-by: Tonis Tiigi --- Dockerfile | 19 +++- api/types/worker.pb.go | 30 ++++-- api/types/worker.proto | 1 + api/types/worker_vtproto.pb.go | 37 +++++++ client/info.go | 2 + cmd/buildctl/debug/workers.go | 7 +- cmd/buildkitd/main_containerd_worker.go | 3 +- control/control.go | 10 +- executor/containerdexecutor/executor.go | 6 +- executor/oci/spec.go | 4 +- executor/oci/spec_darwin.go | 4 +- executor/oci/spec_freebsd.go | 4 +- executor/oci/spec_linux.go | 10 +- executor/oci/spec_windows.go | 4 +- executor/runcexecutor/executor.go | 6 +- solver/llbsolver/cdidevices/manager.go | 128 ++++++++++++++++++++++++ solver/llbsolver/ops/exec.go | 14 +++ solver/progress.go | 8 +- worker/base/worker.go | 6 +- worker/containerd/containerd.go | 4 +- worker/runc/runc.go | 5 +- worker/worker.go | 4 +- 22 files changed, 268 insertions(+), 48 deletions(-) create mode 100644 solver/llbsolver/cdidevices/manager.go diff --git a/Dockerfile b/Dockerfile index abdd0cffb0fc..200a61d3e82b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,6 +22,7 @@ ARG GO_VERSION=1.23 ARG ALPINE_VERSION=3.21 ARG XX_VERSION=1.6.1 ARG BUILDKIT_DEBUG +ARG EXPORT_BASE=alpine # minio for s3 integration tests FROM minio/minio:${MINIO_VERSION} AS minio @@ -194,12 +195,28 @@ RUN --mount=from=binaries \ FROM scratch AS release COPY --link --from=releaser /out/ / -FROM alpine:${ALPINE_VERSION} AS buildkit-export +FROM alpine:${ALPINE_VERSION} AS buildkit-export-alpine RUN apk add --no-cache fuse3 git openssh pigz xz iptables ip6tables \ && ln -s fusermount3 /usr/bin/fusermount COPY --link examples/buildctl-daemonless/buildctl-daemonless.sh /usr/bin/ VOLUME /var/lib/buildkit +FROM ubuntu:24.04 AS buildkit-export-ubuntu +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + fuse3 \ + git \ + openssh-client \ + pigz \ + xz-utils \ + iptables \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* +COPY --link examples/buildctl-daemonless/buildctl-daemonless.sh /usr/bin/ +VOLUME /var/lib/buildkit + +FROM buildkit-export-${EXPORT_BASE} AS buildkit-export + FROM gobuild-base AS containerd-build WORKDIR /go/src/github.com/containerd/containerd ARG TARGETPLATFORM diff --git a/api/types/worker.pb.go b/api/types/worker.pb.go index 3d95d0e80855..b49b47d322ed 100644 --- a/api/types/worker.pb.go +++ b/api/types/worker.pb.go @@ -261,6 +261,7 @@ type CDIDevice struct { Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"` AutoAllow bool `protobuf:"varint,2,opt,name=AutoAllow,proto3" json:"AutoAllow,omitempty"` Annotations map[string]string `protobuf:"bytes,3,rep,name=Annotations,proto3" json:"Annotations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + OnDemand bool `protobuf:"varint,4,opt,name=OnDemand,proto3" json:"OnDemand,omitempty"` } func (x *CDIDevice) Reset() { @@ -314,6 +315,13 @@ func (x *CDIDevice) GetAnnotations() map[string]string { return nil } +func (x *CDIDevice) GetOnDemand() bool { + if x != nil { + return x.OnDemand + } + return false +} + var File_github_com_moby_buildkit_api_types_worker_proto protoreflect.FileDescriptor var file_github_com_moby_buildkit_api_types_worker_proto_rawDesc = []byte{ @@ -369,7 +377,7 @@ var file_github_com_moby_buildkit_api_types_worker_proto_rawDesc = []byte{ 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xd3, 0x01, 0x0a, 0x09, + 0x09, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xef, 0x01, 0x0a, 0x09, 0x43, 0x44, 0x49, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x41, 0x75, 0x74, 0x6f, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, @@ -379,15 +387,17 @@ var file_github_com_moby_buildkit_api_types_worker_proto_rawDesc = []byte{ 0x2e, 0x76, 0x31, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x43, 0x44, 0x49, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x6d, 0x6f, 0x62, 0x79, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x6b, 0x69, 0x74, 0x2f, 0x61, 0x70, - 0x69, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x3b, 0x6d, 0x6f, 0x62, 0x79, 0x5f, 0x62, 0x75, 0x69, - 0x6c, 0x64, 0x6b, 0x69, 0x74, 0x5f, 0x76, 0x31, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x4f, 0x6e, 0x44, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x08, 0x4f, 0x6e, 0x44, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x1a, 0x3e, 0x0a, + 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x3b, 0x5a, + 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x6f, 0x62, 0x79, + 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x6b, 0x69, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x79, + 0x70, 0x65, 0x73, 0x3b, 0x6d, 0x6f, 0x62, 0x79, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x6b, 0x69, + 0x74, 0x5f, 0x76, 0x31, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( diff --git a/api/types/worker.proto b/api/types/worker.proto index 81a0d8c4cda8..8f565664671d 100644 --- a/api/types/worker.proto +++ b/api/types/worker.proto @@ -36,4 +36,5 @@ message CDIDevice { string Name = 1; bool AutoAllow = 2; map Annotations = 3; + bool OnDemand = 4; } \ No newline at end of file diff --git a/api/types/worker_vtproto.pb.go b/api/types/worker_vtproto.pb.go index 016412ac0d8f..6113a6048404 100644 --- a/api/types/worker_vtproto.pb.go +++ b/api/types/worker_vtproto.pb.go @@ -118,6 +118,7 @@ func (m *CDIDevice) CloneVT() *CDIDevice { r := new(CDIDevice) r.Name = m.Name r.AutoAllow = m.AutoAllow + r.OnDemand = m.OnDemand if rhs := m.Annotations; rhs != nil { tmpContainer := make(map[string]string, len(rhs)) for k, v := range rhs { @@ -310,6 +311,9 @@ func (this *CDIDevice) EqualVT(that *CDIDevice) bool { return false } } + if this.OnDemand != that.OnDemand { + return false + } return string(this.unknownFields) == string(that.unknownFields) } @@ -581,6 +585,16 @@ func (m *CDIDevice) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if m.OnDemand { + i-- + if m.OnDemand { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } if len(m.Annotations) > 0 { for k := range m.Annotations { v := m.Annotations[k] @@ -738,6 +752,9 @@ func (m *CDIDevice) SizeVT() (n int) { n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) } } + if m.OnDemand { + n += 2 + } n += len(m.unknownFields) return n } @@ -1624,6 +1641,26 @@ func (m *CDIDevice) UnmarshalVT(dAtA []byte) error { } m.Annotations[mapkey] = mapvalue iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OnDemand", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.OnDemand = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) diff --git a/client/info.go b/client/info.go index 0aa95bb9537d..a637ffae1627 100644 --- a/client/info.go +++ b/client/info.go @@ -22,6 +22,7 @@ type CDIDevice struct { Name string `json:"name"` AutoAllow bool `json:"autoAllow"` Annotations map[string]string `json:"annotations"` + OnDemand bool `json:"onDemand"` } func (c *Client) Info(ctx context.Context) (*Info, error) { @@ -52,6 +53,7 @@ func fromAPICDIDevices(in []*apitypes.CDIDevice) []CDIDevice { Name: d.Name, AutoAllow: d.AutoAllow, Annotations: d.Annotations, + OnDemand: d.OnDemand, }) } return out diff --git a/cmd/buildctl/debug/workers.go b/cmd/buildctl/debug/workers.go index 0778d5620e02..58fc6ad10d1a 100644 --- a/cmd/buildctl/debug/workers.go +++ b/cmd/buildctl/debug/workers.go @@ -86,7 +86,12 @@ func printWorkersVerbose(tw *tabwriter.Writer, winfo []*client.WorkerInfo) { fmt.Fprint(tw, "Devices:\n") for _, d := range wi.CDIDevices { fmt.Fprintf(tw, "\tName:\t%s\n", d.Name) - fmt.Fprintf(tw, "\tAutoAllow:\t%v\n", d.AutoAllow) + if d.OnDemand { + fmt.Fprintf(tw, "\tOnDemand:\t%v\n", d.OnDemand) + } else { + fmt.Fprintf(tw, "\tAutoAllow:\t%v\n", d.AutoAllow) + } + for _, k := range sortedKeys(d.Annotations) { v := d.Annotations[k] fmt.Fprintf(tw, "\t\t%s:\t%s\n", k, v) diff --git a/cmd/buildkitd/main_containerd_worker.go b/cmd/buildkitd/main_containerd_worker.go index 60139e4f2d77..13694a48d5bc 100644 --- a/cmd/buildkitd/main_containerd_worker.go +++ b/cmd/buildkitd/main_containerd_worker.go @@ -12,6 +12,7 @@ import ( ctd "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/defaults" "github.com/moby/buildkit/cmd/buildkitd/config" + "github.com/moby/buildkit/solver/llbsolver/cdidevices" "github.com/moby/buildkit/util/bklog" "github.com/moby/buildkit/util/disk" "github.com/moby/buildkit/util/network/cniprovider" @@ -344,7 +345,7 @@ func containerdWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([ ParallelismSem: parallelismSem, TraceSocket: common.traceSocket, Runtime: runtime, - CDIManager: cdiManager, + CDIManager: cdidevices.NewManager(cdiManager), } opt, err := containerd.NewWorkerOpt(workerOpts, ctd.WithTimeout(60*time.Second)) diff --git a/control/control.go b/control/control.go index a93ee5b7653d..ca50913190b9 100644 --- a/control/control.go +++ b/control/control.go @@ -33,6 +33,7 @@ import ( "github.com/moby/buildkit/solver" "github.com/moby/buildkit/solver/bboltcachestorage" "github.com/moby/buildkit/solver/llbsolver" + "github.com/moby/buildkit/solver/llbsolver/cdidevices" "github.com/moby/buildkit/solver/llbsolver/proc" "github.com/moby/buildkit/solver/pb" "github.com/moby/buildkit/util/bklog" @@ -54,7 +55,6 @@ import ( "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" - "tags.cncf.io/container-device-interface/pkg/cdi" ) type Opt struct { @@ -686,18 +686,18 @@ func toPBBuildkitVersion(in client.BuildkitVersion) *apitypes.BuildkitVersion { } } -func toPBCDIDevices(manager *cdi.Cache) []*apitypes.CDIDevice { +func toPBCDIDevices(manager *cdidevices.Manager) []*apitypes.CDIDevice { if manager == nil { return nil } devs := manager.ListDevices() out := make([]*apitypes.CDIDevice, 0, len(devs)) for _, dev := range devs { - spec := manager.GetDevice(dev).GetSpec() out = append(out, &apitypes.CDIDevice{ - Name: dev, + Name: dev.Name, AutoAllow: true, // TODO - Annotations: spec.Annotations, + Annotations: dev.Annotations, + OnDemand: dev.OnDemand, }) } return out diff --git a/executor/containerdexecutor/executor.go b/executor/containerdexecutor/executor.go index 8c9097aad5cc..366959f01492 100644 --- a/executor/containerdexecutor/executor.go +++ b/executor/containerdexecutor/executor.go @@ -13,7 +13,6 @@ import ( "github.com/moby/buildkit/util/bklog" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" - "tags.cncf.io/container-device-interface/pkg/cdi" ctd "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/core/mount" @@ -23,6 +22,7 @@ import ( resourcestypes "github.com/moby/buildkit/executor/resources/types" gatewayapi "github.com/moby/buildkit/frontend/gateway/pb" "github.com/moby/buildkit/identity" + "github.com/moby/buildkit/solver/llbsolver/cdidevices" "github.com/moby/buildkit/solver/pb" "github.com/moby/buildkit/util/network" "github.com/pkg/errors" @@ -41,7 +41,7 @@ type containerdExecutor struct { traceSocket string rootless bool runtime *RuntimeInfo - cdiManager *cdi.Cache + cdiManager *cdidevices.Manager } // OnCreateRuntimer provides an alternative to OCI hooks for applying network @@ -74,7 +74,7 @@ type ExecutorOptions struct { TraceSocket string Rootless bool Runtime *RuntimeInfo - CDIManager *cdi.Cache + CDIManager *cdidevices.Manager } // New creates a new executor backed by connection to containerd API diff --git a/executor/oci/spec.go b/executor/oci/spec.go index 08f92e64789b..a6034e6ed000 100644 --- a/executor/oci/spec.go +++ b/executor/oci/spec.go @@ -17,6 +17,7 @@ import ( "github.com/mitchellh/hashstructure/v2" "github.com/moby/buildkit/executor" "github.com/moby/buildkit/snapshot" + "github.com/moby/buildkit/solver/llbsolver/cdidevices" "github.com/moby/buildkit/util/network" rootlessmountopts "github.com/moby/buildkit/util/rootless/mountopts" traceexec "github.com/moby/buildkit/util/tracing/exec" @@ -24,7 +25,6 @@ import ( specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/selinux/go-selinux" "github.com/pkg/errors" - "tags.cncf.io/container-device-interface/pkg/cdi" ) // ProcessMode configures PID namespaces @@ -60,7 +60,7 @@ func (pm ProcessMode) String() string { // GenerateSpec generates spec using containerd functionality. // opts are ignored for s.Process, s.Hostname, and s.Mounts . -func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mount, id, resolvConf, hostsFile string, namespace network.Namespace, cgroupParent string, processMode ProcessMode, idmap *idtools.IdentityMapping, apparmorProfile string, selinuxB bool, tracingSocket string, cdiManager *cdi.Cache, opts ...oci.SpecOpts) (*specs.Spec, func(), error) { +func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mount, id, resolvConf, hostsFile string, namespace network.Namespace, cgroupParent string, processMode ProcessMode, idmap *idtools.IdentityMapping, apparmorProfile string, selinuxB bool, tracingSocket string, cdiManager *cdidevices.Manager, opts ...oci.SpecOpts) (*specs.Spec, func(), error) { c := &containers.Container{ ID: id, } diff --git a/executor/oci/spec_darwin.go b/executor/oci/spec_darwin.go index bfc642289720..ae609a216bce 100644 --- a/executor/oci/spec_darwin.go +++ b/executor/oci/spec_darwin.go @@ -5,10 +5,10 @@ import ( "github.com/containerd/containerd/v2/pkg/oci" "github.com/containerd/continuity/fs" "github.com/docker/docker/pkg/idtools" + "github.com/moby/buildkit/solver/llbsolver/cdidevices" "github.com/moby/buildkit/solver/pb" "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" - "tags.cncf.io/container-device-interface/pkg/cdi" ) func withProcessArgs(args ...string) oci.SpecOpts { @@ -64,7 +64,7 @@ func sub(m mount.Mount, subPath string) (mount.Mount, func() error, error) { return m, func() error { return nil }, nil } -func generateCDIOpts(_ *cdi.Cache, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) { +func generateCDIOpts(_ *cdidevices.Manager, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) { if len(devices) == 0 { return nil, nil } diff --git a/executor/oci/spec_freebsd.go b/executor/oci/spec_freebsd.go index a30a18165db5..d1456a8eca53 100644 --- a/executor/oci/spec_freebsd.go +++ b/executor/oci/spec_freebsd.go @@ -5,10 +5,10 @@ import ( "github.com/containerd/containerd/v2/pkg/oci" "github.com/containerd/continuity/fs" "github.com/docker/docker/pkg/idtools" + "github.com/moby/buildkit/solver/llbsolver/cdidevices" "github.com/moby/buildkit/solver/pb" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" - "tags.cncf.io/container-device-interface/pkg/cdi" ) func withProcessArgs(args ...string) oci.SpecOpts { @@ -72,7 +72,7 @@ func sub(m mount.Mount, subPath string) (mount.Mount, func() error, error) { return m, func() error { return nil }, nil } -func generateCDIOpts(_ *cdi.Cache, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) { +func generateCDIOpts(_ *cdidevices.Manager, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) { if len(devices) == 0 { return nil, nil } diff --git a/executor/oci/spec_linux.go b/executor/oci/spec_linux.go index 2b4e3c78e7c4..1a394ac04d6b 100644 --- a/executor/oci/spec_linux.go +++ b/executor/oci/spec_linux.go @@ -17,6 +17,7 @@ import ( "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/profiles/seccomp" "github.com/moby/buildkit/snapshot" + "github.com/moby/buildkit/solver/llbsolver/cdidevices" "github.com/moby/buildkit/solver/pb" "github.com/moby/buildkit/util/bklog" "github.com/moby/buildkit/util/entitlements/security" @@ -25,7 +26,6 @@ import ( "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "golang.org/x/sys/unix" - "tags.cncf.io/container-device-interface/pkg/cdi" "tags.cncf.io/container-device-interface/pkg/parser" ) @@ -153,7 +153,7 @@ func generateRlimitOpts(ulimits []*pb.Ulimit) ([]oci.SpecOpts, error) { // genereateCDIOptions creates the OCI runtime spec options for injecting CDI // devices. -func generateCDIOpts(manager *cdi.Cache, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) { +func generateCDIOpts(manager *cdidevices.Manager, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) { if len(devices) == 0 { return nil, nil } @@ -166,8 +166,8 @@ func generateCDIOpts(manager *cdi.Cache, devices []*pb.CDIDevice) ([]oci.SpecOpt registeredDevices := manager.ListDevices() isDeviceRegistered := func(device *pb.CDIDevice) bool { - for _, name := range registeredDevices { - if device.Name == name { + for _, d := range registeredDevices { + if device.Name == d.Name { return true } } @@ -190,7 +190,7 @@ func generateCDIOpts(manager *cdi.Cache, devices []*pb.CDIDevice) ([]oci.SpecOpt } bklog.G(ctx).Debugf("Injecting CDI devices %v", dd) - if _, err := manager.InjectDevices(s, dd...); err != nil { + if err := manager.InjectDevices(s, dd...); err != nil { return errors.Wrapf(err, "CDI device injection failed") } diff --git a/executor/oci/spec_windows.go b/executor/oci/spec_windows.go index 8b75803be22e..f21b28ed8bbe 100644 --- a/executor/oci/spec_windows.go +++ b/executor/oci/spec_windows.go @@ -14,10 +14,10 @@ import ( "github.com/containerd/containerd/v2/pkg/oci" "github.com/containerd/continuity/fs" "github.com/docker/docker/pkg/idtools" + "github.com/moby/buildkit/solver/llbsolver/cdidevices" "github.com/moby/buildkit/solver/pb" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" - "tags.cncf.io/container-device-interface/pkg/cdi" ) const ( @@ -112,7 +112,7 @@ func sub(m mount.Mount, subPath string) (mount.Mount, func() error, error) { return m, func() error { return nil }, nil } -func generateCDIOpts(_ *cdi.Cache, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) { +func generateCDIOpts(_ *cdidevices.Manager, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) { if len(devices) == 0 { return nil, nil } diff --git a/executor/runcexecutor/executor.go b/executor/runcexecutor/executor.go index 5308bf9a4df8..1ca978bbb75f 100644 --- a/executor/runcexecutor/executor.go +++ b/executor/runcexecutor/executor.go @@ -18,7 +18,6 @@ import ( "github.com/moby/buildkit/util/bklog" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" - "tags.cncf.io/container-device-interface/pkg/cdi" "github.com/containerd/containerd/v2/core/mount" containerdoci "github.com/containerd/containerd/v2/pkg/oci" @@ -31,6 +30,7 @@ import ( resourcestypes "github.com/moby/buildkit/executor/resources/types" gatewayapi "github.com/moby/buildkit/frontend/gateway/pb" "github.com/moby/buildkit/identity" + "github.com/moby/buildkit/solver/llbsolver/cdidevices" "github.com/moby/buildkit/solver/pb" "github.com/moby/buildkit/util/network" rootlessspecconv "github.com/moby/buildkit/util/rootless/specconv" @@ -58,7 +58,7 @@ type Opt struct { SELinux bool TracingSocket string ResourceMonitor *resources.Monitor - CDIManager *cdi.Cache + CDIManager *cdidevices.Manager } var defaultCommandCandidates = []string{"buildkit-runc", "runc"} @@ -80,7 +80,7 @@ type runcExecutor struct { selinux bool tracingSocket string resmon *resources.Monitor - cdiManager *cdi.Cache + cdiManager *cdidevices.Manager } func New(opt Opt, networkProviders map[pb.NetMode]network.Provider) (executor.Executor, error) { diff --git a/solver/llbsolver/cdidevices/manager.go b/solver/llbsolver/cdidevices/manager.go new file mode 100644 index 000000000000..168daa81bf45 --- /dev/null +++ b/solver/llbsolver/cdidevices/manager.go @@ -0,0 +1,128 @@ +package cdidevices + +import ( + "context" + "strings" + + "github.com/moby/locker" + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "tags.cncf.io/container-device-interface/pkg/cdi" +) + +var installers = map[string]Setup{} + +type Setup interface { + Validate() error + Run(ctx context.Context) error +} + +// Register registers new setup for a device. +func Register(name string, setup Setup) { + installers[name] = setup +} + +type Device struct { + Name string + AutoAllow bool + OnDemand bool + Annotations map[string]string +} + +type Manager struct { + cache *cdi.Cache + locker *locker.Locker +} + +func NewManager(cache *cdi.Cache) *Manager { + return &Manager{ + cache: cache, + locker: locker.New(), + } +} + +func (m *Manager) ListDevices() []Device { + devs := m.cache.ListDevices() + out := make([]Device, 0, len(devs)) + kinds := make(map[string]struct{}) + for _, dev := range devs { + kind, _, _ := strings.Cut(dev, "=") + spec := m.cache.GetDevice(dev).GetSpec() + out = append(out, Device{ + Name: dev, + AutoAllow: true, // TODO + Annotations: spec.Annotations, + }) + kinds[kind] = struct{}{} + } + + for k, setup := range installers { + if _, ok := kinds[k]; ok { + continue + } + if err := setup.Validate(); err != nil { + continue + } + out = append(out, Device{ + Name: k, + OnDemand: true, + }) + } + + return out +} + +func (m *Manager) Refresh() error { + return m.cache.Refresh() +} + +func (m *Manager) InjectDevices(spec *specs.Spec, devs ...string) error { + _, err := m.cache.InjectDevices(spec, devs...) + return err +} + +func (m *Manager) hasDevice(name string) bool { + for _, d := range m.cache.ListDevices() { + kind, _, _ := strings.Cut(d, "=") + if kind == name { + return true + } + } + return false +} + +func (m *Manager) OnDemandInstaller(name string) (func(context.Context) error, bool) { + name, _, _ = strings.Cut(name, "=") + + installer, ok := installers[name] + if !ok { + return nil, false + } + + if m.hasDevice(name) { + return nil, false + } + + return func(ctx context.Context) error { + m.locker.Lock(name) + defer m.locker.Unlock(name) + + if m.hasDevice(name) { + return nil + } + + if err := installer.Validate(); err != nil { + return errors.Wrapf(err, "failed to find preconditions for %s device", name) + } + + if err := installer.Run(ctx); err != nil { + return errors.Wrapf(err, "failed to create %s device", name) + } + + if err := m.cache.Refresh(); err != nil { + return errors.Wrapf(err, "failed to refresh CDI cache") + } + + return nil + }, true +} diff --git a/solver/llbsolver/ops/exec.go b/solver/llbsolver/ops/exec.go index 8a68ced9909c..483e76f872b2 100644 --- a/solver/llbsolver/ops/exec.go +++ b/solver/llbsolver/ops/exec.go @@ -199,6 +199,20 @@ func (e *ExecOp) CacheMap(ctx context.Context, g session.Group, index int) (*sol cm.Deps[i].PreprocessFunc = unlazyResultFunc } + for _, d := range e.op.CdiDevices { + setup, ok := e.w.CDIManager().OnDemandInstaller(d.Name) + if ok { + prev := cm.Deps[0].PreprocessFunc + cm.Deps[0].PreprocessFunc = func(ctx context.Context, r solver.Result, g session.Group) error { + if err := prev(ctx, r, g); err != nil { + return err + } + // we could pass glibc/musl type in here based on rootfs to get correct dynamic libs + return setup(ctx) + } + } + } + return cm, true, nil } diff --git a/solver/progress.go b/solver/progress.go index 92e2c6cb009f..dfc01b801e44 100644 --- a/solver/progress.go +++ b/solver/progress.go @@ -60,7 +60,9 @@ func (j *Job) Status(ctx context.Context, ch chan *client.SolveStatus) error { bklog.G(ctx).Warnf("progress %s log without vertex info", p.ID) continue } - v.Vertex = vtx.(digest.Digest) + if v.Vertex == "" { + v.Vertex = vtx.(digest.Digest) + } v.Timestamp = p.Timestamp ss.Logs = append(ss.Logs, &v) case client.VertexWarning: @@ -69,7 +71,9 @@ func (j *Job) Status(ctx context.Context, ch chan *client.SolveStatus) error { bklog.G(ctx).Warnf("progress %s warning without vertex info", p.ID) continue } - v.Vertex = vtx.(digest.Digest) + if v.Vertex == "" { + v.Vertex = vtx.(digest.Digest) + } ss.Warnings = append(ss.Warnings, &v) } } diff --git a/worker/base/worker.go b/worker/base/worker.go index ca3ff8279879..c1569cd98a1e 100644 --- a/worker/base/worker.go +++ b/worker/base/worker.go @@ -33,6 +33,7 @@ import ( containerdsnapshot "github.com/moby/buildkit/snapshot/containerd" "github.com/moby/buildkit/snapshot/imagerefchecker" "github.com/moby/buildkit/solver" + "github.com/moby/buildkit/solver/llbsolver/cdidevices" "github.com/moby/buildkit/solver/llbsolver/mounts" "github.com/moby/buildkit/solver/llbsolver/ops" "github.com/moby/buildkit/solver/pb" @@ -52,7 +53,6 @@ import ( "github.com/pkg/errors" "golang.org/x/sync/errgroup" "golang.org/x/sync/semaphore" - "tags.cncf.io/container-device-interface/pkg/cdi" ) const labelCreatedAt = "buildkit/createdat" @@ -83,7 +83,7 @@ type WorkerOpt struct { MetadataStore *metadata.Store MountPoolRoot string ResourceMonitor *resources.Monitor - CDIManager *cdi.Cache + CDIManager *cdidevices.Manager } // Worker is a local worker instance with dedicated snapshotter, cache, and so on. @@ -247,7 +247,7 @@ func (w *Worker) LeaseManager() *leaseutil.Manager { return w.WorkerOpt.LeaseManager } -func (w *Worker) CDIManager() *cdi.Cache { +func (w *Worker) CDIManager() *cdidevices.Manager { return w.WorkerOpt.CDIManager } diff --git a/worker/containerd/containerd.go b/worker/containerd/containerd.go index de30fdc95768..c6a5b1e058f6 100644 --- a/worker/containerd/containerd.go +++ b/worker/containerd/containerd.go @@ -16,6 +16,7 @@ import ( "github.com/moby/buildkit/executor/containerdexecutor" "github.com/moby/buildkit/executor/oci" containerdsnapshot "github.com/moby/buildkit/snapshot/containerd" + "github.com/moby/buildkit/solver/llbsolver/cdidevices" "github.com/moby/buildkit/util/leaseutil" "github.com/moby/buildkit/util/network/netproviders" "github.com/moby/buildkit/util/winlayers" @@ -24,7 +25,6 @@ import ( ocispecs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "golang.org/x/sync/semaphore" - "tags.cncf.io/container-device-interface/pkg/cdi" ) type RuntimeInfo = containerdexecutor.RuntimeInfo @@ -44,7 +44,7 @@ type WorkerOptions struct { ParallelismSem *semaphore.Weighted TraceSocket string Runtime *RuntimeInfo - CDIManager *cdi.Cache + CDIManager *cdidevices.Manager } // NewWorkerOpt creates a WorkerOpt. diff --git a/worker/runc/runc.go b/worker/runc/runc.go index 95bf49312b74..0ea36169bdd2 100644 --- a/worker/runc/runc.go +++ b/worker/runc/runc.go @@ -21,6 +21,7 @@ import ( "github.com/moby/buildkit/executor/resources" "github.com/moby/buildkit/executor/runcexecutor" containerdsnapshot "github.com/moby/buildkit/snapshot/containerd" + "github.com/moby/buildkit/solver/llbsolver/cdidevices" "github.com/moby/buildkit/util/leaseutil" "github.com/moby/buildkit/util/network/netproviders" "github.com/moby/buildkit/util/winlayers" @@ -79,7 +80,7 @@ func NewWorkerOpt(root string, snFactory SnapshotterFactory, rootless bool, proc TracingSocket: traceSocket, DefaultCgroupParent: defaultCgroupParent, ResourceMonitor: rm, - CDIManager: cdiManager, + CDIManager: cdidevices.NewManager(cdiManager), }, np) if err != nil { return opt, err @@ -168,7 +169,7 @@ func NewWorkerOpt(root string, snFactory SnapshotterFactory, rootless bool, proc ParallelismSem: parallelismSem, MountPoolRoot: filepath.Join(root, "cachemounts"), ResourceMonitor: rm, - CDIManager: cdiManager, + CDIManager: cdidevices.NewManager(cdiManager), } return opt, nil } diff --git a/worker/worker.go b/worker/worker.go index 182eaebee174..0b5b4cd3b4f7 100644 --- a/worker/worker.go +++ b/worker/worker.go @@ -13,10 +13,10 @@ import ( "github.com/moby/buildkit/session" containerdsnapshot "github.com/moby/buildkit/snapshot/containerd" "github.com/moby/buildkit/solver" + "github.com/moby/buildkit/solver/llbsolver/cdidevices" "github.com/moby/buildkit/solver/pb" "github.com/moby/buildkit/util/leaseutil" ocispecs "github.com/opencontainers/image-spec/specs-go/v1" - "tags.cncf.io/container-device-interface/pkg/cdi" ) type Worker interface { @@ -42,7 +42,7 @@ type Worker interface { CacheManager() cache.Manager LeaseManager() *leaseutil.Manager GarbageCollect(context.Context) error - CDIManager() *cdi.Cache + CDIManager() *cdidevices.Manager } type Infos interface { From 54c73d17854509d8689b1602d83addf0c6e816d3 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Mon, 10 Feb 2025 17:39:21 -0800 Subject: [PATCH 16/23] contrib: add on demand setup for nvidia cdi Signed-off-by: Tonis Tiigi --- cmd/buildkitd/devices_nvidia.go | 8 + contrib/cdisetup/nvidia/apt.go | 45 ++++ contrib/cdisetup/nvidia/log.go | 37 +++ contrib/cdisetup/nvidia/nvidia.go | 332 +++++++++++++++++++++++++ contrib/cdisetup/nvidia/nvidia_test.go | 20 ++ 5 files changed, 442 insertions(+) create mode 100644 cmd/buildkitd/devices_nvidia.go create mode 100644 contrib/cdisetup/nvidia/apt.go create mode 100644 contrib/cdisetup/nvidia/log.go create mode 100644 contrib/cdisetup/nvidia/nvidia.go create mode 100644 contrib/cdisetup/nvidia/nvidia_test.go diff --git a/cmd/buildkitd/devices_nvidia.go b/cmd/buildkitd/devices_nvidia.go new file mode 100644 index 000000000000..3980cb6b0a8d --- /dev/null +++ b/cmd/buildkitd/devices_nvidia.go @@ -0,0 +1,8 @@ +//go:build nvidia +// +build nvidia + +package main + +import ( + _ "github.com/moby/buildkit/contrib/cdisetup/nvidia" +) diff --git a/contrib/cdisetup/nvidia/apt.go b/contrib/cdisetup/nvidia/apt.go new file mode 100644 index 000000000000..f1131fca36bf --- /dev/null +++ b/contrib/cdisetup/nvidia/apt.go @@ -0,0 +1,45 @@ +package nvidia + +import ( + "bufio" + "bytes" + "os/exec" + "strings" + + "github.com/pkg/errors" +) + +type PackageInfo struct { + Package string `json:"Package"` + Description string `json:"Description"` +} + +func searchAptCache(query string) ([]PackageInfo, error) { + cmd := exec.Command("apt-cache", "search", query) + var out bytes.Buffer + cmd.Stdout = &out + + if err := cmd.Run(); err != nil { + return nil, errors.Wrapf(err, "failed to run apt-cache search") + } + + var packages []PackageInfo + scanner := bufio.NewScanner(&out) + for scanner.Scan() { + line := scanner.Text() + parts := strings.SplitN(line, " - ", 2) + if len(parts) == 2 { + pkg := PackageInfo{ + Package: strings.TrimSpace(parts[0]), + Description: strings.TrimSpace(parts[1]), + } + packages = append(packages, pkg) + } + } + + if err := scanner.Err(); err != nil { + return nil, errors.Wrapf(err, "failed to read apt-cache search output") + } + + return packages, nil +} diff --git a/contrib/cdisetup/nvidia/log.go b/contrib/cdisetup/nvidia/log.go new file mode 100644 index 000000000000..554b86358195 --- /dev/null +++ b/contrib/cdisetup/nvidia/log.go @@ -0,0 +1,37 @@ +package nvidia + +import ( + "log" + + "github.com/moby/buildkit/client" + "github.com/moby/buildkit/identity" + "github.com/moby/buildkit/util/progress" + digest "github.com/opencontainers/go-digest" +) + +func newStream(pw progress.Writer, stream int, dgst digest.Digest) *streamWriter { + return &streamWriter{ + pw: pw, + stream: stream, + dgst: dgst, + } +} + +type streamWriter struct { + pw progress.Writer + stream int + dgst digest.Digest +} + +func (sw *streamWriter) Write(dt []byte) (int, error) { + err := sw.pw.Write(identity.NewID(), client.VertexLog{ + Vertex: sw.dgst, + Stream: sw.stream, + Data: dt, + }) + if err != nil { + return 0, err + } + log.Printf("%d %s", sw.stream, dt) + return len(dt), nil +} diff --git a/contrib/cdisetup/nvidia/nvidia.go b/contrib/cdisetup/nvidia/nvidia.go new file mode 100644 index 000000000000..9c67edbef24a --- /dev/null +++ b/contrib/cdisetup/nvidia/nvidia.go @@ -0,0 +1,332 @@ +package nvidia + +import ( + "bufio" + "bytes" + "context" + "fmt" + "net/http" + "os" + "os/exec" + "path/filepath" + "regexp" + "runtime" + "strings" + "time" + + "github.com/moby/buildkit/client" + "github.com/moby/buildkit/identity" + "github.com/moby/buildkit/solver/llbsolver/cdidevices" + "github.com/moby/buildkit/util/progress" + digest "github.com/opencontainers/go-digest" + "github.com/pkg/errors" +) + +// This is example of experimental on-demand setup of a CDI devices. +// This code is not currently shipping with BuildKit and will probably change. + +const ( + cdiKind = "nvidia.com/gpu" + defaultVersion = "570.0" +) + +func init() { + cdidevices.Register(cdiKind, &setup{}) +} + +type setup struct{} + +var _ cdidevices.Setup = &setup{} + +func (s *setup) Validate() error { + _, err := readVersion() + if err == nil { + return nil + } + b, err := hasNvidiaDevices() + if err != nil { + return err + } + if !b { + return errors.Errorf("no NVIDIA devices found") + } + return nil +} + +func newVertex(ctx context.Context, name string) (progress.Writer, digest.Digest, func(error)) { + pw, _, ctx := progress.NewFromContext(ctx) + start := time.Now() + id := identity.NewID() + v := &client.Vertex{ + Name: name, + Digest: digest.FromBytes([]byte(id)), + } + v.Started = &start + v.Completed = nil + v.Cached = false + pw.Write(id, *v) + + pw2, _, _ := progress.NewFromContext(ctx, progress.WithMetadata("vertex", v.Digest)) + + return pw2, v.Digest, func(err error) { + pw2.Close() + stop := time.Now() + v.Completed = &stop + if err != nil { + v.Error = err.Error() + } else { + v.Error = "" + } + pw.Write(id, *v) + pw.Close() + } +} + +func (s *setup) Run(ctx context.Context) (err error) { + pw, dgst, closeProgress := newVertex(ctx, fmt.Sprintf("preparing device %s", cdiKind)) + defer func() { + closeProgress(err) + }() + + isDistro, _ := isDebianOrUbuntu() + if !isDistro { + return errors.Errorf("NVIDIA setup is currently only supported on Debian/Ubuntu") + } + + var needsDriver bool + + if _, err := os.Stat("/proc/driver/nvidia"); err != nil { + needsDriver = true + } + + var arch string + switch runtime.GOARCH { + case "amd64": + arch = "x86_64" + case "arm64": + arch = "sbsa" + // for non-sbsa could use https://nvidia.github.io/libnvidia-container/stable/deb + } + + if arch == "" { + return errors.Errorf("unsupported architecture: %s", runtime.GOARCH) + } + + if needsDriver { + pw.Write(identity.NewID(), client.VertexWarning{ + Vertex: dgst, + Short: []byte("NVIDIA Drivers not found. Installing prebuilt drivers is not recommended"), + }) + } + + version, err := readVersion() + if err != nil && !needsDriver { + return errors.Wrapf(err, "failed to read NVIDIA driver version") + } + if version == "" { + version = defaultVersion + } + v1, _, ok := strings.Cut(version, ".") + if !ok { + return errors.Errorf("failed to parse NVIDIA driver version %q", version) + } + + if err := run(ctx, []string{"apt-get", "update"}, pw, dgst); err != nil { + return err + } + + if err := run(ctx, []string{"apt-get", "install", "-y", "gpg"}, pw, dgst); err != nil { + return err + } + + const aptDistro = "ubuntu2404" + aptURL := "https://developer.download.nvidia.com/compute/cuda/repos/" + aptDistro + "/" + arch + "/" + + keyTarget := "/usr/share/keyrings/nvidia-cuda-keyring.gpg" + + if _, err := os.Stat(keyTarget); err != nil { + fmt.Fprintf(newStream(pw, 2, dgst), "Downloading NVIDIA GPG key\n") + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, aptURL+"3bf863cc.pub", nil) + if err != nil { + return errors.Wrapf(err, "failed to create request for NVIDIA GPG key") + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return errors.Wrapf(err, "failed to download NVIDIA GPG key") + } + + cmd := exec.CommandContext(ctx, "gpg", "--dearmor", "-o", keyTarget) + cmd.Stdin = resp.Body + cmd.Stderr = newStream(pw, 2, dgst) + if err := cmd.Run(); err != nil { + return errors.Wrapf(err, "failed to install NVIDIA GPG key") + } + resp.Body.Close() + } + + if err := os.WriteFile("/etc/apt/sources.list.d/nvidia-cuda.list", []byte("deb [signed-by="+keyTarget+"] "+aptURL+" /"), 0644); err != nil { + return errors.Wrapf(err, "failed to add NVIDIA apt repo") + } + + if err := run(ctx, []string{"apt-get", "update"}, pw, dgst); err != nil { + return err + } + + if needsDriver { + // this pretty much never works, is it even worth having? + // better approach could be to try to create another chroot/container that is built with same kernel packages as the host + // could nvidia-headless-no-dkms- be reusable + if err := run(ctx, []string{"apt-get", "install", "-y", "nvidia-driver-" + v1}, pw, dgst); err != nil { + return err + } + _, err := os.Stat("/proc/driver/nvidia") + if err != nil { + return errors.Wrapf(err, "failed to install NVIDIA kernel module. Please install NVIDIA drivers manually") + } + } + + if err := run(ctx, []string{"apt-get", "install", "-y", "--no-install-recommends", + "libnvidia-compute-" + v1, + "libnvidia-extra-" + v1, + "libnvidia-gl-" + v1, + "nvidia-utils-" + v1, + "nvidia-container-toolkit-base", + }, pw, dgst); err != nil { + return err + } + + if err := os.MkdirAll("/etc/cdi", 0700); err != nil { + return errors.Wrapf(err, "failed to create /etc/cdi") + } + + buf := &bytes.Buffer{} + + cmd := exec.CommandContext(ctx, "nvidia-ctk", "cdi", "generate") + cmd.Stdout = buf + cmd.Stderr = newStream(pw, 2, dgst) + if err := cmd.Run(); err != nil { + return errors.Wrapf(err, "failed to generate CDI spec") + } + + if len(buf.Bytes()) == 0 { + return errors.Errorf("nvidia-ctk output is empty") + } + + if err := os.WriteFile("/etc/cdi/nvidia.yaml", buf.Bytes(), 0644); err != nil { + return errors.Wrapf(err, "failed to write /etc/cdi/nvidia.yaml") + } + + return nil +} + +func run(ctx context.Context, args []string, pw progress.Writer, dgst digest.Digest) error { + fmt.Fprintf(newStream(pw, 2, dgst), "> %s\n", strings.Join(args, " ")) + cmd := exec.CommandContext(ctx, args[0], args[1:]...) //nolint:gosec + cmd.Stderr = newStream(pw, 2, dgst) + cmd.Stdout = newStream(pw, 1, dgst) + return cmd.Run() +} + +func readVersion() (string, error) { + dt, err := os.ReadFile("/proc/driver/nvidia/version") + if err != nil { + return "", err + } + return parseVersion(string(dt)) +} + +func parseVersion(dt string) (string, error) { + re := regexp.MustCompile(`NVIDIA .* Kernel Module(?:[\s\w\d]+)?\s+(\d+\.\d+)`) + matches := re.FindStringSubmatch(dt) + if len(matches) < 2 { + return "", errors.Errorf("could not parse NVIDIA driver version") + } + return matches[1], nil +} + +func hasNvidiaDevices() (bool, error) { + const pciDevicesPath = "/sys/bus/pci/devices" + const nvidiaVendorID = "0x10de" + + found := false + + dirs, err := os.ReadDir(pciDevicesPath) + if err != nil { + return false, err + } + + for _, dir := range dirs { + data, err := os.ReadFile(filepath.Join(pciDevicesPath, dir.Name(), "vendor")) + if err != nil { + continue + } + if strings.TrimSpace(string(data)) == nvidiaVendorID { + found = true + break + } + } + + return found, nil +} + +func getOSID() (string, error) { + file, err := os.Open("/etc/os-release") + if err != nil { + return "", err + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "ID=") { + id := strings.TrimPrefix(line, "ID=") + return strings.Trim(id, `"`), nil // Remove potential quotes + } + } + + if err := scanner.Err(); err != nil { + return "", err + } + + return "", errors.Errorf("ID not found in /etc/os-release") +} + +func isDebianOrUbuntu() (bool, error) { + id, err := getOSID() + if err != nil { + return false, err + } + + return id == "debian" || id == "ubuntu", nil +} + +func checkSBSA() (bool, error) { + file, err := os.Open("/proc/cpuinfo") + if err != nil { + return false, errors.Errorf("failed to open /proc/cpuinfo: %v", err) + } + defer file.Close() + + serverVendors := []string{"Ampere", "Graviton", "Marvell", "ThunderX"} + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "model name") || strings.HasPrefix(line, "Hardware") || strings.HasPrefix(line, "vendor_id") { + for _, vendor := range serverVendors { + if strings.Contains(line, vendor) { + return true, nil // Likely SBSA + } + } + } + } + + if err := scanner.Err(); err != nil { + return false, errors.Errorf("error reading /proc/cpuinfo: %v", err) + } + + return false, nil // Generic ARM64 +} diff --git a/contrib/cdisetup/nvidia/nvidia_test.go b/contrib/cdisetup/nvidia/nvidia_test.go new file mode 100644 index 000000000000..c80d1140dfd6 --- /dev/null +++ b/contrib/cdisetup/nvidia/nvidia_test.go @@ -0,0 +1,20 @@ +package nvidia + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParseVersion(t *testing.T) { + in := `NVRM version: NVIDIA UNIX aarch64 Kernel Module 550.120 Fri Sep 13 11:01:10 UTC 2024` + out, err := parseVersion(in) + require.NoError(t, err) + require.Equal(t, "550.120", out) + + in = `NVRM version: NVIDIA UNIX Open Kernel Module for aarch64 550.144.03 Release Build (dvs-builder@U16-I2-C01-12-4) Mon Dec 30 17:33:24 UTC 2024 +GCC version: gcc version 12.3.0 (Ubuntu 12.3.0-1ubuntu1~22.04)` + out, err = parseVersion(in) + require.NoError(t, err) + require.Equal(t, "550.144", out) +} From cdcb1f1eaa944eb1f2e507eca3e81799c505d49e Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Mon, 10 Feb 2025 17:41:17 -0800 Subject: [PATCH 17/23] contrib: remove unused code from nvidia setup Signed-off-by: Tonis Tiigi --- contrib/cdisetup/nvidia/apt.go | 45 ------------------------------- contrib/cdisetup/nvidia/nvidia.go | 28 ------------------- 2 files changed, 73 deletions(-) delete mode 100644 contrib/cdisetup/nvidia/apt.go diff --git a/contrib/cdisetup/nvidia/apt.go b/contrib/cdisetup/nvidia/apt.go deleted file mode 100644 index f1131fca36bf..000000000000 --- a/contrib/cdisetup/nvidia/apt.go +++ /dev/null @@ -1,45 +0,0 @@ -package nvidia - -import ( - "bufio" - "bytes" - "os/exec" - "strings" - - "github.com/pkg/errors" -) - -type PackageInfo struct { - Package string `json:"Package"` - Description string `json:"Description"` -} - -func searchAptCache(query string) ([]PackageInfo, error) { - cmd := exec.Command("apt-cache", "search", query) - var out bytes.Buffer - cmd.Stdout = &out - - if err := cmd.Run(); err != nil { - return nil, errors.Wrapf(err, "failed to run apt-cache search") - } - - var packages []PackageInfo - scanner := bufio.NewScanner(&out) - for scanner.Scan() { - line := scanner.Text() - parts := strings.SplitN(line, " - ", 2) - if len(parts) == 2 { - pkg := PackageInfo{ - Package: strings.TrimSpace(parts[0]), - Description: strings.TrimSpace(parts[1]), - } - packages = append(packages, pkg) - } - } - - if err := scanner.Err(); err != nil { - return nil, errors.Wrapf(err, "failed to read apt-cache search output") - } - - return packages, nil -} diff --git a/contrib/cdisetup/nvidia/nvidia.go b/contrib/cdisetup/nvidia/nvidia.go index 9c67edbef24a..9a4d32bab84c 100644 --- a/contrib/cdisetup/nvidia/nvidia.go +++ b/contrib/cdisetup/nvidia/nvidia.go @@ -302,31 +302,3 @@ func isDebianOrUbuntu() (bool, error) { return id == "debian" || id == "ubuntu", nil } - -func checkSBSA() (bool, error) { - file, err := os.Open("/proc/cpuinfo") - if err != nil { - return false, errors.Errorf("failed to open /proc/cpuinfo: %v", err) - } - defer file.Close() - - serverVendors := []string{"Ampere", "Graviton", "Marvell", "ThunderX"} - - scanner := bufio.NewScanner(file) - for scanner.Scan() { - line := scanner.Text() - if strings.HasPrefix(line, "model name") || strings.HasPrefix(line, "Hardware") || strings.HasPrefix(line, "vendor_id") { - for _, vendor := range serverVendors { - if strings.Contains(line, vendor) { - return true, nil // Likely SBSA - } - } - } - } - - if err := scanner.Err(); err != nil { - return false, errors.Errorf("error reading /proc/cpuinfo: %v", err) - } - - return false, nil // Generic ARM64 -} From 037252bebf8b01df6a75c8a8d4db8da8fa85f75f Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Mon, 10 Feb 2025 18:43:56 -0800 Subject: [PATCH 18/23] contrib: add setup definition for virtio-venus device Signed-off-by: Tonis Tiigi --- cmd/buildkitd/devices_venus.go | 8 +++ contrib/cdisetup/venus/venus.go | 86 +++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 cmd/buildkitd/devices_venus.go create mode 100644 contrib/cdisetup/venus/venus.go diff --git a/cmd/buildkitd/devices_venus.go b/cmd/buildkitd/devices_venus.go new file mode 100644 index 000000000000..caca3b919bb5 --- /dev/null +++ b/cmd/buildkitd/devices_venus.go @@ -0,0 +1,8 @@ +//go:build venus +// +build venus + +package main + +import ( + _ "github.com/moby/buildkit/contrib/cdisetup/venus" +) diff --git a/contrib/cdisetup/venus/venus.go b/contrib/cdisetup/venus/venus.go new file mode 100644 index 000000000000..24e62e82a51d --- /dev/null +++ b/contrib/cdisetup/venus/venus.go @@ -0,0 +1,86 @@ +package venus + +import ( + "bytes" + "context" + "os" + "strings" + + "github.com/moby/buildkit/solver/llbsolver/cdidevices" + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +const ( + cdiKind = "docker.com/gpu" +) + +func init() { + cdidevices.Register(cdiKind, &setup{}) +} + +type setup struct { +} + +var _ cdidevices.Setup = &setup{} + +func (s *setup) Validate() error { + kVersion, err := getKernelVersion() + if err != nil { + return errors.Wrap(err, "failed to get kernel version") + } + if !strings.Contains(kVersion, "linuxkit") { + return errors.Errorf("%s currently requires a linuxkit kernel", cdiKind) + } + + _, err = os.Stat("/dev/dri") + if err != nil { + if os.IsNotExist(err) { + return errors.Errorf("no DRI device found, make you use Docker VMM Hypervisor") + } + return errors.Wrap(err, "failed to check DRI device") + } + + for _, dev := range []string{"renderD128", "card0"} { + if _, err := os.Stat("/dev/dri/" + dev); err != nil { + return errors.Wrapf(err, "failed to check DRI device %s", dev) + } + } + return nil +} + +func (s *setup) Run(ctx context.Context) error { + if err := s.Validate(); err != nil { + return err + } + + const dt = `cdiVersion: "0.6.0" +kind: "docker.com/gpu" +annotations: + cdi.device.name: "Virtio-GPU Venus (Docker Desktop)" +devices: +- name: venus + containerEdits: + deviceNodes: + - path: /dev/dri/card0 + - path: /dev/dri/renderD128 +` + + if err := os.MkdirAll("/etc/cdi", 0700); err != nil { + return errors.Wrap(err, "failed to create /etc/cdi") + } + + if err := os.WriteFile("/etc/cdi/venus.yaml", []byte(dt), 0600); err != nil { + return errors.Wrap(err, "failed to write /etc/cdi/venus.yaml") + } + + return nil +} + +func getKernelVersion() (string, error) { + var uts unix.Utsname + if err := unix.Uname(&uts); err != nil { + return "", err + } + return string(uts.Release[:bytes.IndexByte(uts.Release[:], 0)]), nil +} From a83a25361b8a5589e242018ad96aefb0169594c1 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Tue, 11 Feb 2025 10:43:11 +0100 Subject: [PATCH 19/23] contrib: fix build with venus Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- contrib/cdisetup/venus/{venus.go => venus_unix.go} | 2 ++ contrib/cdisetup/venus/venus_windows.go | 1 + 2 files changed, 3 insertions(+) rename contrib/cdisetup/venus/{venus.go => venus_unix.go} (98%) create mode 100644 contrib/cdisetup/venus/venus_windows.go diff --git a/contrib/cdisetup/venus/venus.go b/contrib/cdisetup/venus/venus_unix.go similarity index 98% rename from contrib/cdisetup/venus/venus.go rename to contrib/cdisetup/venus/venus_unix.go index 24e62e82a51d..2398511a27b8 100644 --- a/contrib/cdisetup/venus/venus.go +++ b/contrib/cdisetup/venus/venus_unix.go @@ -1,3 +1,5 @@ +//go:build !windows + package venus import ( diff --git a/contrib/cdisetup/venus/venus_windows.go b/contrib/cdisetup/venus/venus_windows.go new file mode 100644 index 000000000000..4ecdb3fc1456 --- /dev/null +++ b/contrib/cdisetup/venus/venus_windows.go @@ -0,0 +1 @@ +package venus From 88509a98ed6085fc579624ab5b1c1f06e481731f Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Tue, 11 Feb 2025 11:00:19 +0100 Subject: [PATCH 20/23] dockerfile: remove device frontend attr support Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- .../dockerfile/dockerfile_rundevice_test.go | 60 ------------------- frontend/dockerui/attr.go | 19 ------ frontend/dockerui/config.go | 7 --- 3 files changed, 86 deletions(-) diff --git a/frontend/dockerfile/dockerfile_rundevice_test.go b/frontend/dockerfile/dockerfile_rundevice_test.go index 92b2322f8832..4975fb08fbdc 100644 --- a/frontend/dockerfile/dockerfile_rundevice_test.go +++ b/frontend/dockerfile/dockerfile_rundevice_test.go @@ -17,70 +17,10 @@ import ( func init() { allTests = append(allTests, integration.TestFuncs( - testDeviceEnv, testDeviceRunEnv, )...) } -func testDeviceEnv(t *testing.T, sb integration.Sandbox) { - if sb.Rootless() { - t.SkipNow() - } - - integration.SkipOnPlatform(t, "windows") - f := getFrontend(t, sb) - - require.NoError(t, os.WriteFile(filepath.Join(sb.CDISpecDir(), "vendor1-device.yaml"), []byte(` -cdiVersion: "0.3.0" -kind: "vendor1.com/device" -devices: -- name: foo - containerEdits: - env: - - FOO=injected -`), 0600)) - - dockerfile := []byte(` -FROM busybox AS base -RUN env|sort | tee foo.env -FROM scratch -COPY --from=base /foo.env / -`) - - dir := integration.Tmpdir( - t, - fstest.CreateFile("Dockerfile", dockerfile, 0600), - ) - - c, err := client.New(sb.Context(), sb.Address()) - require.NoError(t, err) - defer c.Close() - - destDir := t.TempDir() - - _, err = f.Solve(sb.Context(), c, client.SolveOpt{ - FrontendAttrs: map[string]string{ - "device:0": "vendor1.com/device=foo,required", - "device:1": "vendor2.com/device=bar", - }, - LocalMounts: map[string]fsutil.FS{ - dockerui.DefaultLocalNameDockerfile: dir, - dockerui.DefaultLocalNameContext: dir, - }, - Exports: []client.ExportEntry{ - { - Type: client.ExporterLocal, - OutputDir: destDir, - }, - }, - }, nil) - require.NoError(t, err) - - dt, err := os.ReadFile(filepath.Join(destDir, "foo.env")) - require.NoError(t, err) - require.Contains(t, string(dt), `FOO=injected`) -} - func testDeviceRunEnv(t *testing.T, sb integration.Sandbox) { if sb.Rootless() { t.SkipNow() diff --git a/frontend/dockerui/attr.go b/frontend/dockerui/attr.go index 5c055372e770..259100b46381 100644 --- a/frontend/dockerui/attr.go +++ b/frontend/dockerui/attr.go @@ -9,7 +9,6 @@ import ( "github.com/containerd/platforms" "github.com/docker/go-units" "github.com/moby/buildkit/client/llb" - "github.com/moby/buildkit/frontend/dockerfile/instructions" "github.com/moby/buildkit/solver/pb" ocispecs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" @@ -98,24 +97,6 @@ func parseUlimits(v string) ([]*pb.Ulimit, error) { return out, nil } -func parseDevices(v map[string]string) ([]*pb.CDIDevice, error) { - if v == nil { - return nil, nil - } - out := make([]*pb.CDIDevice, 0) - for _, attrs := range v { - device, err := instructions.ParseDevice(attrs) - if err != nil { - return nil, err - } - out = append(out, &pb.CDIDevice{ - Name: device.Name, - Optional: !device.Required, - }) - } - return out, nil -} - func parseNetMode(v string) (pb.NetMode, error) { if v == "" { return llb.NetModeSandbox, nil diff --git a/frontend/dockerui/config.go b/frontend/dockerui/config.go index 5769e94914cb..ee8a2667548d 100644 --- a/frontend/dockerui/config.go +++ b/frontend/dockerui/config.go @@ -28,7 +28,6 @@ const ( buildArgPrefix = "build-arg:" labelPrefix = "label:" localSessionIDPrefix = "local-sessionid:" - devicePrefix = "device:" keyTarget = "target" keyCgroupParent = "cgroup-parent" @@ -189,12 +188,6 @@ func (bc *Client) init() error { } bc.Ulimits = ulimits - devices, err := parseDevices(filter(opts, devicePrefix)) - if err != nil { - return errors.Wrap(err, "failed to parse devices") - } - bc.Devices = devices - defaultNetMode, err := parseNetMode(opts[keyForceNetwork]) if err != nil { return err From 3c072dcffce25514f803a7ea7fadd41217cc7956 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Tue, 11 Feb 2025 14:22:27 +0100 Subject: [PATCH 21/23] cdi: support custom and wildcard class for injection Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- client/client_test.go | 193 ++++++++++++++++++ executor/oci/spec_linux.go | 39 +--- .../instructions/commands_rundevice.go | 27 +-- .../instructions/commands_rundevice_test.go | 15 +- solver/llbsolver/cdidevices/manager.go | 110 +++++++++- 5 files changed, 320 insertions(+), 64 deletions(-) diff --git a/client/client_test.go b/client/client_test.go index b13ab645a82e..497e8ebd084e 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -277,6 +277,9 @@ func testIntegration(t *testing.T, funcs ...func(t *testing.T, sb integration.Sa integration.Run(t, integration.TestFuncs( testCDI, + testCDIFirst, + testCDIWildcard, + testCDIClass, ), mirrors) } @@ -11054,3 +11057,193 @@ devices: require.NoError(t, err) require.Contains(t, strings.TrimSpace(string(dt2)), `BAR=injected`) } + +func testCDIFirst(t *testing.T, sb integration.Sandbox) { + if sb.Rootless() { + t.SkipNow() + } + + integration.SkipOnPlatform(t, "windows") + c, err := New(sb.Context(), sb.Address()) + require.NoError(t, err) + defer c.Close() + + require.NoError(t, os.WriteFile(filepath.Join(sb.CDISpecDir(), "vendor1-device.yaml"), []byte(` +cdiVersion: "0.3.0" +kind: "vendor1.com/device" +devices: +- name: foo + containerEdits: + env: + - FOO=injected +- name: bar + containerEdits: + env: + - BAR=injected +- name: baz + containerEdits: + env: + - BAZ=injected +- name: qux + containerEdits: + env: + - QUX=injected +`), 0600)) + + busybox := llb.Image("busybox:latest") + st := llb.Scratch() + + run := func(cmd string, ro ...llb.RunOption) { + st = busybox.Run(append(ro, llb.Shlex(cmd), llb.Dir("/wd"))...).AddMount("/wd", st) + } + + run(`sh -c 'env|sort | tee first.env'`, llb.AddCDIDevice(llb.CDIDeviceName("vendor1.com/device"))) + + def, err := st.Marshal(sb.Context()) + require.NoError(t, err) + + destDir := t.TempDir() + + _, err = c.Solve(sb.Context(), def, SolveOpt{ + Exports: []ExportEntry{ + { + Type: ExporterLocal, + OutputDir: destDir, + }, + }, + }, nil) + require.NoError(t, err) + + dt, err := os.ReadFile(filepath.Join(destDir, "first.env")) + require.NoError(t, err) + require.Contains(t, strings.TrimSpace(string(dt)), `BAR=injected`) + require.NotContains(t, strings.TrimSpace(string(dt)), `FOO=injected`) + require.NotContains(t, strings.TrimSpace(string(dt)), `BAZ=injected`) + require.NotContains(t, strings.TrimSpace(string(dt)), `QUX=injected`) +} + +func testCDIWildcard(t *testing.T, sb integration.Sandbox) { + if sb.Rootless() { + t.SkipNow() + } + + integration.SkipOnPlatform(t, "windows") + c, err := New(sb.Context(), sb.Address()) + require.NoError(t, err) + defer c.Close() + + require.NoError(t, os.WriteFile(filepath.Join(sb.CDISpecDir(), "vendor1-device.yaml"), []byte(` +cdiVersion: "0.3.0" +kind: "vendor1.com/device" +devices: +- name: foo + containerEdits: + env: + - FOO=injected +- name: bar + containerEdits: + env: + - BAR=injected +`), 0600)) + + busybox := llb.Image("busybox:latest") + st := llb.Scratch() + + run := func(cmd string, ro ...llb.RunOption) { + st = busybox.Run(append(ro, llb.Shlex(cmd), llb.Dir("/wd"))...).AddMount("/wd", st) + } + + run(`sh -c 'env|sort | tee all.env'`, llb.AddCDIDevice(llb.CDIDeviceName("vendor1.com/device=*"))) + + def, err := st.Marshal(sb.Context()) + require.NoError(t, err) + + destDir := t.TempDir() + + _, err = c.Solve(sb.Context(), def, SolveOpt{ + Exports: []ExportEntry{ + { + Type: ExporterLocal, + OutputDir: destDir, + }, + }, + }, nil) + require.NoError(t, err) + + dt, err := os.ReadFile(filepath.Join(destDir, "all.env")) + require.NoError(t, err) + require.Contains(t, strings.TrimSpace(string(dt)), `FOO=injected`) + require.Contains(t, strings.TrimSpace(string(dt)), `BAR=injected`) +} + +func testCDIClass(t *testing.T, sb integration.Sandbox) { + if sb.Rootless() { + t.SkipNow() + } + + integration.SkipOnPlatform(t, "windows") + c, err := New(sb.Context(), sb.Address()) + require.NoError(t, err) + defer c.Close() + + require.NoError(t, os.WriteFile(filepath.Join(sb.CDISpecDir(), "vendor1-device.yaml"), []byte(` +cdiVersion: "0.6.0" +kind: "vendor1.com/device" +annotations: + foo.bar.baz: FOO +devices: +- name: foo + annotations: + org.mobyproject.buildkit.device.class: class1 + containerEdits: + env: + - FOO=injected +- name: bar + annotations: + org.mobyproject.buildkit.device.class: class1 + containerEdits: + env: + - BAR=injected +- name: baz + annotations: + org.mobyproject.buildkit.device.class: class2 + containerEdits: + env: + - BAZ=injected +- name: qux + containerEdits: + env: + - QUX=injected +`), 0600)) + + busybox := llb.Image("busybox:latest") + st := llb.Scratch() + + run := func(cmd string, ro ...llb.RunOption) { + st = busybox.Run(append(ro, llb.Shlex(cmd), llb.Dir("/wd"))...).AddMount("/wd", st) + } + + run(`sh -c 'env|sort | tee class.env'`, llb.AddCDIDevice(llb.CDIDeviceName("vendor1.com/device=class1"))) + + def, err := st.Marshal(sb.Context()) + require.NoError(t, err) + + destDir := t.TempDir() + + _, err = c.Solve(sb.Context(), def, SolveOpt{ + Exports: []ExportEntry{ + { + Type: ExporterLocal, + OutputDir: destDir, + }, + }, + }, nil) + require.NoError(t, err) + + dt, err := os.ReadFile(filepath.Join(destDir, "class.env")) + require.NoError(t, err) + require.Contains(t, strings.TrimSpace(string(dt)), `FOO=injected`) + require.Contains(t, strings.TrimSpace(string(dt)), `BAR=injected`) + require.NotContains(t, strings.TrimSpace(string(dt)), `BAZ=injected`) + require.NotContains(t, strings.TrimSpace(string(dt)), `QUX=injected`) +} diff --git a/executor/oci/spec_linux.go b/executor/oci/spec_linux.go index 1a394ac04d6b..7b437c28b8b0 100644 --- a/executor/oci/spec_linux.go +++ b/executor/oci/spec_linux.go @@ -26,7 +26,6 @@ import ( "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "golang.org/x/sys/unix" - "tags.cncf.io/container-device-interface/pkg/parser" ) var ( @@ -153,47 +152,19 @@ func generateRlimitOpts(ulimits []*pb.Ulimit) ([]oci.SpecOpts, error) { // genereateCDIOptions creates the OCI runtime spec options for injecting CDI // devices. -func generateCDIOpts(manager *cdidevices.Manager, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) { - if len(devices) == 0 { +func generateCDIOpts(manager *cdidevices.Manager, devs []*pb.CDIDevice) ([]oci.SpecOpts, error) { + if len(devs) == 0 { return nil, nil } - withCDIDevices := func(devices []*pb.CDIDevice) oci.SpecOpts { + withCDIDevices := func(devs []*pb.CDIDevice) oci.SpecOpts { return func(ctx context.Context, _ oci.Client, c *containers.Container, s *specs.Spec) error { if err := manager.Refresh(); err != nil { bklog.G(ctx).Warnf("CDI registry refresh failed: %v", err) } - - registeredDevices := manager.ListDevices() - isDeviceRegistered := func(device *pb.CDIDevice) bool { - for _, d := range registeredDevices { - if device.Name == d.Name { - return true - } - } - return false - } - - var dd []string - for _, d := range devices { - if d == nil { - continue - } - if _, _, _, err := parser.ParseQualifiedName(d.Name); err != nil { - return errors.Wrapf(err, "invalid CDI device name %s", d.Name) - } - if !isDeviceRegistered(d) && d.Optional { - bklog.G(ctx).Warnf("Optional CDI device %q is not registered", d.Name) - continue - } - dd = append(dd, d.Name) - } - - bklog.G(ctx).Debugf("Injecting CDI devices %v", dd) - if err := manager.InjectDevices(s, dd...); err != nil { + if err := manager.InjectDevices(s, devs...); err != nil { return errors.Wrapf(err, "CDI device injection failed") } - // One crucial thing to keep in mind is that CDI device injection // might add OCI Spec environment variables, hooks, and mounts as // well. Therefore, it is important that none of the corresponding @@ -203,7 +174,7 @@ func generateCDIOpts(manager *cdidevices.Manager, devices []*pb.CDIDevice) ([]oc } return []oci.SpecOpts{ - withCDIDevices(devices), + withCDIDevices(devs), }, nil } diff --git a/frontend/dockerfile/instructions/commands_rundevice.go b/frontend/dockerfile/instructions/commands_rundevice.go index 975c95454978..693f28bdc19d 100644 --- a/frontend/dockerfile/instructions/commands_rundevice.go +++ b/frontend/dockerfile/instructions/commands_rundevice.go @@ -7,7 +7,6 @@ import ( "github.com/moby/buildkit/util/suggest" "github.com/pkg/errors" "github.com/tonistiigi/go-csvvalue" - "tags.cncf.io/container-device-interface/pkg/parser" ) var devicesKey = "dockerfile/run/devices" @@ -75,28 +74,20 @@ func ParseDevice(val string) (*Device, error) { d := &Device{} - for i, field := range fields { - // check if the first field is a valid device name - var firstFieldErr error - if i == 0 { - if _, _, _, firstFieldErr = parser.ParseQualifiedName(field); firstFieldErr == nil { - d.Name = field - continue - } - } - + for _, field := range fields { key, value, ok := strings.Cut(field, "=") key = strings.ToLower(key) if !ok { - if len(fields) == 1 && firstFieldErr != nil { - return nil, errors.Wrapf(firstFieldErr, "invalid device name %s", field) - } switch key { case "required": d.Required = true continue default: + if d.Name == "" { + d.Name = field + continue + } // any other option requires a value. return nil, errors.Errorf("invalid field '%s' must be a key=value pair", field) } @@ -114,14 +105,14 @@ func ParseDevice(val string) (*Device, error) { return nil, errors.Errorf("invalid value for %s: %s", key, value) } default: + if d.Name == "" { + d.Name = field + continue + } allKeys := []string{"name", "required"} return nil, suggest.WrapError(errors.Errorf("unexpected key '%s' in '%s'", key, field), key, allKeys, true) } } - if _, _, _, err := parser.ParseQualifiedName(d.Name); err != nil { - return nil, errors.Wrapf(err, "invalid device name %s", d.Name) - } - return d, nil } diff --git a/frontend/dockerfile/instructions/commands_rundevice_test.go b/frontend/dockerfile/instructions/commands_rundevice_test.go index 92d103cbd847..79b9be79ff19 100644 --- a/frontend/dockerfile/instructions/commands_rundevice_test.go +++ b/frontend/dockerfile/instructions/commands_rundevice_test.go @@ -18,6 +18,11 @@ func TestParseDevice(t *testing.T) { expected: &Device{Name: "vendor1.com/device=foo", Required: false}, expectedErr: nil, }, + { + input: "vendor1.com/device", + expected: &Device{Name: "vendor1.com/device", Required: false}, + expectedErr: nil, + }, { input: "vendor1.com/device=foo,required", expected: &Device{Name: "vendor1.com/device=foo", Required: true}, @@ -48,16 +53,6 @@ func TestParseDevice(t *testing.T) { expected: nil, expectedErr: errors.New("device name already set to vendor1.com/device=foo"), }, - { - input: "invalid-device-name", - expected: nil, - expectedErr: errors.New(`invalid device name invalid-device-name: unqualified device "invalid-device-name", missing vendor`), - }, - { - input: "name=invalid-device-name", - expected: nil, - expectedErr: errors.New(`invalid device name invalid-device-name: unqualified device "invalid-device-name", missing vendor`), - }, } for _, tt := range cases { t.Run(tt.input, func(t *testing.T) { diff --git a/solver/llbsolver/cdidevices/manager.go b/solver/llbsolver/cdidevices/manager.go index 168daa81bf45..45dfbe9eb8f0 100644 --- a/solver/llbsolver/cdidevices/manager.go +++ b/solver/llbsolver/cdidevices/manager.go @@ -4,12 +4,17 @@ import ( "context" "strings" + "github.com/moby/buildkit/solver/pb" + "github.com/moby/buildkit/util/bklog" "github.com/moby/locker" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "tags.cncf.io/container-device-interface/pkg/cdi" + "tags.cncf.io/container-device-interface/pkg/parser" ) +const deviceAnnotationClass = "org.mobyproject.buildkit.device.class" + var installers = map[string]Setup{} type Setup interface { @@ -76,11 +81,97 @@ func (m *Manager) Refresh() error { return m.cache.Refresh() } -func (m *Manager) InjectDevices(spec *specs.Spec, devs ...string) error { - _, err := m.cache.InjectDevices(spec, devs...) +func (m *Manager) InjectDevices(spec *specs.Spec, devs ...*pb.CDIDevice) error { + pdevs, err := m.parseDevices(devs...) + if err != nil { + return err + } else if len(pdevs) == 0 { + return nil + } + bklog.G(context.TODO()).Debugf("Injecting devices %v", pdevs) + _, err = m.cache.InjectDevices(spec, pdevs...) return err } +func (m *Manager) parseDevices(devs ...*pb.CDIDevice) ([]string, error) { + var out []string + for _, dev := range devs { + if dev == nil { + continue + } + pdev, err := m.parseDevice(dev) + if err != nil { + return nil, err + } else if len(pdev) == 0 { + continue + } + out = append(out, pdev...) + } + return dedupSlice(out), nil +} + +func (m *Manager) parseDevice(dev *pb.CDIDevice) ([]string, error) { + var out []string + + kind, name, _ := strings.Cut(dev.Name, "=") + + // validate kind + if vendor, class := parser.ParseQualifier(kind); vendor == "" { + return nil, errors.Errorf("invalid device %q", dev.Name) + } else if err := parser.ValidateVendorName(vendor); err != nil { + return nil, errors.Wrapf(err, "invalid device %q", dev.Name) + } else if err := parser.ValidateClassName(class); err != nil { + return nil, errors.Wrapf(err, "invalid device %q", dev.Name) + } + + switch name { + case "": + // first device of kind if no name is specified + for _, d := range m.cache.ListDevices() { + if strings.HasPrefix(d, kind+"=") { + out = append(out, d) + break + } + } + case "*": + // all devices of kind if the name is a wildcard + for _, d := range m.cache.ListDevices() { + if strings.HasPrefix(d, kind+"=") { + out = append(out, d) + } + } + default: + // the specified device + for _, d := range m.cache.ListDevices() { + if d == dev.Name { + out = append(out, d) + break + } + } + if len(out) == 0 { + // check class annotation if name unknown + for _, d := range m.cache.ListDevices() { + if !strings.HasPrefix(d, kind+"=") { + continue + } + if dd := m.cache.GetDevice(d).Device; dd != nil { + if class, ok := dd.Annotations[deviceAnnotationClass]; ok && class == name { + out = append(out, d) + } + } + } + } + } + + if len(out) == 0 { + if !dev.Optional { + return nil, errors.Errorf("required device %q is not registered", dev.Name) + } + bklog.G(context.TODO()).Warnf("Optional device %q is not registered", dev.Name) + } + return out, nil +} + func (m *Manager) hasDevice(name string) bool { for _, d := range m.cache.ListDevices() { kind, _, _ := strings.Cut(d, "=") @@ -126,3 +217,18 @@ func (m *Manager) OnDemandInstaller(name string) (func(context.Context) error, b return nil }, true } + +func dedupSlice(s []string) []string { + if len(s) == 0 { + return s + } + var res []string + seen := make(map[string]struct{}) + for _, val := range s { + if _, ok := seen[val]; !ok { + res = append(res, val) + seen[val] = struct{}{} + } + } + return res +} From 06abbdfe704d5591c53ac5c9c03f12f8c4204184 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Tue, 11 Feb 2025 16:58:30 +0100 Subject: [PATCH 22/23] cdi: merge spec and device annotations Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- solver/llbsolver/cdidevices/manager.go | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/solver/llbsolver/cdidevices/manager.go b/solver/llbsolver/cdidevices/manager.go index 45dfbe9eb8f0..1996a41c8f7f 100644 --- a/solver/llbsolver/cdidevices/manager.go +++ b/solver/llbsolver/cdidevices/manager.go @@ -52,11 +52,11 @@ func (m *Manager) ListDevices() []Device { kinds := make(map[string]struct{}) for _, dev := range devs { kind, _, _ := strings.Cut(dev, "=") - spec := m.cache.GetDevice(dev).GetSpec() + dd := m.cache.GetDevice(dev) out = append(out, Device{ Name: dev, AutoAllow: true, // TODO - Annotations: spec.Annotations, + Annotations: deviceAnnotations(dd), }) kinds[kind] = struct{}{} } @@ -154,8 +154,8 @@ func (m *Manager) parseDevice(dev *pb.CDIDevice) ([]string, error) { if !strings.HasPrefix(d, kind+"=") { continue } - if dd := m.cache.GetDevice(d).Device; dd != nil { - if class, ok := dd.Annotations[deviceAnnotationClass]; ok && class == name { + if a := deviceAnnotations(m.cache.GetDevice(d)); a != nil { + if class, ok := a[deviceAnnotationClass]; ok && class == name { out = append(out, d) } } @@ -218,6 +218,22 @@ func (m *Manager) OnDemandInstaller(name string) (func(context.Context) error, b }, true } +func deviceAnnotations(dev *cdi.Device) map[string]string { + if dev == nil { + return nil + } + out := make(map[string]string) + // spec annotations + for k, v := range dev.GetSpec().Annotations { + out[k] = v + } + // device annotations + for k, v := range dev.Device.Annotations { + out[k] = v + } + return out +} + func dedupSlice(s []string) []string { if len(s) == 0 { return s From 60d305d9fd184c5e42b0cd1ce475927b29dd1871 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Tue, 11 Feb 2025 15:20:09 +0100 Subject: [PATCH 23/23] cdi: docs Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- docs/cdi.md | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 docs/cdi.md diff --git a/docs/cdi.md b/docs/cdi.md new file mode 100644 index 000000000000..ff1668115f4c --- /dev/null +++ b/docs/cdi.md @@ -0,0 +1,114 @@ +# CDI + +[CDI (Container Device Interface)](https://github.com/cncf-tags/container-device-interface/blob/main/SPEC.md) +provides a standard mechanism for device vendors to describe what is required +to provide access to a specific resource such as a GPU beyond a simple device +name. + +Since BuildKit 0.20.0, you can access devices using the CDI interface. This +allows you to use devices like GPUs in your builds. + +## Usage + +To use CDI with BuildKit, you need to create the [CDI configuration file](https://github.com/cncf-tags/container-device-interface/blob/main/SPEC.md#cdi-json-specification) +for your device (either JSON or YAML) and put it in one of the following +locations: +* `/etc/cdi` +* `/var/run/cdi` +* `/etc/buildkit/cdi` + +> [!NOTE] +> Location can be changed by setting the `specDirs` option in the `cdi` section +> of the [`buildkitd.toml` configuration file](buildkitd.toml.md). + +Let's create a simple CDI configuration file that injects an environment +variable into the build environment and write it to `/etc/cdi/foo.yml`: + +```yaml +cdiVersion: "0.6.0" +kind: "vendor1.com/device" +devices: +- name: foo + containerEdits: + env: + - FOO=injected +``` + +Start BuildKit and check the list of available devices for the default worker: + +```bash +buildctl debug workers -v + +Platforms: linux/amd64,linux/amd64/v2,linux/amd64/v3,linux/arm64,linux/riscv64,linux/ppc64le,linux/s390x,linux/386,linux/arm/v7,linux/arm/v6 +BuildKit: github.com/moby/buildkit v0.19.0-rc3-72-gb89ee491b.m b89ee491bc1c40b8533f098dab18e414c8b70885.m +Labels: + org.mobyproject.buildkit.worker.executor: oci + org.mobyproject.buildkit.worker.hostname: cc39352c87dd + org.mobyproject.buildkit.worker.network: host + org.mobyproject.buildkit.worker.oci.process-mode: sandbox + org.mobyproject.buildkit.worker.selinux.enabled: false + org.mobyproject.buildkit.worker.snapshotter: overlayfs +Devices: + Name: vendor1.com/device=foo + AutoAllow: true + +GC Policy rule#0: + All: false + Filters: type==source.local,type==exec.cachemount,type==source.git.checkout + Keep duration: 48h0m0s + Maximum used space: 512MB +GC Policy rule#1: + All: false + Keep duration: 1440h0m0s + Reserved space: 10GB + Minimum free space: 202GB + Maximum used space: 100GB +GC Policy rule#2: + All: false + Reserved space: 10GB + Minimum free space: 202GB + Maximum used space: 100GB +GC Policy rule#3: + All: true + Reserved space: 10GB + Minimum free space: 202GB + Maximum used space: 100GB +``` + +Now let's create a Dockerfile to use this device: + +```dockerfile +FROM busybox +RUN --device=vendor1.com/device=foo \ + env|grep FOO +``` + +And check if the environment variable is injected during build: + +```bash +buildctl build --frontend=dockerfile.v0 --no-cache --local context=. --local dockerfile=. +#1 [internal] load build definition from Dockerfile +#1 transferring dockerfile: 99B done +#1 DONE 0.0s + +#2 [internal] load metadata for docker.io/library/busybox:latest +#2 ... + +#3 [auth] library/busybox:pull token for registry-1.docker.io +#3 DONE 0.0s + +#2 [internal] load metadata for docker.io/library/busybox:latest +#2 DONE 0.8s + +#4 [internal] load .dockerignore +#4 transferring context: 2B done +#4 DONE 0.0s + +#5 [1/2] FROM docker.io/library/busybox:latest@sha256:a5d0ce49aa801d475da48f8cb163c354ab95cab073cd3c138bd458fc8257fbf1 +#5 resolve docker.io/library/busybox:latest@sha256:a5d0ce49aa801d475da48f8cb163c354ab95cab073cd3c138bd458fc8257fbf1 0.0s done +#5 CACHED + +#6 [2/2] RUN --device=vendor1.com/device=foo env|grep FOO +#6 0.062 FOO=injected +#6 DONE 0.1s +```