Skip to content

Commit

Permalink
support default platform config in nerdctl.toml
Browse files Browse the repository at this point in the history
Signed-off-by: YangKian <1207783292@qq.com>
  • Loading branch information
YangKian committed Dec 18, 2022
1 parent e83edd8 commit dc08bdd
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 37 deletions.
6 changes: 4 additions & 2 deletions cmd/nerdctl/build.go
Expand Up @@ -26,6 +26,8 @@ import (
"strconv"
"strings"

"github.com/compose-spec/compose-go/types"

"path/filepath"

"github.com/containerd/containerd/errdefs"
Expand All @@ -39,7 +41,7 @@ import (
"github.com/spf13/cobra"
)

func newBuildCommand() *cobra.Command {
func newBuildCommand(cfg *types.BuildConfig) *cobra.Command {
var buildCommand = &cobra.Command{
Use: "build [flags] PATH",
Short: "Build an image from a Dockerfile. Needs buildkitd to be running.",
Expand All @@ -66,7 +68,7 @@ If Dockerfile is not present and -f is not specified, it will look for Container

// #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.Platforms, "Set target platform for build (e.g., \"amd64\", \"arm64\")")
buildCommand.RegisterFlagCompletionFunc("platform", shellCompletePlatforms)
// #endregion

Expand Down
43 changes: 43 additions & 0 deletions cmd/nerdctl/build_test.go
Expand Up @@ -23,6 +23,8 @@ import (
"strings"
"testing"

ncdefaults "github.com/containerd/nerdctl/pkg/defaults"

"github.com/containerd/nerdctl/pkg/testutil"
"gotest.tools/v3/assert"
)
Expand Down Expand Up @@ -420,3 +422,44 @@ CMD ["echo", "nerdctl-build-notag-string"]
base.Cmd("images").AssertOutContains("<none>")
base.Cmd("image", "prune", "--force", "--all").AssertOK()
}

func TestBuildWithConfigFile(t *testing.T) {
testutil.RequiresBuild(t)
base := testutil.NewBase(t)
defer base.Cmd("builder", "prune").AssertOK()

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(`
namespace = "normal"
[default_config]
[default_config.normal]
build = {Platforms=["linux/amd64", "linux/arm64"]}
`), 0755)
assert.NilError(t, err)

if len(base.Env) == 0 {
base.Env = os.Environ()
}
base.Env = append(base.Env, "NERDCTL_TOML="+tomlPath)

imageName := testutil.Identifier(t)
defer base.Cmd("rmi", imageName).Run()

dockerfile := fmt.Sprintf(`FROM %s
CMD ["echo", "dummy"]
`, testutil.AlpineImage)

buildCtx, err := createBuildContext(dockerfile)
assert.NilError(t, err)
defer os.RemoveAll(buildCtx)

base.Cmd("build", "-t", imageName, buildCtx).AssertOK()
testMultiPlatformRun(base, imageName)
}
8 changes: 5 additions & 3 deletions cmd/nerdctl/container.go
Expand Up @@ -17,10 +17,11 @@
package main

import (
"github.com/compose-spec/compose-go/types"
"github.com/spf13/cobra"
)

func newContainerCommand() *cobra.Command {
func newContainerCommand(cfg *types.ServiceConfig) *cobra.Command {
containerCommand := &cobra.Command{
Annotations: map[string]string{Category: Management},
Use: "container",
Expand All @@ -29,9 +30,10 @@ func newContainerCommand() *cobra.Command {
SilenceUsage: true,
SilenceErrors: true,
}

containerCommand.AddCommand(
newCreateCommand(),
newRunCommand(),
newCreateCommand(cfg),
newRunCommand(cfg),
newUpdateCommand(),
newExecCommand(),
containerLsCommand(),
Expand Down
6 changes: 4 additions & 2 deletions cmd/nerdctl/create.go
Expand Up @@ -20,10 +20,12 @@ import (
"fmt"
"runtime"

"github.com/compose-spec/compose-go/types"

"github.com/spf13/cobra"
)

func newCreateCommand() *cobra.Command {
func newCreateCommand(cfg *types.ServiceConfig) *cobra.Command {
shortHelp := "Create a new container. Optionally specify \"ipfs://\" or \"ipns://\" scheme to pull image from IPFS."
longHelp := shortHelp
switch runtime.GOOS {
Expand All @@ -45,7 +47,7 @@ func newCreateCommand() *cobra.Command {
SilenceErrors: true,
}
createCommand.Flags().SetInterspersed(false)
setCreateFlags(createCommand)
setCreateFlags(createCommand, cfg)
return createCommand
}

Expand Down
5 changes: 3 additions & 2 deletions cmd/nerdctl/image.go
Expand Up @@ -17,10 +17,11 @@
package main

import (
"github.com/compose-spec/compose-go/types"
"github.com/spf13/cobra"
)

func newImageCommand() *cobra.Command {
func newImageCommand(cfg *types.BuildConfig) *cobra.Command {
cmd := &cobra.Command{
Annotations: map[string]string{Category: Management},
Use: "image",
Expand All @@ -30,7 +31,7 @@ func newImageCommand() *cobra.Command {
SilenceErrors: true,
}
cmd.AddCommand(
newBuildCommand(),
newBuildCommand(cfg),
// commitCommand is in "container", not in "image"
imageLsCommand(),
newHistoryCommand(),
Expand Down
117 changes: 95 additions & 22 deletions cmd/nerdctl/main.go
Expand Up @@ -24,6 +24,11 @@ import (
"strconv"
"strings"

"github.com/containerd/nerdctl/pkg/netutil"

"github.com/compose-spec/compose-go/types"
"github.com/containerd/nerdctl/pkg/reflectutil"

"github.com/containerd/containerd"
"github.com/containerd/containerd/defaults"
"github.com/containerd/containerd/namespaces"
Expand Down Expand Up @@ -136,18 +141,72 @@ 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"`
Experimental bool `toml:"experimental"`
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"`
Experimental bool `toml:"experimental"`
DefaultConfig ConfigTemplates `toml:"default_config"`
}

type ConfigTemplates map[string]types.ServiceConfig

func (d *ConfigTemplates) UnmarshalTOML(v interface{}) error {
defaultConfig, ok := v.(map[string]interface{})
if !ok {
return fmt.Errorf("parse ConfigTemplates error")
}

res := make(map[string]types.ServiceConfig)
for namespace, vl := range defaultConfig {
dcfg := defaultConfigTemplate()
tree, err := toml.TreeFromMap(vl.(map[string]interface{}))
if err != nil {
return err
}
if err = tree.Unmarshal(&dcfg); err != nil {
return err
}
warnUnknownFields(dcfg, namespace)
res[namespace] = dcfg
}
*d = res
return nil
}

func defaultConfigTemplate() types.ServiceConfig {
return types.ServiceConfig{
Build: &types.BuildConfig{
Platforms: []string{},
},
Platform: "",
Net: netutil.DefaultNetworkName,
}
}

func warnUnknownFields(svc types.ServiceConfig, namespace string) {
if unknown := reflectutil.UnknownNonEmptyFields(&svc,
"Net",
"Build",
"Platform",
); len(unknown) > 0 {
logrus.Warnf("Ignoring: [namespace %s] service %s: %+v", namespace, svc.Name, unknown)
}

if svc.Build != nil {
if unknown := reflectutil.UnknownNonEmptyFields(svc.Build,
"Platforms",
); len(unknown) > 0 {
logrus.Warnf("Ignoring: [namespace %s] service %s: build: %+v", namespace, svc.Name, unknown)
}
}
}

// NewConfig creates a default Config object statically,
Expand All @@ -166,23 +225,24 @@ func NewConfig() *Config {
InsecureRegistry: false,
HostsDir: ncdefaults.HostsDirs(),
Experimental: true,
DefaultConfig: make(map[string]types.ServiceConfig),
}
}

func initRootCmdFlags(rootCmd *cobra.Command, tomlPath string) (*pflag.FlagSet, error) {
func initRootCmdFlags(rootCmd *cobra.Command, tomlPath string) (*pflag.FlagSet, map[string]types.ServiceConfig, 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 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)
return nil, 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 nil, err
return nil, nil, err
}
}
aliasToBeInherited := pflag.NewFlagSet(rootCmd.Name(), pflag.ExitOnError)
Expand All @@ -207,7 +267,7 @@ func initRootCmdFlags(rootCmd *cobra.Command, tomlPath string) (*pflag.FlagSet,
rootCmd.PersistentFlags().StringSlice("hosts-dir", cfg.HostsDir, "A directory that contains <HOST:PORT>/hosts.toml (containerd style) or <HOST:PORT>/{ca.cert, cert.pem, key.pem} (docker style)")
// Experimental enable experimental feature, see in https://github.com/containerd/nerdctl/blob/main/docs/experimental.md
AddPersistentBoolFlag(rootCmd, "experimental", nil, nil, cfg.Experimental, "NERDCTL_EXPERIMENTAL", "Control experimental: https://github.com/containerd/nerdctl/blob/main/docs/experimental.md")
return aliasToBeInherited, nil
return aliasToBeInherited, cfg.DefaultConfig, nil
}

func newApp() (*cobra.Command, error) {
Expand All @@ -232,11 +292,24 @@ Config file ($NERDCTL_TOML): %s
}

rootCmd.SetUsageFunc(usage)
aliasToBeInherited, err := initRootCmdFlags(rootCmd, tomlPath)
aliasToBeInherited, defaultConfigs, err := initRootCmdFlags(rootCmd, tomlPath)
if err != nil {
return nil, err
}

ns, err := rootCmd.Flags().GetString("namespace")
if err != nil {
return nil, err
}
namespaceCfg, ok := defaultConfigs[ns]
if !ok {
namespaceCfg = defaultConfigTemplate()
}
//size := len(namespaceCfg.Build.Platforms)
//if size != 0 && namespaceCfg.Platform != namespaceCfg.Build.Platforms[size-1] {
// return nil, fmt.Errorf("conflict platform config")
//}

rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
debug, err := cmd.Flags().GetBool("debug-full")
if err != nil {
Expand Down Expand Up @@ -274,9 +347,9 @@ Config file ($NERDCTL_TOML): %s
}
rootCmd.RunE = unknownSubcommandAction
rootCmd.AddCommand(
newCreateCommand(),
newCreateCommand(&namespaceCfg),
// #region Run & Exec
newRunCommand(),
newRunCommand(&namespaceCfg),
newUpdateCommand(),
newExecCommand(),
// #endregion
Expand All @@ -298,7 +371,7 @@ Config file ($NERDCTL_TOML): %s
// #endregion

// Build
newBuildCommand(),
newBuildCommand(namespaceCfg.Build),

// #region Image management
newImagesCommand(),
Expand All @@ -325,8 +398,8 @@ Config file ($NERDCTL_TOML): %s
newStatsCommand(),

// #region Management
newContainerCommand(),
newImageCommand(),
newContainerCommand(&namespaceCfg),
newImageCommand(namespaceCfg.Build),
newNetworkCommand(),
newVolumeCommand(),
newSystemCommand(),
Expand Down

0 comments on commit dc08bdd

Please sign in to comment.