github.com/khulnasoft/cli@v0.0.0-20240402070845-01bcad7beefa/cli/command/image/build.go (about)

     1  package image
     2  
     3  import (
     4  	"archive/tar"
     5  	"bufio"
     6  	"bytes"
     7  	"context"
     8  	"encoding/json"
     9  	"fmt"
    10  	"io"
    11  	"os"
    12  	"path/filepath"
    13  	"regexp"
    14  	"runtime"
    15  	"strings"
    16  
    17  	"github.com/distribution/reference"
    18  	"github.com/docker/docker/api"
    19  	"github.com/docker/docker/api/types"
    20  	"github.com/docker/docker/api/types/container"
    21  	registrytypes "github.com/docker/docker/api/types/registry"
    22  	"github.com/docker/docker/builder/remotecontext/urlutil"
    23  	"github.com/docker/docker/pkg/archive"
    24  	"github.com/docker/docker/pkg/idtools"
    25  	"github.com/docker/docker/pkg/jsonmessage"
    26  	"github.com/docker/docker/pkg/progress"
    27  	"github.com/docker/docker/pkg/streamformatter"
    28  	units "github.com/khulnasoft-lab/go-units"
    29  	"github.com/khulnasoft/cli/cli"
    30  	"github.com/khulnasoft/cli/cli/command"
    31  	"github.com/khulnasoft/cli/cli/command/image/build"
    32  	"github.com/khulnasoft/cli/opts"
    33  	"github.com/pkg/errors"
    34  	"github.com/spf13/cobra"
    35  )
    36  
    37  type buildOptions struct {
    38  	context        string
    39  	dockerfileName string
    40  	tags           opts.ListOpts
    41  	labels         opts.ListOpts
    42  	buildArgs      opts.ListOpts
    43  	extraHosts     opts.ListOpts
    44  	ulimits        *opts.UlimitOpt
    45  	memory         opts.MemBytes
    46  	memorySwap     opts.MemSwapBytes
    47  	shmSize        opts.MemBytes
    48  	cpuShares      int64
    49  	cpuPeriod      int64
    50  	cpuQuota       int64
    51  	cpuSetCpus     string
    52  	cpuSetMems     string
    53  	cgroupParent   string
    54  	isolation      string
    55  	quiet          bool
    56  	noCache        bool
    57  	rm             bool
    58  	forceRm        bool
    59  	pull           bool
    60  	cacheFrom      []string
    61  	compress       bool
    62  	securityOpt    []string
    63  	networkMode    string
    64  	squash         bool
    65  	target         string
    66  	imageIDFile    string
    67  	platform       string
    68  	untrusted      bool
    69  }
    70  
    71  // dockerfileFromStdin returns true when the user specified that the Dockerfile
    72  // should be read from stdin instead of a file
    73  func (o buildOptions) dockerfileFromStdin() bool {
    74  	return o.dockerfileName == "-"
    75  }
    76  
    77  // contextFromStdin returns true when the user specified that the build context
    78  // should be read from stdin
    79  func (o buildOptions) contextFromStdin() bool {
    80  	return o.context == "-"
    81  }
    82  
    83  func newBuildOptions() buildOptions {
    84  	ulimits := make(map[string]*units.Ulimit)
    85  	return buildOptions{
    86  		tags:       opts.NewListOpts(validateTag),
    87  		buildArgs:  opts.NewListOpts(opts.ValidateEnv),
    88  		ulimits:    opts.NewUlimitOpt(&ulimits),
    89  		labels:     opts.NewListOpts(opts.ValidateLabel),
    90  		extraHosts: opts.NewListOpts(opts.ValidateExtraHost),
    91  	}
    92  }
    93  
    94  // NewBuildCommand creates a new `docker build` command
    95  func NewBuildCommand(dockerCli command.Cli) *cobra.Command {
    96  	options := newBuildOptions()
    97  
    98  	cmd := &cobra.Command{
    99  		Use:   "build [OPTIONS] PATH | URL | -",
   100  		Short: "Build an image from a Dockerfile",
   101  		Args:  cli.ExactArgs(1),
   102  		RunE: func(cmd *cobra.Command, args []string) error {
   103  			options.context = args[0]
   104  			return runBuild(cmd.Context(), dockerCli, options)
   105  		},
   106  		Annotations: map[string]string{
   107  			"category-top": "4",
   108  			"aliases":      "docker image build, docker build, docker buildx build, docker builder build",
   109  		},
   110  		ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
   111  			return nil, cobra.ShellCompDirectiveFilterDirs
   112  		},
   113  	}
   114  
   115  	flags := cmd.Flags()
   116  
   117  	flags.VarP(&options.tags, "tag", "t", `Name and optionally a tag in the "name:tag" format`)
   118  	flags.Var(&options.buildArgs, "build-arg", "Set build-time variables")
   119  	flags.Var(options.ulimits, "ulimit", "Ulimit options")
   120  	flags.StringVarP(&options.dockerfileName, "file", "f", "", `Name of the Dockerfile (Default is "PATH/Dockerfile")`)
   121  	flags.VarP(&options.memory, "memory", "m", "Memory limit")
   122  	flags.Var(&options.memorySwap, "memory-swap", `Swap limit equal to memory plus swap: -1 to enable unlimited swap`)
   123  	flags.Var(&options.shmSize, "shm-size", `Size of "/dev/shm"`)
   124  	flags.Int64VarP(&options.cpuShares, "cpu-shares", "c", 0, "CPU shares (relative weight)")
   125  	flags.Int64Var(&options.cpuPeriod, "cpu-period", 0, "Limit the CPU CFS (Completely Fair Scheduler) period")
   126  	flags.Int64Var(&options.cpuQuota, "cpu-quota", 0, "Limit the CPU CFS (Completely Fair Scheduler) quota")
   127  	flags.StringVar(&options.cpuSetCpus, "cpuset-cpus", "", "CPUs in which to allow execution (0-3, 0,1)")
   128  	flags.StringVar(&options.cpuSetMems, "cpuset-mems", "", "MEMs in which to allow execution (0-3, 0,1)")
   129  	flags.StringVar(&options.cgroupParent, "cgroup-parent", "", `Set the parent cgroup for the "RUN" instructions during build`)
   130  	flags.StringVar(&options.isolation, "isolation", "", "Container isolation technology")
   131  	flags.Var(&options.labels, "label", "Set metadata for an image")
   132  	flags.BoolVar(&options.noCache, "no-cache", false, "Do not use cache when building the image")
   133  	flags.BoolVar(&options.rm, "rm", true, "Remove intermediate containers after a successful build")
   134  	flags.BoolVar(&options.forceRm, "force-rm", false, "Always remove intermediate containers")
   135  	flags.BoolVarP(&options.quiet, "quiet", "q", false, "Suppress the build output and print image ID on success")
   136  	flags.BoolVar(&options.pull, "pull", false, "Always attempt to pull a newer version of the image")
   137  	flags.StringSliceVar(&options.cacheFrom, "cache-from", []string{}, "Images to consider as cache sources")
   138  	flags.BoolVar(&options.compress, "compress", false, "Compress the build context using gzip")
   139  	flags.StringSliceVar(&options.securityOpt, "security-opt", []string{}, "Security options")
   140  	flags.StringVar(&options.networkMode, "network", "default", "Set the networking mode for the RUN instructions during build")
   141  	flags.SetAnnotation("network", "version", []string{"1.25"})
   142  	flags.Var(&options.extraHosts, "add-host", `Add a custom host-to-IP mapping ("host:ip")`)
   143  	flags.StringVar(&options.target, "target", "", "Set the target build stage to build.")
   144  	flags.StringVar(&options.imageIDFile, "iidfile", "", "Write the image ID to the file")
   145  
   146  	command.AddTrustVerificationFlags(flags, &options.untrusted, dockerCli.ContentTrustEnabled())
   147  
   148  	flags.StringVar(&options.platform, "platform", os.Getenv("DOCKER_DEFAULT_PLATFORM"), "Set platform if server is multi-platform capable")
   149  	flags.SetAnnotation("platform", "version", []string{"1.38"})
   150  
   151  	flags.BoolVar(&options.squash, "squash", false, "Squash newly built layers into a single new layer")
   152  	flags.SetAnnotation("squash", "experimental", nil)
   153  	flags.SetAnnotation("squash", "version", []string{"1.25"})
   154  
   155  	return cmd
   156  }
   157  
   158  // lastProgressOutput is the same as progress.Output except
   159  // that it only output with the last update. It is used in
   160  // non terminal scenarios to suppress verbose messages
   161  type lastProgressOutput struct {
   162  	output progress.Output
   163  }
   164  
   165  // WriteProgress formats progress information from a ProgressReader.
   166  func (out *lastProgressOutput) WriteProgress(prog progress.Progress) error {
   167  	if !prog.LastUpdate {
   168  		return nil
   169  	}
   170  
   171  	return out.output.WriteProgress(prog)
   172  }
   173  
   174  //nolint:gocyclo
   175  func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions) error {
   176  	var (
   177  		err           error
   178  		buildCtx      io.ReadCloser
   179  		dockerfileCtx io.ReadCloser
   180  		contextDir    string
   181  		tempDir       string
   182  		relDockerfile string
   183  		progBuff      io.Writer
   184  		buildBuff     io.Writer
   185  		remote        string
   186  	)
   187  
   188  	if options.dockerfileFromStdin() {
   189  		if options.contextFromStdin() {
   190  			return errors.New("invalid argument: can't use stdin for both build context and dockerfile")
   191  		}
   192  		dockerfileCtx = dockerCli.In()
   193  	}
   194  
   195  	specifiedContext := options.context
   196  	progBuff = dockerCli.Out()
   197  	buildBuff = dockerCli.Out()
   198  	if options.quiet {
   199  		progBuff = bytes.NewBuffer(nil)
   200  		buildBuff = bytes.NewBuffer(nil)
   201  	}
   202  	if options.imageIDFile != "" {
   203  		// Avoid leaving a stale file if we eventually fail
   204  		if err := os.Remove(options.imageIDFile); err != nil && !os.IsNotExist(err) {
   205  			return errors.Wrap(err, "Removing image ID file")
   206  		}
   207  	}
   208  
   209  	switch {
   210  	case options.contextFromStdin():
   211  		// buildCtx is tar archive. if stdin was dockerfile then it is wrapped
   212  		buildCtx, relDockerfile, err = build.GetContextFromReader(dockerCli.In(), options.dockerfileName)
   213  	case isLocalDir(specifiedContext):
   214  		contextDir, relDockerfile, err = build.GetContextFromLocalDir(specifiedContext, options.dockerfileName)
   215  		if err == nil && strings.HasPrefix(relDockerfile, ".."+string(filepath.Separator)) {
   216  			// Dockerfile is outside of build-context; read the Dockerfile and pass it as dockerfileCtx
   217  			dockerfileCtx, err = os.Open(options.dockerfileName)
   218  			if err != nil {
   219  				return errors.Errorf("unable to open Dockerfile: %v", err)
   220  			}
   221  			defer dockerfileCtx.Close()
   222  		}
   223  	case urlutil.IsGitURL(specifiedContext):
   224  		tempDir, relDockerfile, err = build.GetContextFromGitURL(specifiedContext, options.dockerfileName)
   225  	case urlutil.IsURL(specifiedContext):
   226  		buildCtx, relDockerfile, err = build.GetContextFromURL(progBuff, specifiedContext, options.dockerfileName)
   227  	default:
   228  		return errors.Errorf("unable to prepare context: path %q not found", specifiedContext)
   229  	}
   230  
   231  	if err != nil {
   232  		if options.quiet && urlutil.IsURL(specifiedContext) {
   233  			fmt.Fprintln(dockerCli.Err(), progBuff)
   234  		}
   235  		return errors.Errorf("unable to prepare context: %s", err)
   236  	}
   237  
   238  	if tempDir != "" {
   239  		defer os.RemoveAll(tempDir)
   240  		contextDir = tempDir
   241  	}
   242  
   243  	// read from a directory into tar archive
   244  	if buildCtx == nil {
   245  		excludes, err := build.ReadDockerignore(contextDir)
   246  		if err != nil {
   247  			return err
   248  		}
   249  
   250  		if err := build.ValidateContextDirectory(contextDir, excludes); err != nil {
   251  			return errors.Wrap(err, "error checking context")
   252  		}
   253  
   254  		// And canonicalize dockerfile name to a platform-independent one
   255  		relDockerfile = filepath.ToSlash(relDockerfile)
   256  
   257  		excludes = build.TrimBuildFilesFromExcludes(excludes, relDockerfile, options.dockerfileFromStdin())
   258  		buildCtx, err = archive.TarWithOptions(contextDir, &archive.TarOptions{
   259  			ExcludePatterns: excludes,
   260  			ChownOpts:       &idtools.Identity{UID: 0, GID: 0},
   261  		})
   262  		if err != nil {
   263  			return err
   264  		}
   265  	}
   266  
   267  	// replace Dockerfile if it was added from stdin or a file outside the build-context, and there is archive context
   268  	if dockerfileCtx != nil && buildCtx != nil {
   269  		buildCtx, relDockerfile, err = build.AddDockerfileToBuildContext(dockerfileCtx, buildCtx)
   270  		if err != nil {
   271  			return err
   272  		}
   273  	}
   274  
   275  	ctx, cancel := context.WithCancel(ctx)
   276  	defer cancel()
   277  
   278  	var resolvedTags []*resolvedTag
   279  	if !options.untrusted {
   280  		translator := func(ctx context.Context, ref reference.NamedTagged) (reference.Canonical, error) {
   281  			return TrustedReference(ctx, dockerCli, ref)
   282  		}
   283  		// if there is a tar wrapper, the dockerfile needs to be replaced inside it
   284  		if buildCtx != nil {
   285  			// Wrap the tar archive to replace the Dockerfile entry with the rewritten
   286  			// Dockerfile which uses trusted pulls.
   287  			buildCtx = replaceDockerfileForContentTrust(ctx, buildCtx, relDockerfile, translator, &resolvedTags)
   288  		} else if dockerfileCtx != nil {
   289  			// if there was not archive context still do the possible replacements in Dockerfile
   290  			newDockerfile, _, err := rewriteDockerfileFromForContentTrust(ctx, dockerfileCtx, translator)
   291  			if err != nil {
   292  				return err
   293  			}
   294  			dockerfileCtx = io.NopCloser(bytes.NewBuffer(newDockerfile))
   295  		}
   296  	}
   297  
   298  	if options.compress {
   299  		buildCtx, err = build.Compress(buildCtx)
   300  		if err != nil {
   301  			return err
   302  		}
   303  	}
   304  
   305  	// Setup an upload progress bar
   306  	progressOutput := streamformatter.NewProgressOutput(progBuff)
   307  	if !dockerCli.Out().IsTerminal() {
   308  		progressOutput = &lastProgressOutput{output: progressOutput}
   309  	}
   310  
   311  	// if up to this point nothing has set the context then we must have another
   312  	// way for sending it(streaming) and set the context to the Dockerfile
   313  	if dockerfileCtx != nil && buildCtx == nil {
   314  		buildCtx = dockerfileCtx
   315  	}
   316  
   317  	var body io.Reader
   318  	if buildCtx != nil {
   319  		body = progress.NewProgressReader(buildCtx, progressOutput, 0, "", "Sending build context to Docker daemon")
   320  	}
   321  
   322  	configFile := dockerCli.ConfigFile()
   323  	creds, _ := configFile.GetAllCredentials()
   324  	authConfigs := make(map[string]registrytypes.AuthConfig, len(creds))
   325  	for k, auth := range creds {
   326  		authConfigs[k] = registrytypes.AuthConfig(auth)
   327  	}
   328  	buildOptions := imageBuildOptions(dockerCli, options)
   329  	buildOptions.Version = types.BuilderV1
   330  	buildOptions.Dockerfile = relDockerfile
   331  	buildOptions.AuthConfigs = authConfigs
   332  	buildOptions.RemoteContext = remote
   333  
   334  	response, err := dockerCli.Client().ImageBuild(ctx, body, buildOptions)
   335  	if err != nil {
   336  		if options.quiet {
   337  			fmt.Fprintf(dockerCli.Err(), "%s", progBuff)
   338  		}
   339  		cancel()
   340  		return err
   341  	}
   342  	defer response.Body.Close()
   343  
   344  	imageID := ""
   345  	aux := func(msg jsonmessage.JSONMessage) {
   346  		var result types.BuildResult
   347  		if err := json.Unmarshal(*msg.Aux, &result); err != nil {
   348  			fmt.Fprintf(dockerCli.Err(), "Failed to parse aux message: %s", err)
   349  		} else {
   350  			imageID = result.ID
   351  		}
   352  	}
   353  
   354  	err = jsonmessage.DisplayJSONMessagesStream(response.Body, buildBuff, dockerCli.Out().FD(), dockerCli.Out().IsTerminal(), aux)
   355  	if err != nil {
   356  		if jerr, ok := err.(*jsonmessage.JSONError); ok {
   357  			// If no error code is set, default to 1
   358  			if jerr.Code == 0 {
   359  				jerr.Code = 1
   360  			}
   361  			if options.quiet {
   362  				fmt.Fprintf(dockerCli.Err(), "%s%s", progBuff, buildBuff)
   363  			}
   364  			return cli.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
   365  		}
   366  		return err
   367  	}
   368  
   369  	// Windows: show error message about modified file permissions if the
   370  	// daemon isn't running Windows.
   371  	if response.OSType != "windows" && runtime.GOOS == "windows" && !options.quiet {
   372  		fmt.Fprintln(dockerCli.Out(), "SECURITY WARNING: You are building a Docker "+
   373  			"image from Windows against a non-Windows Docker host. All files and "+
   374  			"directories added to build context will have '-rwxr-xr-x' permissions. "+
   375  			"It is recommended to double check and reset permissions for sensitive "+
   376  			"files and directories.")
   377  	}
   378  
   379  	// Everything worked so if -q was provided the output from the daemon
   380  	// should be just the image ID and we'll print that to stdout.
   381  	if options.quiet {
   382  		imageID = fmt.Sprintf("%s", buildBuff)
   383  		_, _ = fmt.Fprint(dockerCli.Out(), imageID)
   384  	}
   385  
   386  	if options.imageIDFile != "" {
   387  		if imageID == "" {
   388  			return errors.Errorf("Server did not provide an image ID. Cannot write %s", options.imageIDFile)
   389  		}
   390  		if err := os.WriteFile(options.imageIDFile, []byte(imageID), 0o666); err != nil {
   391  			return err
   392  		}
   393  	}
   394  	if !options.untrusted {
   395  		// Since the build was successful, now we must tag any of the resolved
   396  		// images from the above Dockerfile rewrite.
   397  		for _, resolved := range resolvedTags {
   398  			if err := TagTrusted(ctx, dockerCli, resolved.digestRef, resolved.tagRef); err != nil {
   399  				return err
   400  			}
   401  		}
   402  	}
   403  
   404  	return nil
   405  }
   406  
   407  func isLocalDir(c string) bool {
   408  	_, err := os.Stat(c)
   409  	return err == nil
   410  }
   411  
   412  type translatorFunc func(context.Context, reference.NamedTagged) (reference.Canonical, error)
   413  
   414  // validateTag checks if the given image name can be resolved.
   415  func validateTag(rawRepo string) (string, error) {
   416  	_, err := reference.ParseNormalizedNamed(rawRepo)
   417  	if err != nil {
   418  		return "", err
   419  	}
   420  
   421  	return rawRepo, nil
   422  }
   423  
   424  var dockerfileFromLinePattern = regexp.MustCompile(`(?i)^[\s]*FROM[ \f\r\t\v]+(?P<image>[^ \f\r\t\v\n#]+)`)
   425  
   426  // resolvedTag records the repository, tag, and resolved digest reference
   427  // from a Dockerfile rewrite.
   428  type resolvedTag struct {
   429  	digestRef reference.Canonical
   430  	tagRef    reference.NamedTagged
   431  }
   432  
   433  // rewriteDockerfileFromForContentTrust rewrites the given Dockerfile by resolving images in
   434  // "FROM <image>" instructions to a digest reference. `translator` is a
   435  // function that takes a repository name and tag reference and returns a
   436  // trusted digest reference.
   437  // This should be called *only* when content trust is enabled
   438  func rewriteDockerfileFromForContentTrust(ctx context.Context, dockerfile io.Reader, translator translatorFunc) (newDockerfile []byte, resolvedTags []*resolvedTag, err error) {
   439  	scanner := bufio.NewScanner(dockerfile)
   440  	buf := bytes.NewBuffer(nil)
   441  
   442  	// Scan the lines of the Dockerfile, looking for a "FROM" line.
   443  	for scanner.Scan() {
   444  		line := scanner.Text()
   445  
   446  		matches := dockerfileFromLinePattern.FindStringSubmatch(line)
   447  		if matches != nil && matches[1] != api.NoBaseImageSpecifier {
   448  			// Replace the line with a resolved "FROM repo@digest"
   449  			var ref reference.Named
   450  			ref, err = reference.ParseNormalizedNamed(matches[1])
   451  			if err != nil {
   452  				return nil, nil, err
   453  			}
   454  			ref = reference.TagNameOnly(ref)
   455  			if ref, ok := ref.(reference.NamedTagged); ok {
   456  				trustedRef, err := translator(ctx, ref)
   457  				if err != nil {
   458  					return nil, nil, err
   459  				}
   460  
   461  				line = dockerfileFromLinePattern.ReplaceAllLiteralString(line, fmt.Sprintf("FROM %s", reference.FamiliarString(trustedRef)))
   462  				resolvedTags = append(resolvedTags, &resolvedTag{
   463  					digestRef: trustedRef,
   464  					tagRef:    ref,
   465  				})
   466  			}
   467  		}
   468  
   469  		_, err := fmt.Fprintln(buf, line)
   470  		if err != nil {
   471  			return nil, nil, err
   472  		}
   473  	}
   474  
   475  	return buf.Bytes(), resolvedTags, scanner.Err()
   476  }
   477  
   478  // replaceDockerfileForContentTrust wraps the given input tar archive stream and
   479  // uses the translator to replace the Dockerfile which uses a trusted reference.
   480  // Returns a new tar archive stream with the replaced Dockerfile.
   481  func replaceDockerfileForContentTrust(ctx context.Context, inputTarStream io.ReadCloser, dockerfileName string, translator translatorFunc, resolvedTags *[]*resolvedTag) io.ReadCloser {
   482  	pipeReader, pipeWriter := io.Pipe()
   483  	go func() {
   484  		tarReader := tar.NewReader(inputTarStream)
   485  		tarWriter := tar.NewWriter(pipeWriter)
   486  
   487  		defer inputTarStream.Close()
   488  
   489  		for {
   490  			hdr, err := tarReader.Next()
   491  			if err == io.EOF {
   492  				// Signals end of archive.
   493  				tarWriter.Close()
   494  				pipeWriter.Close()
   495  				return
   496  			}
   497  			if err != nil {
   498  				pipeWriter.CloseWithError(err)
   499  				return
   500  			}
   501  
   502  			content := io.Reader(tarReader)
   503  			if hdr.Name == dockerfileName {
   504  				// This entry is the Dockerfile. Since the tar archive was
   505  				// generated from a directory on the local filesystem, the
   506  				// Dockerfile will only appear once in the archive.
   507  				var newDockerfile []byte
   508  				newDockerfile, *resolvedTags, err = rewriteDockerfileFromForContentTrust(ctx, content, translator)
   509  				if err != nil {
   510  					pipeWriter.CloseWithError(err)
   511  					return
   512  				}
   513  				hdr.Size = int64(len(newDockerfile))
   514  				content = bytes.NewBuffer(newDockerfile)
   515  			}
   516  
   517  			if err := tarWriter.WriteHeader(hdr); err != nil {
   518  				pipeWriter.CloseWithError(err)
   519  				return
   520  			}
   521  
   522  			if _, err := io.Copy(tarWriter, content); err != nil {
   523  				pipeWriter.CloseWithError(err)
   524  				return
   525  			}
   526  		}
   527  	}()
   528  
   529  	return pipeReader
   530  }
   531  
   532  func imageBuildOptions(dockerCli command.Cli, options buildOptions) types.ImageBuildOptions {
   533  	configFile := dockerCli.ConfigFile()
   534  	return types.ImageBuildOptions{
   535  		Memory:         options.memory.Value(),
   536  		MemorySwap:     options.memorySwap.Value(),
   537  		Tags:           options.tags.GetAll(),
   538  		SuppressOutput: options.quiet,
   539  		NoCache:        options.noCache,
   540  		Remove:         options.rm,
   541  		ForceRemove:    options.forceRm,
   542  		PullParent:     options.pull,
   543  		Isolation:      container.Isolation(options.isolation),
   544  		CPUSetCPUs:     options.cpuSetCpus,
   545  		CPUSetMems:     options.cpuSetMems,
   546  		CPUShares:      options.cpuShares,
   547  		CPUQuota:       options.cpuQuota,
   548  		CPUPeriod:      options.cpuPeriod,
   549  		CgroupParent:   options.cgroupParent,
   550  		ShmSize:        options.shmSize.Value(),
   551  		Ulimits:        options.ulimits.GetList(),
   552  		BuildArgs:      configFile.ParseProxyConfig(dockerCli.Client().DaemonHost(), opts.ConvertKVStringsToMapWithNil(options.buildArgs.GetAll())),
   553  		Labels:         opts.ConvertKVStringsToMap(options.labels.GetAll()),
   554  		CacheFrom:      options.cacheFrom,
   555  		SecurityOpt:    options.securityOpt,
   556  		NetworkMode:    options.networkMode,
   557  		Squash:         options.squash,
   558  		ExtraHosts:     options.extraHosts.GetAll(),
   559  		Target:         options.target,
   560  		Platform:       options.platform,
   561  	}
   562  }