From 85811eb7b2caa05536efc656dcf2e3aeec2772e4 Mon Sep 17 00:00:00 2001 From: YangKian <1207783292@qq.com> Date: Sun, 3 Jul 2022 18:34:26 +0800 Subject: [PATCH] support default platform config in nerdctl.toml --- cmd/nerdctl/build.go | 4 +-- cmd/nerdctl/container.go | 2 +- cmd/nerdctl/create.go | 2 +- cmd/nerdctl/image.go | 2 +- cmd/nerdctl/main.go | 65 ++++++++++++++++++++++++++++------------ cmd/nerdctl/main_test.go | 51 +++++++++++++++++++++++++++++++ cmd/nerdctl/run.go | 10 +++---- 7 files changed, 107 insertions(+), 29 deletions(-) diff --git a/cmd/nerdctl/build.go b/cmd/nerdctl/build.go index 0434c847e14..081b647d34b 100644 --- a/cmd/nerdctl/build.go +++ b/cmd/nerdctl/build.go @@ -38,7 +38,7 @@ import ( "github.com/spf13/cobra" ) -func newBuildCommand() *cobra.Command { +func newBuildCommand(cfg *BuildConfig) *cobra.Command { var buildCommand = &cobra.Command{ Use: "build", Short: "Build an image from a Dockerfile. Needs buildkitd to be running.", @@ -63,7 +63,7 @@ func newBuildCommand() *cobra.Command { // #region platform flags // platform is defined as StringSlice, not StringArray, to allow specifying "--platform=amd64,arm64" - buildCommand.Flags().StringSlice("platform", []string{}, "Set target platform for build (e.g., \"amd64\", \"arm64\")") + buildCommand.Flags().StringSlice("platform", cfg.Platform, "Set target platform for build (e.g., \"amd64\", \"arm64\")") buildCommand.RegisterFlagCompletionFunc("platform", shellCompletePlatforms) // #endregion diff --git a/cmd/nerdctl/container.go b/cmd/nerdctl/container.go index b8f3af6902e..cb1b4ce6a27 100644 --- a/cmd/nerdctl/container.go +++ b/cmd/nerdctl/container.go @@ -31,7 +31,7 @@ func newContainerCommand() *cobra.Command { } containerCommand.AddCommand( newCreateCommand(), - newRunCommand(), + newRunCommand(runConfig()), newUpdateCommand(), newExecCommand(), containerLsCommand(), diff --git a/cmd/nerdctl/create.go b/cmd/nerdctl/create.go index 5167e8445d5..1111d85f7ef 100644 --- a/cmd/nerdctl/create.go +++ b/cmd/nerdctl/create.go @@ -45,7 +45,7 @@ func newCreateCommand() *cobra.Command { SilenceErrors: true, } createCommand.Flags().SetInterspersed(false) - setCreateFlags(createCommand) + setCreateFlags(createCommand, runConfig()) return createCommand } diff --git a/cmd/nerdctl/image.go b/cmd/nerdctl/image.go index 0c7a2fa2f0b..22c7eb4ae1b 100644 --- a/cmd/nerdctl/image.go +++ b/cmd/nerdctl/image.go @@ -30,7 +30,7 @@ func newImageCommand() *cobra.Command { SilenceErrors: true, } cmd.AddCommand( - newBuildCommand(), + newBuildCommand(buildConfig()), // commitCommand is in "container", not in "image" imageLsCommand(), newHistoryCommand(), diff --git a/cmd/nerdctl/main.go b/cmd/nerdctl/main.go index d951f63ee95..00aff21905d 100644 --- a/cmd/nerdctl/main.go +++ b/cmd/nerdctl/main.go @@ -28,10 +28,10 @@ import ( "github.com/containerd/containerd/namespaces" ncdefaults "github.com/containerd/nerdctl/pkg/defaults" "github.com/containerd/nerdctl/pkg/logging" + "github.com/containerd/nerdctl/pkg/netutil" "github.com/containerd/nerdctl/pkg/rootlessutil" "github.com/containerd/nerdctl/pkg/version" "github.com/pelletier/go-toml" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -87,17 +87,19 @@ func xmain() error { // Config corresponds to nerdctl.toml . // See docs/config.md . type Config struct { - Debug bool `toml:"debug"` - DebugFull bool `toml:"debug_full"` - Address string `toml:"address"` - Namespace string `toml:"namespace"` - Snapshotter string `toml:"snapshotter"` - CNIPath string `toml:"cni_path"` - CNINetConfPath string `toml:"cni_netconfpath"` - DataRoot string `toml:"data_root"` - CgroupManager string `toml:"cgroup_manager"` - InsecureRegistry bool `toml:"insecure_registry"` - HostsDir []string `toml:"hosts_dir"` + Debug bool `toml:"debug"` + DebugFull bool `toml:"debug_full"` + Address string `toml:"address"` + Namespace string `toml:"namespace"` + Snapshotter string `toml:"snapshotter"` + CNIPath string `toml:"cni_path"` + CNINetConfPath string `toml:"cni_netconfpath"` + DataRoot string `toml:"data_root"` + CgroupManager string `toml:"cgroup_manager"` + InsecureRegistry bool `toml:"insecure_registry"` + HostsDir []string `toml:"hosts_dir"` + RunConfig *RunConfig `toml:"default_run_config"` + BuildConfig *BuildConfig `toml:"default_build_config"` } // NewConfig creates a default Config object statically, @@ -115,23 +117,25 @@ func NewConfig() *Config { CgroupManager: ncdefaults.CgroupManager(), InsecureRegistry: false, HostsDir: ncdefaults.HostsDirs(), + RunConfig: runConfig(), + BuildConfig: buildConfig(), } } -func initRootCmdFlags(rootCmd *cobra.Command, tomlPath string) error { +func initRootCmdFlags(rootCmd *cobra.Command, tomlPath string) (*Config, error) { cfg := NewConfig() if r, err := os.Open(tomlPath); err == nil { logrus.Debugf("Loading config from %q", tomlPath) defer r.Close() dec := toml.NewDecoder(r).Strict(true) // set Strict to detect typo if err := dec.Decode(cfg); err != nil { - return fmt.Errorf("failed to load nerdctl config (not daemon config) from %q (Hint: don't mix up daemon's `config.toml` with `nerdctl.toml`): %w", tomlPath, err) + return nil, fmt.Errorf("failed to load nerdctl config (not daemon config) from %q (Hint: don't mix up daemon's `config.toml` with `nerdctl.toml`): %w", tomlPath, err) } logrus.Debugf("Loaded config %+v", cfg) } else { logrus.WithError(err).Debugf("Not loading config from %q", tomlPath) if !errors.Is(err, os.ErrNotExist) { - return err + return nil, err } } rootCmd.PersistentFlags().Bool("debug", cfg.Debug, "debug mode") @@ -152,7 +156,7 @@ func initRootCmdFlags(rootCmd *cobra.Command, tomlPath string) error { rootCmd.PersistentFlags().Bool("insecure-registry", cfg.InsecureRegistry, "skips verifying HTTPS certs, and allows falling back to plain HTTP") // hosts-dir is defined as StringSlice, not StringArray, to allow specifying "--hosts-dir=/etc/containerd/certs.d,/etc/docker/certs.d" rootCmd.PersistentFlags().StringSlice("hosts-dir", cfg.HostsDir, "A directory that contains /hosts.toml (containerd style) or /{ca.cert, cert.pem, key.pem} (docker style)") - return nil + return cfg, nil } func newApp() (*cobra.Command, error) { @@ -176,7 +180,8 @@ Config file ($NERDCTL_TOML): %s TraverseChildren: true, // required for global short hands like -a, -H, -n } rootCmd.SetUsageTemplate(mainHelpTemplate) - if err := initRootCmdFlags(rootCmd, tomlPath); err != nil { + cfg, err := initRootCmdFlags(rootCmd, tomlPath) + if err != nil { return nil, err } @@ -219,7 +224,7 @@ Config file ($NERDCTL_TOML): %s rootCmd.AddCommand( newCreateCommand(), // #region Run & Exec - newRunCommand(), + newRunCommand(cfg.RunConfig), newUpdateCommand(), newExecCommand(), // #endregion @@ -241,7 +246,7 @@ Config file ($NERDCTL_TOML): %s // #endregion // Build - newBuildCommand(), + newBuildCommand(cfg.BuildConfig), // #region Image management newImagesCommand(), @@ -297,6 +302,28 @@ Config file ($NERDCTL_TOML): %s return rootCmd, nil } +type RunConfig struct { + Platform string `toml:"platform"` + Network []string `toml:"network"` +} + +func runConfig() *RunConfig { + return &RunConfig{ + Platform: "", + Network: []string{netutil.DefaultNetworkName}, + } +} + +type BuildConfig struct { + Platform []string `toml:"platform"` +} + +func buildConfig() *BuildConfig { + return &BuildConfig{ + Platform: []string{}, + } +} + func globalFlags(cmd *cobra.Command) (string, []string) { args0, err := os.Executable() if err != nil { diff --git a/cmd/nerdctl/main_test.go b/cmd/nerdctl/main_test.go index 03a09360147..4ce6c29587c 100644 --- a/cmd/nerdctl/main_test.go +++ b/cmd/nerdctl/main_test.go @@ -19,9 +19,12 @@ package main import ( "os" "path/filepath" + "strings" "testing" "github.com/containerd/containerd" + ncdefaults "github.com/containerd/nerdctl/pkg/defaults" + "github.com/containerd/nerdctl/pkg/platformutil" "github.com/containerd/nerdctl/pkg/testutil" "gotest.tools/v3/assert" ) @@ -88,3 +91,51 @@ version = 2 base.Env = append(base.Env, "NERDCTL_TOML="+tomlPath) base.Cmd("info").AssertFail() } + +// TestNerdctlConditionalConfig validates the execution of conditional configuration options [CLI, TOML, Default]. +func TestNerdctlConditionalConfig(t *testing.T) { + testutil.DockerIncompatible(t) + t.Parallel() + tomlPath := ncdefaults.NerdctlTOML() + err := os.MkdirAll(filepath.Dir(tomlPath), 0755) + assert.NilError(t, err) + defer func(path string) { + _ = os.Remove(path) + }(tomlPath) + err = os.WriteFile(tomlPath, []byte(` +[default_run_config] +platform = "arm64" +`), 0400) + assert.NilError(t, err) + base := testutil.NewBase(t) + testutil.RequireExecPlatform(t, "linux/amd64", "linux/arm64") + + // [DEFAULT] + platformAmd64, err := platformutil.NormalizeString("amd64") + assert.NilError(t, err) + base.Cmd("run", testutil.CommonImage, "uname", "-m").AssertOutWithFunc(checkPlatForm(t, platformAmd64)) + + // [CLI, DEFAULT] + platformArm64, err := platformutil.NormalizeString("arm64") + assert.NilError(t, err) + base.Cmd("run", "--platform", "arm64", "--rm", testutil.CommonImage, "uname", "-m").AssertOutWithFunc(checkPlatForm(t, platformArm64)) + + // [TOML, DEFAULT] + if len(base.Env) == 0 { + base.Env = os.Environ() + } + base.Env = append(base.Env, "NERDCTL_TOML="+tomlPath) + base.Cmd("run", "--rm", testutil.CommonImage, "uname", "-m").AssertOutWithFunc(checkPlatForm(t, platformArm64)) + + // [CLI, TOML, DEFAULT] + base.Cmd("run", "--platform", "amd64", "--rm", testutil.CommonImage, "uname", "-m").AssertOutWithFunc(checkPlatForm(t, platformAmd64)) +} + +func checkPlatForm(t *testing.T, target string) func(out string) error { + return func(out string) error { + platformOut, err := platformutil.NormalizeString(strings.TrimSuffix(out, "\n")) + assert.NilError(t, err) + assert.Equal(t, target, platformOut) + return nil + } +} diff --git a/cmd/nerdctl/run.go b/cmd/nerdctl/run.go index d66290fa799..df6ce18424e 100644 --- a/cmd/nerdctl/run.go +++ b/cmd/nerdctl/run.go @@ -62,7 +62,7 @@ const ( tiniInitBinary = "tini" ) -func newRunCommand() *cobra.Command { +func newRunCommand(cfg *RunConfig) *cobra.Command { shortHelp := "Run a command in a new container. Optionally specify \"ipfs://\" or \"ipns://\" scheme to pull image from IPFS." longHelp := shortHelp switch runtime.GOOS { @@ -85,14 +85,14 @@ func newRunCommand() *cobra.Command { } runCommand.Flags().SetInterspersed(false) - setCreateFlags(runCommand) + setCreateFlags(runCommand, cfg) runCommand.Flags().BoolP("detach", "d", false, "Run container in background and print container ID") return runCommand } -func setCreateFlags(cmd *cobra.Command) { +func setCreateFlags(cmd *cobra.Command, cfg *RunConfig) { // No "-h" alias for "--help", because "-h" for "--hostname". cmd.Flags().Bool("help", false, "show help") @@ -117,13 +117,13 @@ func setCreateFlags(cmd *cobra.Command) { // #endregion // #region platform flags - cmd.Flags().String("platform", "", "Set platform (e.g. \"amd64\", \"arm64\")") // not a slice, and there is no --all-platforms + cmd.Flags().String("platform", cfg.Platform, "Set platform (e.g. \"amd64\", \"arm64\")") // not a slice, and there is no --all-platforms cmd.RegisterFlagCompletionFunc("platform", shellCompletePlatforms) // #endregion // #region network flags // network (net) is defined as StringSlice, not StringArray, to allow specifying "--network=cni1,cni2" - cmd.Flags().StringSlice("network", []string{netutil.DefaultNetworkName}, `Connect a container to a network ("bridge"|"host"|"none"|)`) + cmd.Flags().StringSlice("network", cfg.Network, `Connect a container to a network ("bridge"|"host"|"none"|)`) cmd.RegisterFlagCompletionFunc("network", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return shellCompleteNetworkNames(cmd, []string{}) })