diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index e4f6da789ce3..7b8aed7262f9 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -27,6 +27,7 @@ jobs: - lint - validate-vendor - validate-docs + - validate-generated-files steps: - name: Checkout diff --git a/Makefile b/Makefile index da70e877c015..c72feff55540 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ install: binaries release: ./hack/release -validate-all: lint test validate-vendor validate-docs +validate-all: lint test validate-vendor validate-docs validate-generated-files lint: $(BUILDX_CMD) bake lint @@ -44,6 +44,9 @@ validate-docs: validate-authors: $(BUILDX_CMD) bake validate-authors +validate-generated-files: + ./hack/validate-generated-files + test-driver: ./hack/test-driver @@ -59,4 +62,7 @@ authors: mod-outdated: $(BUILDX_CMD) bake mod-outdated +generated-files: + ./hack/update-generated-files + .PHONY: shell binaries binaries-cross install release validate-all lint validate-vendor validate-docs validate-authors vendor docs authors diff --git a/commands/bake.go b/commands/bake.go index 2266545dc738..367bb90363a5 100644 --- a/commands/bake.go +++ b/commands/bake.go @@ -9,6 +9,7 @@ import ( "github.com/containerd/containerd/platforms" "github.com/docker/buildx/bake" "github.com/docker/buildx/build" + builderapi "github.com/docker/buildx/commands/builder/pb" "github.com/docker/buildx/util/confutil" "github.com/docker/buildx/util/progress" "github.com/docker/buildx/util/tracing" @@ -22,10 +23,10 @@ type bakeOptions struct { files []string overrides []string printOnly bool - commonOptions + builderapi.CommonOptions } -func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error) { +func runBake(dockerCli command.Cli, targets []string, in bakeOptions, cFlags commonFlags) (err error) { ctx := appcontext.Context() ctx, end, err := tracing.TraceCurrentCommand(ctx, "bake") @@ -57,25 +58,25 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error } overrides := in.overrides - if in.exportPush { - if in.exportLoad { + if in.ExportPush { + if in.ExportLoad { return errors.Errorf("push and load may not be set together at the moment") } overrides = append(overrides, "*.push=true") - } else if in.exportLoad { + } else if in.ExportLoad { overrides = append(overrides, "*.output=type=docker") } - if in.noCache != nil { - overrides = append(overrides, fmt.Sprintf("*.no-cache=%t", *in.noCache)) + if cFlags.noCache != nil { + overrides = append(overrides, fmt.Sprintf("*.no-cache=%t", *cFlags.noCache)) } - if in.pull != nil { - overrides = append(overrides, fmt.Sprintf("*.pull=%t", *in.pull)) + if cFlags.pull != nil { + overrides = append(overrides, fmt.Sprintf("*.pull=%t", *cFlags.pull)) } contextPathHash, _ := os.Getwd() ctx2, cancel := context.WithCancel(context.TODO()) defer cancel() - printer := progress.NewPrinter(ctx2, os.Stderr, os.Stderr, in.progress) + printer := progress.NewPrinter(ctx2, os.Stderr, os.Stderr, cFlags.progress) defer func() { if printer != nil { @@ -92,7 +93,7 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error // instance only needed for reading remote bake files or building if url != "" || !in.printOnly { - dis, err = getInstanceOrDefault(ctx, dockerCli, in.builder, contextPathHash) + dis, err = getInstanceOrDefault(ctx, dockerCli, in.Builder, contextPathHash) if err != nil { return err } @@ -154,12 +155,12 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error return wrapBuildError(err, true) } - if len(in.metadataFile) > 0 { + if len(in.MetadataFile) > 0 { dt := make(map[string]interface{}) for t, r := range resp { dt[t] = decodeExporterResponse(r.ExporterResponse) } - if err := writeMetadataFile(in.metadataFile, dt); err != nil { + if err := writeMetadataFile(in.MetadataFile, dt); err != nil { return err } } @@ -169,6 +170,7 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { var options bakeOptions + var cFlags commonFlags cmd := &cobra.Command{ Use: "bake [OPTIONS] [TARGET...]", @@ -177,25 +179,27 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { // reset to nil to avoid override is unset if !cmd.Flags().Lookup("no-cache").Changed { - options.noCache = nil + cFlags.noCache = nil } if !cmd.Flags().Lookup("pull").Changed { - options.pull = nil + cFlags.pull = nil } - options.commonOptions.builder = rootOpts.builder - return runBake(dockerCli, args, options) + options.Builder = rootOpts.builder + options.MetadataFile = cFlags.metadataFile + // Other common flags (noCache, pull and progress) are processed in runBake function. + return runBake(dockerCli, args, options, cFlags) }, } flags := cmd.Flags() flags.StringArrayVarP(&options.files, "file", "f", []string{}, "Build definition file") - flags.BoolVar(&options.exportLoad, "load", false, `Shorthand for "--set=*.output=type=docker"`) + flags.BoolVar(&options.ExportLoad, "load", false, `Shorthand for "--set=*.output=type=docker"`) flags.BoolVar(&options.printOnly, "print", false, "Print the options without building") - flags.BoolVar(&options.exportPush, "push", false, `Shorthand for "--set=*.output=type=registry"`) + flags.BoolVar(&options.ExportPush, "push", false, `Shorthand for "--set=*.output=type=registry"`) flags.StringArrayVar(&options.overrides, "set", nil, `Override target value (e.g., "targetpattern.key=value")`) - commonBuildFlags(&options.commonOptions, flags) + commonBuildFlags(&cFlags, flags) return cmd } diff --git a/commands/build.go b/commands/build.go index 42be0690ab4b..d12b832f07e2 100644 --- a/commands/build.go +++ b/commands/build.go @@ -10,15 +10,18 @@ import ( "io" "os" "path/filepath" + "runtime" "strconv" "strings" "sync" "github.com/containerd/console" "github.com/docker/buildx/build" + builderapi "github.com/docker/buildx/commands/builder/pb" "github.com/docker/buildx/monitor" "github.com/docker/buildx/util/buildflags" "github.com/docker/buildx/util/confutil" + "github.com/docker/buildx/util/ioset" "github.com/docker/buildx/util/platformutil" "github.com/docker/buildx/util/progress" "github.com/docker/buildx/util/tracing" @@ -46,46 +49,15 @@ import ( const defaultTargetName = "default" type buildOptions struct { - contextPath string - dockerfileName string - printFunc string - - allow []string - buildArgs []string - cacheFrom []string - cacheTo []string - cgroupParent string - contexts []string - extraHosts []string - imageIDFile string - labels []string - networkMode string - noCacheFilter []string - outputs []string - platforms []string - quiet bool - secrets []string - shmSize dockeropts.MemBytes - ssh []string - tags []string - target string - ulimits *dockeropts.UlimitOpt - invoke string - commonOptions -} - -type commonOptions struct { - builder string - metadataFile string - noCache *bool progress string - pull *bool - - exportPush bool - exportLoad bool + invoke string + serverConfig string + root string + detach bool + builderapi.BuildOptions } -func runBuild(dockerCli command.Cli, in buildOptions) (err error) { +func runBuild(dockerCli command.Cli, in buildOptions) error { ctx := appcontext.Context() ctx, end, err := tracing.TraceCurrentCommand(ctx, "build") @@ -96,89 +68,85 @@ func runBuild(dockerCli command.Cli, in buildOptions) (err error) { end(err) }() - noCache := false - if in.noCache != nil { - noCache = *in.noCache - } - pull := false - if in.pull != nil { - pull = *in.pull - } + _, err = runBuildContext(ctx, dockerCli, in.BuildOptions, os.Stdin, in.progress, in.invoke != "", nil) + return err +} - if noCache && len(in.noCacheFilter) > 0 { - return errors.Errorf("--no-cache and --no-cache-filter cannot currently be used together") +func runBuildContext(ctx context.Context, dockerCli command.Cli, in builderapi.BuildOptions, inStream io.Reader, progressMode string, allowNoOutput bool, statusChan chan *client.SolveStatus) (res *build.ResultContext, err error) { + if in.Opts.NoCache && len(in.NoCacheFilter) > 0 { + return nil, errors.Errorf("--no-cache and --no-cache-filter cannot currently be used together") } - if in.quiet && in.progress != "auto" && in.progress != "quiet" { - return errors.Errorf("progress=%s and quiet cannot be used together", in.progress) - } else if in.quiet { - in.progress = "quiet" + if in.Quiet && progressMode != "auto" && progressMode != "quiet" { + return nil, errors.Errorf("progress=%s and quiet cannot be used together", progressMode) + } else if in.Quiet { + progressMode = "quiet" } - contexts, err := parseContextNames(in.contexts) + contexts, err := parseContextNames(in.Contexts) if err != nil { - return err + return nil, err } - printFunc, err := parsePrintFunc(in.printFunc) + printFunc, err := parsePrintFunc(in.PrintFunc) if err != nil { - return err + return nil, err } opts := build.Options{ Inputs: build.Inputs{ - ContextPath: in.contextPath, - DockerfilePath: in.dockerfileName, - InStream: os.Stdin, + ContextPath: in.ContextPath, + DockerfilePath: in.DockerfileName, + InStream: inStream, NamedContexts: contexts, }, - BuildArgs: listToMap(in.buildArgs, true), - ExtraHosts: in.extraHosts, - ImageIDFile: in.imageIDFile, - Labels: listToMap(in.labels, false), - NetworkMode: in.networkMode, - NoCache: noCache, - NoCacheFilter: in.noCacheFilter, - Pull: pull, - ShmSize: in.shmSize, - Tags: in.tags, - Target: in.target, - Ulimits: in.ulimits, + BuildArgs: listToMap(in.BuildArgs, true), + ExtraHosts: in.ExtraHosts, + ImageIDFile: in.ImageIDFile, + Labels: listToMap(in.Labels, false), + NetworkMode: in.NetworkMode, + NoCache: in.Opts.NoCache, + NoCacheFilter: in.NoCacheFilter, + Pull: in.Opts.Pull, + ShmSize: dockeropts.MemBytes(in.ShmSize), + Tags: in.Tags, + Target: in.Target, + Ulimits: builderUlimitOpt2DockerUlimit(in.Ulimits), PrintFunc: printFunc, } - platforms, err := platformutil.Parse(in.platforms) + platforms, err := platformutil.Parse(in.Platforms) if err != nil { - return err + return nil, err } opts.Platforms = platforms dockerConfig := config.LoadDefaultConfigFile(os.Stderr) opts.Session = append(opts.Session, authprovider.NewDockerAuthProvider(dockerConfig)) - secrets, err := buildflags.ParseSecretSpecs(in.secrets) + secrets, err := buildflags.ParseSecretSpecs(in.Secrets) if err != nil { - return err + return nil, err } opts.Session = append(opts.Session, secrets) - sshSpecs := in.ssh - if len(sshSpecs) == 0 && buildflags.IsGitSSH(in.contextPath) { + sshSpecs := in.SSH + if len(sshSpecs) == 0 && buildflags.IsGitSSH(in.ContextPath) { sshSpecs = []string{"default"} } ssh, err := buildflags.ParseSSHSpecs(sshSpecs) if err != nil { - return err + return nil, err } opts.Session = append(opts.Session, ssh) - outputs, err := buildflags.ParseOutputs(in.outputs) + outputs, err := buildflags.ParseOutputs(in.Outputs) if err != nil { - return err + return nil, err } - if in.exportPush { - if in.exportLoad { - return errors.Errorf("push and load may not be set together at the moment") + if in.Opts.ExportPush { + if in.Opts.ExportLoad { + return nil, errors.Errorf("push and load may not be set together at the moment") } if len(outputs) == 0 { outputs = []client.ExportEntry{{ @@ -192,11 +160,11 @@ func runBuild(dockerCli command.Cli, in buildOptions) (err error) { case "image": outputs[0].Attrs["push"] = "true" default: - return errors.Errorf("push and %q output can't be used together", outputs[0].Type) + return nil, errors.Errorf("push and %q output can't be used together", outputs[0].Type) } } } - if in.exportLoad { + if in.Opts.ExportLoad { if len(outputs) == 0 { outputs = []client.ExportEntry{{ Type: "docker", @@ -206,76 +174,50 @@ func runBuild(dockerCli command.Cli, in buildOptions) (err error) { switch outputs[0].Type { case "docker": default: - return errors.Errorf("load and %q output can't be used together", outputs[0].Type) + return nil, errors.Errorf("load and %q output can't be used together", outputs[0].Type) } } } opts.Exports = outputs - cacheImports, err := buildflags.ParseCacheEntry(in.cacheFrom) + cacheImports, err := buildflags.ParseCacheEntry(in.CacheFrom) if err != nil { - return err + return nil, err } opts.CacheFrom = cacheImports - cacheExports, err := buildflags.ParseCacheEntry(in.cacheTo) + cacheExports, err := buildflags.ParseCacheEntry(in.CacheTo) if err != nil { - return err + return nil, err } opts.CacheTo = cacheExports - allow, err := buildflags.ParseEntitlements(in.allow) + allow, err := buildflags.ParseEntitlements(in.Allow) if err != nil { - return err + return nil, err } opts.Allow = allow // key string used for kubernetes "sticky" mode - contextPathHash, err := filepath.Abs(in.contextPath) + contextPathHash, err := filepath.Abs(in.ContextPath) if err != nil { - contextPathHash = in.contextPath + contextPathHash = in.ContextPath } - imageID, res, err := buildTargets(ctx, dockerCli, map[string]build.Options{defaultTargetName: opts}, in.progress, contextPathHash, in.builder, in.metadataFile, in.invoke != "") + imageID, res, err := buildTargets(ctx, dockerCli, map[string]build.Options{defaultTargetName: opts}, progressMode, contextPathHash, in.Opts.Builder, in.Opts.MetadataFile, allowNoOutput, statusChan) err = wrapBuildError(err, false) if err != nil { - return err - } - - if in.invoke != "" { - cfg, err := parseInvokeConfig(in.invoke) - if err != nil { - return err - } - cfg.ResultCtx = res - con := console.Current() - if err := con.SetRaw(); err != nil { - return errors.Errorf("failed to configure terminal: %v", err) - } - err = monitor.RunMonitor(ctx, cfg, func(ctx context.Context) (*build.ResultContext, error) { - _, rr, err := buildTargets(ctx, dockerCli, map[string]build.Options{defaultTargetName: opts}, in.progress, contextPathHash, in.builder, in.metadataFile, true) - return rr, err - }, io.NopCloser(os.Stdin), nopCloser{os.Stdout}, nopCloser{os.Stderr}) - if err != nil { - logrus.Warnf("failed to run monitor: %v", err) - } - con.Reset() + return nil, err } - if in.quiet { + if in.Quiet { fmt.Println(imageID) } - return nil -} - -type nopCloser struct { - io.WriteCloser + return res, nil } -func (c nopCloser) Close() error { return nil } - -func buildTargets(ctx context.Context, dockerCli command.Cli, opts map[string]build.Options, progressMode, contextPathHash, instance string, metadataFile string, allowNoOutput bool) (imageID string, res *build.ResultContext, err error) { +func buildTargets(ctx context.Context, dockerCli command.Cli, opts map[string]build.Options, progressMode, contextPathHash, instance string, metadataFile string, allowNoOutput bool, statusChan chan *client.SolveStatus) (imageID string, res *build.ResultContext, err error) { dis, err := getInstanceOrDefault(ctx, dockerCli, instance, contextPathHash) if err != nil { return "", nil, err @@ -288,7 +230,7 @@ func buildTargets(ctx context.Context, dockerCli command.Cli, opts map[string]bu var mu sync.Mutex var idx int - resp, err := build.BuildWithResultHandler(ctx, dis, opts, dockerAPI(dockerCli), confutil.ConfigDir(dockerCli), printer, func(driverIndex int, gotRes *build.ResultContext) { + resp, err := build.BuildWithResultHandler(ctx, dis, opts, dockerAPI(dockerCli), confutil.ConfigDir(dockerCli), progress.Tee(printer, statusChan), func(driverIndex int, gotRes *build.ResultContext) { mu.Lock() defer mu.Unlock() if res == nil || driverIndex < idx { @@ -322,51 +264,6 @@ func buildTargets(ctx context.Context, dockerCli command.Cli, opts map[string]bu return resp[defaultTargetName].ExporterResponse["containerimage.digest"], res, err } -func parseInvokeConfig(invoke string) (cfg build.ContainerConfig, err error) { - cfg.Tty = true - if invoke == "default" { - return cfg, nil - } - - csvReader := csv.NewReader(strings.NewReader(invoke)) - fields, err := csvReader.Read() - if err != nil { - return cfg, err - } - if len(fields) == 1 && !strings.Contains(fields[0], "=") { - cfg.Cmd = []string{fields[0]} - return cfg, nil - } - for _, field := range fields { - parts := strings.SplitN(field, "=", 2) - if len(parts) != 2 { - return cfg, errors.Errorf("invalid value %s", field) - } - key := strings.ToLower(parts[0]) - value := parts[1] - switch key { - case "args": - cfg.Cmd = append(cfg.Cmd, value) // TODO: support JSON - case "entrypoint": - cfg.Entrypoint = append(cfg.Entrypoint, value) // TODO: support JSON - case "env": - cfg.Env = append(cfg.Env, value) - case "user": - cfg.User = &value - case "cwd": - cfg.Cwd = &value - case "tty": - cfg.Tty, err = strconv.ParseBool(value) - if err != nil { - return cfg, errors.Errorf("failed to parse tty: %v", err) - } - default: - return cfg, errors.Errorf("unknown key %q", key) - } - } - return cfg, nil -} - func printWarnings(w io.Writer, warnings []client.VertexWarning, mode string) { if len(warnings) == 0 || mode == progress.PrinterModeQuiet { return @@ -407,15 +304,9 @@ func printWarnings(w io.Writer, warnings []client.VertexWarning, mode string) { } } -func newBuildOptions() buildOptions { - ulimits := make(map[string]*units.Ulimit) - return buildOptions{ - ulimits: dockeropts.NewUlimitOpt(&ulimits), - } -} - func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { options := newBuildOptions() + cFlags := &commonFlags{} cmd := &cobra.Command{ Use: "build [OPTIONS] PATH | URL | -", @@ -423,9 +314,22 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { Short: "Start a build", Args: cli.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - options.contextPath = args[0] - options.builder = rootOpts.builder + options.ContextPath = args[0] + options.Opts.Builder = rootOpts.builder + options.Opts.MetadataFile = cFlags.metadataFile + options.Opts.NoCache = false + if cFlags.noCache != nil { + options.Opts.NoCache = *cFlags.noCache + } + options.Opts.Pull = false + if cFlags.pull != nil { + options.Opts.Pull = *cFlags.pull + } + options.progress = cFlags.progress cmd.Flags().VisitAll(checkWarnedFlags) + if isExperimental() { + return runBuildOnBuilder(dockerCli, options) + } return runBuild(dockerCli, options) }, } @@ -437,63 +341,66 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { flags := cmd.Flags() - flags.StringSliceVar(&options.extraHosts, "add-host", []string{}, `Add a custom host-to-IP mapping (format: "host:ip")`) + flags.StringSliceVar(&options.ExtraHosts, "add-host", []string{}, `Add a custom host-to-IP mapping (format: "host:ip")`) flags.SetAnnotation("add-host", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#add-entries-to-container-hosts-file---add-host"}) - flags.StringSliceVar(&options.allow, "allow", []string{}, `Allow extra privileged entitlement (e.g., "network.host", "security.insecure")`) + flags.StringSliceVar(&options.Allow, "allow", []string{}, `Allow extra privileged entitlement (e.g., "network.host", "security.insecure")`) - flags.StringArrayVar(&options.buildArgs, "build-arg", []string{}, "Set build-time variables") + flags.StringArrayVar(&options.BuildArgs, "build-arg", []string{}, "Set build-time variables") - flags.StringArrayVar(&options.cacheFrom, "cache-from", []string{}, `External cache sources (e.g., "user/app:cache", "type=local,src=path/to/dir")`) + flags.StringArrayVar(&options.CacheFrom, "cache-from", []string{}, `External cache sources (e.g., "user/app:cache", "type=local,src=path/to/dir")`) - flags.StringArrayVar(&options.cacheTo, "cache-to", []string{}, `Cache export destinations (e.g., "user/app:cache", "type=local,dest=path/to/dir")`) + flags.StringArrayVar(&options.CacheTo, "cache-to", []string{}, `Cache export destinations (e.g., "user/app:cache", "type=local,dest=path/to/dir")`) - flags.StringVar(&options.cgroupParent, "cgroup-parent", "", "Optional parent cgroup for the container") + flags.StringVar(&options.CgroupParent, "cgroup-parent", "", "Optional parent cgroup for the container") flags.SetAnnotation("cgroup-parent", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#use-a-custom-parent-cgroup---cgroup-parent"}) - flags.StringArrayVar(&options.contexts, "build-context", []string{}, "Additional build contexts (e.g., name=path)") + flags.StringArrayVar(&options.Contexts, "build-context", []string{}, "Additional build contexts (e.g., name=path)") - flags.StringVarP(&options.dockerfileName, "file", "f", "", `Name of the Dockerfile (default: "PATH/Dockerfile")`) + flags.StringVarP(&options.DockerfileName, "file", "f", "", `Name of the Dockerfile (default: "PATH/Dockerfile")`) flags.SetAnnotation("file", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#specify-a-dockerfile--f"}) - flags.StringVar(&options.imageIDFile, "iidfile", "", "Write the image ID to the file") + flags.StringVar(&options.ImageIDFile, "iidfile", "", "Write the image ID to the file") - flags.StringArrayVar(&options.labels, "label", []string{}, "Set metadata for an image") + flags.StringArrayVar(&options.Labels, "label", []string{}, "Set metadata for an image") - flags.BoolVar(&options.exportLoad, "load", false, `Shorthand for "--output=type=docker"`) + flags.BoolVar(&options.Opts.ExportLoad, "load", false, `Shorthand for "--output=type=docker"`) - flags.StringVar(&options.networkMode, "network", "default", `Set the networking mode for the "RUN" instructions during build`) + flags.StringVar(&options.NetworkMode, "network", "default", `Set the networking mode for the "RUN" instructions during build`) - flags.StringArrayVar(&options.noCacheFilter, "no-cache-filter", []string{}, "Do not cache specified stages") + flags.StringArrayVar(&options.NoCacheFilter, "no-cache-filter", []string{}, "Do not cache specified stages") - flags.StringArrayVarP(&options.outputs, "output", "o", []string{}, `Output destination (format: "type=local,dest=path")`) + flags.StringArrayVarP(&options.Outputs, "output", "o", []string{}, `Output destination (format: "type=local,dest=path")`) - flags.StringArrayVar(&options.platforms, "platform", platformsDefault, "Set target platform for build") + flags.StringArrayVar(&options.Platforms, "platform", platformsDefault, "Set target platform for build") if isExperimental() { - flags.StringVar(&options.printFunc, "print", "", "Print result of information request (e.g., outline, targets) [experimental]") + flags.StringVar(&options.PrintFunc, "print", "", "Print result of information request (e.g., outline, targets) [experimental]") } - flags.BoolVar(&options.exportPush, "push", false, `Shorthand for "--output=type=registry"`) + flags.BoolVar(&options.Opts.ExportPush, "push", false, `Shorthand for "--output=type=registry"`) - flags.BoolVarP(&options.quiet, "quiet", "q", false, "Suppress the build output and print image ID on success") + flags.BoolVarP(&options.Quiet, "quiet", "q", false, "Suppress the build output and print image ID on success") - flags.StringArrayVar(&options.secrets, "secret", []string{}, `Secret to expose to the build (format: "id=mysecret[,src=/local/secret]")`) + flags.StringArrayVar(&options.Secrets, "secret", []string{}, `Secret to expose to the build (format: "id=mysecret[,src=/local/secret]")`) - flags.Var(&options.shmSize, "shm-size", `Size of "/dev/shm"`) + flags.Var(newShmSize(&options), "shm-size", `Size of "/dev/shm"`) - flags.StringArrayVar(&options.ssh, "ssh", []string{}, `SSH agent socket or keys to expose to the build (format: "default|[=|[,]]")`) + flags.StringArrayVar(&options.SSH, "ssh", []string{}, `SSH agent socket or keys to expose to the build (format: "default|[=|[,]]")`) - flags.StringArrayVarP(&options.tags, "tag", "t", []string{}, `Name and optionally a tag (format: "name:tag")`) + flags.StringArrayVarP(&options.Tags, "tag", "t", []string{}, `Name and optionally a tag (format: "name:tag")`) flags.SetAnnotation("tag", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#tag-an-image--t"}) - flags.StringVar(&options.target, "target", "", "Set the target build stage to build") + flags.StringVar(&options.Target, "target", "", "Set the target build stage to build") flags.SetAnnotation("target", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#specifying-target-build-stage---target"}) - flags.Var(options.ulimits, "ulimit", "Ulimit options") + flags.Var(newUlimits(&options), "ulimit", "Ulimit options") if isExperimental() { flags.StringVar(&options.invoke, "invoke", "", "Invoke a command after the build [experimental]") + flags.StringVar(&options.root, "root", "", "Specify root directory of server to connect [experimental]") + flags.BoolVar(&options.detach, "detach", runtime.GOOS == "linux", "Detach buildx server (supported only on linux) [experimental]") + flags.StringVar(&options.serverConfig, "server-config", "", "Specify buildx server config file (used only when launching new server) [experimental]") } // hidden flags @@ -544,11 +451,19 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { flags.BoolVar(&ignoreBool, "force-rm", false, "Always remove intermediate containers") flags.MarkHidden("force-rm") - commonBuildFlags(&options.commonOptions, flags) + commonBuildFlags(cFlags, flags) return cmd } -func commonBuildFlags(options *commonOptions, flags *pflag.FlagSet) { +// comomnFlags is a set of flags commonly shared among subcommands. +type commonFlags struct { + metadataFile string + progress string + noCache *bool + pull *bool +} + +func commonBuildFlags(options *commonFlags, flags *pflag.FlagSet) { options.noCache = flags.Bool("no-cache", false, "Do not use cache when building the image") flags.StringVar(&options.progress, "progress", "auto", `Set type of progress output ("auto", "plain", "tty"). Use plain to show container output`) options.pull = flags.Bool("pull", false, "Always attempt to pull all referenced images") @@ -699,3 +614,235 @@ func isExperimental() bool { } return false } + +func runBuildOnBuilder(dockerCli command.Cli, options buildOptions) error { + ctx := context.TODO() + + if options.Quiet && options.progress != "auto" && options.progress != "quiet" { + return errors.Errorf("progress=%s and quiet cannot be used together", options.progress) + } else if options.Quiet { + options.progress = "quiet" + } + if options.invoke != "" && (options.DockerfileName == "-" || options.ContextPath == "-") { + // stdin must be usable for monitor + return errors.Errorf("Dockerfile or context from stdin is not supported with invoke") + } + var invokeConfig builderapi.ContainerConfig + if inv := options.invoke; inv != "" { + var err error + invokeConfig, err = parseInvokeConfig(inv) // TODO: produce *builder.ContainerConfig directly. + if err != nil { + return err + } + + } + + var c monitor.BuildxController + var err error + if options.detach { + logrus.Infof("connecting to buildx server") + c, err = newRemoteBuildxController(ctx, options) + if err != nil { + return fmt.Errorf("failed to use buildx server; use --detach=false: %w", err) + } + } else { + logrus.Infof("launching local buildx builder") + c = newLocalBuildxController(ctx, dockerCli) + } + defer func() { + if err := c.Close(); err != nil { + logrus.Warnf("failed to close server connection %v", err) + } + }() + + f := ioset.NewSingleForwarder() + pr, pw := io.Pipe() + f.SetWriter(pw, func() io.WriteCloser { + pw.Close() // propagate EOF + logrus.Debug("propagating stdin close") + return nil + }) + f.SetReader(os.Stdin) + + // Start build + ref, err := c.Build(ctx, options.BuildOptions, pr, os.Stdout, os.Stderr, options.progress) + if err != nil { + return fmt.Errorf("failed to build: %w", err) // TODO: allow invoke even on error + } + if err := pw.Close(); err != nil { + logrus.Debug("failed to close stdin pipe writer") + } + if err := pr.Close(); err != nil { + logrus.Debug("failed to close stdin pipe reader") + } + + // post-build operations + if options.invoke != "" { + pr2, pw2 := io.Pipe() + f.SetWriter(pw2, func() io.WriteCloser { + pw2.Close() // propagate EOF + return nil + }) + con := console.Current() + if err := con.SetRaw(); err != nil { + if err := c.Disconnect(ctx, ref); err != nil { + logrus.Warnf("disconnect error: %v", err) + } + return errors.Errorf("failed to configure terminal: %v", err) + } + err = monitor.RunMonitor(ctx, ref, options.BuildOptions, invokeConfig, c, options.progress, pr2, os.Stdout, os.Stderr) + con.Reset() + if err := pw2.Close(); err != nil { + logrus.Debug("failed to close monitor stdin pipe reader") + } + if err != nil { + logrus.Warnf("failed to run monitor: %v", err) + } + } else { + if err := c.Disconnect(ctx, ref); err != nil { + logrus.Warnf("disconnect error: %v", err) + } + // If "invoke" isn't specified, further inspection ins't provided. Finish the buildx server. + if err := c.Kill(ctx); err != nil { + return err + } + } + return nil +} + +func parseInvokeConfig(invoke string) (cfg builderapi.ContainerConfig, err error) { + cfg.Tty = true + if invoke == "default" { + return cfg, nil + } + + csvReader := csv.NewReader(strings.NewReader(invoke)) + fields, err := csvReader.Read() + if err != nil { + return cfg, err + } + if len(fields) == 1 && !strings.Contains(fields[0], "=") { + cfg.Cmd = []string{fields[0]} + return cfg, nil + } + cfg.NoUser = true + cfg.NoCwd = true + for _, field := range fields { + parts := strings.SplitN(field, "=", 2) + if len(parts) != 2 { + return cfg, errors.Errorf("invalid value %s", field) + } + key := strings.ToLower(parts[0]) + value := parts[1] + switch key { + case "args": + cfg.Cmd = append(cfg.Cmd, value) // TODO: support JSON + case "entrypoint": + cfg.Entrypoint = append(cfg.Entrypoint, value) // TODO: support JSON + case "env": + cfg.Env = append(cfg.Env, value) + case "user": + cfg.User = value + cfg.NoUser = false + case "cwd": + cfg.Cwd = value + cfg.NoCwd = false + case "tty": + cfg.Tty, err = strconv.ParseBool(value) + if err != nil { + return cfg, errors.Errorf("failed to parse tty: %v", err) + } + default: + return cfg, errors.Errorf("unknown key %q", key) + } + } + return cfg, nil +} + +func builderUlimitOpt2DockerUlimit(u *builderapi.UlimitOpt) *dockeropts.UlimitOpt { + if u == nil { + return nil + } + values := make(map[string]*units.Ulimit) + for k, v := range u.Values { + values[k] = &units.Ulimit{ + Name: v.Name, + Hard: v.Hard, + Soft: v.Soft, + } + } + return dockeropts.NewUlimitOpt(&values) +} + +func newBuildOptions() buildOptions { + return buildOptions{ + BuildOptions: builderapi.BuildOptions{ + Opts: &builderapi.CommonOptions{}, + }, + } +} + +func newUlimits(opt *buildOptions) *ulimits { + ul := make(map[string]*units.Ulimit) + return &ulimits{opt: opt, org: dockeropts.NewUlimitOpt(&ul)} +} + +type ulimits struct { + opt *buildOptions + org *dockeropts.UlimitOpt +} + +func (u *ulimits) sync() { + du := &builderapi.UlimitOpt{ + Values: make(map[string]*builderapi.Ulimit), + } + for _, l := range u.org.GetList() { + du.Values[l.Name] = &builderapi.Ulimit{ + Name: l.Name, + Hard: l.Hard, + Soft: l.Soft, + } + } + u.opt.Ulimits = du +} + +func (u *ulimits) String() string { + return u.org.String() +} + +func (u *ulimits) Set(v string) error { + err := u.org.Set(v) + u.sync() + return err +} + +func (u *ulimits) Type() string { + return u.org.Type() +} + +func newShmSize(opt *buildOptions) *shmSize { + return &shmSize{opt: opt} +} + +type shmSize struct { + opt *buildOptions + org dockeropts.MemBytes +} + +func (s *shmSize) sync() { + s.opt.ShmSize = s.org.Value() +} + +func (s *shmSize) String() string { + return s.org.String() +} + +func (s *shmSize) Set(v string) error { + err := s.org.Set(v) + s.sync() + return err +} + +func (s *shmSize) Type() string { + return s.org.Type() +} diff --git a/commands/builder/builder.go b/commands/builder/builder.go new file mode 100644 index 000000000000..b7459d96c29b --- /dev/null +++ b/commands/builder/builder.go @@ -0,0 +1,440 @@ +package builder + +import ( + "context" + "errors" + "fmt" + "io" + "sync" + "time" + + "github.com/docker/buildx/build" + "github.com/docker/buildx/commands/builder/pb" + "github.com/docker/buildx/util/ioset" + "github.com/docker/buildx/version" + controlapi "github.com/moby/buildkit/api/services/control" + "github.com/moby/buildkit/client" + "github.com/sirupsen/logrus" + "golang.org/x/sync/errgroup" +) + +type BuildFunc func(ctx context.Context, options *pb.BuildOptions, stdin io.Reader, statusChan chan *client.SolveStatus) (res *build.ResultContext, err error) + +func New(buildFunc BuildFunc) *Builder { + return &Builder{ + buildFunc: buildFunc, + } +} + +type Builder struct { + buildFunc BuildFunc + session map[string]session + sessionMu sync.Mutex +} + +type session struct { + statusChan chan *client.SolveStatus + result *build.ResultContext + inputPipe *io.PipeWriter + curInvokeCancel func() + curBuildCancel func() +} + +func (m *Builder) Info(ctx context.Context, req *pb.InfoRequest) (res *pb.InfoResponse, err error) { + return &pb.InfoResponse{ + BuildxVersion: &pb.BuildxVersion{ + Package: version.Package, + Version: version.Version, + Revision: version.Revision, + }, + }, nil +} + +func (m *Builder) List(ctx context.Context, req *pb.ListRequest) (res *pb.ListResponse, err error) { + keys := make(map[string]struct{}) + + m.sessionMu.Lock() + for k := range m.session { + keys[k] = struct{}{} + } + m.sessionMu.Unlock() + + var keysL []string + for k := range keys { + keysL = append(keysL, k) + } + return &pb.ListResponse{ + Keys: keysL, + }, nil +} + +func (m *Builder) Disconnect(ctx context.Context, req *pb.DisconnectRequest) (res *pb.DisconnectResponse, err error) { + key := req.Ref + if key == "" { + return nil, fmt.Errorf("disconnect: empty key") + } + + m.sessionMu.Lock() + if s, ok := m.session[key]; ok { + if s.curBuildCancel != nil { + s.curBuildCancel() + } + if s.curInvokeCancel != nil { + s.curInvokeCancel() + } + } + delete(m.session, key) + m.sessionMu.Unlock() + + return &pb.DisconnectResponse{}, nil +} + +func (m *Builder) Close() error { + m.sessionMu.Lock() + for k := range m.session { + if s, ok := m.session[k]; ok { + if s.curBuildCancel != nil { + s.curBuildCancel() + } + if s.curInvokeCancel != nil { + s.curInvokeCancel() + } + } + } + m.sessionMu.Unlock() + return nil +} + +func (m *Builder) Build(ctx context.Context, req *pb.BuildRequest) (*pb.BuildResponse, error) { + ref := req.Ref + if ref == "" { + return nil, fmt.Errorf("build: empty key") + } + + // Prepare status channel and session if not exists + m.sessionMu.Lock() + if m.session == nil { + m.session = make(map[string]session) + } + s, ok := m.session[ref] + if ok && m.session[ref].statusChan != nil { + m.sessionMu.Unlock() + return &pb.BuildResponse{}, fmt.Errorf("build or status ongoing or status didn't called") + } + statusChan := make(chan *client.SolveStatus) + s.statusChan = statusChan + m.session[ref] = session{statusChan: statusChan} + m.sessionMu.Unlock() + defer func() { + close(statusChan) + m.sessionMu.Lock() + s, ok := m.session[ref] + if ok { + s.statusChan = nil + } + m.sessionMu.Unlock() + }() + + // Prepare input stream pipe + inR, inW := io.Pipe() + m.sessionMu.Lock() + if s, ok := m.session[ref]; ok { + s.inputPipe = inW + m.session[ref] = s + } else { + m.sessionMu.Unlock() + return nil, fmt.Errorf("build: unknown key %v", ref) + } + m.sessionMu.Unlock() + defer inR.Close() + + // Build the specified request + ctx, cancel := context.WithCancel(ctx) + defer cancel() + res, err := m.buildFunc(ctx, req.Options, inR, statusChan) + m.sessionMu.Lock() + if s, ok := m.session[ref]; ok { + s.result = res + s.curBuildCancel = cancel + m.session[ref] = s + } else { + m.sessionMu.Unlock() + return nil, fmt.Errorf("build: unknown key %v", ref) + } + m.sessionMu.Unlock() + + return &pb.BuildResponse{}, err +} + +func (m *Builder) Status(req *pb.StatusRequest, stream pb.Builder_StatusServer) error { + ref := req.Ref + if ref == "" { + return fmt.Errorf("status: empty key") + } + + // Wait and get status channel prepared by Build() + var statusChan <-chan *client.SolveStatus + for { + // TODO: timeout? + m.sessionMu.Lock() + if _, ok := m.session[ref]; !ok || m.session[ref].statusChan == nil { + m.sessionMu.Unlock() + time.Sleep(time.Millisecond) // TODO: wait Build without busy loop and make it cancellable + continue + } + statusChan = m.session[ref].statusChan + m.sessionMu.Unlock() + break + } + + // forward status + for ss := range statusChan { + if ss == nil { + break + } + cs := toControlStatus(ss) + if err := stream.Send(cs); err != nil { + return err + } + } + + return nil +} + +func (m *Builder) Input(stream pb.Builder_InputServer) (err error) { + // Get the target ref from init message + msg, err := stream.Recv() + if err != nil { + if !errors.Is(err, io.EOF) { + return err + } + return nil + } + init := msg.GetInit() + if init == nil { + return fmt.Errorf("unexpected message: %T; wanted init", msg.GetInit()) + } + ref := init.Ref + if ref == "" { + return fmt.Errorf("input: no ref is provided") + } + + // Wait and get input stream pipe prepared by Build() + var inputPipeW *io.PipeWriter + for { + // TODO: timeout? + m.sessionMu.Lock() + if _, ok := m.session[ref]; !ok || m.session[ref].inputPipe == nil { + m.sessionMu.Unlock() + time.Sleep(time.Millisecond) // TODO: wait Build without busy loop and make it cancellable + continue + } + inputPipeW = m.session[ref].inputPipe + m.sessionMu.Unlock() + break + } + + // Forward input stream + eg, ctx := errgroup.WithContext(context.TODO()) + done := make(chan struct{}) + msgCh := make(chan *pb.InputMessage) + eg.Go(func() error { + defer close(msgCh) + for { + msg, err := stream.Recv() + if err != nil { + if !errors.Is(err, io.EOF) { + return err + } + return nil + } + select { + case msgCh <- msg: + case <-done: + return nil + case <-ctx.Done(): + return nil + } + } + }) + eg.Go(func() (retErr error) { + defer close(done) + defer func() { + if retErr != nil { + inputPipeW.CloseWithError(retErr) + return + } + inputPipeW.Close() + }() + for { + var msg *pb.InputMessage + select { + case msg = <-msgCh: + case <-ctx.Done(): + return fmt.Errorf("canceled: %w", ctx.Err()) + } + if msg == nil { + return nil + } + if data := msg.GetData(); data != nil { + if len(data.Data) > 0 { + _, err := inputPipeW.Write(data.Data) + if err != nil { + return err + } + } + if data.EOF { + return nil + } + } + } + }) + + return eg.Wait() +} + +func (m *Builder) Invoke(srv pb.Builder_InvokeServer) error { + ctx, cancel := context.WithCancel(context.TODO()) + defer cancel() + containerIn, containerOut := ioset.Pipe() + waitInvokeDoneCh := make(chan struct{}) + var cancelOnce sync.Once + curInvokeCancel := func() { + cancelOnce.Do(func() { containerOut.Close(); containerIn.Close(); cancel() }) + <-waitInvokeDoneCh + } + defer curInvokeCancel() + + var cfg *pb.ContainerConfig + var resultCtx *build.ResultContext + initDoneCh := make(chan struct{}) + initErrCh := make(chan error) + eg, egCtx := errgroup.WithContext(ctx) + eg.Go(func() error { + return serveIO(egCtx, srv, func(initMessage *pb.InitMessage) (retErr error) { + defer func() { + if retErr != nil { + initErrCh <- retErr + } + close(initDoneCh) + }() + ref := initMessage.Ref + cfg = initMessage.ContainerConfig + + // Register cancel callback + m.sessionMu.Lock() + if s, ok := m.session[ref]; ok { + if cancel := s.curInvokeCancel; cancel != nil { + logrus.Warnf("invoke: cancelling ongoing invoke of %q", ref) + cancel() + } + s.curInvokeCancel = curInvokeCancel + m.session[ref] = s + } else { + m.sessionMu.Unlock() + return fmt.Errorf("invoke: unknown key %v", ref) + } + m.sessionMu.Unlock() + + // Get the target result to invoke a container from + m.sessionMu.Lock() + if _, ok := m.session[ref]; !ok || m.session[ref].result == nil { + m.sessionMu.Unlock() + return fmt.Errorf("unknown reference: %q", ref) + } + resultCtx = m.session[ref].result + m.sessionMu.Unlock() + return nil + }, &ioServerConfig{ + stdin: containerOut.Stdin, + stdout: containerOut.Stdout, + stderr: containerOut.Stderr, + // TODO: signal, resize + }) + }) + eg.Go(func() error { + defer containerIn.Close() + defer cancel() + select { + case <-initDoneCh: + case err := <-initErrCh: + return err + } + if cfg == nil { + return fmt.Errorf("no container config is provided") + } + if resultCtx == nil { + return fmt.Errorf("no result is provided") + } + ccfg := build.ContainerConfig{ + ResultCtx: resultCtx, + Entrypoint: cfg.Entrypoint, + Cmd: cfg.Cmd, + Env: cfg.Env, + Tty: cfg.Tty, + Stdin: containerIn.Stdin, + Stdout: containerIn.Stdout, + Stderr: containerIn.Stderr, + } + if !cfg.NoUser { + ccfg.User = &cfg.User + } + if !cfg.NoCwd { + ccfg.Cwd = &cfg.Cwd + } + return build.Invoke(egCtx, ccfg) + }) + err := eg.Wait() + close(waitInvokeDoneCh) + curInvokeCancel() + + return err +} + +func toControlStatus(s *client.SolveStatus) *pb.StatusResponse { + resp := pb.StatusResponse{} + for _, v := range s.Vertexes { + resp.Vertexes = append(resp.Vertexes, &controlapi.Vertex{ + Digest: v.Digest, + Inputs: v.Inputs, + Name: v.Name, + Started: v.Started, + Completed: v.Completed, + Error: v.Error, + Cached: v.Cached, + ProgressGroup: v.ProgressGroup, + }) + } + for _, v := range s.Statuses { + resp.Statuses = append(resp.Statuses, &controlapi.VertexStatus{ + ID: v.ID, + Vertex: v.Vertex, + Name: v.Name, + Total: v.Total, + Current: v.Current, + Timestamp: v.Timestamp, + Started: v.Started, + Completed: v.Completed, + }) + } + for _, v := range s.Logs { + resp.Logs = append(resp.Logs, &controlapi.VertexLog{ + Vertex: v.Vertex, + Stream: int64(v.Stream), + Msg: v.Data, + Timestamp: v.Timestamp, + }) + } + for _, v := range s.Warnings { + resp.Warnings = append(resp.Warnings, &controlapi.VertexWarning{ + Vertex: v.Vertex, + Level: int64(v.Level), + Short: v.Short, + Detail: v.Detail, + Url: v.URL, + Info: v.SourceInfo, + Ranges: v.Range, + }) + } + return &resp +} diff --git a/commands/builder/client.go b/commands/builder/client.go new file mode 100644 index 000000000000..5d30dda57ad1 --- /dev/null +++ b/commands/builder/client.go @@ -0,0 +1,259 @@ +package builder + +import ( + "context" + "fmt" + "io" + "sync" + "time" + + "github.com/containerd/console" + "github.com/containerd/containerd/defaults" + "github.com/containerd/containerd/pkg/dialer" + "github.com/docker/buildx/commands/builder/pb" + "github.com/docker/buildx/util/progress" + "github.com/moby/buildkit/client" + "github.com/moby/buildkit/identity" + "github.com/pkg/errors" + "golang.org/x/sync/errgroup" + "google.golang.org/grpc" + "google.golang.org/grpc/backoff" + "google.golang.org/grpc/credentials/insecure" +) + +func NewClient(addr string) (*Client, error) { + backoffConfig := backoff.DefaultConfig + backoffConfig.MaxDelay = 3 * time.Second + connParams := grpc.ConnectParams{ + Backoff: backoffConfig, + } + gopts := []grpc.DialOption{ + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithConnectParams(connParams), + grpc.WithContextDialer(dialer.ContextDialer), + grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize)), + grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize)), + } + conn, err := grpc.Dial(dialer.DialAddress(addr), gopts...) + if err != nil { + return nil, err + } + return &Client{conn: conn}, nil +} + +type Client struct { + conn *grpc.ClientConn + closeOnce sync.Once +} + +func (c *Client) Close() (err error) { + c.closeOnce.Do(func() { + err = c.conn.Close() + }) + return +} + +func (c *Client) Version(ctx context.Context) (string, string, string, error) { + res, err := c.client().Info(ctx, &pb.InfoRequest{}) + if err != nil { + return "", "", "", err + } + v := res.BuildxVersion + return v.Package, v.Version, v.Revision, nil +} + +func (c *Client) List(ctx context.Context) (keys []string, retErr error) { + res, err := c.client().List(ctx, &pb.ListRequest{}) + if err != nil { + return nil, err + } + return res.Keys, nil +} + +func (c *Client) Disconnect(ctx context.Context, key string) error { + _, err := c.client().Disconnect(ctx, &pb.DisconnectRequest{Ref: key}) + return err +} + +func (c *Client) Invoke(ctx context.Context, ref string, containerConfig pb.ContainerConfig, in io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error { + if ref == "" { + return fmt.Errorf("build reference must be specified") + } + stream, err := c.client().Invoke(ctx) + if err != nil { + return err + } + return attachIO(ctx, stream, &pb.InitMessage{Ref: ref, ContainerConfig: &containerConfig}, ioAttachConfig{ + stdin: in, + stdout: stdout, + stderr: stderr, + // TODO: Signal, Resize + }) +} + +func (c *Client) Build(ctx context.Context, options pb.BuildOptions, in io.ReadCloser, w io.Writer, out console.File, progressMode string) (string, error) { + ref := identity.NewID() + pw := progress.NewPrinter(context.TODO(), w, out, progressMode) + statusChan := make(chan *client.SolveStatus) + statusDone := make(chan struct{}) + eg, egCtx := errgroup.WithContext(ctx) + eg.Go(func() error { + defer close(statusChan) + return c.build(egCtx, ref, options, in, statusChan) + }) + eg.Go(func() error { + defer close(statusDone) + for s := range statusChan { + st := s + pw.Write(st) + } + return nil + }) + eg.Go(func() error { + <-statusDone + return pw.Wait() + }) + return ref, eg.Wait() +} + +func (c *Client) build(ctx context.Context, ref string, options pb.BuildOptions, in io.ReadCloser, statusChan chan *client.SolveStatus) error { + eg, egCtx := errgroup.WithContext(ctx) + done := make(chan struct{}) + eg.Go(func() error { + defer close(done) + if _, err := c.client().Build(egCtx, &pb.BuildRequest{ + Ref: ref, + Options: &options, + }); err != nil { + return err + } + return nil + }) + eg.Go(func() error { + stream, err := c.client().Status(egCtx, &pb.StatusRequest{ + Ref: ref, + }) + if err != nil { + return err + } + for { + resp, err := stream.Recv() + if err != nil { + if err == io.EOF { + return nil + } + return errors.Wrap(err, "failed to receive status") + } + s := client.SolveStatus{} + for _, v := range resp.Vertexes { + s.Vertexes = append(s.Vertexes, &client.Vertex{ + Digest: v.Digest, + Inputs: v.Inputs, + Name: v.Name, + Started: v.Started, + Completed: v.Completed, + Error: v.Error, + Cached: v.Cached, + ProgressGroup: v.ProgressGroup, + }) + } + for _, v := range resp.Statuses { + s.Statuses = append(s.Statuses, &client.VertexStatus{ + ID: v.ID, + Vertex: v.Vertex, + Name: v.Name, + Total: v.Total, + Current: v.Current, + Timestamp: v.Timestamp, + Started: v.Started, + Completed: v.Completed, + }) + } + for _, v := range resp.Logs { + s.Logs = append(s.Logs, &client.VertexLog{ + Vertex: v.Vertex, + Stream: int(v.Stream), + Data: v.Msg, + Timestamp: v.Timestamp, + }) + } + for _, v := range resp.Warnings { + s.Warnings = append(s.Warnings, &client.VertexWarning{ + Vertex: v.Vertex, + Level: int(v.Level), + Short: v.Short, + Detail: v.Detail, + URL: v.Url, + SourceInfo: v.Info, + Range: v.Ranges, + }) + } + statusChan <- &s + } + }) + if in != nil { + eg.Go(func() error { + stream, err := c.client().Input(egCtx) + if err != nil { + return err + } + if err := stream.Send(&pb.InputMessage{ + Input: &pb.InputMessage_Init{ + Init: &pb.InputInitMessage{ + Ref: ref, + }, + }, + }); err != nil { + return fmt.Errorf("failed to init input: %w", err) + } + + inReader, inWriter := io.Pipe() + eg2, _ := errgroup.WithContext(ctx) + eg2.Go(func() error { + <-done + return inWriter.Close() + }) + go func() { + // do not wait for read completion but return here and let the caller send EOF + // this allows us to return on ctx.Done() without being blocked by this reader. + io.Copy(inWriter, in) + inWriter.Close() + }() + eg2.Go(func() error { + for { + buf := make([]byte, 32*1024) + n, err := inReader.Read(buf) + if err != nil { + if err == io.EOF { + break // break loop and send EOF + } + return err + } else if n > 0 { + if stream.Send(&pb.InputMessage{ + Input: &pb.InputMessage_Data{ + Data: &pb.DataMessage{ + Data: buf[:n], + }, + }, + }); err != nil { + return err + } + } + } + return stream.Send(&pb.InputMessage{ + Input: &pb.InputMessage_Data{ + Data: &pb.DataMessage{ + EOF: true, + }, + }, + }) + }) + return eg2.Wait() + }) + } + return eg.Wait() +} + +func (c *Client) client() pb.BuilderClient { + return pb.NewBuilderClient(c.conn) +} diff --git a/commands/builder/io.go b/commands/builder/io.go new file mode 100644 index 000000000000..d9d2cbefccdb --- /dev/null +++ b/commands/builder/io.go @@ -0,0 +1,431 @@ +package builder + +import ( + "context" + "errors" + "fmt" + "io" + "syscall" + "time" + + "github.com/docker/buildx/commands/builder/pb" + "github.com/moby/sys/signal" + "github.com/sirupsen/logrus" + "golang.org/x/sync/errgroup" +) + +type msgStream interface { + Send(*pb.Message) error + Recv() (*pb.Message, error) +} + +type ioServerConfig struct { + stdin io.WriteCloser + stdout, stderr io.ReadCloser + + // signalFn is a callback function called when a signal is reached to the client. + signalFn func(context.Context, syscall.Signal) error + + // resizeFn is a callback function called when a resize event is reached to the client. + resizeFn func(context.Context, winSize) error +} + +func serveIO(attachCtx context.Context, srv msgStream, initFn func(*pb.InitMessage) error, ioConfig *ioServerConfig) (err error) { + stdin, stdout, stderr := ioConfig.stdin, ioConfig.stdout, ioConfig.stderr + stream := &debugStream{srv, "server=" + time.Now().String()} + eg, ctx := errgroup.WithContext(attachCtx) + done := make(chan struct{}) + + msg, err := receive(ctx, stream) + if err != nil { + return err + } + init := msg.GetInit() + if init == nil { + return fmt.Errorf("unexpected message: %T; wanted init", msg.GetInput()) + } + ref := init.Ref + if ref == "" { + return fmt.Errorf("no ref is provided") + } + if err := initFn(init); err != nil { + return fmt.Errorf("failed to initialize IO server: %w", err) + } + + if stdout != nil { + stdoutReader, stdoutWriter := io.Pipe() + eg.Go(func() error { + <-done + return stdoutWriter.Close() + }) + + go func() { + // do not wait for read completion but return here and let the caller send EOF + // this allows us to return on ctx.Done() without being blocked by this reader. + io.Copy(stdoutWriter, stdout) + stdoutWriter.Close() + }() + + eg.Go(func() error { + defer stdoutReader.Close() + return copyToStream(1, stream, stdoutReader) + }) + } + + if stderr != nil { + stderrReader, stderrWriter := io.Pipe() + eg.Go(func() error { + <-done + return stderrWriter.Close() + }) + + go func() { + // do not wait for read completion but return here and let the caller send EOF + // this allows us to return on ctx.Done() without being blocked by this reader. + io.Copy(stderrWriter, stderr) + stderrWriter.Close() + }() + + eg.Go(func() error { + defer stderrReader.Close() + return copyToStream(2, stream, stderrReader) + }) + } + + msgCh := make(chan *pb.Message) + eg.Go(func() error { + defer close(msgCh) + for { + msg, err := receive(ctx, stream) + if err != nil { + return err + } + select { + case msgCh <- msg: + case <-done: + return nil + case <-ctx.Done(): + return nil + } + } + }) + + eg.Go(func() error { + defer close(done) + for { + var msg *pb.Message + select { + case msg = <-msgCh: + case <-ctx.Done(): + return nil + } + if msg == nil { + return nil + } + if file := msg.GetFile(); file != nil { + if file.Fd != 0 { + return fmt.Errorf("unexpected fd: %v", file.Fd) + } + if stdin == nil { + continue // no stdin destination is specified so ignore the data + } + if len(file.Data) > 0 { + _, err := stdin.Write(file.Data) + if err != nil { + return err + } + } + if file.EOF { + stdin.Close() + } + } else if resize := msg.GetResize(); resize != nil { + if ioConfig.resizeFn != nil { + ioConfig.resizeFn(ctx, winSize{ + cols: resize.Cols, + rows: resize.Rows, + }) + } + } else if sig := msg.GetSignal(); sig != nil { + if ioConfig.signalFn != nil { + syscallSignal, ok := signal.SignalMap[sig.Name] + if !ok { + continue + } + ioConfig.signalFn(ctx, syscallSignal) + } + } else { + return fmt.Errorf("unexpected message: %T", msg.GetInput()) + } + } + }) + + return eg.Wait() +} + +type ioAttachConfig struct { + stdin io.ReadCloser + stdout, stderr io.WriteCloser + signal <-chan syscall.Signal + resize <-chan winSize +} + +type winSize struct { + rows uint32 + cols uint32 +} + +func attachIO(ctx context.Context, stream msgStream, initMessage *pb.InitMessage, cfg ioAttachConfig) (retErr error) { + eg, ctx := errgroup.WithContext(ctx) + done := make(chan struct{}) + + if err := stream.Send(&pb.Message{ + Input: &pb.Message_Init{ + Init: initMessage, + }, + }); err != nil { + return fmt.Errorf("failed to init: %w", err) + } + + if cfg.stdin != nil { + stdinReader, stdinWriter := io.Pipe() + eg.Go(func() error { + <-done + return stdinWriter.Close() + }) + + go func() { + // do not wait for read completion but return here and let the caller send EOF + // this allows us to return on ctx.Done() without being blocked by this reader. + io.Copy(stdinWriter, cfg.stdin) + stdinWriter.Close() + }() + + eg.Go(func() error { + defer stdinReader.Close() + return copyToStream(0, stream, stdinReader) + }) + } + + if cfg.signal != nil { + eg.Go(func() error { + for { + var sig syscall.Signal + select { + case sig = <-cfg.signal: + case <-done: + return nil + case <-ctx.Done(): + return nil + } + name := sigToName[sig] + if name == "" { + continue + } + if err := stream.Send(&pb.Message{ + Input: &pb.Message_Signal{ + Signal: &pb.SignalMessage{ + Name: name, + }, + }, + }); err != nil { + return fmt.Errorf("failed to send signal: %w", err) + } + } + }) + } + + if cfg.resize != nil { + eg.Go(func() error { + for { + var win winSize + select { + case win = <-cfg.resize: + case <-done: + return nil + case <-ctx.Done(): + return nil + } + if err := stream.Send(&pb.Message{ + Input: &pb.Message_Resize{ + Resize: &pb.ResizeMessage{ + Rows: win.rows, + Cols: win.cols, + }, + }, + }); err != nil { + return fmt.Errorf("failed to send resize: %w", err) + } + } + }) + } + + msgCh := make(chan *pb.Message) + eg.Go(func() error { + defer close(msgCh) + for { + msg, err := receive(ctx, stream) + if err != nil { + return err + } + select { + case msgCh <- msg: + case <-done: + return nil + case <-ctx.Done(): + return nil + } + } + }) + + eg.Go(func() error { + eofs := make(map[uint32]struct{}) + defer close(done) + for { + var msg *pb.Message + select { + case msg = <-msgCh: + case <-ctx.Done(): + return nil + } + if msg == nil { + return nil + } + if file := msg.GetFile(); file != nil { + if _, ok := eofs[file.Fd]; ok { + continue + } + var out io.WriteCloser + switch file.Fd { + case 1: + out = cfg.stdout + case 2: + out = cfg.stderr + default: + return fmt.Errorf("unsupported fd %d", file.Fd) + + } + if out == nil { + logrus.Warnf("attachIO: no writer for fd %d", file.Fd) + continue + } + if len(file.Data) > 0 { + if _, err := out.Write(file.Data); err != nil { + return err + } + } + if file.EOF { + eofs[file.Fd] = struct{}{} + } + } else { + return fmt.Errorf("unexpected message: %T", msg.GetInput()) + } + } + }) + + return eg.Wait() +} + +func receive(ctx context.Context, stream msgStream) (*pb.Message, error) { + msgCh := make(chan *pb.Message) + errCh := make(chan error) + go func() { + msg, err := stream.Recv() + if err != nil { + if errors.Is(err, io.EOF) { + return + } + errCh <- err + return + } + msgCh <- msg + }() + select { + case msg := <-msgCh: + return msg, nil + case err := <-errCh: + return nil, err + case <-ctx.Done(): + return nil, ctx.Err() + } +} + +func copyToStream(fd uint32, snd msgStream, r io.Reader) error { + for { + buf := make([]byte, 32*1024) + n, err := r.Read(buf) + if err != nil { + if err == io.EOF { + break // break loop and send EOF + } + return err + } else if n > 0 { + if snd.Send(&pb.Message{ + Input: &pb.Message_File{ + File: &pb.FdMessage{ + Fd: fd, + Data: buf[:n], + }, + }, + }); err != nil { + return err + } + } + } + return snd.Send(&pb.Message{ + Input: &pb.Message_File{ + File: &pb.FdMessage{ + Fd: fd, + EOF: true, + }, + }, + }) +} + +var sigToName = map[syscall.Signal]string{} + +func init() { + for name, value := range signal.SignalMap { + sigToName[value] = name + } +} + +type debugStream struct { + msgStream + prefix string +} + +func (s *debugStream) Send(msg *pb.Message) error { + switch m := msg.GetInput().(type) { + case *pb.Message_File: + if m.File.EOF { + logrus.Debugf("|---> File Message (sender:%v) fd=%d, EOF", s.prefix, m.File.Fd) + } else { + logrus.Debugf("|---> File Message (sender:%v) fd=%d, %d bytes", s.prefix, m.File.Fd, len(m.File.Data)) + } + case *pb.Message_Resize: + logrus.Debugf("|---> Resize Message (sender:%v): %+v", s.prefix, m.Resize) + case *pb.Message_Signal: + logrus.Debugf("|---> Signal Message (sender:%v): %s", s.prefix, m.Signal.Name) + } + return s.msgStream.Send(msg) +} + +func (s *debugStream) Recv() (*pb.Message, error) { + msg, err := s.msgStream.Recv() + if err != nil { + return nil, err + } + switch m := msg.GetInput().(type) { + case *pb.Message_File: + if m.File.EOF { + logrus.Debugf("|<--- File Message (receiver:%v) fd=%d, EOF", s.prefix, m.File.Fd) + } else { + logrus.Debugf("|<--- File Message (receiver:%v) fd=%d, %d bytes", s.prefix, m.File.Fd, len(m.File.Data)) + } + case *pb.Message_Resize: + logrus.Debugf("|<--- Resize Message (receiver:%v): %+v", s.prefix, m.Resize) + case *pb.Message_Signal: + logrus.Debugf("|<--- Signal Message (receiver:%v): %s", s.prefix, m.Signal.Name) + } + return msg, nil +} diff --git a/commands/builder/pb/builder.pb.go b/commands/builder/pb/builder.pb.go new file mode 100644 index 000000000000..73ce9fd8cbaf --- /dev/null +++ b/commands/builder/pb/builder.pb.go @@ -0,0 +1,1948 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: builder.proto + +package pb + +import ( + context "context" + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + control "github.com/moby/buildkit/api/services/control" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type BuildRequest struct { + Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` + Options *BuildOptions `protobuf:"bytes,2,opt,name=Options,proto3" json:"Options,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *BuildRequest) Reset() { *m = BuildRequest{} } +func (m *BuildRequest) String() string { return proto.CompactTextString(m) } +func (*BuildRequest) ProtoMessage() {} +func (*BuildRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{0} +} +func (m *BuildRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_BuildRequest.Unmarshal(m, b) +} +func (m *BuildRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_BuildRequest.Marshal(b, m, deterministic) +} +func (m *BuildRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_BuildRequest.Merge(m, src) +} +func (m *BuildRequest) XXX_Size() int { + return xxx_messageInfo_BuildRequest.Size(m) +} +func (m *BuildRequest) XXX_DiscardUnknown() { + xxx_messageInfo_BuildRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_BuildRequest proto.InternalMessageInfo + +func (m *BuildRequest) GetRef() string { + if m != nil { + return m.Ref + } + return "" +} + +func (m *BuildRequest) GetOptions() *BuildOptions { + if m != nil { + return m.Options + } + return nil +} + +type BuildOptions struct { + ContextPath string `protobuf:"bytes,1,opt,name=ContextPath,proto3" json:"ContextPath,omitempty"` + DockerfileName string `protobuf:"bytes,2,opt,name=DockerfileName,proto3" json:"DockerfileName,omitempty"` + PrintFunc string `protobuf:"bytes,3,opt,name=PrintFunc,proto3" json:"PrintFunc,omitempty"` + Allow []string `protobuf:"bytes,4,rep,name=Allow,proto3" json:"Allow,omitempty"` + BuildArgs []string `protobuf:"bytes,5,rep,name=BuildArgs,proto3" json:"BuildArgs,omitempty"` + CacheFrom []string `protobuf:"bytes,6,rep,name=CacheFrom,proto3" json:"CacheFrom,omitempty"` + CacheTo []string `protobuf:"bytes,7,rep,name=CacheTo,proto3" json:"CacheTo,omitempty"` + CgroupParent string `protobuf:"bytes,8,opt,name=CgroupParent,proto3" json:"CgroupParent,omitempty"` + Contexts []string `protobuf:"bytes,9,rep,name=Contexts,proto3" json:"Contexts,omitempty"` + ExtraHosts []string `protobuf:"bytes,10,rep,name=ExtraHosts,proto3" json:"ExtraHosts,omitempty"` + ImageIDFile string `protobuf:"bytes,11,opt,name=ImageIDFile,proto3" json:"ImageIDFile,omitempty"` + Labels []string `protobuf:"bytes,12,rep,name=Labels,proto3" json:"Labels,omitempty"` + NetworkMode string `protobuf:"bytes,13,opt,name=NetworkMode,proto3" json:"NetworkMode,omitempty"` + NoCacheFilter []string `protobuf:"bytes,14,rep,name=NoCacheFilter,proto3" json:"NoCacheFilter,omitempty"` + Outputs []string `protobuf:"bytes,15,rep,name=Outputs,proto3" json:"Outputs,omitempty"` + Platforms []string `protobuf:"bytes,16,rep,name=Platforms,proto3" json:"Platforms,omitempty"` + Quiet bool `protobuf:"varint,17,opt,name=Quiet,proto3" json:"Quiet,omitempty"` + Secrets []string `protobuf:"bytes,18,rep,name=Secrets,proto3" json:"Secrets,omitempty"` + ShmSize int64 `protobuf:"varint,19,opt,name=ShmSize,proto3" json:"ShmSize,omitempty"` + SSH []string `protobuf:"bytes,20,rep,name=SSH,proto3" json:"SSH,omitempty"` + Tags []string `protobuf:"bytes,21,rep,name=Tags,proto3" json:"Tags,omitempty"` + Target string `protobuf:"bytes,22,opt,name=Target,proto3" json:"Target,omitempty"` + Ulimits *UlimitOpt `protobuf:"bytes,23,opt,name=Ulimits,proto3" json:"Ulimits,omitempty"` + // string Invoke: provided via Invoke API + Opts *CommonOptions `protobuf:"bytes,24,opt,name=Opts,proto3" json:"Opts,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *BuildOptions) Reset() { *m = BuildOptions{} } +func (m *BuildOptions) String() string { return proto.CompactTextString(m) } +func (*BuildOptions) ProtoMessage() {} +func (*BuildOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{1} +} +func (m *BuildOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_BuildOptions.Unmarshal(m, b) +} +func (m *BuildOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_BuildOptions.Marshal(b, m, deterministic) +} +func (m *BuildOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_BuildOptions.Merge(m, src) +} +func (m *BuildOptions) XXX_Size() int { + return xxx_messageInfo_BuildOptions.Size(m) +} +func (m *BuildOptions) XXX_DiscardUnknown() { + xxx_messageInfo_BuildOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_BuildOptions proto.InternalMessageInfo + +func (m *BuildOptions) GetContextPath() string { + if m != nil { + return m.ContextPath + } + return "" +} + +func (m *BuildOptions) GetDockerfileName() string { + if m != nil { + return m.DockerfileName + } + return "" +} + +func (m *BuildOptions) GetPrintFunc() string { + if m != nil { + return m.PrintFunc + } + return "" +} + +func (m *BuildOptions) GetAllow() []string { + if m != nil { + return m.Allow + } + return nil +} + +func (m *BuildOptions) GetBuildArgs() []string { + if m != nil { + return m.BuildArgs + } + return nil +} + +func (m *BuildOptions) GetCacheFrom() []string { + if m != nil { + return m.CacheFrom + } + return nil +} + +func (m *BuildOptions) GetCacheTo() []string { + if m != nil { + return m.CacheTo + } + return nil +} + +func (m *BuildOptions) GetCgroupParent() string { + if m != nil { + return m.CgroupParent + } + return "" +} + +func (m *BuildOptions) GetContexts() []string { + if m != nil { + return m.Contexts + } + return nil +} + +func (m *BuildOptions) GetExtraHosts() []string { + if m != nil { + return m.ExtraHosts + } + return nil +} + +func (m *BuildOptions) GetImageIDFile() string { + if m != nil { + return m.ImageIDFile + } + return "" +} + +func (m *BuildOptions) GetLabels() []string { + if m != nil { + return m.Labels + } + return nil +} + +func (m *BuildOptions) GetNetworkMode() string { + if m != nil { + return m.NetworkMode + } + return "" +} + +func (m *BuildOptions) GetNoCacheFilter() []string { + if m != nil { + return m.NoCacheFilter + } + return nil +} + +func (m *BuildOptions) GetOutputs() []string { + if m != nil { + return m.Outputs + } + return nil +} + +func (m *BuildOptions) GetPlatforms() []string { + if m != nil { + return m.Platforms + } + return nil +} + +func (m *BuildOptions) GetQuiet() bool { + if m != nil { + return m.Quiet + } + return false +} + +func (m *BuildOptions) GetSecrets() []string { + if m != nil { + return m.Secrets + } + return nil +} + +func (m *BuildOptions) GetShmSize() int64 { + if m != nil { + return m.ShmSize + } + return 0 +} + +func (m *BuildOptions) GetSSH() []string { + if m != nil { + return m.SSH + } + return nil +} + +func (m *BuildOptions) GetTags() []string { + if m != nil { + return m.Tags + } + return nil +} + +func (m *BuildOptions) GetTarget() string { + if m != nil { + return m.Target + } + return "" +} + +func (m *BuildOptions) GetUlimits() *UlimitOpt { + if m != nil { + return m.Ulimits + } + return nil +} + +func (m *BuildOptions) GetOpts() *CommonOptions { + if m != nil { + return m.Opts + } + return nil +} + +type UlimitOpt struct { + Values map[string]*Ulimit `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UlimitOpt) Reset() { *m = UlimitOpt{} } +func (m *UlimitOpt) String() string { return proto.CompactTextString(m) } +func (*UlimitOpt) ProtoMessage() {} +func (*UlimitOpt) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{2} +} +func (m *UlimitOpt) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_UlimitOpt.Unmarshal(m, b) +} +func (m *UlimitOpt) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_UlimitOpt.Marshal(b, m, deterministic) +} +func (m *UlimitOpt) XXX_Merge(src proto.Message) { + xxx_messageInfo_UlimitOpt.Merge(m, src) +} +func (m *UlimitOpt) XXX_Size() int { + return xxx_messageInfo_UlimitOpt.Size(m) +} +func (m *UlimitOpt) XXX_DiscardUnknown() { + xxx_messageInfo_UlimitOpt.DiscardUnknown(m) +} + +var xxx_messageInfo_UlimitOpt proto.InternalMessageInfo + +func (m *UlimitOpt) GetValues() map[string]*Ulimit { + if m != nil { + return m.Values + } + return nil +} + +type Ulimit struct { + Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"` + Hard int64 `protobuf:"varint,2,opt,name=Hard,proto3" json:"Hard,omitempty"` + Soft int64 `protobuf:"varint,3,opt,name=Soft,proto3" json:"Soft,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Ulimit) Reset() { *m = Ulimit{} } +func (m *Ulimit) String() string { return proto.CompactTextString(m) } +func (*Ulimit) ProtoMessage() {} +func (*Ulimit) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{3} +} +func (m *Ulimit) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Ulimit.Unmarshal(m, b) +} +func (m *Ulimit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Ulimit.Marshal(b, m, deterministic) +} +func (m *Ulimit) XXX_Merge(src proto.Message) { + xxx_messageInfo_Ulimit.Merge(m, src) +} +func (m *Ulimit) XXX_Size() int { + return xxx_messageInfo_Ulimit.Size(m) +} +func (m *Ulimit) XXX_DiscardUnknown() { + xxx_messageInfo_Ulimit.DiscardUnknown(m) +} + +var xxx_messageInfo_Ulimit proto.InternalMessageInfo + +func (m *Ulimit) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Ulimit) GetHard() int64 { + if m != nil { + return m.Hard + } + return 0 +} + +func (m *Ulimit) GetSoft() int64 { + if m != nil { + return m.Soft + } + return 0 +} + +type CommonOptions struct { + Builder string `protobuf:"bytes,1,opt,name=Builder,proto3" json:"Builder,omitempty"` + MetadataFile string `protobuf:"bytes,2,opt,name=MetadataFile,proto3" json:"MetadataFile,omitempty"` + NoCache bool `protobuf:"varint,3,opt,name=NoCache,proto3" json:"NoCache,omitempty"` + // string Progress: no progress view on server side + Pull bool `protobuf:"varint,4,opt,name=Pull,proto3" json:"Pull,omitempty"` + ExportPush bool `protobuf:"varint,5,opt,name=ExportPush,proto3" json:"ExportPush,omitempty"` + ExportLoad bool `protobuf:"varint,6,opt,name=ExportLoad,proto3" json:"ExportLoad,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CommonOptions) Reset() { *m = CommonOptions{} } +func (m *CommonOptions) String() string { return proto.CompactTextString(m) } +func (*CommonOptions) ProtoMessage() {} +func (*CommonOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{4} +} +func (m *CommonOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CommonOptions.Unmarshal(m, b) +} +func (m *CommonOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CommonOptions.Marshal(b, m, deterministic) +} +func (m *CommonOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_CommonOptions.Merge(m, src) +} +func (m *CommonOptions) XXX_Size() int { + return xxx_messageInfo_CommonOptions.Size(m) +} +func (m *CommonOptions) XXX_DiscardUnknown() { + xxx_messageInfo_CommonOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_CommonOptions proto.InternalMessageInfo + +func (m *CommonOptions) GetBuilder() string { + if m != nil { + return m.Builder + } + return "" +} + +func (m *CommonOptions) GetMetadataFile() string { + if m != nil { + return m.MetadataFile + } + return "" +} + +func (m *CommonOptions) GetNoCache() bool { + if m != nil { + return m.NoCache + } + return false +} + +func (m *CommonOptions) GetPull() bool { + if m != nil { + return m.Pull + } + return false +} + +func (m *CommonOptions) GetExportPush() bool { + if m != nil { + return m.ExportPush + } + return false +} + +func (m *CommonOptions) GetExportLoad() bool { + if m != nil { + return m.ExportLoad + } + return false +} + +type BuildResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *BuildResponse) Reset() { *m = BuildResponse{} } +func (m *BuildResponse) String() string { return proto.CompactTextString(m) } +func (*BuildResponse) ProtoMessage() {} +func (*BuildResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{5} +} +func (m *BuildResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_BuildResponse.Unmarshal(m, b) +} +func (m *BuildResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_BuildResponse.Marshal(b, m, deterministic) +} +func (m *BuildResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_BuildResponse.Merge(m, src) +} +func (m *BuildResponse) XXX_Size() int { + return xxx_messageInfo_BuildResponse.Size(m) +} +func (m *BuildResponse) XXX_DiscardUnknown() { + xxx_messageInfo_BuildResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_BuildResponse proto.InternalMessageInfo + +type DisconnectRequest struct { + Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DisconnectRequest) Reset() { *m = DisconnectRequest{} } +func (m *DisconnectRequest) String() string { return proto.CompactTextString(m) } +func (*DisconnectRequest) ProtoMessage() {} +func (*DisconnectRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{6} +} +func (m *DisconnectRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DisconnectRequest.Unmarshal(m, b) +} +func (m *DisconnectRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DisconnectRequest.Marshal(b, m, deterministic) +} +func (m *DisconnectRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DisconnectRequest.Merge(m, src) +} +func (m *DisconnectRequest) XXX_Size() int { + return xxx_messageInfo_DisconnectRequest.Size(m) +} +func (m *DisconnectRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DisconnectRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_DisconnectRequest proto.InternalMessageInfo + +func (m *DisconnectRequest) GetRef() string { + if m != nil { + return m.Ref + } + return "" +} + +type DisconnectResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DisconnectResponse) Reset() { *m = DisconnectResponse{} } +func (m *DisconnectResponse) String() string { return proto.CompactTextString(m) } +func (*DisconnectResponse) ProtoMessage() {} +func (*DisconnectResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{7} +} +func (m *DisconnectResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DisconnectResponse.Unmarshal(m, b) +} +func (m *DisconnectResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DisconnectResponse.Marshal(b, m, deterministic) +} +func (m *DisconnectResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_DisconnectResponse.Merge(m, src) +} +func (m *DisconnectResponse) XXX_Size() int { + return xxx_messageInfo_DisconnectResponse.Size(m) +} +func (m *DisconnectResponse) XXX_DiscardUnknown() { + xxx_messageInfo_DisconnectResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_DisconnectResponse proto.InternalMessageInfo + +type ListRequest struct { + Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListRequest) Reset() { *m = ListRequest{} } +func (m *ListRequest) String() string { return proto.CompactTextString(m) } +func (*ListRequest) ProtoMessage() {} +func (*ListRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{8} +} +func (m *ListRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ListRequest.Unmarshal(m, b) +} +func (m *ListRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ListRequest.Marshal(b, m, deterministic) +} +func (m *ListRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListRequest.Merge(m, src) +} +func (m *ListRequest) XXX_Size() int { + return xxx_messageInfo_ListRequest.Size(m) +} +func (m *ListRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ListRequest proto.InternalMessageInfo + +func (m *ListRequest) GetRef() string { + if m != nil { + return m.Ref + } + return "" +} + +type ListResponse struct { + Keys []string `protobuf:"bytes,1,rep,name=keys,proto3" json:"keys,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListResponse) Reset() { *m = ListResponse{} } +func (m *ListResponse) String() string { return proto.CompactTextString(m) } +func (*ListResponse) ProtoMessage() {} +func (*ListResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{9} +} +func (m *ListResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ListResponse.Unmarshal(m, b) +} +func (m *ListResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ListResponse.Marshal(b, m, deterministic) +} +func (m *ListResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListResponse.Merge(m, src) +} +func (m *ListResponse) XXX_Size() int { + return xxx_messageInfo_ListResponse.Size(m) +} +func (m *ListResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ListResponse proto.InternalMessageInfo + +func (m *ListResponse) GetKeys() []string { + if m != nil { + return m.Keys + } + return nil +} + +type InputMessage struct { + // Types that are valid to be assigned to Input: + // *InputMessage_Init + // *InputMessage_Data + Input isInputMessage_Input `protobuf_oneof:"Input"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *InputMessage) Reset() { *m = InputMessage{} } +func (m *InputMessage) String() string { return proto.CompactTextString(m) } +func (*InputMessage) ProtoMessage() {} +func (*InputMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{10} +} +func (m *InputMessage) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_InputMessage.Unmarshal(m, b) +} +func (m *InputMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_InputMessage.Marshal(b, m, deterministic) +} +func (m *InputMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_InputMessage.Merge(m, src) +} +func (m *InputMessage) XXX_Size() int { + return xxx_messageInfo_InputMessage.Size(m) +} +func (m *InputMessage) XXX_DiscardUnknown() { + xxx_messageInfo_InputMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_InputMessage proto.InternalMessageInfo + +type isInputMessage_Input interface { + isInputMessage_Input() +} + +type InputMessage_Init struct { + Init *InputInitMessage `protobuf:"bytes,1,opt,name=Init,proto3,oneof" json:"Init,omitempty"` +} +type InputMessage_Data struct { + Data *DataMessage `protobuf:"bytes,2,opt,name=Data,proto3,oneof" json:"Data,omitempty"` +} + +func (*InputMessage_Init) isInputMessage_Input() {} +func (*InputMessage_Data) isInputMessage_Input() {} + +func (m *InputMessage) GetInput() isInputMessage_Input { + if m != nil { + return m.Input + } + return nil +} + +func (m *InputMessage) GetInit() *InputInitMessage { + if x, ok := m.GetInput().(*InputMessage_Init); ok { + return x.Init + } + return nil +} + +func (m *InputMessage) GetData() *DataMessage { + if x, ok := m.GetInput().(*InputMessage_Data); ok { + return x.Data + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*InputMessage) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*InputMessage_Init)(nil), + (*InputMessage_Data)(nil), + } +} + +type InputInitMessage struct { + Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *InputInitMessage) Reset() { *m = InputInitMessage{} } +func (m *InputInitMessage) String() string { return proto.CompactTextString(m) } +func (*InputInitMessage) ProtoMessage() {} +func (*InputInitMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{11} +} +func (m *InputInitMessage) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_InputInitMessage.Unmarshal(m, b) +} +func (m *InputInitMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_InputInitMessage.Marshal(b, m, deterministic) +} +func (m *InputInitMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_InputInitMessage.Merge(m, src) +} +func (m *InputInitMessage) XXX_Size() int { + return xxx_messageInfo_InputInitMessage.Size(m) +} +func (m *InputInitMessage) XXX_DiscardUnknown() { + xxx_messageInfo_InputInitMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_InputInitMessage proto.InternalMessageInfo + +func (m *InputInitMessage) GetRef() string { + if m != nil { + return m.Ref + } + return "" +} + +type DataMessage struct { + EOF bool `protobuf:"varint,1,opt,name=EOF,proto3" json:"EOF,omitempty"` + Data []byte `protobuf:"bytes,2,opt,name=Data,proto3" json:"Data,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DataMessage) Reset() { *m = DataMessage{} } +func (m *DataMessage) String() string { return proto.CompactTextString(m) } +func (*DataMessage) ProtoMessage() {} +func (*DataMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{12} +} +func (m *DataMessage) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DataMessage.Unmarshal(m, b) +} +func (m *DataMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DataMessage.Marshal(b, m, deterministic) +} +func (m *DataMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_DataMessage.Merge(m, src) +} +func (m *DataMessage) XXX_Size() int { + return xxx_messageInfo_DataMessage.Size(m) +} +func (m *DataMessage) XXX_DiscardUnknown() { + xxx_messageInfo_DataMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_DataMessage proto.InternalMessageInfo + +func (m *DataMessage) GetEOF() bool { + if m != nil { + return m.EOF + } + return false +} + +func (m *DataMessage) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +type InputResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *InputResponse) Reset() { *m = InputResponse{} } +func (m *InputResponse) String() string { return proto.CompactTextString(m) } +func (*InputResponse) ProtoMessage() {} +func (*InputResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{13} +} +func (m *InputResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_InputResponse.Unmarshal(m, b) +} +func (m *InputResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_InputResponse.Marshal(b, m, deterministic) +} +func (m *InputResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_InputResponse.Merge(m, src) +} +func (m *InputResponse) XXX_Size() int { + return xxx_messageInfo_InputResponse.Size(m) +} +func (m *InputResponse) XXX_DiscardUnknown() { + xxx_messageInfo_InputResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_InputResponse proto.InternalMessageInfo + +type Message struct { + // Types that are valid to be assigned to Input: + // *Message_Init + // *Message_File + // *Message_Resize + // *Message_Signal + Input isMessage_Input `protobuf_oneof:"Input"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Message) Reset() { *m = Message{} } +func (m *Message) String() string { return proto.CompactTextString(m) } +func (*Message) ProtoMessage() {} +func (*Message) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{14} +} +func (m *Message) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Message.Unmarshal(m, b) +} +func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Message.Marshal(b, m, deterministic) +} +func (m *Message) XXX_Merge(src proto.Message) { + xxx_messageInfo_Message.Merge(m, src) +} +func (m *Message) XXX_Size() int { + return xxx_messageInfo_Message.Size(m) +} +func (m *Message) XXX_DiscardUnknown() { + xxx_messageInfo_Message.DiscardUnknown(m) +} + +var xxx_messageInfo_Message proto.InternalMessageInfo + +type isMessage_Input interface { + isMessage_Input() +} + +type Message_Init struct { + Init *InitMessage `protobuf:"bytes,1,opt,name=Init,proto3,oneof" json:"Init,omitempty"` +} +type Message_File struct { + File *FdMessage `protobuf:"bytes,2,opt,name=File,proto3,oneof" json:"File,omitempty"` +} +type Message_Resize struct { + Resize *ResizeMessage `protobuf:"bytes,3,opt,name=Resize,proto3,oneof" json:"Resize,omitempty"` +} +type Message_Signal struct { + Signal *SignalMessage `protobuf:"bytes,4,opt,name=Signal,proto3,oneof" json:"Signal,omitempty"` +} + +func (*Message_Init) isMessage_Input() {} +func (*Message_File) isMessage_Input() {} +func (*Message_Resize) isMessage_Input() {} +func (*Message_Signal) isMessage_Input() {} + +func (m *Message) GetInput() isMessage_Input { + if m != nil { + return m.Input + } + return nil +} + +func (m *Message) GetInit() *InitMessage { + if x, ok := m.GetInput().(*Message_Init); ok { + return x.Init + } + return nil +} + +func (m *Message) GetFile() *FdMessage { + if x, ok := m.GetInput().(*Message_File); ok { + return x.File + } + return nil +} + +func (m *Message) GetResize() *ResizeMessage { + if x, ok := m.GetInput().(*Message_Resize); ok { + return x.Resize + } + return nil +} + +func (m *Message) GetSignal() *SignalMessage { + if x, ok := m.GetInput().(*Message_Signal); ok { + return x.Signal + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*Message) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*Message_Init)(nil), + (*Message_File)(nil), + (*Message_Resize)(nil), + (*Message_Signal)(nil), + } +} + +type InitMessage struct { + Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` + ContainerConfig *ContainerConfig `protobuf:"bytes,2,opt,name=ContainerConfig,proto3" json:"ContainerConfig,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *InitMessage) Reset() { *m = InitMessage{} } +func (m *InitMessage) String() string { return proto.CompactTextString(m) } +func (*InitMessage) ProtoMessage() {} +func (*InitMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{15} +} +func (m *InitMessage) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_InitMessage.Unmarshal(m, b) +} +func (m *InitMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_InitMessage.Marshal(b, m, deterministic) +} +func (m *InitMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_InitMessage.Merge(m, src) +} +func (m *InitMessage) XXX_Size() int { + return xxx_messageInfo_InitMessage.Size(m) +} +func (m *InitMessage) XXX_DiscardUnknown() { + xxx_messageInfo_InitMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_InitMessage proto.InternalMessageInfo + +func (m *InitMessage) GetRef() string { + if m != nil { + return m.Ref + } + return "" +} + +func (m *InitMessage) GetContainerConfig() *ContainerConfig { + if m != nil { + return m.ContainerConfig + } + return nil +} + +type ContainerConfig struct { + Entrypoint []string `protobuf:"bytes,1,rep,name=Entrypoint,proto3" json:"Entrypoint,omitempty"` + Cmd []string `protobuf:"bytes,2,rep,name=Cmd,proto3" json:"Cmd,omitempty"` + Env []string `protobuf:"bytes,3,rep,name=Env,proto3" json:"Env,omitempty"` + User string `protobuf:"bytes,4,opt,name=User,proto3" json:"User,omitempty"` + NoUser bool `protobuf:"varint,5,opt,name=NoUser,proto3" json:"NoUser,omitempty"` + Cwd string `protobuf:"bytes,6,opt,name=Cwd,proto3" json:"Cwd,omitempty"` + NoCwd bool `protobuf:"varint,7,opt,name=NoCwd,proto3" json:"NoCwd,omitempty"` + Tty bool `protobuf:"varint,8,opt,name=Tty,proto3" json:"Tty,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ContainerConfig) Reset() { *m = ContainerConfig{} } +func (m *ContainerConfig) String() string { return proto.CompactTextString(m) } +func (*ContainerConfig) ProtoMessage() {} +func (*ContainerConfig) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{16} +} +func (m *ContainerConfig) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ContainerConfig.Unmarshal(m, b) +} +func (m *ContainerConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ContainerConfig.Marshal(b, m, deterministic) +} +func (m *ContainerConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_ContainerConfig.Merge(m, src) +} +func (m *ContainerConfig) XXX_Size() int { + return xxx_messageInfo_ContainerConfig.Size(m) +} +func (m *ContainerConfig) XXX_DiscardUnknown() { + xxx_messageInfo_ContainerConfig.DiscardUnknown(m) +} + +var xxx_messageInfo_ContainerConfig proto.InternalMessageInfo + +func (m *ContainerConfig) GetEntrypoint() []string { + if m != nil { + return m.Entrypoint + } + return nil +} + +func (m *ContainerConfig) GetCmd() []string { + if m != nil { + return m.Cmd + } + return nil +} + +func (m *ContainerConfig) GetEnv() []string { + if m != nil { + return m.Env + } + return nil +} + +func (m *ContainerConfig) GetUser() string { + if m != nil { + return m.User + } + return "" +} + +func (m *ContainerConfig) GetNoUser() bool { + if m != nil { + return m.NoUser + } + return false +} + +func (m *ContainerConfig) GetCwd() string { + if m != nil { + return m.Cwd + } + return "" +} + +func (m *ContainerConfig) GetNoCwd() bool { + if m != nil { + return m.NoCwd + } + return false +} + +func (m *ContainerConfig) GetTty() bool { + if m != nil { + return m.Tty + } + return false +} + +type FdMessage struct { + Fd uint32 `protobuf:"varint,1,opt,name=Fd,proto3" json:"Fd,omitempty"` + EOF bool `protobuf:"varint,2,opt,name=EOF,proto3" json:"EOF,omitempty"` + Data []byte `protobuf:"bytes,3,opt,name=Data,proto3" json:"Data,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *FdMessage) Reset() { *m = FdMessage{} } +func (m *FdMessage) String() string { return proto.CompactTextString(m) } +func (*FdMessage) ProtoMessage() {} +func (*FdMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{17} +} +func (m *FdMessage) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_FdMessage.Unmarshal(m, b) +} +func (m *FdMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_FdMessage.Marshal(b, m, deterministic) +} +func (m *FdMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_FdMessage.Merge(m, src) +} +func (m *FdMessage) XXX_Size() int { + return xxx_messageInfo_FdMessage.Size(m) +} +func (m *FdMessage) XXX_DiscardUnknown() { + xxx_messageInfo_FdMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_FdMessage proto.InternalMessageInfo + +func (m *FdMessage) GetFd() uint32 { + if m != nil { + return m.Fd + } + return 0 +} + +func (m *FdMessage) GetEOF() bool { + if m != nil { + return m.EOF + } + return false +} + +func (m *FdMessage) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +type ResizeMessage struct { + Rows uint32 `protobuf:"varint,1,opt,name=Rows,proto3" json:"Rows,omitempty"` + Cols uint32 `protobuf:"varint,2,opt,name=Cols,proto3" json:"Cols,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ResizeMessage) Reset() { *m = ResizeMessage{} } +func (m *ResizeMessage) String() string { return proto.CompactTextString(m) } +func (*ResizeMessage) ProtoMessage() {} +func (*ResizeMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{18} +} +func (m *ResizeMessage) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ResizeMessage.Unmarshal(m, b) +} +func (m *ResizeMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ResizeMessage.Marshal(b, m, deterministic) +} +func (m *ResizeMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResizeMessage.Merge(m, src) +} +func (m *ResizeMessage) XXX_Size() int { + return xxx_messageInfo_ResizeMessage.Size(m) +} +func (m *ResizeMessage) XXX_DiscardUnknown() { + xxx_messageInfo_ResizeMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_ResizeMessage proto.InternalMessageInfo + +func (m *ResizeMessage) GetRows() uint32 { + if m != nil { + return m.Rows + } + return 0 +} + +func (m *ResizeMessage) GetCols() uint32 { + if m != nil { + return m.Cols + } + return 0 +} + +type SignalMessage struct { + // we only send name (ie HUP, INT) because the int values + // are platform dependent. + Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SignalMessage) Reset() { *m = SignalMessage{} } +func (m *SignalMessage) String() string { return proto.CompactTextString(m) } +func (*SignalMessage) ProtoMessage() {} +func (*SignalMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{19} +} +func (m *SignalMessage) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SignalMessage.Unmarshal(m, b) +} +func (m *SignalMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SignalMessage.Marshal(b, m, deterministic) +} +func (m *SignalMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignalMessage.Merge(m, src) +} +func (m *SignalMessage) XXX_Size() int { + return xxx_messageInfo_SignalMessage.Size(m) +} +func (m *SignalMessage) XXX_DiscardUnknown() { + xxx_messageInfo_SignalMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_SignalMessage proto.InternalMessageInfo + +func (m *SignalMessage) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +type StatusRequest struct { + Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *StatusRequest) Reset() { *m = StatusRequest{} } +func (m *StatusRequest) String() string { return proto.CompactTextString(m) } +func (*StatusRequest) ProtoMessage() {} +func (*StatusRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{20} +} +func (m *StatusRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_StatusRequest.Unmarshal(m, b) +} +func (m *StatusRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_StatusRequest.Marshal(b, m, deterministic) +} +func (m *StatusRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_StatusRequest.Merge(m, src) +} +func (m *StatusRequest) XXX_Size() int { + return xxx_messageInfo_StatusRequest.Size(m) +} +func (m *StatusRequest) XXX_DiscardUnknown() { + xxx_messageInfo_StatusRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_StatusRequest proto.InternalMessageInfo + +func (m *StatusRequest) GetRef() string { + if m != nil { + return m.Ref + } + return "" +} + +type StatusResponse struct { + Vertexes []*control.Vertex `protobuf:"bytes,1,rep,name=vertexes,proto3" json:"vertexes,omitempty"` + Statuses []*control.VertexStatus `protobuf:"bytes,2,rep,name=statuses,proto3" json:"statuses,omitempty"` + Logs []*control.VertexLog `protobuf:"bytes,3,rep,name=logs,proto3" json:"logs,omitempty"` + Warnings []*control.VertexWarning `protobuf:"bytes,4,rep,name=warnings,proto3" json:"warnings,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *StatusResponse) Reset() { *m = StatusResponse{} } +func (m *StatusResponse) String() string { return proto.CompactTextString(m) } +func (*StatusResponse) ProtoMessage() {} +func (*StatusResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{21} +} +func (m *StatusResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_StatusResponse.Unmarshal(m, b) +} +func (m *StatusResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_StatusResponse.Marshal(b, m, deterministic) +} +func (m *StatusResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_StatusResponse.Merge(m, src) +} +func (m *StatusResponse) XXX_Size() int { + return xxx_messageInfo_StatusResponse.Size(m) +} +func (m *StatusResponse) XXX_DiscardUnknown() { + xxx_messageInfo_StatusResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_StatusResponse proto.InternalMessageInfo + +func (m *StatusResponse) GetVertexes() []*control.Vertex { + if m != nil { + return m.Vertexes + } + return nil +} + +func (m *StatusResponse) GetStatuses() []*control.VertexStatus { + if m != nil { + return m.Statuses + } + return nil +} + +func (m *StatusResponse) GetLogs() []*control.VertexLog { + if m != nil { + return m.Logs + } + return nil +} + +func (m *StatusResponse) GetWarnings() []*control.VertexWarning { + if m != nil { + return m.Warnings + } + return nil +} + +type InfoRequest struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *InfoRequest) Reset() { *m = InfoRequest{} } +func (m *InfoRequest) String() string { return proto.CompactTextString(m) } +func (*InfoRequest) ProtoMessage() {} +func (*InfoRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{22} +} +func (m *InfoRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_InfoRequest.Unmarshal(m, b) +} +func (m *InfoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_InfoRequest.Marshal(b, m, deterministic) +} +func (m *InfoRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_InfoRequest.Merge(m, src) +} +func (m *InfoRequest) XXX_Size() int { + return xxx_messageInfo_InfoRequest.Size(m) +} +func (m *InfoRequest) XXX_DiscardUnknown() { + xxx_messageInfo_InfoRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_InfoRequest proto.InternalMessageInfo + +type InfoResponse struct { + BuildxVersion *BuildxVersion `protobuf:"bytes,1,opt,name=buildxVersion,proto3" json:"buildxVersion,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *InfoResponse) Reset() { *m = InfoResponse{} } +func (m *InfoResponse) String() string { return proto.CompactTextString(m) } +func (*InfoResponse) ProtoMessage() {} +func (*InfoResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{23} +} +func (m *InfoResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_InfoResponse.Unmarshal(m, b) +} +func (m *InfoResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_InfoResponse.Marshal(b, m, deterministic) +} +func (m *InfoResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_InfoResponse.Merge(m, src) +} +func (m *InfoResponse) XXX_Size() int { + return xxx_messageInfo_InfoResponse.Size(m) +} +func (m *InfoResponse) XXX_DiscardUnknown() { + xxx_messageInfo_InfoResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_InfoResponse proto.InternalMessageInfo + +func (m *InfoResponse) GetBuildxVersion() *BuildxVersion { + if m != nil { + return m.BuildxVersion + } + return nil +} + +type BuildxVersion struct { + Package string `protobuf:"bytes,1,opt,name=package,proto3" json:"package,omitempty"` + Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` + Revision string `protobuf:"bytes,3,opt,name=revision,proto3" json:"revision,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *BuildxVersion) Reset() { *m = BuildxVersion{} } +func (m *BuildxVersion) String() string { return proto.CompactTextString(m) } +func (*BuildxVersion) ProtoMessage() {} +func (*BuildxVersion) Descriptor() ([]byte, []int) { + return fileDescriptor_68a5e6cb4f7c8dc9, []int{24} +} +func (m *BuildxVersion) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_BuildxVersion.Unmarshal(m, b) +} +func (m *BuildxVersion) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_BuildxVersion.Marshal(b, m, deterministic) +} +func (m *BuildxVersion) XXX_Merge(src proto.Message) { + xxx_messageInfo_BuildxVersion.Merge(m, src) +} +func (m *BuildxVersion) XXX_Size() int { + return xxx_messageInfo_BuildxVersion.Size(m) +} +func (m *BuildxVersion) XXX_DiscardUnknown() { + xxx_messageInfo_BuildxVersion.DiscardUnknown(m) +} + +var xxx_messageInfo_BuildxVersion proto.InternalMessageInfo + +func (m *BuildxVersion) GetPackage() string { + if m != nil { + return m.Package + } + return "" +} + +func (m *BuildxVersion) GetVersion() string { + if m != nil { + return m.Version + } + return "" +} + +func (m *BuildxVersion) GetRevision() string { + if m != nil { + return m.Revision + } + return "" +} + +func init() { + proto.RegisterType((*BuildRequest)(nil), "buildx.builder.v1.BuildRequest") + proto.RegisterType((*BuildOptions)(nil), "buildx.builder.v1.BuildOptions") + proto.RegisterType((*UlimitOpt)(nil), "buildx.builder.v1.UlimitOpt") + proto.RegisterMapType((map[string]*Ulimit)(nil), "buildx.builder.v1.UlimitOpt.ValuesEntry") + proto.RegisterType((*Ulimit)(nil), "buildx.builder.v1.Ulimit") + proto.RegisterType((*CommonOptions)(nil), "buildx.builder.v1.CommonOptions") + proto.RegisterType((*BuildResponse)(nil), "buildx.builder.v1.BuildResponse") + proto.RegisterType((*DisconnectRequest)(nil), "buildx.builder.v1.DisconnectRequest") + proto.RegisterType((*DisconnectResponse)(nil), "buildx.builder.v1.DisconnectResponse") + proto.RegisterType((*ListRequest)(nil), "buildx.builder.v1.ListRequest") + proto.RegisterType((*ListResponse)(nil), "buildx.builder.v1.ListResponse") + proto.RegisterType((*InputMessage)(nil), "buildx.builder.v1.InputMessage") + proto.RegisterType((*InputInitMessage)(nil), "buildx.builder.v1.InputInitMessage") + proto.RegisterType((*DataMessage)(nil), "buildx.builder.v1.DataMessage") + proto.RegisterType((*InputResponse)(nil), "buildx.builder.v1.InputResponse") + proto.RegisterType((*Message)(nil), "buildx.builder.v1.Message") + proto.RegisterType((*InitMessage)(nil), "buildx.builder.v1.InitMessage") + proto.RegisterType((*ContainerConfig)(nil), "buildx.builder.v1.ContainerConfig") + proto.RegisterType((*FdMessage)(nil), "buildx.builder.v1.FdMessage") + proto.RegisterType((*ResizeMessage)(nil), "buildx.builder.v1.ResizeMessage") + proto.RegisterType((*SignalMessage)(nil), "buildx.builder.v1.SignalMessage") + proto.RegisterType((*StatusRequest)(nil), "buildx.builder.v1.StatusRequest") + proto.RegisterType((*StatusResponse)(nil), "buildx.builder.v1.StatusResponse") + proto.RegisterType((*InfoRequest)(nil), "buildx.builder.v1.InfoRequest") + proto.RegisterType((*InfoResponse)(nil), "buildx.builder.v1.InfoResponse") + proto.RegisterType((*BuildxVersion)(nil), "buildx.builder.v1.BuildxVersion") +} + +func init() { proto.RegisterFile("builder.proto", fileDescriptor_68a5e6cb4f7c8dc9) } + +var fileDescriptor_68a5e6cb4f7c8dc9 = []byte{ + // 1389 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x57, 0xdd, 0x72, 0x1a, 0xc7, + 0x12, 0x3e, 0x2b, 0x10, 0x82, 0x46, 0xc8, 0xf6, 0x1c, 0x1f, 0x9f, 0x09, 0x71, 0x59, 0x78, 0x6d, + 0xa7, 0xb8, 0x02, 0x47, 0x76, 0x25, 0xb1, 0x7d, 0x13, 0x0b, 0x09, 0x4b, 0x29, 0x59, 0x52, 0x16, + 0x59, 0xa9, 0x24, 0x17, 0xa9, 0x15, 0x0c, 0x68, 0x8b, 0x65, 0x87, 0xec, 0x0c, 0x48, 0xca, 0x13, + 0xe4, 0x3d, 0x72, 0x91, 0x87, 0xc8, 0x4b, 0xa5, 0x52, 0x79, 0x80, 0x54, 0xf7, 0xcc, 0xae, 0x16, + 0x79, 0x51, 0xae, 0x98, 0xfe, 0xfa, 0xeb, 0x9e, 0x9d, 0x9e, 0xfe, 0x19, 0xa0, 0x76, 0x36, 0x0b, + 0xc2, 0x81, 0x88, 0x5b, 0xd3, 0x58, 0x6a, 0xc9, 0xee, 0x91, 0x78, 0xd9, 0x4a, 0xd0, 0xf9, 0xe7, + 0xf5, 0x37, 0xa3, 0x40, 0x9f, 0xcf, 0xce, 0x5a, 0x7d, 0x39, 0x69, 0x4f, 0xe4, 0xd9, 0x55, 0x9b, + 0x74, 0xe3, 0x40, 0xb7, 0xfd, 0x69, 0xd0, 0x56, 0x22, 0x9e, 0x07, 0x7d, 0xa1, 0xda, 0x7d, 0x19, + 0xe9, 0x58, 0x86, 0xc9, 0xaf, 0xf1, 0xe7, 0xfe, 0x08, 0xeb, 0xdb, 0x48, 0xf7, 0xc4, 0xcf, 0x33, + 0xa1, 0x34, 0xbb, 0x0b, 0x05, 0x4f, 0x0c, 0xb9, 0xd3, 0x70, 0x9a, 0x15, 0x0f, 0x97, 0xec, 0x15, + 0xac, 0x1d, 0x4d, 0x75, 0x20, 0x23, 0xc5, 0x57, 0x1a, 0x4e, 0xb3, 0xba, 0xb5, 0xd9, 0xfa, 0xe8, + 0x1b, 0x5a, 0xe4, 0xc3, 0xd2, 0xbc, 0x84, 0xef, 0xfe, 0xb5, 0x6a, 0xbd, 0x5b, 0x80, 0x35, 0xa0, + 0xda, 0x91, 0x91, 0x16, 0x97, 0xfa, 0xd8, 0xd7, 0xe7, 0x76, 0x97, 0x2c, 0xc4, 0x3e, 0x83, 0x8d, + 0x1d, 0xd9, 0x1f, 0x8b, 0x78, 0x18, 0x84, 0xe2, 0xd0, 0x9f, 0x08, 0xda, 0xb4, 0xe2, 0xdd, 0x40, + 0xd9, 0x43, 0xa8, 0x1c, 0xc7, 0x41, 0xa4, 0xbb, 0xb3, 0xa8, 0xcf, 0x0b, 0x44, 0xb9, 0x06, 0xd8, + 0x7d, 0x58, 0x7d, 0x1b, 0x86, 0xf2, 0x82, 0x17, 0x1b, 0x85, 0x66, 0xc5, 0x33, 0x02, 0xda, 0xd0, + 0xd7, 0xbc, 0x8d, 0x47, 0x8a, 0xaf, 0x92, 0xe6, 0x1a, 0x40, 0x6d, 0xc7, 0xef, 0x9f, 0x8b, 0x6e, + 0x2c, 0x27, 0xbc, 0x64, 0xb4, 0x29, 0xc0, 0x38, 0xac, 0x91, 0x70, 0x22, 0xf9, 0x1a, 0xe9, 0x12, + 0x91, 0xb9, 0xb0, 0xde, 0x19, 0xc5, 0x72, 0x36, 0x3d, 0xf6, 0x63, 0x11, 0x69, 0x5e, 0xa6, 0x8f, + 0x59, 0xc0, 0x58, 0x1d, 0xca, 0xf6, 0x90, 0x8a, 0x57, 0xc8, 0x3c, 0x95, 0xd9, 0x23, 0x80, 0xdd, + 0x4b, 0x1d, 0xfb, 0x7b, 0x52, 0x69, 0xc5, 0x81, 0xb4, 0x19, 0x04, 0x63, 0xb6, 0x3f, 0xf1, 0x47, + 0x62, 0x7f, 0xa7, 0x1b, 0x84, 0x82, 0x57, 0x4d, 0xcc, 0x32, 0x10, 0x7b, 0x00, 0xa5, 0x03, 0xff, + 0x4c, 0x84, 0x8a, 0xaf, 0x93, 0xb5, 0x95, 0xd0, 0xf2, 0x50, 0xe8, 0x0b, 0x19, 0x8f, 0xdf, 0xcb, + 0x81, 0xe0, 0x35, 0x63, 0x99, 0x81, 0xd8, 0x53, 0xa8, 0x1d, 0x4a, 0x73, 0xc8, 0x20, 0xd4, 0x22, + 0xe6, 0x1b, 0xe4, 0x60, 0x11, 0xc4, 0xb3, 0x1f, 0xcd, 0xf4, 0x74, 0xa6, 0x15, 0xbf, 0x63, 0xce, + 0x6e, 0x45, 0xba, 0x85, 0xd0, 0xd7, 0x43, 0x19, 0x4f, 0x14, 0xbf, 0x6b, 0x62, 0x96, 0x02, 0x78, + 0x0b, 0xdf, 0xce, 0x02, 0xa1, 0xf9, 0xbd, 0x86, 0xd3, 0x2c, 0x7b, 0x46, 0x40, 0x6f, 0x3d, 0xd1, + 0x8f, 0x85, 0x56, 0x9c, 0x19, 0x6f, 0x56, 0x24, 0xcd, 0xf9, 0xa4, 0x17, 0xfc, 0x22, 0xf8, 0x7f, + 0x1b, 0x4e, 0xb3, 0xe0, 0x25, 0x22, 0x66, 0x65, 0xaf, 0xb7, 0xc7, 0xef, 0x13, 0x1f, 0x97, 0x8c, + 0x41, 0xf1, 0xc4, 0x1f, 0x29, 0xfe, 0x3f, 0x82, 0x68, 0x8d, 0x71, 0x38, 0xf1, 0xe3, 0x91, 0xd0, + 0xfc, 0x01, 0x1d, 0xd5, 0x4a, 0xec, 0x0b, 0x58, 0xfb, 0x10, 0x06, 0x93, 0x40, 0x2b, 0xfe, 0x7f, + 0xca, 0xe0, 0x87, 0x39, 0x19, 0x6c, 0x18, 0x47, 0x53, 0xed, 0x25, 0x64, 0xf6, 0x12, 0x8a, 0x47, + 0x53, 0xad, 0x38, 0x27, 0xa3, 0x46, 0x8e, 0x51, 0x47, 0x4e, 0x26, 0x32, 0x4a, 0xf2, 0x9e, 0xd8, + 0xee, 0x6f, 0x0e, 0x54, 0x52, 0x67, 0xec, 0x6b, 0x28, 0xcd, 0xfd, 0x70, 0x26, 0x14, 0x77, 0x1a, + 0x85, 0x66, 0x75, 0xab, 0x79, 0xdb, 0xd6, 0xad, 0x53, 0xa2, 0xee, 0x46, 0x3a, 0xbe, 0xf2, 0xac, + 0x5d, 0xfd, 0x04, 0xaa, 0x19, 0x18, 0x43, 0x31, 0x16, 0x57, 0x49, 0x81, 0x8e, 0xc5, 0x15, 0x6b, + 0xc3, 0x2a, 0x51, 0x6d, 0x79, 0x7e, 0xb2, 0x74, 0x07, 0xcf, 0xf0, 0x5e, 0xaf, 0x7c, 0xe5, 0xb8, + 0x3b, 0x50, 0x32, 0x20, 0x46, 0x92, 0xea, 0xcc, 0x78, 0xa4, 0x35, 0x62, 0x7b, 0x7e, 0x3c, 0x20, + 0x8f, 0x05, 0x8f, 0xd6, 0x88, 0xf5, 0xe4, 0x50, 0x53, 0xb1, 0x15, 0x3c, 0x5a, 0xbb, 0x7f, 0x38, + 0x50, 0x5b, 0x88, 0x01, 0xde, 0xe1, 0xb6, 0xd9, 0xd7, 0x3a, 0x4c, 0x44, 0xac, 0x93, 0xf7, 0x42, + 0xfb, 0x03, 0x5f, 0xfb, 0x94, 0xc8, 0xa6, 0xae, 0x17, 0x30, 0xb4, 0xb6, 0xa9, 0x47, 0xdb, 0x94, + 0xbd, 0x44, 0xc4, 0xdd, 0x8f, 0x67, 0x61, 0xc8, 0x8b, 0x04, 0xd3, 0xda, 0x54, 0xce, 0x54, 0xc6, + 0xfa, 0x78, 0xa6, 0xce, 0xf9, 0x2a, 0x69, 0x32, 0xc8, 0xb5, 0xfe, 0x40, 0xfa, 0x03, 0x5e, 0xca, + 0xea, 0x11, 0x71, 0xef, 0x40, 0xcd, 0xf6, 0x3e, 0x35, 0x95, 0x91, 0x12, 0xee, 0x33, 0xb8, 0xb7, + 0x13, 0xa8, 0xbe, 0x8c, 0x22, 0xd1, 0xd7, 0x4b, 0x3b, 0xa2, 0x7b, 0x1f, 0x58, 0x96, 0x66, 0x8d, + 0x37, 0xa1, 0x7a, 0x10, 0xa8, 0x5b, 0xcc, 0x5c, 0x58, 0x37, 0x04, 0x63, 0x80, 0x47, 0x1a, 0x8b, + 0x2b, 0x93, 0x18, 0x15, 0x8f, 0xd6, 0xee, 0xaf, 0x0e, 0xac, 0xef, 0x47, 0xd3, 0x99, 0x7e, 0x2f, + 0x94, 0xf2, 0x47, 0x82, 0xbd, 0x82, 0xe2, 0x7e, 0x14, 0x68, 0xf2, 0x53, 0xdd, 0x7a, 0x92, 0x73, + 0xb7, 0x44, 0x47, 0x8e, 0x35, 0xd9, 0xfb, 0x8f, 0x47, 0x26, 0x98, 0xbe, 0x3b, 0xbe, 0xf6, 0x6d, + 0x5a, 0x3c, 0xca, 0x31, 0x45, 0x75, 0xc6, 0x0a, 0xc5, 0xed, 0x35, 0x58, 0x25, 0x8f, 0xee, 0x53, + 0xb8, 0x7b, 0xd3, 0x75, 0xce, 0xa1, 0x5e, 0x40, 0x35, 0xe3, 0x05, 0x09, 0xbb, 0x47, 0x5d, 0x22, + 0x94, 0x3d, 0x5c, 0xe2, 0x29, 0xd3, 0xaf, 0x58, 0x37, 0x7b, 0x60, 0xe0, 0xc9, 0x75, 0x1a, 0xbb, + 0xbf, 0x1d, 0x58, 0x4b, 0x5c, 0xbc, 0x5c, 0x38, 0xf1, 0xa3, 0xdc, 0x13, 0x7f, 0x7c, 0xd8, 0x2d, + 0x28, 0xa6, 0x59, 0x95, 0x5f, 0xe0, 0xdd, 0x41, 0xc6, 0x86, 0xb2, 0xed, 0x35, 0x94, 0x3c, 0xa1, + 0xb0, 0xdd, 0x14, 0x96, 0x56, 0xb8, 0x21, 0x5c, 0x5b, 0x5a, 0x0b, 0xb4, 0xed, 0x05, 0xa3, 0xc8, + 0x37, 0x19, 0x99, 0x6f, 0x6b, 0x08, 0x19, 0x5b, 0x03, 0x5c, 0x87, 0x78, 0x02, 0xd5, 0x5b, 0xa3, + 0xcb, 0x0e, 0xe0, 0x0e, 0xce, 0x09, 0x3f, 0x88, 0x44, 0xdc, 0x91, 0xd1, 0x30, 0x18, 0xd9, 0x03, + 0xba, 0xb9, 0xcd, 0x68, 0x81, 0xe9, 0xdd, 0x34, 0xc5, 0x6a, 0xbd, 0x89, 0x51, 0x8d, 0x60, 0x5f, + 0x99, 0xca, 0x20, 0xd2, 0x36, 0x15, 0x33, 0x08, 0x7e, 0x53, 0x67, 0x82, 0x8d, 0x80, 0x3a, 0x6f, + 0x67, 0x32, 0xa0, 0x2b, 0x8e, 0xe6, 0xbc, 0x60, 0x90, 0xdd, 0x68, 0x8e, 0x57, 0xfc, 0x41, 0x89, + 0x98, 0x22, 0x51, 0xf1, 0x68, 0x8d, 0xbd, 0xf8, 0x50, 0x12, 0x6a, 0xea, 0xd2, 0x4a, 0xe4, 0xef, + 0xc2, 0x14, 0x23, 0xfa, 0xbb, 0x18, 0xe0, 0x94, 0x38, 0x94, 0x88, 0xad, 0x99, 0x29, 0x41, 0x02, + 0xf2, 0x4e, 0xf4, 0x15, 0x0d, 0xd3, 0xb2, 0x87, 0x4b, 0xf7, 0x2d, 0x54, 0xd2, 0x2b, 0x64, 0x1b, + 0xb0, 0xd2, 0x1d, 0x50, 0xa4, 0x6a, 0xde, 0x4a, 0x77, 0x90, 0xe4, 0xdd, 0xca, 0xc7, 0x79, 0x57, + 0xc8, 0xe4, 0xdd, 0x97, 0x50, 0x5b, 0xb8, 0x4f, 0x24, 0x79, 0xf2, 0x42, 0x59, 0x47, 0xb4, 0x46, + 0xac, 0x23, 0x43, 0xf3, 0xd8, 0xa9, 0x79, 0xb4, 0x76, 0x9f, 0x40, 0x6d, 0xe1, 0x32, 0xf3, 0x9a, + 0xa6, 0xfb, 0x18, 0x6a, 0x3d, 0xed, 0xeb, 0x99, 0x5a, 0xde, 0x02, 0xfe, 0x74, 0x60, 0x23, 0xe1, + 0xd8, 0x2e, 0xf0, 0x12, 0xca, 0x73, 0x11, 0x6b, 0x71, 0x99, 0x8e, 0x08, 0xde, 0xc2, 0x57, 0x5c, + 0x2b, 0x79, 0xc5, 0xe1, 0xd5, 0x9e, 0x12, 0xc3, 0x4b, 0x99, 0xec, 0x35, 0x94, 0x15, 0xf9, 0x11, + 0x8a, 0xee, 0x06, 0x0b, 0x65, 0x89, 0x95, 0xdd, 0x2f, 0xe5, 0xb3, 0x36, 0x14, 0x43, 0x39, 0x52, + 0x74, 0x83, 0xd5, 0xad, 0x4f, 0x97, 0xd9, 0x1d, 0xc8, 0x91, 0x47, 0x44, 0xf6, 0x06, 0xca, 0x17, + 0x7e, 0x1c, 0x05, 0xd1, 0x48, 0xd1, 0x83, 0x0a, 0x9f, 0x80, 0x4b, 0x8c, 0xbe, 0x33, 0x3c, 0x2f, + 0x35, 0x70, 0x6b, 0x98, 0xe3, 0x43, 0x69, 0x63, 0xe2, 0x9e, 0x62, 0x7f, 0x43, 0xd1, 0x1e, 0xbf, + 0x6b, 0x1f, 0xb8, 0x97, 0xa7, 0x22, 0x56, 0x81, 0x8c, 0x6c, 0xd9, 0x37, 0x96, 0xbd, 0x31, 0x13, + 0x9e, 0xb7, 0x68, 0xe6, 0xfe, 0x64, 0x7b, 0x79, 0x02, 0xe0, 0x28, 0x99, 0xfa, 0xfd, 0xb1, 0x3f, + 0x4a, 0x2e, 0x29, 0x11, 0x51, 0x33, 0xb7, 0x9b, 0x99, 0x19, 0x94, 0x88, 0xf8, 0x4c, 0x8b, 0xc5, + 0x3c, 0x20, 0x95, 0x79, 0x53, 0xa6, 0xf2, 0xd6, 0xef, 0xc5, 0x74, 0xb2, 0xb1, 0x6f, 0x60, 0x95, + 0x96, 0x6c, 0xe9, 0x53, 0xd8, 0x1e, 0xb7, 0xde, 0x58, 0x4e, 0xb0, 0x01, 0x38, 0x82, 0x92, 0xb9, + 0x21, 0x96, 0xdb, 0x42, 0xb2, 0x09, 0x55, 0x7f, 0x7c, 0x0b, 0xc3, 0xb8, 0x7b, 0xee, 0xb0, 0x03, + 0xdb, 0x5d, 0x72, 0x3f, 0x2e, 0x3b, 0x5b, 0x72, 0x3f, 0x6e, 0xa1, 0x2f, 0x37, 0x1d, 0xb6, 0x03, + 0xa5, 0xfd, 0x68, 0x2e, 0xc7, 0x82, 0xd5, 0x73, 0xd8, 0x89, 0xa7, 0x5b, 0x74, 0x4d, 0xe7, 0xb9, + 0xc3, 0xde, 0x41, 0x11, 0x47, 0x1f, 0xcb, 0xeb, 0xe6, 0x99, 0xa1, 0x59, 0xdf, 0x5c, 0xaa, 0xb7, + 0xd1, 0xfa, 0x1e, 0xe0, 0x7a, 0xf4, 0xb2, 0xa7, 0x79, 0x33, 0xed, 0xe6, 0x00, 0xaf, 0x3f, 0xfb, + 0x17, 0x96, 0x75, 0xfd, 0x0e, 0xe7, 0xce, 0x50, 0xb2, 0xfc, 0x89, 0x93, 0x66, 0x70, 0x7d, 0x73, + 0xa9, 0xde, 0x38, 0xda, 0x2e, 0xfe, 0xb0, 0x32, 0x3d, 0x3b, 0x2b, 0xd1, 0xff, 0xab, 0x17, 0xff, + 0x04, 0x00, 0x00, 0xff, 0xff, 0x27, 0xa7, 0xcb, 0x41, 0xc0, 0x0d, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// BuilderClient is the client API for Builder service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type BuilderClient interface { + Build(ctx context.Context, in *BuildRequest, opts ...grpc.CallOption) (*BuildResponse, error) + Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (Builder_StatusClient, error) + Input(ctx context.Context, opts ...grpc.CallOption) (Builder_InputClient, error) + Invoke(ctx context.Context, opts ...grpc.CallOption) (Builder_InvokeClient, error) + List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) + Disconnect(ctx context.Context, in *DisconnectRequest, opts ...grpc.CallOption) (*DisconnectResponse, error) + Info(ctx context.Context, in *InfoRequest, opts ...grpc.CallOption) (*InfoResponse, error) +} + +type builderClient struct { + cc *grpc.ClientConn +} + +func NewBuilderClient(cc *grpc.ClientConn) BuilderClient { + return &builderClient{cc} +} + +func (c *builderClient) Build(ctx context.Context, in *BuildRequest, opts ...grpc.CallOption) (*BuildResponse, error) { + out := new(BuildResponse) + err := c.cc.Invoke(ctx, "/buildx.builder.v1.Builder/Build", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *builderClient) Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (Builder_StatusClient, error) { + stream, err := c.cc.NewStream(ctx, &_Builder_serviceDesc.Streams[0], "/buildx.builder.v1.Builder/Status", opts...) + if err != nil { + return nil, err + } + x := &builderStatusClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type Builder_StatusClient interface { + Recv() (*StatusResponse, error) + grpc.ClientStream +} + +type builderStatusClient struct { + grpc.ClientStream +} + +func (x *builderStatusClient) Recv() (*StatusResponse, error) { + m := new(StatusResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *builderClient) Input(ctx context.Context, opts ...grpc.CallOption) (Builder_InputClient, error) { + stream, err := c.cc.NewStream(ctx, &_Builder_serviceDesc.Streams[1], "/buildx.builder.v1.Builder/Input", opts...) + if err != nil { + return nil, err + } + x := &builderInputClient{stream} + return x, nil +} + +type Builder_InputClient interface { + Send(*InputMessage) error + CloseAndRecv() (*InputResponse, error) + grpc.ClientStream +} + +type builderInputClient struct { + grpc.ClientStream +} + +func (x *builderInputClient) Send(m *InputMessage) error { + return x.ClientStream.SendMsg(m) +} + +func (x *builderInputClient) CloseAndRecv() (*InputResponse, error) { + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + m := new(InputResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *builderClient) Invoke(ctx context.Context, opts ...grpc.CallOption) (Builder_InvokeClient, error) { + stream, err := c.cc.NewStream(ctx, &_Builder_serviceDesc.Streams[2], "/buildx.builder.v1.Builder/Invoke", opts...) + if err != nil { + return nil, err + } + x := &builderInvokeClient{stream} + return x, nil +} + +type Builder_InvokeClient interface { + Send(*Message) error + Recv() (*Message, error) + grpc.ClientStream +} + +type builderInvokeClient struct { + grpc.ClientStream +} + +func (x *builderInvokeClient) Send(m *Message) error { + return x.ClientStream.SendMsg(m) +} + +func (x *builderInvokeClient) Recv() (*Message, error) { + m := new(Message) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *builderClient) List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) { + out := new(ListResponse) + err := c.cc.Invoke(ctx, "/buildx.builder.v1.Builder/List", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *builderClient) Disconnect(ctx context.Context, in *DisconnectRequest, opts ...grpc.CallOption) (*DisconnectResponse, error) { + out := new(DisconnectResponse) + err := c.cc.Invoke(ctx, "/buildx.builder.v1.Builder/Disconnect", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *builderClient) Info(ctx context.Context, in *InfoRequest, opts ...grpc.CallOption) (*InfoResponse, error) { + out := new(InfoResponse) + err := c.cc.Invoke(ctx, "/buildx.builder.v1.Builder/Info", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// BuilderServer is the server API for Builder service. +type BuilderServer interface { + Build(context.Context, *BuildRequest) (*BuildResponse, error) + Status(*StatusRequest, Builder_StatusServer) error + Input(Builder_InputServer) error + Invoke(Builder_InvokeServer) error + List(context.Context, *ListRequest) (*ListResponse, error) + Disconnect(context.Context, *DisconnectRequest) (*DisconnectResponse, error) + Info(context.Context, *InfoRequest) (*InfoResponse, error) +} + +// UnimplementedBuilderServer can be embedded to have forward compatible implementations. +type UnimplementedBuilderServer struct { +} + +func (*UnimplementedBuilderServer) Build(ctx context.Context, req *BuildRequest) (*BuildResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Build not implemented") +} +func (*UnimplementedBuilderServer) Status(req *StatusRequest, srv Builder_StatusServer) error { + return status.Errorf(codes.Unimplemented, "method Status not implemented") +} +func (*UnimplementedBuilderServer) Input(srv Builder_InputServer) error { + return status.Errorf(codes.Unimplemented, "method Input not implemented") +} +func (*UnimplementedBuilderServer) Invoke(srv Builder_InvokeServer) error { + return status.Errorf(codes.Unimplemented, "method Invoke not implemented") +} +func (*UnimplementedBuilderServer) List(ctx context.Context, req *ListRequest) (*ListResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method List not implemented") +} +func (*UnimplementedBuilderServer) Disconnect(ctx context.Context, req *DisconnectRequest) (*DisconnectResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Disconnect not implemented") +} +func (*UnimplementedBuilderServer) Info(ctx context.Context, req *InfoRequest) (*InfoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Info not implemented") +} + +func RegisterBuilderServer(s *grpc.Server, srv BuilderServer) { + s.RegisterService(&_Builder_serviceDesc, srv) +} + +func _Builder_Build_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(BuildRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BuilderServer).Build(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/buildx.builder.v1.Builder/Build", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BuilderServer).Build(ctx, req.(*BuildRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Builder_Status_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(StatusRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(BuilderServer).Status(m, &builderStatusServer{stream}) +} + +type Builder_StatusServer interface { + Send(*StatusResponse) error + grpc.ServerStream +} + +type builderStatusServer struct { + grpc.ServerStream +} + +func (x *builderStatusServer) Send(m *StatusResponse) error { + return x.ServerStream.SendMsg(m) +} + +func _Builder_Input_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(BuilderServer).Input(&builderInputServer{stream}) +} + +type Builder_InputServer interface { + SendAndClose(*InputResponse) error + Recv() (*InputMessage, error) + grpc.ServerStream +} + +type builderInputServer struct { + grpc.ServerStream +} + +func (x *builderInputServer) SendAndClose(m *InputResponse) error { + return x.ServerStream.SendMsg(m) +} + +func (x *builderInputServer) Recv() (*InputMessage, error) { + m := new(InputMessage) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func _Builder_Invoke_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(BuilderServer).Invoke(&builderInvokeServer{stream}) +} + +type Builder_InvokeServer interface { + Send(*Message) error + Recv() (*Message, error) + grpc.ServerStream +} + +type builderInvokeServer struct { + grpc.ServerStream +} + +func (x *builderInvokeServer) Send(m *Message) error { + return x.ServerStream.SendMsg(m) +} + +func (x *builderInvokeServer) Recv() (*Message, error) { + m := new(Message) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func _Builder_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BuilderServer).List(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/buildx.builder.v1.Builder/List", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BuilderServer).List(ctx, req.(*ListRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Builder_Disconnect_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DisconnectRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BuilderServer).Disconnect(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/buildx.builder.v1.Builder/Disconnect", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BuilderServer).Disconnect(ctx, req.(*DisconnectRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Builder_Info_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BuilderServer).Info(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/buildx.builder.v1.Builder/Info", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BuilderServer).Info(ctx, req.(*InfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Builder_serviceDesc = grpc.ServiceDesc{ + ServiceName: "buildx.builder.v1.Builder", + HandlerType: (*BuilderServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Build", + Handler: _Builder_Build_Handler, + }, + { + MethodName: "List", + Handler: _Builder_List_Handler, + }, + { + MethodName: "Disconnect", + Handler: _Builder_Disconnect_Handler, + }, + { + MethodName: "Info", + Handler: _Builder_Info_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "Status", + Handler: _Builder_Status_Handler, + ServerStreams: true, + }, + { + StreamName: "Input", + Handler: _Builder_Input_Handler, + ClientStreams: true, + }, + { + StreamName: "Invoke", + Handler: _Builder_Invoke_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "builder.proto", +} diff --git a/commands/builder/pb/builder.proto b/commands/builder/pb/builder.proto new file mode 100644 index 000000000000..10373f63324d --- /dev/null +++ b/commands/builder/pb/builder.proto @@ -0,0 +1,176 @@ +syntax = "proto3"; + +package buildx.builder.v1; + +import "github.com/moby/buildkit/api/services/control/control.proto"; + +option go_package = "pb"; + +service Builder { + rpc Build(BuildRequest) returns (BuildResponse); + rpc Status(StatusRequest) returns (stream StatusResponse); + rpc Input(stream InputMessage) returns (InputResponse); + rpc Invoke(stream Message) returns (stream Message); + rpc List(ListRequest) returns (ListResponse); + rpc Disconnect(DisconnectRequest) returns (DisconnectResponse); + rpc Info(InfoRequest) returns (InfoResponse); +} + +message BuildRequest { + string Ref = 1; + BuildOptions Options = 2; +} + +message BuildOptions { + string ContextPath = 1; + string DockerfileName = 2; + string PrintFunc = 3; + + repeated string Allow = 4; + repeated string BuildArgs = 5; + repeated string CacheFrom = 6; + repeated string CacheTo = 7; + string CgroupParent = 8; + repeated string Contexts = 9; + repeated string ExtraHosts = 10; + string ImageIDFile = 11; + repeated string Labels = 12; + string NetworkMode = 13; + repeated string NoCacheFilter = 14; + repeated string Outputs = 15; + repeated string Platforms = 16; + bool Quiet = 17; + repeated string Secrets = 18; + int64 ShmSize = 19; + repeated string SSH = 20; + repeated string Tags = 21; + string Target = 22; + UlimitOpt Ulimits = 23; + // string Invoke: provided via Invoke API + CommonOptions Opts = 24; +} + +message UlimitOpt { + map values = 1; +} + +message Ulimit { + string Name = 1; + int64 Hard = 2; + int64 Soft = 3; +} + +message CommonOptions { + string Builder = 1; + string MetadataFile = 2; + bool NoCache = 3; + // string Progress: no progress view on server side + bool Pull = 4; + bool ExportPush = 5; + bool ExportLoad = 6; +} + +message BuildResponse {} + +message DisconnectRequest { + string Ref = 1; +} + +message DisconnectResponse {} + +message ListRequest { + string Ref = 1; +} + +message ListResponse { + repeated string keys = 1; +} + +message InputMessage { + oneof Input { + InputInitMessage Init = 1; + DataMessage Data = 2; + } +} + +message InputInitMessage { + string Ref = 1; +} + +message DataMessage { + bool EOF = 1; // true if eof was reached + bytes Data = 2; // should be chunked smaller than 4MB: + // https://pkg.go.dev/google.golang.org/grpc#MaxRecvMsgSize +} + +message InputResponse {} + +message Message { + oneof Input { + InitMessage Init = 1; + // FdMessage used from client to server for input (stdin) and + // from server to client for output (stdout, stderr) + FdMessage File = 2; + // ResizeMessage used from client to server for terminal resize events + ResizeMessage Resize = 3; + // SignalMessage is used from client to server to send signal events + SignalMessage Signal = 4; + } +} + +message InitMessage { + string Ref = 1; + ContainerConfig ContainerConfig = 2; +} + +message ContainerConfig { + repeated string Entrypoint = 1; + repeated string Cmd = 2; + repeated string Env = 3; + string User = 4; + bool NoUser = 5; // Do not set user but use the image's default + string Cwd = 6; + bool NoCwd = 7; // Do not set cwd but use the image's default + bool Tty = 8; +} + +message FdMessage { + uint32 Fd = 1; // what fd the data was from + bool EOF = 2; // true if eof was reached + bytes Data = 3; // should be chunked smaller than 4MB: + // https://pkg.go.dev/google.golang.org/grpc#MaxRecvMsgSize +} + +message ResizeMessage { + uint32 Rows = 1; + uint32 Cols = 2; +} + +message SignalMessage { + // we only send name (ie HUP, INT) because the int values + // are platform dependent. + string Name = 1; +} + +message StatusRequest { + string Ref = 1; +} + +message StatusResponse { + repeated moby.buildkit.v1.Vertex vertexes = 1; + repeated moby.buildkit.v1.VertexStatus statuses = 2; + repeated moby.buildkit.v1.VertexLog logs = 3; + repeated moby.buildkit.v1.VertexWarning warnings = 4; +} + +message InfoRequest {} + +message InfoResponse { + BuildxVersion buildxVersion = 1; +} + +message BuildxVersion { + string package = 1; + string version = 2; + string revision = 3; +} diff --git a/commands/builder/pb/generate.go b/commands/builder/pb/generate.go new file mode 100644 index 000000000000..da10fb16084c --- /dev/null +++ b/commands/builder/pb/generate.go @@ -0,0 +1,3 @@ +package pb + +//go:generate protoc -I=. -I=../../../vendor/ --gogo_out=plugins=grpc:. builder.proto diff --git a/commands/builderlocal.go b/commands/builderlocal.go new file mode 100644 index 000000000000..53c15b4e510e --- /dev/null +++ b/commands/builderlocal.go @@ -0,0 +1,78 @@ +package commands + +import ( + "context" + "fmt" + "io" + + "github.com/containerd/console" + "github.com/docker/buildx/build" + builderapi "github.com/docker/buildx/commands/builder/pb" + "github.com/docker/buildx/monitor" + "github.com/docker/cli/cli/command" +) + +func newLocalBuildxController(ctx context.Context, dockerCli command.Cli) monitor.BuildxController { + return &localBuilder{ + dockerCli: dockerCli, + ref: "local", + } +} + +type localBuilder struct { + dockerCli command.Cli + ref string + resultCtx *build.ResultContext +} + +func (b *localBuilder) Invoke(ctx context.Context, ref string, cfg builderapi.ContainerConfig, ioIn io.ReadCloser, ioOut io.WriteCloser, ioErr io.WriteCloser) error { + if ref != b.ref { + return fmt.Errorf("unknown ref %q", ref) + } + if b.resultCtx == nil { + return fmt.Errorf("no build result is registered") + } + ccfg := build.ContainerConfig{ + ResultCtx: b.resultCtx, + Entrypoint: cfg.Entrypoint, + Cmd: cfg.Cmd, + Env: cfg.Env, + Tty: cfg.Tty, + Stdin: ioIn, + Stdout: ioOut, + Stderr: ioErr, + } + if !cfg.NoUser { + ccfg.User = &cfg.User + } + if !cfg.NoCwd { + ccfg.Cwd = &cfg.Cwd + } + return build.Invoke(ctx, ccfg) +} + +func (b *localBuilder) Build(ctx context.Context, options builderapi.BuildOptions, in io.ReadCloser, w io.Writer, out console.File, progressMode string) (string, error) { + res, err := runBuildContext(ctx, b.dockerCli, options, in, progressMode, true, nil) + if err != nil { + return "", err + } + b.resultCtx = res + return b.ref, nil +} + +func (b *localBuilder) Kill(context.Context) error { + return nil // nop +} + +func (b *localBuilder) Close() error { + // TODO: cancel current build and invoke + return nil +} + +func (b *localBuilder) List(ctx context.Context) (res []string, _ error) { + return []string{b.ref}, nil +} + +func (b *localBuilder) Disconnect(ctx context.Context, key string) error { + return nil // nop +} diff --git a/commands/builderremote_linux.go b/commands/builderremote_linux.go new file mode 100644 index 000000000000..f602a0fd842c --- /dev/null +++ b/commands/builderremote_linux.go @@ -0,0 +1,396 @@ +//go:build linux + +package commands + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net" + "os" + "os/exec" + "os/signal" + "path/filepath" + "syscall" + "time" + + "github.com/containerd/containerd/log" + "github.com/docker/buildx/build" + "github.com/docker/buildx/commands/builder" + builderapi "github.com/docker/buildx/commands/builder/pb" + "github.com/docker/buildx/monitor" + "github.com/docker/buildx/version" + "github.com/docker/cli/cli/command" + "github.com/moby/buildkit/client" + "github.com/pelletier/go-toml" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "google.golang.org/grpc" +) + +const ( + launchCommandName = "_INTERNAL_LAUNCH" + serveCommandName = "_INTERNAL_SERVE" +) + +type serverConfig struct { + // Specify buildx server root + Root string `toml:"root"` + + // LogLevel sets the logging level [trace, debug, info, warn, error, fatal, panic] + LogLevel string `toml:"log_level"` + + // Specify file to output buildx server log + LogFile string `toml:"log_file"` +} + +func newRemoteBuildxController(ctx context.Context, opts buildOptions) (monitor.BuildxController, error) { + rootDir := opts.root + if rootDir == "" { + var err error + rootDir, err = rootDataDir() + if err != nil { + return nil, err + } + } + serverRoot := filepath.Join(rootDir, "shared") + c, err := newBuildxClientAndCheck(filepath.Join(serverRoot, "buildx.sock"), 1, 0) + if err != nil { + logrus.Info("no buildx server found; launching...") + // start buildx server via subcommand + launchFlags := []string{} + if opts.serverConfig != "" { + launchFlags = append(launchFlags, "--config", opts.serverConfig) + } + logFile, err := getLogFilePath(opts.serverConfig) + if err != nil { + return nil, err + } + wait, err := launch(ctx, true, logFile, append([]string{launchCommandName}, launchFlags...)...) + if err != nil { + return nil, err + } + if err := wait(); err != nil { + return nil, err + } + c, err = newBuildxClientAndCheck(filepath.Join(serverRoot, "buildx.sock"), 10, time.Second) + if err != nil { + return nil, fmt.Errorf("cannot connect to the buildx server: %w", err) + } + } + return &buildxController{c, serverRoot}, nil +} + +func addBuilderCommands(cmd *cobra.Command, dockerCli command.Cli, rootOpts *rootOptions) { + cmd.AddCommand( + launchCmd(dockerCli, rootOpts), + serveCmd(dockerCli, rootOpts), + ) +} + +func launchCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { + var serverConfigPath string + cmd := &cobra.Command{ + Use: fmt.Sprintf("%s [OPTIONS]", launchCommandName), + Hidden: true, + RunE: func(cmd *cobra.Command, args []string) error { + logrus.Debugf("start launch") + + // start buildx server via "serve" + opts := []string{} + if serverConfigPath != "" { + opts = append(opts, "--config", serverConfigPath) + } + logFile, err := getLogFilePath(serverConfigPath) + if err != nil { + return err + } + wait, err := launch(context.TODO(), false, logFile, append([]string{serveCommandName}, opts...)...) + if err != nil { + return err + } + go wait() + return nil + }, + } + + flags := cmd.Flags() + flags.StringVar(&serverConfigPath, "config", "", "Specify buildx server config file") + return cmd +} + +func serveCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { + var serverConfigPath string + cmd := &cobra.Command{ + Use: fmt.Sprintf("%s [OPTIONS]", serveCommandName), + Hidden: true, + RunE: func(cmd *cobra.Command, args []string) error { + // Parse config + config, err := getConfig(serverConfigPath) + if err != nil { + return fmt.Errorf("failed to get config") + } + if config.LogLevel == "" { + logrus.SetLevel(logrus.InfoLevel) + } else { + lvl, err := logrus.ParseLevel(config.LogLevel) + if err != nil { + return fmt.Errorf("failed to prepare logger: %w", err) + } + logrus.SetLevel(lvl) + } + logrus.SetFormatter(&logrus.JSONFormatter{ + TimestampFormat: log.RFC3339NanoFixed, + }) + root, err := prepareRootDir(config) + if err != nil { + return err + } + infoF, err := writeBuildxInfo(root, BuildxInfo{ + Name: "shared", + Pid: os.Getpid(), + Started: time.Now(), + }) + if err != nil { + return err + } + defer func() { + if err := os.Remove(infoF); err != nil { + logrus.Errorf("failed to clean up info file %q: %v", infoF, err) + } + }() + + // prepare server + b := builder.New(func(ctx context.Context, options *builderapi.BuildOptions, stdin io.Reader, statusChan chan *client.SolveStatus) (res *build.ResultContext, err error) { + return runBuildContext(ctx, dockerCli, *options, stdin, "quiet", true, statusChan) + }) + defer b.Close() + + // serve server + addr := filepath.Join(root, "buildx.sock") + if err := os.Remove(addr); err != nil && !os.IsNotExist(err) { // avoid EADDRINUSE + return err + } + defer func() { + if err := os.Remove(addr); err != nil { + logrus.Errorf("failed to clean up socket %q: %v", addr, err) + } + }() + logrus.Infof("starting server at %q", addr) + l, err := net.Listen("unix", addr) + if err != nil { + return err + } + rpc := grpc.NewServer() + builderapi.RegisterBuilderServer(rpc, b) + doneCh := make(chan struct{}) + errCh := make(chan error, 1) + go func() { + defer close(doneCh) + if err := rpc.Serve(l); err != nil { + errCh <- fmt.Errorf("error on serving via socket %q: %w", addr, err) + } + return + }() + var s os.Signal + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, os.Interrupt) + select { + case s = <-sigCh: + logrus.Debugf("got signal %v", s) + case err := <-errCh: + return err + case <-doneCh: + } + return nil + + }, + } + + flags := cmd.Flags() + flags.StringVar(&serverConfigPath, "config", "", "Specify buildx server config file") + return cmd +} + +func getLogFilePath(configPath string) (string, error) { + config, err := getConfig(configPath) + if err != nil { + return "", fmt.Errorf("failed to get config") + } + logFile := config.LogFile + if logFile == "" { + root, err := prepareRootDir(config) + if err != nil { + return "", err + } + logFile = filepath.Join(root, "log") + } + return logFile, nil +} + +func getConfig(configPath string) (*serverConfig, error) { + var defaultConfigPath bool + if configPath == "" { + defaultRoot, err := rootDataDir() + if err != nil { + return nil, err + } + configPath = filepath.Join(defaultRoot, "config.toml") + defaultConfigPath = true + } + var config serverConfig + tree, err := toml.LoadFile(configPath) + if err != nil && !(os.IsNotExist(err) && defaultConfigPath) { + return nil, fmt.Errorf("failed to load config file %q", configPath) + } else if err == nil { + if err := tree.Unmarshal(&config); err != nil { + return nil, fmt.Errorf("failed to unmarshal config file %q", configPath) + } + } + return &config, nil +} + +func prepareRootDir(config *serverConfig) (string, error) { + rootDir := config.Root + if rootDir == "" { + var err error + rootDir, err = rootDataDir() + if err != nil { + return "", err + } + } + if rootDir == "" { + return "", fmt.Errorf("buildx root dir must be determined") + } + if err := os.MkdirAll(rootDir, 0700); err != nil { + return "", err + } + serverRoot := filepath.Join(rootDir, "shared") + if err := os.MkdirAll(serverRoot, 0700); err != nil { + return "", err + } + return serverRoot, nil +} + +func rootDataDir() (string, error) { + if xdh := os.Getenv("XDG_DATA_HOME"); xdh != "" { + return filepath.Join(xdh, "buildx"), nil + } + home := os.Getenv("HOME") + if home == "" { + return "", fmt.Errorf("environment variable HOME is not set") + } + return filepath.Join(home, ".local/share/buildx"), nil +} + +type BuildxInfo struct { + Name string `json:"name"` + Pid int `json:"pid"` + Started time.Time `json:"started"` +} + +func writeBuildxInfo(dir string, info BuildxInfo) (string, error) { + dt, err := json.MarshalIndent(info, "", " ") + if err != nil { + return "", err + } + f := filepath.Join(dir, "info") + if err := os.WriteFile(f, dt, 0600); err != nil { + return "", err + } + return f, nil +} + +func readBuildxInfo(dir string) (*BuildxInfo, error) { + infoB, err := os.ReadFile(filepath.Join(dir, "info")) + if err != nil { + return nil, err + } + info := BuildxInfo{} + if err := json.Unmarshal(infoB, &info); err != nil { + return nil, err + } + return &info, nil +} + +func newBuildxClientAndCheck(addr string, checkNum int, duration time.Duration) (*builder.Client, error) { + c, err := builder.NewClient(addr) + if err != nil { + return nil, err + } + var lastErr error + for i := 0; i < checkNum; i++ { + _, err := c.List(context.TODO()) + if err == nil { + lastErr = nil + break + } + err = fmt.Errorf("failed to access server (tried %d times): %w", i, err) + logrus.Debugf("connection failure: %v", err) + lastErr = err + time.Sleep(duration) + } + if lastErr != nil { + return nil, lastErr + } + p, v, r, err := c.Version(context.TODO()) + if err != nil { + return nil, err + } + logrus.Debugf("connected to server (\"%v %v %v\")", p, v, r) + if !(p == version.Package && v == version.Version && r == version.Revision) { + logrus.Warnf("version mismatch (server: \"%v %v %v\", client: \"%v %v %v\"); please kill and restart buildx server", + p, v, r, version.Package, version.Version, version.Revision) + } + return c, nil +} + +type buildxController struct { + *builder.Client + serverRoot string +} + +func (c *buildxController) Kill(ctx context.Context) error { + info, err := readBuildxInfo(c.serverRoot) + if err != nil { + return err + } + if info.Pid <= 0 { + return fmt.Errorf("no PID is recorded for buildx server") + } + p, err := os.FindProcess(int(info.Pid)) + if err != nil { + return err + } + if err := p.Signal(syscall.SIGINT); err != nil { + return err + } + // TODO: send SIGKILL if process doesn't finish. + return nil +} + +func launch(ctx context.Context, setSid bool, logFile string, args ...string) (func() error, error) { + bCmd := exec.CommandContext(ctx, os.Args[0], args...) + if logFile != "" { + f, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return nil, err + } + defer f.Close() + bCmd.Stdout = f + bCmd.Stderr = f + } + cwd, err := os.Getwd() + if err != nil { + return nil, err + } + bCmd.Dir = cwd + bCmd.SysProcAttr = &syscall.SysProcAttr{ + Setsid: setSid, + } + if err := bCmd.Start(); err != nil { + return nil, err + } + return bCmd.Wait, nil +} diff --git a/commands/builderremote_nolinux.go b/commands/builderremote_nolinux.go new file mode 100644 index 000000000000..9ed3280319d9 --- /dev/null +++ b/commands/builderremote_nolinux.go @@ -0,0 +1,18 @@ +//go:build !linux + +package commands + +import ( + "context" + "fmt" + + "github.com/docker/buildx/monitor" + "github.com/docker/cli/cli/command" + "github.com/spf13/cobra" +) + +func newRemoteBuildxController(ctx context.Context, opts buildOptions) (monitor.BuildxController, error) { + return nil, fmt.Errorf("remote buildx unsupported") +} + +func addBuilderCommands(cmd *cobra.Command, dockerCli command.Cli, rootOpts *rootOptions) {} diff --git a/commands/root.go b/commands/root.go index 8449b2e2d3b9..5799fef6f080 100644 --- a/commands/root.go +++ b/commands/root.go @@ -86,6 +86,9 @@ func addCommands(cmd *cobra.Command, dockerCli command.Cli) { duCmd(dockerCli, opts), imagetoolscmd.RootCmd(dockerCli, imagetoolscmd.RootOptions{Builder: &opts.builder}), ) + if isExperimental() { + addBuilderCommands(cmd, dockerCli, opts) + } } func rootFlags(options *rootOptions, flags *pflag.FlagSet) { diff --git a/docs/reference/buildx.md b/docs/reference/buildx.md index 48acfad8ec4a..df51c8e720c8 100644 --- a/docs/reference/buildx.md +++ b/docs/reference/buildx.md @@ -11,6 +11,8 @@ Extended build capabilities with BuildKit | Name | Description | | --- | --- | +| [`_INTERNAL_LAUNCH`](buildx__INTERNAL_LAUNCH.md) | | +| [`_INTERNAL_SERVE`](buildx__INTERNAL_SERVE.md) | | | [`bake`](buildx_bake.md) | Build from a file | | [`build`](buildx_build.md) | Start a build | | [`create`](buildx_create.md) | Create a new builder instance | diff --git a/docs/reference/buildx__INTERNAL_LAUNCH.md b/docs/reference/buildx__INTERNAL_LAUNCH.md new file mode 100644 index 000000000000..e6ca1a5da9f3 --- /dev/null +++ b/docs/reference/buildx__INTERNAL_LAUNCH.md @@ -0,0 +1,13 @@ +# docker buildx _INTERNAL_LAUNCH + + +### Options + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| `--builder` | `string` | | Override the configured builder instance | +| `--config` | `string` | | Specify buildx server config file | + + + + diff --git a/docs/reference/buildx__INTERNAL_SERVE.md b/docs/reference/buildx__INTERNAL_SERVE.md new file mode 100644 index 000000000000..8f0718cc6f9c --- /dev/null +++ b/docs/reference/buildx__INTERNAL_SERVE.md @@ -0,0 +1,13 @@ +# docker buildx _INTERNAL_SERVE + + +### Options + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| `--builder` | `string` | | Override the configured builder instance | +| `--config` | `string` | | Specify buildx server config file | + + + + diff --git a/docs/reference/buildx_build.md b/docs/reference/buildx_build.md index ebc02a4dd7b0..2e4a23e3849e 100644 --- a/docs/reference/buildx_build.md +++ b/docs/reference/buildx_build.md @@ -23,6 +23,7 @@ Start a build | [`--cache-from`](#cache-from) | `stringArray` | | External cache sources (e.g., `user/app:cache`, `type=local,src=path/to/dir`) | | [`--cache-to`](#cache-to) | `stringArray` | | Cache export destinations (e.g., `user/app:cache`, `type=local,dest=path/to/dir`) | | [`--cgroup-parent`](https://docs.docker.com/engine/reference/commandline/build/#use-a-custom-parent-cgroup---cgroup-parent) | `string` | | Optional parent cgroup for the container | +| `--detach` | | | Detach buildx server (supported only on linux) [experimental] | | [`-f`](https://docs.docker.com/engine/reference/commandline/build/#specify-a-dockerfile--f), [`--file`](https://docs.docker.com/engine/reference/commandline/build/#specify-a-dockerfile--f) | `string` | | Name of the Dockerfile (default: `PATH/Dockerfile`) | | `--iidfile` | `string` | | Write the image ID to the file | | `--invoke` | `string` | | Invoke a command after the build [experimental] | @@ -39,7 +40,9 @@ Start a build | `--pull` | | | Always attempt to pull all referenced images | | [`--push`](#push) | | | Shorthand for `--output=type=registry` | | `-q`, `--quiet` | | | Suppress the build output and print image ID on success | +| `--root` | `string` | | Specify root directory of server to connect [experimental] | | [`--secret`](#secret) | `stringArray` | | Secret to expose to the build (format: `id=mysecret[,src=/local/secret]`) | +| `--server-config` | `string` | | Specify buildx server config file (used only when launching new server) [experimental] | | [`--shm-size`](#shm-size) | `bytes` | `0` | Size of `/dev/shm` | | [`--ssh`](#ssh) | `stringArray` | | SSH agent socket or keys to expose to the build (format: `default\|[=\|[,]]`) | | [`-t`](https://docs.docker.com/engine/reference/commandline/build/#tag-an-image--t), [`--tag`](https://docs.docker.com/engine/reference/commandline/build/#tag-an-image--t) | `stringArray` | | Name and optionally a tag (format: `name:tag`) | diff --git a/go.mod b/go.mod index 42d4c5dceeaa..6a0f94c9b001 100644 --- a/go.mod +++ b/go.mod @@ -13,10 +13,12 @@ require ( github.com/docker/docker v20.10.17+incompatible // v22.06.x - see "replace" for the actual version github.com/docker/go-units v0.4.0 github.com/gofrs/flock v0.7.3 + github.com/gogo/protobuf v1.3.2 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840 github.com/hashicorp/hcl/v2 v2.8.2 github.com/moby/buildkit v0.10.1-0.20220816171719-55ba9d14360a + github.com/moby/sys/signal v0.6.0 github.com/morikuni/aec v1.0.0 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 @@ -88,7 +90,6 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-sql-driver/mysql v1.6.0 // indirect github.com/gogo/googleapis v1.4.1 // indirect - github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.2.0 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/certificate-transparency-go v1.0.21 // indirect @@ -115,7 +116,6 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/locker v1.0.1 // indirect github.com/moby/spdystream v0.2.0 // indirect - github.com/moby/sys/signal v0.6.0 // indirect github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect diff --git a/hack/dockerfiles/generated-files.Dockerfile b/hack/dockerfiles/generated-files.Dockerfile new file mode 100644 index 000000000000..64898d701813 --- /dev/null +++ b/hack/dockerfiles/generated-files.Dockerfile @@ -0,0 +1,53 @@ +# Forked from https://github.com/moby/buildkit/blob/v0.10.4/hack/dockerfiles/generated-files.Dockerfile +# Copyright The BuildKit Authors. +# Copyright The Buildx Authors. +# Licensed under the Apache License, Version 2.0 + +# syntax=docker/dockerfile-upstream:master + +# protoc is dynamically linked to glibc to can't use golang:1.19-alpine +FROM golang:1.19-buster AS gobuild-base + +RUN apt-get update && apt-get --no-install-recommends install -y \ + unzip \ + && true + +# https://github.com/golang/protobuf/blob/v1.3.5/.travis.yml#L15 +ARG PROTOC_VERSION=3.11.4 +ARG TARGETOS TARGETARCH +RUN set -e; \ + arch=$(echo $TARGETARCH | sed -e s/amd64/x86_64/ -e s/arm64/aarch_64/); \ + wget -q https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-${TARGETOS}-${arch}.zip && unzip protoc-${PROTOC_VERSION}-${TARGETOS}-${arch}.zip -d /usr/local + +ARG GOGO_VERSION=v1.3.2 +RUN --mount=target=/root/.cache,type=cache GO111MODULE=on go install \ + github.com/gogo/protobuf/protoc-gen-gogo@${GOGO_VERSION} \ + github.com/gogo/protobuf/protoc-gen-gogofaster@${GOGO_VERSION} \ + github.com/gogo/protobuf/protoc-gen-gogoslick@${GOGO_VERSION} + +ARG PROTOBUF_VERSION=v1.3.5 +RUN --mount=target=/root/.cache,type=cache GO111MODULE=on go install \ + github.com/golang/protobuf/protoc-gen-go@${PROTOBUF_VERSION} + +WORKDIR /go/src/github.com/docker/buildx + +# Generate into a subdirectory because if it is in the root then the +# extraction with `docker export` ends up putting `.dockerenv`, `dev`, +# `sys` and `proc` into the source directory. With this we can use +# `tar --strip-components=1 generated-files` on the output of `docker +# export`. +FROM gobuild-base AS generated +RUN mkdir /generated-files +RUN --mount=target=/tmp/src \ + cp -r /tmp/src/. . && \ + git add -A && \ + go generate -mod=vendor -v ./... && \ + git ls-files -m --others -- **/*.pb.go | tar -cf - --files-from - | tar -C /generated-files -xf - + +FROM scratch AS update +COPY --from=generated /generated-files /generated-files + +FROM gobuild-base AS validate +RUN --mount=target=/tmp/src \ + cp -r /tmp/src/. . && \ + go generate -mod=vendor -v ./... && git diff && ./hack/validate-generated-files check diff --git a/hack/update-generated-files b/hack/update-generated-files new file mode 100755 index 000000000000..907f989524fd --- /dev/null +++ b/hack/update-generated-files @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# Forked from: https://github.com/moby/buildkit/blob/v0.10.4/hack/update-generated-files +# Copyright The BuildKit Authors. +# Copyright The Buildx Authors. +# Licensed under the Apache License, Version 2.0 + +set -eu -o pipefail + +: ${BUILDX_CMD=docker buildx} + +gogo_version=$(awk '$1 == "github.com/gogo/protobuf" { print $2 }' go.mod) +# protobuf_version=$(awk '$3 == "github.com/golang/protobuf" { print $4 }' go.mod) +output=$(mktemp -d -t buildx-output.XXXXXXXXXX) + +${BUILDX_CMD} build \ + --target "update" \ + --build-arg "GOGO_VERSION=$gogo_version" \ + --output "type=local,dest=$output" \ + --file "./hack/dockerfiles/generated-files.Dockerfile" \ + . + +# --build-arg "PROTOBUF_VERSION=$protobuf_version" \ + +cp -R "$output/generated-files/." . +rm -rf $output diff --git a/hack/validate-generated-files b/hack/validate-generated-files new file mode 100755 index 000000000000..e01ff6060d26 --- /dev/null +++ b/hack/validate-generated-files @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# Forked from: https://github.com/moby/buildkit/blob/v0.10.4/hack/validate-generated-files +# Copyright The BuildKit Authors. +# Copyright The Buildx Authors. +# Licensed under the Apache License, Version 2.0 + +set -eu + +case ${1:-} in + '') + gogo_version=$(awk '$1 == "github.com/gogo/protobuf" { print $2 }' go.mod) + : ${BUILDX_CMD=docker buildx} + ${BUILDX_CMD} build \ + --target validate \ + --build-arg "GOGO_VERSION=$gogo_version" \ + --file ./hack/dockerfiles/generated-files.Dockerfile \ + . + ;; + check) + diffs="$(git status --porcelain -- **/*.pb.go 2>/dev/null)" + set +x + if [ "$diffs" ]; then + { + echo 'The result of "go generate" differs' + echo + echo "$diffs" + echo + echo 'Please update with "make generated-files"' + echo + } >&2 + exit 1 + fi + echo 'Congratulations! All auto generated files are correct.' + ;; +esac diff --git a/monitor/monitor.go b/monitor/monitor.go index fbddbdd67fad..46858297df4b 100644 --- a/monitor/monitor.go +++ b/monitor/monitor.go @@ -1,66 +1,103 @@ package monitor import ( - "bufio" "context" "fmt" "io" + "sort" + "strings" "sync" + "text/tabwriter" - "github.com/docker/buildx/build" - "github.com/pkg/errors" + "github.com/containerd/console" + builderapi "github.com/docker/buildx/commands/builder/pb" + "github.com/docker/buildx/util/ioset" "github.com/sirupsen/logrus" "golang.org/x/term" ) const helpMessage = ` Available commads are: - reload reloads the context and build it. - rollback re-runs the interactive container with initial rootfs contents. - exit exits monitor. - help shows this message. + reload reloads the context and build it. + rollback re-runs the interactive container with initial rootfs contents. + list list buildx sessions. + attach attach to a buildx server. + disconnect disconnect a client from a buildx server. Specific session ID can be specified an arg. + kill kill buildx server. + exit exits monitor. + help shows this message. ` +type BuildxController interface { + Invoke(ctx context.Context, ref string, options builderapi.ContainerConfig, ioIn io.ReadCloser, ioOut io.WriteCloser, ioErr io.WriteCloser) error + Build(ctx context.Context, options builderapi.BuildOptions, in io.ReadCloser, w io.Writer, out console.File, progressMode string) (ref string, err error) + Kill(ctx context.Context) error + Close() error + List(ctx context.Context) (res []string, _ error) + Disconnect(ctx context.Context, ref string) error +} + // RunMonitor provides an interactive session for running and managing containers via specified IO. -func RunMonitor(ctx context.Context, containerConfig build.ContainerConfig, reloadFunc func(context.Context) (*build.ResultContext, error), stdin io.ReadCloser, stdout, stderr io.WriteCloser) error { - monitorIn, monitorOut := ioSetPipe() - defer monitorIn.Close() +func RunMonitor(ctx context.Context, curRef string, options builderapi.BuildOptions, invokeConfig builderapi.ContainerConfig, c BuildxController, progressMode string, stdin io.ReadCloser, stdout io.WriteCloser, stderr console.File) error { + defer func() { + if err := c.Disconnect(ctx, curRef); err != nil { + logrus.Warnf("disconnect error: %v", err) + } + }() + monitorIn, monitorOut := ioset.Pipe() + defer func() { + monitorIn.Close() + }() monitorEnableCh := make(chan struct{}) monitorDisableCh := make(chan struct{}) - monitorOutCtx := ioSetOutContext{monitorOut, - func() { monitorEnableCh <- struct{}{} }, - func() { monitorDisableCh <- struct{}{} }, + monitorOutCtx := ioset.MuxOut{ + Out: monitorOut, + EnableHook: func() { monitorEnableCh <- struct{}{} }, + DisableHook: func() { monitorDisableCh <- struct{}{} }, } - containerIn, containerOut := ioSetPipe() - defer containerIn.Close() - containerOutCtx := ioSetOutContext{containerOut, + containerIn, containerOut := ioset.Pipe() + defer func() { + containerIn.Close() + }() + containerOutCtx := ioset.MuxOut{ + Out: containerOut, // send newline to hopefully get the prompt; TODO: better UI (e.g. reprinting the last line) - func() { containerOut.stdin.Write([]byte("\n")) }, - func() {}, + EnableHook: func() { containerOut.Stdin.Write([]byte("\n")) }, + DisableHook: func() {}, } + invokeForwarder := ioset.NewForwarder() + invokeForwarder.SetIn(&containerIn) m := &monitor{ - invokeIO: newIOForwarder(containerIn), - muxIO: newMuxIO(ioSetIn{stdin, stdout, stderr}, []ioSetOutContext{monitorOutCtx, containerOutCtx}, 1, func(prev int, res int) string { + invokeIO: invokeForwarder, + muxIO: ioset.NewMuxIO(ioset.In{ + Stdin: io.NopCloser(stdin), + Stdout: nopCloser{stdout}, + Stderr: nopCloser{stderr}, + }, []ioset.MuxOut{monitorOutCtx, containerOutCtx}, 1, func(prev int, res int) string { if prev == 0 && res == 0 { // No toggle happened because container I/O isn't enabled. return "No running interactive containers. You can start one by issuing rollback command\n" } return "Switched IO\n" }), + invokeFunc: func(ctx context.Context, ref string, in io.ReadCloser, out io.WriteCloser, err io.WriteCloser) error { + return c.Invoke(ctx, ref, invokeConfig, in, out, err) + }, } // Start container automatically fmt.Fprintf(stdout, "Launching interactive container. Press Ctrl-a-c to switch to monitor console\n") - m.rollback(ctx, containerConfig) + m.rollback(ctx, curRef) // Serve monitor commands - monitorForwarder := newIOForwarder(monitorIn) + monitorForwarder := ioset.NewForwarder() + monitorForwarder.SetIn(&monitorIn) for { <-monitorEnableCh - in, out := ioSetPipe() - monitorForwarder.setDestination(&out) + in, out := ioset.Pipe() + monitorForwarder.SetOut(&out) doneCh, errCh := make(chan struct{}), make(chan error) go func() { defer close(doneCh) @@ -69,7 +106,7 @@ func RunMonitor(ctx context.Context, containerConfig build.ContainerConfig, relo <-ctx.Done() in.Close() }() - t := term.NewTerminal(readWriter{in.stdin, in.stdout}, "(buildx) ") + t := term.NewTerminal(readWriter{in.Stdin, in.Stdout}, "(buildx) ") for { l, err := t.ReadLine() if err != nil { @@ -79,22 +116,63 @@ func RunMonitor(ctx context.Context, containerConfig build.ContainerConfig, relo } return } - switch l { + args := strings.Fields(l) // TODO: use shlex + if len(args) == 0 { + continue + } + switch args[0] { case "": // nop case "reload": - res, err := reloadFunc(ctx) + if curRef != "" { + if err := c.Disconnect(ctx, curRef); err != nil { + fmt.Println("disconnect error", err) + } + } + ref, err := c.Build(ctx, options, nil, stdout, stderr, progressMode) // TODO: support stdin, hold build ref if err != nil { fmt.Printf("failed to reload: %v\n", err) } else { + curRef = ref // rollback the running container with the new result - containerConfig.ResultCtx = res - m.rollback(ctx, containerConfig) + m.rollback(ctx, curRef) fmt.Fprint(stdout, "Interactive container was restarted. Press Ctrl-a-c to switch to the new container\n") } case "rollback": - m.rollback(ctx, containerConfig) + m.rollback(ctx, curRef) fmt.Fprint(stdout, "Interactive container was restarted. Press Ctrl-a-c to switch to the new container\n") + case "list": + refs, err := c.List(ctx) + if err != nil { + fmt.Printf("failed to list: %v\n", err) + } + sort.Strings(refs) + tw := tabwriter.NewWriter(stdout, 1, 8, 1, '\t', 0) + fmt.Fprintln(tw, "ID\tCURRENT_SESSION") + for _, k := range refs { + fmt.Fprintf(tw, "%-20s\t%v\n", k, k == curRef) + } + tw.Flush() + case "disconnect": + target := curRef + if len(args) >= 2 { + target = args[1] + } + if err := c.Disconnect(ctx, target); err != nil { + fmt.Println("disconnect error", err) + } + case "kill": + if err := c.Kill(ctx); err != nil { + fmt.Printf("failed to kill: %v\n", err) + } + case "attach": + if len(args) < 2 { + fmt.Println("attach: server name must be passed") + continue + } + ref := args[1] + m.rollback(ctx, ref) + curRef = ref case "exit": return case "help": @@ -118,7 +196,7 @@ func RunMonitor(ctx context.Context, containerConfig build.ContainerConfig, relo return err case <-monitorDisableCh: } - monitorForwarder.setDestination(nil) + monitorForwarder.SetOut(nil) } } @@ -128,36 +206,37 @@ type readWriter struct { } type monitor struct { - muxIO *muxIO - invokeIO *ioForwarder + muxIO *ioset.MuxIO + invokeIO *ioset.Forwarder + invokeFunc func(context.Context, string, io.ReadCloser, io.WriteCloser, io.WriteCloser) error curInvokeCancel func() } -func (m *monitor) rollback(ctx context.Context, cfg build.ContainerConfig) { +func (m *monitor) rollback(ctx context.Context, ref string) { if m.curInvokeCancel != nil { m.curInvokeCancel() // Finish the running container if exists } go func() { // Start a new container - if err := m.invoke(ctx, cfg); err != nil { + if err := m.invoke(ctx, ref); err != nil { logrus.Debugf("invoke error: %v", err) } }() } -func (m *monitor) invoke(ctx context.Context, cfg build.ContainerConfig) error { - m.muxIO.enable(1) - defer m.muxIO.disable(1) +func (m *monitor) invoke(ctx context.Context, ref string) error { + m.muxIO.Enable(1) + defer m.muxIO.Disable(1) invokeCtx, invokeCancel := context.WithCancel(ctx) - containerIn, containerOut := ioSetPipe() - m.invokeIO.setDestination(&containerOut) + containerIn, containerOut := ioset.Pipe() + m.invokeIO.SetOut(&containerOut) waitInvokeDoneCh := make(chan struct{}) var cancelOnce sync.Once curInvokeCancel := func() { cancelOnce.Do(func() { containerIn.Close() - m.invokeIO.setDestination(nil) + m.invokeIO.SetOut(nil) invokeCancel() }) <-waitInvokeDoneCh @@ -165,352 +244,14 @@ func (m *monitor) invoke(ctx context.Context, cfg build.ContainerConfig) error { defer curInvokeCancel() m.curInvokeCancel = curInvokeCancel - cfg.Stdin = containerIn.stdin - cfg.Stdout = containerIn.stdout - cfg.Stderr = containerIn.stderr - err := build.Invoke(invokeCtx, cfg) + err := m.invokeFunc(invokeCtx, ref, containerIn.Stdin, containerIn.Stdout, containerIn.Stderr) close(waitInvokeDoneCh) return err } -type ioForwarder struct { - curIO *ioSetOut - mu sync.Mutex - updateCh chan struct{} -} - -func newIOForwarder(in ioSetIn) *ioForwarder { - f := &ioForwarder{ - updateCh: make(chan struct{}), - } - doneCh := make(chan struct{}) - go func() { - for { - f.mu.Lock() - w := f.curIO - f.mu.Unlock() - if w != nil && w.stdout != nil && w.stderr != nil { - go func() { - if _, err := io.Copy(in.stdout, w.stdout); err != nil && err != io.ErrClosedPipe { - // ErrClosedPipe is OK as we close this read end during setDestination. - logrus.WithError(err).Warnf("failed to forward stdout: %v", err) - } - }() - go func() { - if _, err := io.Copy(in.stderr, w.stderr); err != nil && err != io.ErrClosedPipe { - // ErrClosedPipe is OK as we close this read end during setDestination. - logrus.WithError(err).Warnf("failed to forward stderr: %v", err) - } - }() - } - select { - case <-f.updateCh: - case <-doneCh: - return - } - } - }() - go func() { - if err := copyToFunc(in.stdin, func() (io.Writer, error) { - f.mu.Lock() - w := f.curIO - f.mu.Unlock() - if w != nil { - return w.stdin, nil - } - return nil, nil - }); err != nil && err != io.ErrClosedPipe { - logrus.WithError(err).Warnf("failed to forward IO: %v", err) - } - close(doneCh) - - if w := f.curIO; w != nil { - // Propagate close - if err := w.Close(); err != nil { - logrus.WithError(err).Warnf("failed to forwarded stdin IO: %v", err) - } - } - }() - return f -} - -func (f *ioForwarder) setDestination(out *ioSetOut) { - f.mu.Lock() - if f.curIO != nil { - // close all stream on the current IO no to mix with the new IO - f.curIO.Close() - } - f.curIO = out - f.mu.Unlock() - f.updateCh <- struct{}{} -} - -type ioSetOutContext struct { - ioSetOut - enableHook func() - disableHook func() -} - -// newMuxIO forwards IO stream to/from "in" and "outs". -// "outs" are closed automatically when "in" reaches EOF. -// "in" doesn't closed automatically so the caller needs to explicitly close it. -func newMuxIO(in ioSetIn, out []ioSetOutContext, initIdx int, toggleMessage func(prev int, res int) string) *muxIO { - m := &muxIO{ - enabled: make(map[int]struct{}), - in: in, - out: out, - closedCh: make(chan struct{}), - toggleMessage: toggleMessage, - } - for i := range out { - m.enabled[i] = struct{}{} - } - m.maxCur = len(out) - m.cur = initIdx - var wg sync.WaitGroup - var mu sync.Mutex - for i, o := range out { - i, o := i, o - wg.Add(1) - go func() { - defer wg.Done() - if err := copyToFunc(o.stdout, func() (io.Writer, error) { - if m.cur == i { - return in.stdout, nil - } - return nil, nil - }); err != nil { - logrus.WithField("output index", i).WithError(err).Warnf("failed to write stdout") - } - if err := o.stdout.Close(); err != nil { - logrus.WithField("output index", i).WithError(err).Warnf("failed to close stdout") - } - }() - wg.Add(1) - go func() { - defer wg.Done() - if err := copyToFunc(o.stderr, func() (io.Writer, error) { - if m.cur == i { - return in.stderr, nil - } - return nil, nil - }); err != nil { - logrus.WithField("output index", i).WithError(err).Warnf("failed to write stderr") - } - if err := o.stderr.Close(); err != nil { - logrus.WithField("output index", i).WithError(err).Warnf("failed to close stderr") - } - }() - } - go func() { - errToggle := errors.Errorf("toggle IO") - for { - prevIsControlSequence := false - if err := copyToFunc(traceReader(in.stdin, func(r rune) (bool, error) { - // Toggle IO when it detects C-a-c - // TODO: make it configurable if needed - if int(r) == 1 { - prevIsControlSequence = true - return false, nil - } - defer func() { prevIsControlSequence = false }() - if prevIsControlSequence { - if string(r) == "c" { - return false, errToggle - } - } - return true, nil - }), func() (io.Writer, error) { - mu.Lock() - o := out[m.cur] - mu.Unlock() - return o.stdin, nil - }); !errors.Is(err, errToggle) { - if err != nil { - logrus.WithError(err).Warnf("failed to read stdin") - } - break - } - m.toggleIO() - } - - // propagate stdin EOF - for i, o := range out { - if err := o.stdin.Close(); err != nil { - logrus.WithError(err).Warnf("failed to close stdin of %d", i) - } - } - wg.Wait() - close(m.closedCh) - }() - return m -} - -type muxIO struct { - cur int - maxCur int - enabled map[int]struct{} - mu sync.Mutex - in ioSetIn - out []ioSetOutContext - closedCh chan struct{} - toggleMessage func(prev int, res int) string -} - -func (m *muxIO) waitClosed() { - <-m.closedCh -} - -func (m *muxIO) enable(i int) { - m.mu.Lock() - defer m.mu.Unlock() - m.enabled[i] = struct{}{} -} - -func (m *muxIO) disable(i int) error { - m.mu.Lock() - defer m.mu.Unlock() - if i == 0 { - return errors.Errorf("disabling 0th io is prohibited") - } - delete(m.enabled, i) - if m.cur == i { - m.toggleIO() - } - return nil -} - -func (m *muxIO) toggleIO() { - if m.out[m.cur].disableHook != nil { - m.out[m.cur].disableHook() - } - prev := m.cur - for { - if m.cur+1 >= m.maxCur { - m.cur = 0 - } else { - m.cur++ - } - if _, ok := m.enabled[m.cur]; !ok { - continue - } - break - } - res := m.cur - if m.out[m.cur].enableHook != nil { - m.out[m.cur].enableHook() - } - fmt.Fprint(m.in.stdout, m.toggleMessage(prev, res)) -} - -func traceReader(r io.ReadCloser, f func(rune) (bool, error)) io.ReadCloser { - pr, pw := io.Pipe() - go func() { - br := bufio.NewReader(r) - for { - rn, _, err := br.ReadRune() - if err != nil { - if err == io.EOF { - pw.Close() - return - } - pw.CloseWithError(err) - return - } - if isWrite, err := f(rn); err != nil { - pw.CloseWithError(err) - return - } else if !isWrite { - continue - } - if _, err := pw.Write([]byte(string(rn))); err != nil { - pw.CloseWithError(err) - return - } - } - }() - return &readerWithClose{ - Reader: pr, - closeFunc: func() error { - pr.Close() - return r.Close() - }, - } -} - -func copyToFunc(r io.Reader, wFunc func() (io.Writer, error)) error { - buf := make([]byte, 4096) - for { - n, readErr := r.Read(buf) - if readErr != nil && readErr != io.EOF { - return readErr - } - w, err := wFunc() - if err != nil { - return err - } - if w != nil { - if _, err := w.Write(buf[:n]); err != nil { - logrus.WithError(err).Debugf("failed to copy") - } - } - if readErr == io.EOF { - return nil - } - } -} - -func ioSetPipe() (ioSetIn, ioSetOut) { - r1, w1 := io.Pipe() - r2, w2 := io.Pipe() - r3, w3 := io.Pipe() - return ioSetIn{r1, w2, w3}, ioSetOut{w1, r2, r3} -} - -type ioSetIn struct { - stdin io.ReadCloser - stdout io.WriteCloser - stderr io.WriteCloser -} - -func (s ioSetIn) Close() (retErr error) { - if err := s.stdin.Close(); err != nil { - retErr = err - } - if err := s.stdout.Close(); err != nil { - retErr = err - } - if err := s.stderr.Close(); err != nil { - retErr = err - } - return -} - -type ioSetOut struct { - stdin io.WriteCloser - stdout io.ReadCloser - stderr io.ReadCloser -} - -func (s ioSetOut) Close() (retErr error) { - if err := s.stdin.Close(); err != nil { - retErr = err - } - if err := s.stdout.Close(); err != nil { - retErr = err - } - if err := s.stderr.Close(); err != nil { - retErr = err - } - return -} - -type readerWithClose struct { - io.Reader - closeFunc func() error +type nopCloser struct { + io.Writer } -func (r *readerWithClose) Close() error { - return r.closeFunc() -} +func (c nopCloser) Close() error { return nil } diff --git a/util/ioset/ioset.go b/util/ioset/ioset.go new file mode 100644 index 000000000000..0d9911782c1d --- /dev/null +++ b/util/ioset/ioset.go @@ -0,0 +1,230 @@ +package ioset + +import ( + "io" + "sync" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +func Pipe() (In, Out) { + r1, w1 := io.Pipe() + r2, w2 := io.Pipe() + r3, w3 := io.Pipe() + return In{r1, w2, w3}, Out{w1, r2, r3} +} + +type In struct { + Stdin io.ReadCloser + Stdout io.WriteCloser + Stderr io.WriteCloser +} + +func (s In) Close() (retErr error) { + if err := s.Stdin.Close(); err != nil { + retErr = err + } + if err := s.Stdout.Close(); err != nil { + retErr = err + } + if err := s.Stderr.Close(); err != nil { + retErr = err + } + return +} + +type Out struct { + Stdin io.WriteCloser + Stdout io.ReadCloser + Stderr io.ReadCloser +} + +func (s Out) Close() (retErr error) { + if err := s.Stdin.Close(); err != nil { + retErr = err + } + if err := s.Stdout.Close(); err != nil { + retErr = err + } + if err := s.Stderr.Close(); err != nil { + retErr = err + } + return +} + +type Forwarder struct { + stdin *SingleForwarder + stdout *SingleForwarder + stderr *SingleForwarder + mu sync.Mutex + + PropagateStdinClose bool +} + +func NewForwarder() *Forwarder { + return &Forwarder{ + stdin: NewSingleForwarder(), + stdout: NewSingleForwarder(), + stderr: NewSingleForwarder(), + PropagateStdinClose: true, + } +} + +func (f *Forwarder) Close() (retErr error) { + if err := f.stdin.Close(); err != nil { + retErr = err + } + if err := f.stdout.Close(); err != nil { + retErr = err + } + if err := f.stderr.Close(); err != nil { + retErr = err + } + return retErr +} + +func (f *Forwarder) SetOut(out *Out) { + f.mu.Lock() + if out == nil { + f.stdin.SetWriter(nil, func() io.WriteCloser { return nil }) + f.stdout.SetReader(nil) + f.stderr.SetReader(nil) + } else { + f.stdin.SetWriter(out.Stdin, func() io.WriteCloser { + if f.PropagateStdinClose { + out.Stdin.Close() // propagate EOF + logrus.Debug("forwarder: propagating stdin close") + return nil + } + return out.Stdin + }) + f.stdout.SetReader(out.Stdout) + f.stderr.SetReader(out.Stderr) + } + f.mu.Unlock() +} + +func (f *Forwarder) SetIn(in *In) { + f.mu.Lock() + if in == nil { + f.stdin.SetReader(nil) + f.stdout.SetWriter(nil, func() io.WriteCloser { return nil }) + f.stderr.SetWriter(nil, func() io.WriteCloser { return nil }) + } else { + f.stdin.SetReader(in.Stdin) + f.stdout.SetWriter(in.Stdout, func() io.WriteCloser { + return in.Stdout // continue write; TODO: make it configurable if needed + }) + f.stderr.SetWriter(in.Stderr, func() io.WriteCloser { + return in.Stderr // continue write; TODO: make it configurable if needed + }) + } + f.mu.Unlock() +} + +type SingleForwarder struct { + curR io.ReadCloser // closed when set another reader + curW io.WriteCloser // closed when set another writer + curWEOFHandler func() io.WriteCloser + curMu sync.Mutex + + updateRCh chan io.ReadCloser + doneCh chan struct{} + + closeOnce sync.Once +} + +func NewSingleForwarder() *SingleForwarder { + f := &SingleForwarder{ + updateRCh: make(chan io.ReadCloser), + doneCh: make(chan struct{}), + } + go func() { + for { + if r := f.curR; r != nil { + go func() { + buf := make([]byte, 4096) + for { + doReturn := func() bool { + n, readErr := r.Read(buf) + if readErr != nil && readErr != io.EOF { + if !errors.Is(readErr, io.ErrClosedPipe) { + logrus.Debugf("single forwarder: reader error: %v", readErr) + } + return true + } + f.curMu.Lock() + w := f.curW + f.curMu.Unlock() + if w != nil { + if _, err := w.Write(buf[:n]); err != nil { + if !errors.Is(err, io.ErrClosedPipe) { + logrus.Debugf("single forwarder: writer error: %v", err) + } + } + } + if readErr == io.EOF { + var newW io.WriteCloser + if f.curWEOFHandler != nil { + newW = f.curWEOFHandler() + } + f.curMu.Lock() + f.curW = newW + f.curMu.Unlock() + return true + } + return false + }() + if doReturn { + return + } + } + }() + } + select { + case newR := <-f.updateRCh: + if f.curR != nil { + f.curR.Close() + } + f.curR = newR + case <-f.doneCh: + return + } + } + }() + return f +} + +func (f *SingleForwarder) Close() (retErr error) { + f.closeOnce.Do(func() { + if r := f.curR; r != nil { + if err := r.Close(); err != nil { + retErr = err + } + } + // TODO: Wait until read data fully written to the current writer if needed. + if w := f.curW; w != nil { + if err := w.Close(); err != nil { + retErr = err + } + } + close(f.doneCh) + }) + return retErr +} + +func (f *SingleForwarder) SetWriter(w io.WriteCloser, curWEOFHandler func() io.WriteCloser) { + f.curMu.Lock() + if f.curW != nil { + // close all stream on the current IO no to mix with the new IO + f.curW.Close() + } + f.curW = w + f.curWEOFHandler = curWEOFHandler + f.curMu.Unlock() +} + +func (f *SingleForwarder) SetReader(r io.ReadCloser) { + f.updateRCh <- r +} diff --git a/util/ioset/mux.go b/util/ioset/mux.go new file mode 100644 index 000000000000..278fd529b8da --- /dev/null +++ b/util/ioset/mux.go @@ -0,0 +1,236 @@ +package ioset + +import ( + "bufio" + "fmt" + "io" + "sync" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +type MuxOut struct { + Out + EnableHook func() + DisableHook func() +} + +// NewMuxIO forwards IO stream to/from "in" and "outs". +// It toggles IO when it detects "C-a-c" key. +// "outs" are closed automatically when "in" reaches EOF. +// "in" doesn't closed automatically so the caller needs to explicitly close it. +func NewMuxIO(in In, outs []MuxOut, initIdx int, toggleMessage func(prev int, res int) string) *MuxIO { + m := &MuxIO{ + enabled: make(map[int]struct{}), + in: in, + outs: outs, + closedCh: make(chan struct{}), + toggleMessage: toggleMessage, + } + for i := range outs { + m.enabled[i] = struct{}{} + } + m.maxCur = len(outs) + m.cur = initIdx + var wg sync.WaitGroup + var mu sync.Mutex + for i, o := range outs { + i, o := i, o + wg.Add(1) + go func() { + defer wg.Done() + if err := copyToFunc(o.Stdout, func() (io.Writer, error) { + if m.cur == i { + return in.Stdout, nil + } + return nil, nil + }); err != nil { + logrus.WithField("output index", i).WithError(err).Warnf("failed to write stdout") + } + if err := o.Stdout.Close(); err != nil { + logrus.WithField("output index", i).WithError(err).Warnf("failed to close stdout") + } + }() + wg.Add(1) + go func() { + defer wg.Done() + if err := copyToFunc(o.Stderr, func() (io.Writer, error) { + if m.cur == i { + return in.Stderr, nil + } + return nil, nil + }); err != nil { + logrus.WithField("output index", i).WithError(err).Warnf("failed to write stderr") + } + if err := o.Stderr.Close(); err != nil { + logrus.WithField("output index", i).WithError(err).Warnf("failed to close stderr") + } + }() + } + go func() { + errToggle := errors.Errorf("toggle IO") + for { + prevIsControlSequence := false + if err := copyToFunc(traceReader(in.Stdin, func(r rune) (bool, error) { + // Toggle IO when it detects C-a-c + // TODO: make it configurable if needed + if int(r) == 1 { + prevIsControlSequence = true + return false, nil + } + defer func() { prevIsControlSequence = false }() + if prevIsControlSequence { + if string(r) == "c" { + return false, errToggle + } + } + return true, nil + }), func() (io.Writer, error) { + mu.Lock() + o := outs[m.cur] + mu.Unlock() + return o.Stdin, nil + }); !errors.Is(err, errToggle) { + if err != nil { + logrus.WithError(err).Warnf("failed to read stdin") + } + break + } + m.toggleIO() + } + + // propagate stdin EOF + for i, o := range outs { + if err := o.Stdin.Close(); err != nil { + logrus.WithError(err).Warnf("failed to close stdin of %d", i) + } + } + wg.Wait() + close(m.closedCh) + }() + return m +} + +type MuxIO struct { + cur int + maxCur int + enabled map[int]struct{} + mu sync.Mutex + in In + outs []MuxOut + closedCh chan struct{} + toggleMessage func(prev int, res int) string +} + +func (m *MuxIO) waitClosed() { + <-m.closedCh +} + +func (m *MuxIO) Enable(i int) { + m.mu.Lock() + defer m.mu.Unlock() + m.enabled[i] = struct{}{} +} + +func (m *MuxIO) Disable(i int) error { + m.mu.Lock() + defer m.mu.Unlock() + if i == 0 { + return errors.Errorf("disabling 0th io is prohibited") + } + delete(m.enabled, i) + if m.cur == i { + m.toggleIO() + } + return nil +} + +func (m *MuxIO) toggleIO() { + if m.outs[m.cur].DisableHook != nil { + m.outs[m.cur].DisableHook() + } + prev := m.cur + for { + if m.cur+1 >= m.maxCur { + m.cur = 0 + } else { + m.cur++ + } + if _, ok := m.enabled[m.cur]; !ok { + continue + } + break + } + res := m.cur + if m.outs[m.cur].EnableHook != nil { + m.outs[m.cur].EnableHook() + } + fmt.Fprint(m.in.Stdout, m.toggleMessage(prev, res)) +} + +func traceReader(r io.ReadCloser, f func(rune) (bool, error)) io.ReadCloser { + pr, pw := io.Pipe() + go func() { + br := bufio.NewReader(r) + for { + rn, _, err := br.ReadRune() + if err != nil { + if err == io.EOF { + pw.Close() + return + } + pw.CloseWithError(err) + return + } + if isWrite, err := f(rn); err != nil { + pw.CloseWithError(err) + return + } else if !isWrite { + continue + } + if _, err := pw.Write([]byte(string(rn))); err != nil { + pw.CloseWithError(err) + return + } + } + }() + return &readerWithClose{ + Reader: pr, + closeFunc: func() error { + pr.Close() + return r.Close() + }, + } +} + +func copyToFunc(r io.Reader, wFunc func() (io.Writer, error)) error { + buf := make([]byte, 4096) + for { + n, readErr := r.Read(buf) + if readErr != nil && readErr != io.EOF { + return readErr + } + w, err := wFunc() + if err != nil { + return err + } + if w != nil { + if _, err := w.Write(buf[:n]); err != nil { + logrus.WithError(err).Debugf("failed to copy") + } + } + if readErr == io.EOF { + return nil + } + } +} + +type readerWithClose struct { + io.Reader + closeFunc func() error +} + +func (r *readerWithClose) Close() error { + return r.closeFunc() +} diff --git a/monitor/monitor_test.go b/util/ioset/mux_test.go similarity index 86% rename from monitor/monitor_test.go rename to util/ioset/mux_test.go index 7a5226e8d080..7aaef570247c 100644 --- a/monitor/monitor_test.go +++ b/util/ioset/mux_test.go @@ -1,4 +1,4 @@ -package monitor +package ioset import ( "bytes" @@ -11,7 +11,7 @@ import ( "golang.org/x/sync/errgroup" ) -// TestMuxIO tests muxIO +// TestMuxIO tests MuxIO func TestMuxIO(t *testing.T) { tests := []struct { name string @@ -122,20 +122,20 @@ func TestMuxIO(t *testing.T) { t.Run(tt.name, func(t *testing.T) { inBuf, end, in := newTestIn(t) var outBufs []*outBuf - var outs []ioSetOutContext + var outs []MuxOut if tt.outputsNum != len(tt.wants) { t.Fatalf("wants != outputsNum") } for i := 0; i < tt.outputsNum; i++ { outBuf, out := newTestOut(t, i) outBufs = append(outBufs, outBuf) - outs = append(outs, ioSetOutContext{out, nil, nil}) + outs = append(outs, MuxOut{out, nil, nil}) } - mio := newMuxIO(in, outs, tt.initIdx, func(prev int, res int) string { return "" }) + mio := NewMuxIO(in, outs, tt.initIdx, func(prev int, res int) string { return "" }) for _, i := range tt.inputs { - // Add input to muxIO + // Add input to MuxIO istr, writeback := i(mio) - if _, err := end.stdin.Write([]byte(istr)); err != nil { + if _, err := end.Stdin.Write([]byte(istr)); err != nil { t.Fatalf("failed to write data to stdin: %v", err) } @@ -143,14 +143,14 @@ func TestMuxIO(t *testing.T) { var eg errgroup.Group eg.Go(func() error { outbuf := make([]byte, len(writeback)) - if _, err := io.ReadAtLeast(end.stdout, outbuf, len(outbuf)); err != nil { + if _, err := io.ReadAtLeast(end.Stdout, outbuf, len(outbuf)); err != nil { return err } return nil }) eg.Go(func() error { errbuf := make([]byte, len(writeback)) - if _, err := io.ReadAtLeast(end.stderr, errbuf, len(errbuf)); err != nil { + if _, err := io.ReadAtLeast(end.Stderr, errbuf, len(errbuf)); err != nil { return err } return nil @@ -160,8 +160,8 @@ func TestMuxIO(t *testing.T) { } } - // Close stdin on this muxIO - end.stdin.Close() + // Close stdin on this MuxIO + end.Stdin.Close() // Wait for all output ends reach EOF mio.waitClosed() @@ -189,30 +189,30 @@ func TestMuxIO(t *testing.T) { } } -type instruction func(m *muxIO) (intput string, writeBackView string) +type instruction func(m *MuxIO) (intput string, writeBackView string) func input(s string) instruction { - return func(m *muxIO) (string, string) { + return func(m *MuxIO) (string, string) { return s, strings.ReplaceAll(s, string([]rune{rune(1)}), "") } } func toggle() instruction { - return func(m *muxIO) (string, string) { + return func(m *MuxIO) (string, string) { return string([]rune{rune(1)}) + "c", "" } } func enable(i int) instruction { - return func(m *muxIO) (string, string) { - m.enable(i) + return func(m *MuxIO) (string, string) { + m.Enable(i) return "", "" } } func disable(i int) instruction { - return func(m *muxIO) (string, string) { - m.disable(i) + return func(m *MuxIO) (string, string) { + m.Disable(i) return "", "" } } @@ -223,7 +223,7 @@ type inBuf struct { doneCh chan struct{} } -func newTestIn(t *testing.T) (*inBuf, ioSetOut, ioSetIn) { +func newTestIn(t *testing.T) (*inBuf, Out, In) { ti := &inBuf{ doneCh: make(chan struct{}), } @@ -253,7 +253,7 @@ func newTestIn(t *testing.T) (*inBuf, ioSetOut, ioSetIn) { close(ti.doneCh) }() inR, inW := io.Pipe() - return ti, ioSetOut{inW, gotOutR, gotErrR}, ioSetIn{inR, outW, errW} + return ti, Out{Stdin: inW, Stdout: gotOutR, Stderr: gotErrR}, In{Stdin: inR, Stdout: outW, Stderr: errW} } type outBuf struct { @@ -262,7 +262,7 @@ type outBuf struct { doneCh chan struct{} } -func newTestOut(t *testing.T, idx int) (*outBuf, ioSetOut) { +func newTestOut(t *testing.T, idx int) (*outBuf, Out) { to := &outBuf{ idx: idx, doneCh: make(chan struct{}), @@ -290,7 +290,7 @@ func newTestOut(t *testing.T, idx int) (*outBuf, ioSetOut) { errW.Close() close(to.doneCh) }() - return to, ioSetOut{inW, outR, errR} + return to, Out{Stdin: inW, Stdout: outR, Stderr: errR} } func writeMasked(w io.Writer, s string) io.Writer { diff --git a/util/progress/writer.go b/util/progress/writer.go index e5cc1ca0e454..7cc63f6a4233 100644 --- a/util/progress/writer.go +++ b/util/progress/writer.go @@ -68,3 +68,24 @@ func NewChannel(w Writer) (chan *client.SolveStatus, chan struct{}) { }() return ch, done } + +type tee struct { + Writer + ch chan *client.SolveStatus +} + +func (t *tee) Write(v *client.SolveStatus) { + v2 := *v + t.ch <- &v2 + t.Writer.Write(v) +} + +func Tee(w Writer, ch chan *client.SolveStatus) Writer { + if ch == nil { + return w + } + return &tee{ + Writer: w, + ch: ch, + } +} diff --git a/vendor/github.com/containerd/containerd/pkg/dialer/dialer.go b/vendor/github.com/containerd/containerd/pkg/dialer/dialer.go new file mode 100644 index 000000000000..74c303b944f9 --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/dialer/dialer.go @@ -0,0 +1,78 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package dialer + +import ( + "context" + "fmt" + "net" + "time" +) + +type dialResult struct { + c net.Conn + err error +} + +// ContextDialer returns a GRPC net.Conn connected to the provided address +func ContextDialer(ctx context.Context, address string) (net.Conn, error) { + if deadline, ok := ctx.Deadline(); ok { + return timeoutDialer(address, time.Until(deadline)) + } + return timeoutDialer(address, 0) +} + +// Dialer returns a GRPC net.Conn connected to the provided address +// Deprecated: use ContextDialer and grpc.WithContextDialer. +var Dialer = timeoutDialer + +func timeoutDialer(address string, timeout time.Duration) (net.Conn, error) { + var ( + stopC = make(chan struct{}) + synC = make(chan *dialResult) + ) + go func() { + defer close(synC) + for { + select { + case <-stopC: + return + default: + c, err := dialer(address, timeout) + if isNoent(err) { + <-time.After(10 * time.Millisecond) + continue + } + synC <- &dialResult{c, err} + return + } + } + }() + select { + case dr := <-synC: + return dr.c, dr.err + case <-time.After(timeout): + close(stopC) + go func() { + dr := <-synC + if dr != nil && dr.c != nil { + dr.c.Close() + } + }() + return nil, fmt.Errorf("dial %s: timeout", address) + } +} diff --git a/vendor/github.com/containerd/containerd/pkg/dialer/dialer_unix.go b/vendor/github.com/containerd/containerd/pkg/dialer/dialer_unix.go new file mode 100644 index 000000000000..b4304ffbf1f2 --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/dialer/dialer_unix.go @@ -0,0 +1,53 @@ +//go:build !windows +// +build !windows + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package dialer + +import ( + "fmt" + "net" + "os" + "strings" + "syscall" + "time" +) + +// DialAddress returns the address with unix:// prepended to the +// provided address +func DialAddress(address string) string { + return fmt.Sprintf("unix://%s", address) +} + +func isNoent(err error) bool { + if err != nil { + if nerr, ok := err.(*net.OpError); ok { + if serr, ok := nerr.Err.(*os.SyscallError); ok { + if serr.Err == syscall.ENOENT { + return true + } + } + } + } + return false +} + +func dialer(address string, timeout time.Duration) (net.Conn, error) { + address = strings.TrimPrefix(address, "unix://") + return net.DialTimeout("unix", address, timeout) +} diff --git a/vendor/github.com/containerd/containerd/pkg/dialer/dialer_windows.go b/vendor/github.com/containerd/containerd/pkg/dialer/dialer_windows.go new file mode 100644 index 000000000000..4dd296ebc3e5 --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/dialer/dialer_windows.go @@ -0,0 +1,38 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package dialer + +import ( + "net" + "os" + "time" + + winio "github.com/Microsoft/go-winio" +) + +func isNoent(err error) bool { + return os.IsNotExist(err) +} + +func dialer(address string, timeout time.Duration) (net.Conn, error) { + return winio.DialPipe(address, &timeout) +} + +// DialAddress returns the dial address +func DialAddress(address string) string { + return address +} diff --git a/vendor/modules.txt b/vendor/modules.txt index be625ca81c63..d11306a2e93d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -163,6 +163,7 @@ github.com/containerd/containerd/labels github.com/containerd/containerd/leases github.com/containerd/containerd/log github.com/containerd/containerd/namespaces +github.com/containerd/containerd/pkg/dialer github.com/containerd/containerd/pkg/seed github.com/containerd/containerd/pkg/userns github.com/containerd/containerd/platforms