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 }