github.com/containerd/nerdctl@v1.7.7/cmd/nerdctl/builder.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"os/exec"
    23  	"strings"
    24  
    25  	"github.com/containerd/log"
    26  	"github.com/containerd/nerdctl/pkg/buildkitutil"
    27  	"github.com/containerd/nerdctl/pkg/defaults"
    28  	"github.com/spf13/cobra"
    29  )
    30  
    31  func newBuilderCommand() *cobra.Command {
    32  	var builderCommand = &cobra.Command{
    33  		Annotations:   map[string]string{Category: Management},
    34  		Use:           "builder",
    35  		Short:         "Manage builds",
    36  		RunE:          unknownSubcommandAction,
    37  		SilenceUsage:  true,
    38  		SilenceErrors: true,
    39  	}
    40  	builderCommand.AddCommand(
    41  		newBuildCommand(),
    42  		newBuilderPruneCommand(),
    43  		newBuilderDebugCommand(),
    44  	)
    45  	return builderCommand
    46  }
    47  
    48  func newBuilderPruneCommand() *cobra.Command {
    49  	shortHelp := `Clean up BuildKit build cache`
    50  	var buildPruneCommand = &cobra.Command{
    51  		Use:           "prune",
    52  		Args:          cobra.NoArgs,
    53  		Short:         shortHelp,
    54  		RunE:          builderPruneAction,
    55  		SilenceUsage:  true,
    56  		SilenceErrors: true,
    57  	}
    58  
    59  	AddStringFlag(buildPruneCommand, "buildkit-host", nil, defaults.BuildKitHost(), "BUILDKIT_HOST", "BuildKit address")
    60  	return buildPruneCommand
    61  }
    62  
    63  func builderPruneAction(cmd *cobra.Command, _ []string) error {
    64  	globalOptions, err := processRootCmdFlags(cmd)
    65  	if err != nil {
    66  		return err
    67  	}
    68  	buildkitHost, err := getBuildkitHost(cmd, globalOptions.Namespace)
    69  	if err != nil {
    70  		return err
    71  	}
    72  	buildctlBinary, err := buildkitutil.BuildctlBinary()
    73  	if err != nil {
    74  		return err
    75  	}
    76  	buildctlArgs := buildkitutil.BuildctlBaseArgs(buildkitHost)
    77  	buildctlArgs = append(buildctlArgs, "prune")
    78  	log.L.Debugf("running %s %v", buildctlBinary, buildctlArgs)
    79  	buildctlCmd := exec.Command(buildctlBinary, buildctlArgs...)
    80  	buildctlCmd.Env = os.Environ()
    81  	buildctlCmd.Stdout = cmd.OutOrStdout()
    82  	return buildctlCmd.Run()
    83  }
    84  
    85  func newBuilderDebugCommand() *cobra.Command {
    86  	shortHelp := `Debug Dockerfile`
    87  	var buildDebugCommand = &cobra.Command{
    88  		Use:           "debug",
    89  		Short:         shortHelp,
    90  		PreRunE:       checkExperimental("`nerdctl builder debug`"),
    91  		RunE:          builderDebugAction,
    92  		SilenceUsage:  true,
    93  		SilenceErrors: true,
    94  	}
    95  	buildDebugCommand.Flags().StringP("file", "f", "", "Name of the Dockerfile")
    96  	buildDebugCommand.Flags().String("target", "", "Set the target build stage to build")
    97  	buildDebugCommand.Flags().StringArray("build-arg", nil, "Set build-time variables")
    98  	buildDebugCommand.Flags().String("image", "", "Image to use for debugging stage")
    99  	buildDebugCommand.Flags().StringArray("ssh", nil, "Allow forwarding SSH agent to the build. Format: default|<id>[=<socket>|<key>[,<key>]]")
   100  	buildDebugCommand.Flags().StringArray("secret", nil, "Expose secret value to the build. Format: id=secretname,src=filepath")
   101  	return buildDebugCommand
   102  }
   103  
   104  func builderDebugAction(cmd *cobra.Command, args []string) error {
   105  	globalOptions, err := processRootCmdFlags(cmd)
   106  	if err != nil {
   107  		return err
   108  	}
   109  	if len(args) < 1 {
   110  		return fmt.Errorf("context needs to be specified")
   111  	}
   112  
   113  	buildgBinary, err := exec.LookPath("buildg")
   114  	if err != nil {
   115  		return err
   116  	}
   117  	buildgArgs := []string{"debug"}
   118  	if globalOptions.Debug {
   119  		buildgArgs = append([]string{"--debug"}, buildgArgs...)
   120  	}
   121  
   122  	if file, err := cmd.Flags().GetString("file"); err != nil {
   123  		return err
   124  	} else if file != "" {
   125  		buildgArgs = append(buildgArgs, "--file="+file)
   126  	}
   127  
   128  	if target, err := cmd.Flags().GetString("target"); err != nil {
   129  		return err
   130  	} else if target != "" {
   131  		buildgArgs = append(buildgArgs, "--target="+target)
   132  	}
   133  
   134  	if buildArgsValue, err := cmd.Flags().GetStringArray("build-arg"); err != nil {
   135  		return err
   136  	} else if len(buildArgsValue) > 0 {
   137  		for _, v := range buildArgsValue {
   138  			arr := strings.Split(v, "=")
   139  			if len(arr) == 1 && len(arr[0]) > 0 {
   140  				// Avoid masking default build arg value from Dockerfile if environment variable is not set
   141  				// https://github.com/moby/moby/issues/24101
   142  				val, ok := os.LookupEnv(arr[0])
   143  				if ok {
   144  					buildgArgs = append(buildgArgs, fmt.Sprintf("--build-arg=%s=%s", v, val))
   145  				}
   146  			} else if len(arr) > 1 && len(arr[0]) > 0 {
   147  				buildgArgs = append(buildgArgs, "--build-arg="+v)
   148  			} else {
   149  				return fmt.Errorf("invalid build arg %q", v)
   150  			}
   151  		}
   152  	}
   153  
   154  	if imageValue, err := cmd.Flags().GetString("image"); err != nil {
   155  		return err
   156  	} else if imageValue != "" {
   157  		buildgArgs = append(buildgArgs, "--image="+imageValue)
   158  	}
   159  
   160  	if sshValue, err := cmd.Flags().GetStringArray("ssh"); err != nil {
   161  		return err
   162  	} else if len(sshValue) > 0 {
   163  		for _, v := range sshValue {
   164  			buildgArgs = append(buildgArgs, "--ssh="+v)
   165  		}
   166  	}
   167  
   168  	if secretValue, err := cmd.Flags().GetStringArray("secret"); err != nil {
   169  		return err
   170  	} else if len(secretValue) > 0 {
   171  		for _, v := range secretValue {
   172  			buildgArgs = append(buildgArgs, "--secret="+v)
   173  		}
   174  	}
   175  
   176  	buildgCmd := exec.Command(buildgBinary, append(buildgArgs, args[0])...)
   177  	buildgCmd.Env = os.Environ()
   178  	buildgCmd.Stdin = cmd.InOrStdin()
   179  	buildgCmd.Stdout = cmd.OutOrStdout()
   180  	buildgCmd.Stderr = cmd.ErrOrStderr()
   181  	if err := buildgCmd.Start(); err != nil {
   182  		return err
   183  	}
   184  
   185  	return buildgCmd.Wait()
   186  }