diff --git a/cmd/cluster/clusterCreate.go b/cmd/cluster/clusterCreate.go index f5fc4af52..574db83da 100644 --- a/cmd/cluster/clusterCreate.go +++ b/cmd/cluster/clusterCreate.go @@ -38,7 +38,7 @@ import ( cliutil "github.com/rancher/k3d/v4/cmd/util" k3dCluster "github.com/rancher/k3d/v4/pkg/client" "github.com/rancher/k3d/v4/pkg/config" - conf "github.com/rancher/k3d/v4/pkg/config/v1alpha2" + conf "github.com/rancher/k3d/v4/pkg/config/v1alpha3" "github.com/rancher/k3d/v4/pkg/runtimes" k3d "github.com/rancher/k3d/v4/pkg/types" "github.com/rancher/k3d/v4/version" @@ -77,11 +77,6 @@ func initConfig() { if _, err := os.Stat(configFile); err != nil { log.Fatalf("Failed to stat config file %s: %+v", configFile, err) } - log.Tracef("Schema: %+v", conf.JSONSchema) - - if err := config.ValidateSchemaFile(configFile, []byte(conf.JSONSchema)); err != nil { - log.Fatalf("Schema Validation failed for config file %s: %+v", configFile, err) - } // try to read config into memory (viper map structure) if err := cfgViper.ReadInConfig(); err != nil { @@ -92,7 +87,16 @@ func initConfig() { log.Fatalf("Failed to read config file %s: %+v", configFile, err) } - log.Infof("Using config file %s", cfgViper.ConfigFileUsed()) + schema, err := config.GetSchemaByVersion(cfgViper.GetString("apiVersion")) + if err != nil { + log.Fatalf("Cannot validate config file %s: %+v", configFile, err) + } + + if err := config.ValidateSchemaFile(configFile, schema); err != nil { + log.Fatalf("Schema Validation failed for config file %s: %+v", configFile, err) + } + + log.Infof("Using config file %s (%s#%s)", cfgViper.ConfigFileUsed(), strings.ToLower(cfgViper.GetString("apiVersion")), strings.ToLower(cfgViper.GetString("kind"))) } if log.GetLevel() >= log.DebugLevel { c, _ := yaml.Marshal(cfgViper.AllSettings()) @@ -121,19 +125,35 @@ func NewCmdClusterCreate() *cobra.Command { /************************* * Compute Configuration * *************************/ - cfg, err := config.FromViperSimple(cfgViper) + if cfgViper.GetString("apiversion") == "" { + cfgViper.Set("apiversion", config.DefaultConfigApiVersion) + } + if cfgViper.GetString("kind") == "" { + cfgViper.Set("kind", "Simple") + } + cfg, err := config.FromViper(cfgViper) if err != nil { log.Fatalln(err) } - log.Debugf("========== Simple Config ==========\n%+v\n==========================\n", cfg) + if cfg.GetAPIVersion() != config.DefaultConfigApiVersion { + log.Warnf("Default config apiVersion is '%s', but you're using '%s': consider migrating.", config.DefaultConfigApiVersion, cfg.GetAPIVersion()) + cfg, err = config.Migrate(cfg, config.DefaultConfigApiVersion) + if err != nil { + log.Fatalln(err) + } + } + + simpleCfg := cfg.(conf.SimpleConfig) + + log.Debugf("========== Simple Config ==========\n%+v\n==========================\n", simpleCfg) - cfg, err = applyCLIOverrides(cfg) + simpleCfg, err = applyCLIOverrides(simpleCfg) if err != nil { log.Fatalf("Failed to apply CLI overrides: %+v", err) } - log.Debugf("========== Merged Simple Config ==========\n%+v\n==========================\n", cfg) + log.Debugf("========== Merged Simple Config ==========\n%+v\n==========================\n", simpleCfg) /************************************** * Transform, Process & Validate Configuration * @@ -141,10 +161,10 @@ func NewCmdClusterCreate() *cobra.Command { // Set the name if len(args) != 0 { - cfg.Name = args[0] + simpleCfg.Name = args[0] } - clusterConfig, err := config.TransformSimpleToClusterConfig(cmd.Context(), runtimes.SelectedRuntime, cfg) + clusterConfig, err := config.TransformSimpleToClusterConfig(cmd.Context(), runtimes.SelectedRuntime, simpleCfg) if err != nil { log.Fatalln(err) } @@ -178,7 +198,7 @@ func NewCmdClusterCreate() *cobra.Command { if err := k3dCluster.ClusterRun(cmd.Context(), runtimes.SelectedRuntime, clusterConfig); err != nil { // rollback if creation failed log.Errorln(err) - if cfg.Options.K3dOptions.NoRollback { // TODO: move rollback mechanics to pkg/ + if simpleCfg.Options.K3dOptions.NoRollback { // TODO: move rollback mechanics to pkg/ log.Fatalln("Cluster creation FAILED, rollback deactivated.") } // rollback if creation failed @@ -202,7 +222,7 @@ func NewCmdClusterCreate() *cobra.Command { if clusterConfig.KubeconfigOpts.UpdateDefaultKubeconfig { log.Debugf("Updating default kubeconfig with a new context for cluster %s", clusterConfig.Cluster.Name) - if _, err := k3dCluster.KubeconfigGetWrite(cmd.Context(), runtimes.SelectedRuntime, &clusterConfig.Cluster, "", &k3dCluster.WriteKubeConfigOptions{UpdateExisting: true, OverwriteExisting: false, UpdateCurrentContext: cfg.Options.KubeconfigOptions.SwitchCurrentContext}); err != nil { + if _, err := k3dCluster.KubeconfigGetWrite(cmd.Context(), runtimes.SelectedRuntime, &clusterConfig.Cluster, "", &k3dCluster.WriteKubeConfigOptions{UpdateExisting: true, OverwriteExisting: false, UpdateCurrentContext: simpleCfg.Options.KubeconfigOptions.SwitchCurrentContext}); err != nil { log.Warningln(err) } } @@ -266,6 +286,10 @@ func NewCmdClusterCreate() *cobra.Command { cmd.Flags().StringArrayP("label", "l", nil, "Add label to node container (Format: `KEY[=VALUE][@NODEFILTER[;NODEFILTER...]]`\n - Example: `k3d cluster create --agents 2 -l \"my.label@agent[0,1]\" -l \"other.label=somevalue@server[0]\"`") _ = ppViper.BindPFlag("cli.labels", cmd.Flags().Lookup("label")) + /* k3s */ + cmd.Flags().StringArray("k3s-arg", nil, "Additional args passed to k3s command (Format: `ARG@NODEFILTER[;@NODEFILTER]`)\n - Example: `k3d cluster create --k3s-arg \"--disable=traefik@server[0]\"") + _ = cfgViper.BindPFlag("cli.k3sargs", cmd.Flags().Lookup("k3s-arg")) + /****************** * "Normal" Flags * ****************** @@ -340,13 +364,6 @@ func NewCmdClusterCreate() *cobra.Command { cmd.Flags().String("registry-config", "", "Specify path to an extra registries.yaml file") _ = cfgViper.BindPFlag("registries.config", cmd.Flags().Lookup("registry-config")) - /* k3s */ - cmd.Flags().StringArray("k3s-server-arg", nil, "Additional args passed to the `k3s server` command on server nodes (new flag per arg)") - _ = cfgViper.BindPFlag("options.k3s.extraserverargs", cmd.Flags().Lookup("k3s-server-arg")) - - cmd.Flags().StringArray("k3s-agent-arg", nil, "Additional args passed to the `k3s agent` command on agent nodes (new flag per arg)") - _ = cfgViper.BindPFlag("options.k3s.extraagentargs", cmd.Flags().Lookup("k3s-agent-arg")) - /* Subcommands */ // done @@ -520,5 +537,30 @@ func applyCLIOverrides(cfg conf.SimpleConfig) (conf.SimpleConfig, error) { log.Tracef("EnvFilterMap: %+v", envFilterMap) + // --k3s-arg + argFilterMap := make(map[string][]string, 1) + for _, argFlag := range ppViper.GetStringSlice("cli.k3sargs") { + + // split node filter from the specified arg + arg, filters, err := cliutil.SplitFiltersFromFlag(argFlag) + if err != nil { + log.Fatalln(err) + } + + // create new entry or append filter to existing entry + if _, exists := argFilterMap[arg]; exists { + argFilterMap[arg] = append(argFilterMap[arg], filters...) + } else { + argFilterMap[arg] = filters + } + } + + for arg, nodeFilters := range argFilterMap { + cfg.Options.K3sOptions.ExtraArgs = append(cfg.Options.K3sOptions.ExtraArgs, conf.K3sArgWithNodeFilters{ + Arg: arg, + NodeFilters: nodeFilters, + }) + } + return cfg, nil } diff --git a/cmd/config/config.go b/cmd/config/config.go index 2a49986a2..7156c576c 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -41,6 +41,7 @@ func NewCmdConfig() *cobra.Command { } cmd.AddCommand(NewCmdConfigInit()) + cmd.AddCommand(NewCmdConfigMigrate()) return cmd } diff --git a/cmd/config/configInit.go b/cmd/config/configInit.go index 34b27b91a..b85b754b6 100644 --- a/cmd/config/configInit.go +++ b/cmd/config/configInit.go @@ -25,7 +25,7 @@ import ( "fmt" "os" - config "github.com/rancher/k3d/v4/pkg/config/v1alpha2" + config "github.com/rancher/k3d/v4/pkg/config/v1alpha3" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) diff --git a/cmd/config/configMigrate.go b/cmd/config/configMigrate.go new file mode 100644 index 000000000..e18b19bc7 --- /dev/null +++ b/cmd/config/configMigrate.go @@ -0,0 +1,111 @@ +/* +Copyright © 2020 The k3d Author(s) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package config + +import ( + "os" + "strings" + + "github.com/rancher/k3d/v4/pkg/config" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "gopkg.in/yaml.v2" +) + +// NewCmdConfigMigrate returns a new cobra command +func NewCmdConfigMigrate() *cobra.Command { + + cmd := &cobra.Command{ + Use: "migrate INPUT OUTPUT", + Args: cobra.RangeArgs(1, 2), + Run: func(cmd *cobra.Command, args []string) { + + configFile := args[0] + + if _, err := os.Stat(configFile); err != nil { + log.Fatalf("Failed to stat config file %s: %+v", configFile, err) + } + + cfgViper := viper.New() + cfgViper.SetConfigType("yaml") + + cfgViper.SetConfigFile(configFile) + + // try to read config into memory (viper map structure) + if err := cfgViper.ReadInConfig(); err != nil { + if _, ok := err.(viper.ConfigFileNotFoundError); ok { + log.Fatalf("Config file %s not found: %+v", configFile, err) + } + // config file found but some other error happened + log.Fatalf("Failed to read config file %s: %+v", configFile, err) + } + + schema, err := config.GetSchemaByVersion(cfgViper.GetString("apiVersion")) + if err != nil { + log.Fatalf("Cannot validate config file %s: %+v", configFile, err) + } + + if err := config.ValidateSchemaFile(configFile, schema); err != nil { + log.Fatalf("Schema Validation failed for config file %s: %+v", configFile, err) + } + + log.Infof("Using config file %s (%s#%s)", cfgViper.ConfigFileUsed(), strings.ToLower(cfgViper.GetString("apiVersion")), strings.ToLower(cfgViper.GetString("kind"))) + + cfg, err := config.FromViper(cfgViper) + if err != nil { + log.Fatalln(err) + } + + if cfg.GetAPIVersion() != config.DefaultConfigApiVersion { + cfg, err = config.Migrate(cfg, config.DefaultConfigApiVersion) + if err != nil { + log.Fatalln(err) + } + } + + yamlout, err := yaml.Marshal(cfg) + if err != nil { + log.Fatalln(err) + } + + output := "-" + + if len(args) > 1 { + output = args[1] + } + + if output == "-" { + if _, err := os.Stdout.Write(yamlout); err != nil { + log.Fatalln(err) + } + } else { + if err := os.WriteFile(output, yamlout, os.ModeAppend); err != nil { + log.Fatalln(err) + } + } + + }, + } + + return cmd +} diff --git a/docgen/README.md b/docgen/README.md index b65264ef6..26f1be624 100644 --- a/docgen/README.md +++ b/docgen/README.md @@ -6,6 +6,8 @@ The code will output files in [`../docs/usage/commands/`](../docs/usage/commands ## Run +- may required a `replace github.com/rancher/k3d/v4 => PATH/TO/LOCAL/REPO` in the `go.mod` + ```bash # ensure that you're in the docgen dir, as the relative path to the docs/ dir is hardcoded cd docgen diff --git a/docgen/go.mod b/docgen/go.mod index 8b20fcaee..dc3a123f5 100644 --- a/docgen/go.mod +++ b/docgen/go.mod @@ -7,11 +7,8 @@ require ( github.com/containerd/cgroups v0.0.0-20210414074453-680c246289fb // indirect github.com/containerd/containerd v1.5.0-rc.1 // indirect github.com/containerd/continuity v0.0.0-20210315143101-93e15499afd5 // indirect - github.com/docker/cli v20.10.6+incompatible // indirect - github.com/docker/docker v20.10.6+incompatible // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/imdario/mergo v0.3.12 // indirect github.com/magiconair/properties v1.8.5 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect github.com/pelletier/go-toml v1.9.0 // indirect @@ -26,7 +23,6 @@ require ( google.golang.org/genproto v0.0.0-20210413151531-c14fb6ef47c3 // indirect google.golang.org/grpc v1.37.0 // indirect gopkg.in/ini.v1 v1.62.0 // indirect - k8s.io/client-go v0.21.0 // indirect k8s.io/utils v0.0.0-20210305010621-2afb4311ab10 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.1.1 // indirect ) diff --git a/docgen/go.sum b/docgen/go.sum index 4cb02b638..93a5ce4ea 100644 --- a/docgen/go.sum +++ b/docgen/go.sum @@ -39,7 +39,6 @@ github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= @@ -212,7 +211,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= @@ -227,17 +225,15 @@ github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/docker/cli v20.10.5+incompatible h1:bjflayQbWg+xOkF2WPEAOi4Y7zWhR7ptoPhV/VqLVDE= github.com/docker/cli v20.10.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/cli v20.10.6+incompatible h1:LAyI6Lnwv+AUjtp2ZyN1lxqXBtkeFUqm4H7CZMWZuP8= -github.com/docker/cli v20.10.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v0.0.0-20171011171712-7484e51bf6af/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v20.10.5+incompatible h1:o5WL5onN4awYGwrW7+oTn5x9AF2prw7V0Ox8ZEkoCdg= github.com/docker/docker v20.10.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.6+incompatible h1:oXI3Vas8TI8Eu/EjH4srKHJBVqraSzJybhxY7Om9faQ= -github.com/docker/docker v20.10.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= @@ -253,7 +249,6 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dvyukov/go-fuzz v0.0.0-20201127111758-49e582c6c23d/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= @@ -286,9 +281,8 @@ github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTD github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -458,9 +452,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= @@ -494,12 +487,12 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= @@ -537,7 +530,6 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/mount v0.2.0 h1:WhCW5B355jtxndN5ovugJlMFJawbUODuW8fSnEH6SSM= github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= @@ -564,8 +556,6 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+ github.com/nbutton23/zxcvbn-go v0.0.0-20160627004424-a22cb81b2ecd/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= @@ -660,8 +650,6 @@ github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= @@ -795,11 +783,6 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go4.org/intern v0.0.0-20210108033219-3eb7198706b2 h1:VFTf+jjIgsldaz/Mr00VaCSswHJrI2hIjQygE/W4IMg= -go4.org/intern v0.0.0-20210108033219-3eb7198706b2/go.mod h1:vLqJ+12kCw61iCWsPto0EOHhBS+o4rO5VIucbc9g2Cc= -go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222175341-b30ae309168e/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= -go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 h1:1tk03FUNpulq2cuWpXZWj649rwJpk0d20rxWiopKRmc= -go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -815,7 +798,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -892,7 +875,6 @@ golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1 h1:4qWs8cYYH6PoEFy4dfhDFgoMGkwAcETd+MmPdCPMzUc= @@ -985,7 +967,6 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -993,9 +974,7 @@ golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210414055047-fe65e336abe0 h1:g9s1Ppvvun/fI+BptTMj909BBIcGrzQ32k9FNlcevOE= golang.org/x/sys v0.0.0-20210414055047-fe65e336abe0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210406210042-72f3dc4e9b72 h1:VqE9gduFZ4dbR7XoL77lHFp0/DyDUBKSXK7CMFkVcV0= golang.org/x/term v0.0.0-20210406210042-72f3dc4e9b72/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1012,9 +991,8 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20170915040203-e531a2a1c15f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1176,9 +1154,8 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= @@ -1218,22 +1195,17 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -inet.af/netaddr v0.0.0-20210403172118-1e1430f727e0 h1:ANl7piXB3SHmhwTNeTO0yl0yf4gO3/aaFjcBCdH9Ftg= -inet.af/netaddr v0.0.0-20210403172118-1e1430f727e0/go.mod h1:I2i9ONCXRZDnG1+7O8fSuYzjcPxHQXrIfzD/IkR87x4= k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= +k8s.io/api v0.20.4 h1:xZjKidCirayzX6tHONRQyTNDVIR55TYVqgATqo6ZULY= k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= -k8s.io/api v0.21.0 h1:gu5iGF4V6tfVCQ/R+8Hc0h7H1JuEhzyEi9S4R5LM8+Y= -k8s.io/api v0.21.0/go.mod h1:+YbrhBBGgsxbF6o6Kj4KJPJnBmAKuXDeS3E18bgHNVU= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.4 h1:vhxQ0PPUUU2Ns1b9r4/UFp13UPs8cw2iOoTjnY9faa0= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.21.0 h1:3Fx+41if+IRavNcKOz09FwEXDBG6ORh6iMsTSelhkMA= -k8s.io/apimachinery v0.21.0/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= +k8s.io/client-go v0.20.4 h1:85crgh1IotNkLpKYKZHVNI1JT86nr/iDCvq2iWKsql4= k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= -k8s.io/client-go v0.21.0 h1:n0zzzJsAQmJngpC0IhgFcApZyoGXPrDIAD601HD09ag= -k8s.io/client-go v0.21.0/go.mod h1:nNBytTF9qPFDEhoqgEPaarobC8QPae13bElIVHzIglA= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= @@ -1241,11 +1213,9 @@ k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.8.0 h1:Q3gmuM9hKEjefWFFYF0Mat+YyFJvsUyYuwyNNJ5C9Ts= -k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210305010621-2afb4311ab10 h1:u5rPykqiCpL+LBfjRkXvnK71gOgIdmq3eHUEkPrbeTI= @@ -1258,7 +1228,6 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.1.1 h1:nYqY2A6oy37sKLYuSBXuQhbj4JVclzJK13BOIvJG5XU= sigs.k8s.io/structured-merge-diff/v4 v4.1.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/docs/usage/commands/k3d_cluster_create.md b/docs/usage/commands/k3d_cluster_create.md index ff4ea16b3..f784462d5 100644 --- a/docs/usage/commands/k3d_cluster_create.md +++ b/docs/usage/commands/k3d_cluster_create.md @@ -25,16 +25,16 @@ k3d cluster create NAME [flags] - Example: `k3d cluster create --servers 3 --api-port 0.0.0.0:6550` -c, --config string Path of a config file to use -e, --env KEY[=VALUE][@NODEFILTER[;NODEFILTER...]] Add environment variables to nodes (Format: KEY[=VALUE][@NODEFILTER[;NODEFILTER...]] - - Example: `k3d cluster create --agents 2 -e "HTTP_PROXY=my.proxy.com" -e "SOME_KEY=SOME_VAL@server[0]"` + - Example: `k3d cluster create --agents 2 -e "HTTP_PROXY=my.proxy.com@server[0]" -e "SOME_KEY=SOME_VAL@server[0]"` --gpus string GPU devices to add to the cluster node containers ('all' to pass all GPUs) [From docker] -h, --help help for create -i, --image string Specify k3s image that you want to use for the nodes - --k3s-agent-arg k3s agent Additional args passed to the k3s agent command on agent nodes (new flag per arg) - --k3s-server-arg k3s server Additional args passed to the k3s server command on server nodes (new flag per arg) + --k3s-arg ARG@NODEFILTER[;@NODEFILTER] Additional args passed to k3s command (Format: ARG@NODEFILTER[;@NODEFILTER]) + - Example: `k3d cluster create --k3s-arg "--disable=traefik@server[0]" --kubeconfig-switch-context Directly switch the default kubeconfig's current-context to the new cluster's context (requires --kubeconfig-update-default) (default true) --kubeconfig-update-default Directly update the default kubeconfig with the new cluster's context (default true) -l, --label KEY[=VALUE][@NODEFILTER[;NODEFILTER...]] Add label to node container (Format: KEY[=VALUE][@NODEFILTER[;NODEFILTER...]] - - Example: `k3d cluster create --agents 2 -l "my.label@agent[0,1]" -v "other.label=somevalue@server[0]"` + - Example: `k3d cluster create --agents 2 -l "my.label@agent[0,1]" -l "other.label=somevalue@server[0]"` --network string Join an existing network --no-hostip Disable the automatic injection of the Host IP as 'host.k3d.internal' into the containers and CoreDNS --no-image-volume Disable the creation of a volume for importing images diff --git a/docs/usage/commands/k3d_config.md b/docs/usage/commands/k3d_config.md index c42923e56..8f8f076e5 100644 --- a/docs/usage/commands/k3d_config.md +++ b/docs/usage/commands/k3d_config.md @@ -28,4 +28,5 @@ k3d config [flags] * [k3d](k3d.md) - https://k3d.io/ -> Run k3s in Docker! * [k3d config init](k3d_config_init.md) - +* [k3d config migrate](k3d_config_migrate.md) - diff --git a/docs/usage/commands/k3d_docgen.md b/docs/usage/commands/k3d_config_migrate.md similarity index 63% rename from docs/usage/commands/k3d_docgen.md rename to docs/usage/commands/k3d_config_migrate.md index 4767bdb63..902687ad5 100644 --- a/docs/usage/commands/k3d_docgen.md +++ b/docs/usage/commands/k3d_config_migrate.md @@ -1,15 +1,15 @@ -## k3d docgen +## k3d config migrate + -Generate command docs ``` -k3d docgen [flags] +k3d config migrate INPUT OUTPUT [flags] ``` ### Options ``` - -h, --help help for docgen + -h, --help help for migrate ``` ### Options inherited from parent commands @@ -22,5 +22,5 @@ k3d docgen [flags] ### SEE ALSO -* [k3d](k3d.md) - https://k3d.io/ -> Run k3s in Docker! +* [k3d config](k3d_config.md) - Work with config file(s) diff --git a/docs/usage/commands/k3d_node_create.md b/docs/usage/commands/k3d_node_create.md index 041bb5cb5..808cbe134 100644 --- a/docs/usage/commands/k3d_node_create.md +++ b/docs/usage/commands/k3d_node_create.md @@ -13,14 +13,15 @@ k3d node create NAME [flags] ### Options ``` - -c, --cluster string Select the cluster that the node shall connect to. (default "k3s-default") - -h, --help help for create - -i, --image string Specify k3s image used for the node(s) (default "docker.io/rancher/k3s:v1.20.0-k3s2") - --memory string Memory limit imposed on the node [From docker] - --replicas int Number of replicas of this node specification. (default 1) - --role string Specify node role [server, agent] (default "agent") - --timeout duration Maximum waiting time for '--wait' before canceling/returning. - --wait Wait for the node(s) to be ready before returning. + -c, --cluster string Select the cluster that the node shall connect to. (default "k3s-default") + -h, --help help for create + -i, --image string Specify k3s image used for the node(s) (default "docker.io/rancher/k3s:v1.20.0-k3s2") + --k3s-node-label strings Specify k3s node labels in format "foo=bar" + --memory string Memory limit imposed on the node [From docker] + --replicas int Number of replicas of this node specification. (default 1) + --role string Specify node role [server, agent] (default "agent") + --timeout duration Maximum waiting time for '--wait' before canceling/returning. + --wait Wait for the node(s) to be ready before returning. ``` ### Options inherited from parent commands diff --git a/docs/usage/configfile.md b/docs/usage/configfile.md index 051b4edb1..97fa45c0d 100644 --- a/docs/usage/configfile.md +++ b/docs/usage/configfile.md @@ -25,13 +25,13 @@ Using a config file is as easy as putting it in a well-known place in your file As of the time of writing this documentation, the config file only **requires** you to define two fields: -- `apiVersion` to match the version of the config file that you want to use (at this time it would be `apiVersion: k3d.io/v1alpha2`) +- `apiVersion` to match the version of the config file that you want to use (at this time it would be `apiVersion: k3d.io/v1alpha3`) - `kind` to define the kind of config file that you want to use (currently we only have the `Simple` config) So this would be the minimal config file, which configures absolutely nothing: ```yaml -apiVersion: k3d.io/v1alpha2 +apiVersion: k3d.io/v1alpha3 kind: Simple ``` @@ -43,7 +43,7 @@ Currently, the config file is still in an Alpha-State, meaning, that it is subje !!! info "Validation via JSON-Schema" k3d uses a [JSON-Schema](https://json-schema.org/) to describe the expected format and fields of the configuration file. This schema is also used to [validate](https://github.com/xeipuuv/gojsonschema#validation) a user-given config file. - This JSON-Schema can be found in the specific config version sub-directory in the repository (e.g. [here for `v1alpha2`](https://github.com/rancher/k3d/blob/main/pkg/config/v1alpha2/schema.json)) and could be used to lookup supported fields or by linters to validate the config file, e.g. in your code editor. + This JSON-Schema can be found in the specific config version sub-directory in the repository (e.g. [here for `v1alpha3`](https://github.com/rancher/k3d/blob/main/pkg/config/v1alpha3/schema.json)) and could be used to lookup supported fields or by linters to validate the config file, e.g. in your code editor. ### All Options: Example @@ -51,7 +51,7 @@ Since the config options and the config file are changing quite a bit, it's hard ```yaml # k3d configuration file, saved as e.g. /home/me/myk3dcluster.yaml -apiVersion: k3d.io/v1alpha2 # this will change in the future as we make everything more stable +apiVersion: k3d.io/v1alpha3 # this will change in the future as we make everything more stable kind: Simple # internally, we also have a Cluster config, which is not yet available externally name: mycluster # name that you want to give to your cluster (will still be prefixed with `k3d-`) servers: 1 # same as `--servers 1` @@ -98,9 +98,10 @@ options: disableRollback: false # same as `--no-Rollback` disableHostIPInjection: false # same as `--no-hostip` k3s: # options passed on to K3s itself - extraServerArgs: # additional arguments passed to the `k3s server` command; same as `--k3s-server-arg` - - --tls-san=my.host.domain - extraAgentArgs: [] # addditional arguments passed to the `k3s agent` command; same as `--k3s-agent-arg` + extraArgs: # additional arguments passed to the `k3s server|agent` command; same as `--k3s-arg` + - arg: --tls-san=my.host.domain + nodeFilters: + - server[*] kubeconfig: updateDefaultKubeconfig: true # add new cluster to your default Kubeconfig; same as `--kubeconfig-update-default` (default: true) switchCurrentContext: true # also set current-context to the new cluster's context; same as `--kubeconfig-switch-context` (default: true) diff --git a/docs/usage/guides/registries.md b/docs/usage/guides/registries.md index 04411dc88..11a485abb 100644 --- a/docs/usage/guides/registries.md +++ b/docs/usage/guides/registries.md @@ -29,7 +29,7 @@ This file can also be used for providing additional information necessary for ac If you're using a `SimpleConfig` file to configure your k3d cluster, you may as well embed the registries.yaml in there directly: ```yaml -apiVersion: k3d.io/v1alpha2 +apiVersion: k3d.io/v1alpha3 kind: Simple name: test servers: 1 diff --git a/pkg/client/cluster.go b/pkg/client/cluster.go index ec6cf8018..4aa29fcb5 100644 --- a/pkg/client/cluster.go +++ b/pkg/client/cluster.go @@ -36,7 +36,7 @@ import ( "github.com/docker/go-connections/nat" "github.com/imdario/mergo" "github.com/rancher/k3d/v4/pkg/actions" - config "github.com/rancher/k3d/v4/pkg/config/v1alpha2" + config "github.com/rancher/k3d/v4/pkg/config/v1alpha3" k3drt "github.com/rancher/k3d/v4/pkg/runtimes" "github.com/rancher/k3d/v4/pkg/runtimes/docker" runtimeErr "github.com/rancher/k3d/v4/pkg/runtimes/errors" diff --git a/pkg/config/config.go b/pkg/config/config.go index 9cf51e235..efd5b2673 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -29,43 +29,51 @@ import ( "github.com/spf13/viper" - conf "github.com/rancher/k3d/v4/pkg/config/v1alpha2" + "github.com/rancher/k3d/v4/pkg/config/v1alpha2" + "github.com/rancher/k3d/v4/pkg/config/v1alpha3" + defaultConfig "github.com/rancher/k3d/v4/pkg/config/v1alpha3" + + types "github.com/rancher/k3d/v4/pkg/config/types" ) -func FromViperSimple(config *viper.Viper) (conf.SimpleConfig, error) { +const DefaultConfigApiVersion = defaultConfig.ApiVersion - var cfg conf.SimpleConfig +var Schemas = map[string]string{ + v1alpha2.ApiVersion: v1alpha2.JSONSchema, + v1alpha3.ApiVersion: v1alpha3.JSONSchema, +} - // determine config kind - if config.GetString("kind") != "" && strings.ToLower(config.GetString("kind")) != "simple" { - return cfg, fmt.Errorf("Wrong `kind` '%s' != 'simple' in config file", config.GetString("kind")) +func GetSchemaByVersion(apiVersion string) ([]byte, error) { + schema, ok := Schemas[strings.ToLower(apiVersion)] + if !ok { + return nil, fmt.Errorf("unsupported apiVersion '%s'", apiVersion) } + return []byte(schema), nil +} - if err := config.Unmarshal(&cfg); err != nil { - log.Errorln("Failed to unmarshal File config") - - return cfg, err - } +func FromViper(config *viper.Viper) (types.Config, error) { - return cfg, nil -} + var cfg types.Config + var err error -func FromViper(config *viper.Viper) (conf.Config, error) { + apiVersion := strings.ToLower(config.GetString("apiversion")) + kind := strings.ToLower(config.GetString("kind")) - var cfg conf.Config + log.Tracef("Trying to read config apiVersion='%s', kind='%s'", apiVersion, kind) - // determine config kind - switch strings.ToLower(config.GetString("kind")) { - case "simple": - cfg = conf.SimpleConfig{} - case "cluster": - cfg = conf.ClusterConfig{} - case "clusterlist": - cfg = conf.ClusterListConfig{} + switch apiVersion { + case "k3d.io/v1alpha2": + cfg, err = v1alpha2.GetConfigByKind(kind) + case "k3d.io/v1alpha3": + cfg, err = v1alpha3.GetConfigByKind(kind) case "": - return nil, fmt.Errorf("Missing `kind` in config file") + cfg, err = defaultConfig.GetConfigByKind(kind) default: - return nil, fmt.Errorf("Unknown `kind` '%s' in config file", config.GetString("kind")) + return nil, fmt.Errorf("cannot read config with apiversion '%s'", config.GetString("apiversion")) + } + + if err != nil { + return nil, err } if err := config.Unmarshal(&cfg); err != nil { @@ -76,3 +84,12 @@ func FromViper(config *viper.Viper) (conf.Config, error) { return cfg, nil } + +func getMigrations(version string) map[string]func(types.Config) (types.Config, error) { + switch version { + case v1alpha3.ApiVersion: + return v1alpha3.Migrations + default: + return nil + } +} diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 9f6e05d74..eb15f1523 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -26,7 +26,8 @@ import ( "time" "github.com/go-test/deep" - conf "github.com/rancher/k3d/v4/pkg/config/v1alpha2" + configtypes "github.com/rancher/k3d/v4/pkg/config/types" + conf "github.com/rancher/k3d/v4/pkg/config/v1alpha3" "github.com/spf13/viper" k3d "github.com/rancher/k3d/v4/pkg/types" @@ -39,8 +40,8 @@ func TestReadSimpleConfig(t *testing.T) { exposedAPI.HostPort = "6443" expectedConfig := conf.SimpleConfig{ - TypeMeta: conf.TypeMeta{ - APIVersion: "k3d.io/v1alpha2", + TypeMeta: configtypes.TypeMeta{ + APIVersion: "k3d.io/v1alpha3", Kind: "Simple", }, Name: "test", @@ -83,8 +84,12 @@ func TestReadSimpleConfig(t *testing.T) { DisableImageVolume: false, }, K3sOptions: conf.SimpleConfigOptionsK3s{ - ExtraServerArgs: []string{"--tls-san=127.0.0.1"}, - ExtraAgentArgs: []string{}, + ExtraArgs: []conf.K3sArgWithNodeFilters{ + { + Arg: "--tls-san=127.0.0.1", + NodeFilters: []string{"server[*]"}, + }, + }, }, KubeconfigOptions: conf.SimpleConfigOptionsKubeconfig{ UpdateDefaultKubeconfig: true, @@ -107,7 +112,7 @@ func TestReadSimpleConfig(t *testing.T) { t.Error(err) } - cfg, err := FromViperSimple(config) + cfg, err := FromViper(config) if err != nil { t.Error(err) } @@ -123,8 +128,8 @@ func TestReadSimpleConfig(t *testing.T) { func TestReadClusterConfig(t *testing.T) { expectedConfig := conf.ClusterConfig{ - TypeMeta: conf.TypeMeta{ - APIVersion: "k3d.io/v1alpha2", + TypeMeta: configtypes.TypeMeta{ + APIVersion: "k3d.io/v1alpha3", Kind: "Cluster", }, Cluster: k3d.Cluster{ @@ -168,8 +173,8 @@ func TestReadClusterConfig(t *testing.T) { func TestReadClusterListConfig(t *testing.T) { expectedConfig := conf.ClusterListConfig{ - TypeMeta: conf.TypeMeta{ - APIVersion: "k3d.io/v1alpha2", + TypeMeta: configtypes.TypeMeta{ + APIVersion: "k3d.io/v1alpha3", Kind: "ClusterList", }, Clusters: []k3d.Cluster{ @@ -237,7 +242,7 @@ func TestReadUnknownConfig(t *testing.T) { t.Error(err) } - _, err := FromViperSimple(config) + _, err := FromViper(config) if err == nil { t.Fail() } diff --git a/pkg/config/jsonschema_test.go b/pkg/config/jsonschema_test.go index 5ece4e790..f3ec75b6f 100644 --- a/pkg/config/jsonschema_test.go +++ b/pkg/config/jsonschema_test.go @@ -24,14 +24,14 @@ package config import ( "testing" - "github.com/rancher/k3d/v4/pkg/config/v1alpha2" + "github.com/rancher/k3d/v4/pkg/config/v1alpha3" ) func TestValidateSchema(t *testing.T) { cfgPath := "./test_assets/config_test_simple.yaml" - if err := ValidateSchemaFile(cfgPath, []byte(v1alpha2.JSONSchema)); err != nil { + if err := ValidateSchemaFile(cfgPath, []byte(v1alpha3.JSONSchema)); err != nil { t.Errorf("Validation of config file %s against the default schema failed: %+v", cfgPath, err) } @@ -42,7 +42,7 @@ func TestValidateSchemaFail(t *testing.T) { cfgPath := "./test_assets/config_test_simple_invalid_servers.yaml" var err error - if err = ValidateSchemaFile(cfgPath, []byte(v1alpha2.JSONSchema)); err == nil { + if err = ValidateSchemaFile(cfgPath, []byte(v1alpha3.JSONSchema)); err == nil { t.Errorf("Validation of config file %s against the default schema passed where we expected a failure", cfgPath) } diff --git a/pkg/config/merge.go b/pkg/config/merge.go index 390269dcc..c86c0eaad 100644 --- a/pkg/config/merge.go +++ b/pkg/config/merge.go @@ -24,7 +24,7 @@ package config import ( "github.com/imdario/mergo" - conf "github.com/rancher/k3d/v4/pkg/config/v1alpha2" + conf "github.com/rancher/k3d/v4/pkg/config/v1alpha3" log "github.com/sirupsen/logrus" ) diff --git a/pkg/config/merge_test.go b/pkg/config/merge_test.go index 349f7d4be..cfb73b724 100644 --- a/pkg/config/merge_test.go +++ b/pkg/config/merge_test.go @@ -25,7 +25,8 @@ package config import ( "testing" - conf "github.com/rancher/k3d/v4/pkg/config/v1alpha2" + configtypes "github.com/rancher/k3d/v4/pkg/config/types" + conf "github.com/rancher/k3d/v4/pkg/config/v1alpha3" "github.com/spf13/viper" "gotest.tools/assert" ) @@ -34,7 +35,7 @@ func TestMergeSimpleConfig(t *testing.T) { srcConfig := "./test_assets/config_test_simple.yaml" destConfig := "./test_assets/config_test_simple_2.yaml" - var src, dest conf.Config + var src, dest configtypes.Config var err error cfg1 := viper.New() @@ -45,11 +46,11 @@ func TestMergeSimpleConfig(t *testing.T) { cfg2.SetConfigFile(destConfig) _ = cfg2.ReadInConfig() - if src, err = FromViperSimple(cfg1); err != nil { + if src, err = FromViper(cfg1); err != nil { t.Fatal(err) } - if dest, err = FromViperSimple(cfg2); err != nil { + if dest, err = FromViper(cfg2); err != nil { t.Fatal(err) } diff --git a/pkg/config/migrate.go b/pkg/config/migrate.go new file mode 100644 index 000000000..28fb5d1f3 --- /dev/null +++ b/pkg/config/migrate.go @@ -0,0 +1,40 @@ +/* +Copyright © 2020 The k3d Author(s) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +package config + +import ( + "fmt" + + types "github.com/rancher/k3d/v4/pkg/config/types" +) + +func Migrate(config types.Config, targetVersion string) (types.Config, error) { + + migration, ok := getMigrations(targetVersion)[config.GetAPIVersion()] + if !ok { + return nil, fmt.Errorf("no migration possible from '%s' to '%s'", config.GetAPIVersion(), targetVersion) + } + + return migration(config) + +} diff --git a/pkg/config/process.go b/pkg/config/process.go index 199a77cf1..d67d55ae2 100644 --- a/pkg/config/process.go +++ b/pkg/config/process.go @@ -23,7 +23,7 @@ THE SOFTWARE. package config import ( - conf "github.com/rancher/k3d/v4/pkg/config/v1alpha2" + conf "github.com/rancher/k3d/v4/pkg/config/v1alpha3" log "github.com/sirupsen/logrus" ) diff --git a/pkg/config/process_test.go b/pkg/config/process_test.go index 5326ca532..c4d890edc 100644 --- a/pkg/config/process_test.go +++ b/pkg/config/process_test.go @@ -26,6 +26,7 @@ import ( "context" "testing" + conf "github.com/rancher/k3d/v4/pkg/config/v1alpha3" "github.com/rancher/k3d/v4/pkg/runtimes" "github.com/spf13/viper" "gotest.tools/assert" @@ -38,14 +39,14 @@ func TestProcessClusterConfig(t *testing.T) { vip.SetConfigFile(cfgFile) _ = vip.ReadInConfig() - cfg, err := FromViperSimple(vip) + cfg, err := FromViper(vip) if err != nil { t.Error(err) } t.Logf("\n========== Read Config and transform to cluster ==========\n%+v\n=================================\n", cfg) - clusterCfg, err := TransformSimpleToClusterConfig(context.Background(), runtimes.Docker, cfg) + clusterCfg, err := TransformSimpleToClusterConfig(context.Background(), runtimes.Docker, cfg.(conf.SimpleConfig)) if err != nil { t.Error(err) } diff --git a/pkg/config/test_assets/config_test_cluster.yaml b/pkg/config/test_assets/config_test_cluster.yaml index e90f30b2b..f1a8438c6 100644 --- a/pkg/config/test_assets/config_test_cluster.yaml +++ b/pkg/config/test_assets/config_test_cluster.yaml @@ -1,4 +1,4 @@ -apiVersion: k3d.io/v1alpha2 +apiVersion: k3d.io/v1alpha3 kind: Cluster name: foo nodes: diff --git a/pkg/config/test_assets/config_test_cluster_list.yaml b/pkg/config/test_assets/config_test_cluster_list.yaml index 0eba2c223..9d2e55ca3 100644 --- a/pkg/config/test_assets/config_test_cluster_list.yaml +++ b/pkg/config/test_assets/config_test_cluster_list.yaml @@ -1,5 +1,5 @@ --- -apiVersion: k3d.io/v1alpha2 +apiVersion: k3d.io/v1alpha3 kind: ClusterList clusters: - name: foo diff --git a/pkg/config/test_assets/config_test_simple.yaml b/pkg/config/test_assets/config_test_simple.yaml index e264bd9ae..4e1321760 100644 --- a/pkg/config/test_assets/config_test_simple.yaml +++ b/pkg/config/test_assets/config_test_simple.yaml @@ -1,4 +1,4 @@ -apiVersion: k3d.io/v1alpha2 +apiVersion: k3d.io/v1alpha3 kind: Simple name: test servers: 1 @@ -25,7 +25,7 @@ env: labels: - label: foo=bar nodeFilters: - - server[0] + - "server[0]" - loadbalancer options: @@ -35,9 +35,10 @@ options: disableLoadbalancer: false disableImageVolume: false k3s: - extraServerArgs: - - --tls-san=127.0.0.1 - extraAgentArgs: [] + extraArgs: + - arg: --tls-san=127.0.0.1 + nodeFilters: + - "server[*]" kubeconfig: updateDefaultKubeconfig: true - switchCurrentContext: true \ No newline at end of file + switchCurrentContext: true diff --git a/pkg/config/test_assets/config_test_simple_2.yaml b/pkg/config/test_assets/config_test_simple_2.yaml index 0d5293eef..a849e3224 100644 --- a/pkg/config/test_assets/config_test_simple_2.yaml +++ b/pkg/config/test_assets/config_test_simple_2.yaml @@ -1,4 +1,4 @@ -apiVersion: k3d.io/v1alpha2 +apiVersion: k3d.io/v1alpha3 kind: Simple name: supertest agents: 8 \ No newline at end of file diff --git a/pkg/config/test_assets/config_test_simple_invalid_servers.yaml b/pkg/config/test_assets/config_test_simple_invalid_servers.yaml index 7f2442c34..b9e75fb68 100644 --- a/pkg/config/test_assets/config_test_simple_invalid_servers.yaml +++ b/pkg/config/test_assets/config_test_simple_invalid_servers.yaml @@ -1,4 +1,4 @@ -apiVersion: k3d.io/v1alpha2 +apiVersion: k3d.io/v1alpha3 kind: Simple name: 1234 servers: 1 @@ -35,9 +35,10 @@ options: disableLoadbalancer: false disableImageVolume: false k3s: - extraServerArgs: - - --tls-san=127.0.0.1 - extraAgentArgs: [] + extraArgs: + - arg: --tls-san=127.0.0.1 + nodeFilters: + - "server[*]" kubeconfig: updateDefaultKubeconfig: true switchCurrentContext: true \ No newline at end of file diff --git a/pkg/config/test_assets/config_test_unknown.yaml b/pkg/config/test_assets/config_test_unknown.yaml index 356972ce1..66fe0c0fa 100644 --- a/pkg/config/test_assets/config_test_unknown.yaml +++ b/pkg/config/test_assets/config_test_unknown.yaml @@ -1,3 +1,3 @@ -apiVersion: k3d.io/v1alpha2 +apiVersion: k3d.io/v1alpha3 kind: Unknown foo: bar \ No newline at end of file diff --git a/pkg/config/transform.go b/pkg/config/transform.go index 76f9751e2..0ec436864 100644 --- a/pkg/config/transform.go +++ b/pkg/config/transform.go @@ -31,7 +31,7 @@ import ( "github.com/docker/go-connections/nat" cliutil "github.com/rancher/k3d/v4/cmd/util" // TODO: move parseapiport to pkg - conf "github.com/rancher/k3d/v4/pkg/config/v1alpha2" + conf "github.com/rancher/k3d/v4/pkg/config/v1alpha3" "github.com/rancher/k3d/v4/pkg/runtimes" k3d "github.com/rancher/k3d/v4/pkg/types" "github.com/rancher/k3d/v4/pkg/types/k3s" @@ -117,7 +117,6 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim serverNode := k3d.Node{ Role: k3d.ServerRole, Image: simpleConfig.Image, - Args: simpleConfig.Options.K3sOptions.ExtraServerArgs, ServerOpts: k3d.ServerOpts{}, Memory: simpleConfig.Options.Runtime.ServersMemory, } @@ -135,7 +134,6 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim agentNode := k3d.Node{ Role: k3d.AgentRole, Image: simpleConfig.Image, - Args: simpleConfig.Options.K3sOptions.ExtraAgentArgs, Memory: simpleConfig.Options.Runtime.AgentsMemory, } newCluster.Nodes = append(newCluster.Nodes, &agentNode) @@ -228,6 +226,22 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim } } + // -> ARGS + for _, argWithNodeFilters := range simpleConfig.Options.K3sOptions.ExtraArgs { + if len(argWithNodeFilters.NodeFilters) == 0 && nodeCount > 1 { + return nil, fmt.Errorf("K3sExtraArg '%s' lacks a node filter, but there's more than one node", argWithNodeFilters.Arg) + } + + nodes, err := util.FilterNodes(nodeList, argWithNodeFilters.NodeFilters) + if err != nil { + return nil, err + } + + for _, node := range nodes { + node.Args = append(node.Args, argWithNodeFilters.Arg) + } + } + /************************** * Cluster Create Options * **************************/ @@ -238,8 +252,6 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim WaitForServer: simpleConfig.Options.K3dOptions.Wait, Timeout: simpleConfig.Options.K3dOptions.Timeout, DisableLoadBalancer: simpleConfig.Options.K3dOptions.DisableLoadbalancer, - K3sServerArgs: simpleConfig.Options.K3sOptions.ExtraServerArgs, - K3sAgentArgs: simpleConfig.Options.K3sOptions.ExtraAgentArgs, GPURequest: simpleConfig.Options.Runtime.GPURequest, ServersMemory: simpleConfig.Options.Runtime.ServersMemory, AgentsMemory: simpleConfig.Options.Runtime.AgentsMemory, diff --git a/pkg/config/transform_test.go b/pkg/config/transform_test.go index 6cfb336d8..495f499c7 100644 --- a/pkg/config/transform_test.go +++ b/pkg/config/transform_test.go @@ -26,6 +26,7 @@ import ( "context" "testing" + conf "github.com/rancher/k3d/v4/pkg/config/v1alpha3" "github.com/rancher/k3d/v4/pkg/runtimes" "github.com/spf13/viper" ) @@ -37,14 +38,14 @@ func TestTransformSimpleConfigToClusterConfig(t *testing.T) { vip.SetConfigFile(cfgFile) _ = vip.ReadInConfig() - cfg, err := FromViperSimple(vip) + cfg, err := FromViper(vip) if err != nil { t.Error(err) } t.Logf("\n========== Read Config ==========\n%+v\n=================================\n", cfg) - clusterCfg, err := TransformSimpleToClusterConfig(context.Background(), runtimes.Docker, cfg) + clusterCfg, err := TransformSimpleToClusterConfig(context.Background(), runtimes.Docker, cfg.(conf.SimpleConfig)) if err != nil { t.Error(err) } diff --git a/pkg/config/types/types.go b/pkg/config/types/types.go new file mode 100644 index 000000000..ff0e26e5c --- /dev/null +++ b/pkg/config/types/types.go @@ -0,0 +1,34 @@ +/* +Copyright © 2020 The k3d Author(s) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package types + +// TypeMeta is basically copied from https://github.com/kubernetes/apimachinery/blob/a3b564b22db316a41e94fdcffcf9995424fe924c/pkg/apis/meta/v1/types.go#L36-L56 +type TypeMeta struct { + Kind string `mapstructure:"kind,omitempty" yaml:"kind,omitempty" json:"kind,omitempty"` + APIVersion string `mapstructure:"apiVersion,omitempty" yaml:"apiVersion,omitempty" json:"apiVersion,omitempty"` +} + +// Config interface. +type Config interface { + GetKind() string + GetAPIVersion() string +} diff --git a/pkg/config/v1alpha2/types.go b/pkg/config/v1alpha2/types.go index 0679cfd43..9f2375d1e 100644 --- a/pkg/config/v1alpha2/types.go +++ b/pkg/config/v1alpha2/types.go @@ -27,6 +27,7 @@ import ( "fmt" "time" + configtypes "github.com/rancher/k3d/v4/pkg/config/types" k3d "github.com/rancher/k3d/v4/pkg/types" "github.com/rancher/k3d/v4/version" ) @@ -35,9 +36,11 @@ import ( //go:embed schema.json var JSONSchema string +const ApiVersion = "k3d.io/v1alpha2" + // DefaultConfigTpl for printing const DefaultConfigTpl = `--- -apiVersion: k3d.io/v1alpha2 +apiVersion: %s kind: Simple name: %s servers: 1 @@ -48,21 +51,11 @@ image: %s // DefaultConfig templated DefaultConfigTpl var DefaultConfig = fmt.Sprintf( DefaultConfigTpl, + ApiVersion, k3d.DefaultClusterName, fmt.Sprintf("%s:%s", k3d.DefaultK3sImageRepo, version.GetK3sVersion(false)), ) -// TypeMeta is basically copied from https://github.com/kubernetes/apimachinery/blob/a3b564b22db316a41e94fdcffcf9995424fe924c/pkg/apis/meta/v1/types.go#L36-L56 -type TypeMeta struct { - Kind string `mapstructure:"kind,omitempty" yaml:"kind,omitempty" json:"kind,omitempty"` - APIVersion string `mapstructure:"apiVersion,omitempty" yaml:"apiVersion,omitempty" json:"apiVersion,omitempty"` -} - -// Config interface. -type Config interface { - GetKind() string -} - type VolumeWithNodeFilters struct { Volume string `mapstructure:"volume" yaml:"volume" json:"volume,omitempty"` NodeFilters []string `mapstructure:"nodeFilters" yaml:"nodeFilters" json:"nodeFilters,omitempty"` @@ -119,21 +112,21 @@ type SimpleConfigOptionsK3s struct { // SimpleConfig describes the toplevel k3d configuration file. type SimpleConfig struct { - TypeMeta `mapstructure:",squash" yaml:",inline"` - Name string `mapstructure:"name" yaml:"name" json:"name,omitempty"` - Servers int `mapstructure:"servers" yaml:"servers" json:"servers,omitempty"` //nolint:lll // default 1 - Agents int `mapstructure:"agents" yaml:"agents" json:"agents,omitempty"` //nolint:lll // default 0 - ExposeAPI SimpleExposureOpts `mapstructure:"kubeAPI" yaml:"kubeAPI" json:"kubeAPI,omitempty"` - Image string `mapstructure:"image" yaml:"image" json:"image,omitempty"` - Network string `mapstructure:"network" yaml:"network" json:"network,omitempty"` - Subnet string `mapstructure:"subnet" yaml:"subnet" json:"subnet,omitempty"` - ClusterToken string `mapstructure:"token" yaml:"clusterToken" json:"clusterToken,omitempty"` // default: auto-generated - Volumes []VolumeWithNodeFilters `mapstructure:"volumes" yaml:"volumes" json:"volumes,omitempty"` - Ports []PortWithNodeFilters `mapstructure:"ports" yaml:"ports" json:"ports,omitempty"` - Labels []LabelWithNodeFilters `mapstructure:"labels" yaml:"labels" json:"labels,omitempty"` - Options SimpleConfigOptions `mapstructure:"options" yaml:"options" json:"options,omitempty"` - Env []EnvVarWithNodeFilters `mapstructure:"env" yaml:"env" json:"env,omitempty"` - Registries struct { + configtypes.TypeMeta `mapstructure:",squash" yaml:",inline"` + Name string `mapstructure:"name" yaml:"name" json:"name,omitempty"` + Servers int `mapstructure:"servers" yaml:"servers" json:"servers,omitempty"` //nolint:lll // default 1 + Agents int `mapstructure:"agents" yaml:"agents" json:"agents,omitempty"` //nolint:lll // default 0 + ExposeAPI SimpleExposureOpts `mapstructure:"kubeAPI" yaml:"kubeAPI" json:"kubeAPI,omitempty"` + Image string `mapstructure:"image" yaml:"image" json:"image,omitempty"` + Network string `mapstructure:"network" yaml:"network" json:"network,omitempty"` + Subnet string `mapstructure:"subnet" yaml:"subnet" json:"subnet,omitempty"` + ClusterToken string `mapstructure:"token" yaml:"clusterToken" json:"clusterToken,omitempty"` // default: auto-generated + Volumes []VolumeWithNodeFilters `mapstructure:"volumes" yaml:"volumes" json:"volumes,omitempty"` + Ports []PortWithNodeFilters `mapstructure:"ports" yaml:"ports" json:"ports,omitempty"` + Labels []LabelWithNodeFilters `mapstructure:"labels" yaml:"labels" json:"labels,omitempty"` + Options SimpleConfigOptions `mapstructure:"options" yaml:"options" json:"options,omitempty"` + Env []EnvVarWithNodeFilters `mapstructure:"env" yaml:"env" json:"env,omitempty"` + Registries struct { Use []string `mapstructure:"use" yaml:"use,omitempty" json:"use,omitempty"` Create bool `mapstructure:"create" yaml:"create,omitempty" json:"create,omitempty"` Config string `mapstructure:"config" yaml:"config,omitempty" json:"config,omitempty"` // registries.yaml (k3s config for containerd registry override) @@ -147,30 +140,60 @@ type SimpleExposureOpts struct { HostPort string `mapstructure:"hostPort" yaml:"hostPort,omitempty" json:"hostPort,omitempty"` } -// GetKind implements Config.GetKind +// Kind implements Config.Kind func (c SimpleConfig) GetKind() string { - return "Cluster" + return "Simple" +} + +func (c SimpleConfig) GetAPIVersion() string { + return ApiVersion } // ClusterConfig describes a single cluster config type ClusterConfig struct { - TypeMeta `mapstructure:",squash" yaml:",inline"` - Cluster k3d.Cluster `mapstructure:",squash" yaml:",inline"` - ClusterCreateOpts k3d.ClusterCreateOpts `mapstructure:"options" yaml:"options"` - KubeconfigOpts SimpleConfigOptionsKubeconfig `mapstructure:"kubeconfig" yaml:"kubeconfig"` + configtypes.TypeMeta `mapstructure:",squash" yaml:",inline"` + Cluster k3d.Cluster `mapstructure:",squash" yaml:",inline"` + ClusterCreateOpts k3d.ClusterCreateOpts `mapstructure:"options" yaml:"options"` + KubeconfigOpts SimpleConfigOptionsKubeconfig `mapstructure:"kubeconfig" yaml:"kubeconfig"` } -// GetKind implements Config.GetKind +// Kind implements Config.Kind func (c ClusterConfig) GetKind() string { - return "Cluster" + return "Simple" +} + +func (c ClusterConfig) GetAPIVersion() string { + return ApiVersion } // ClusterListConfig describes a list of clusters type ClusterListConfig struct { - TypeMeta `mapstructure:",squash" yaml:",inline"` - Clusters []k3d.Cluster `mapstructure:"clusters" yaml:"clusters"` + configtypes.TypeMeta `mapstructure:",squash" yaml:",inline"` + Clusters []k3d.Cluster `mapstructure:"clusters" yaml:"clusters"` } func (c ClusterListConfig) GetKind() string { - return "ClusterList" + return "Simple" +} + +func (c ClusterListConfig) GetAPIVersion() string { + return ApiVersion +} + +func GetConfigByKind(kind string) (configtypes.Config, error) { + + // determine config kind + switch kind { + case "simple": + return SimpleConfig{}, nil + case "cluster": + return ClusterConfig{}, nil + case "clusterlist": + return ClusterListConfig{}, nil + case "": + return nil, fmt.Errorf("missing `kind` in config file") + default: + return nil, fmt.Errorf("unknown `kind` '%s' in config file", kind) + } + } diff --git a/pkg/config/v1alpha3/migrations.go b/pkg/config/v1alpha3/migrations.go new file mode 100644 index 000000000..5c894ca17 --- /dev/null +++ b/pkg/config/v1alpha3/migrations.go @@ -0,0 +1,84 @@ +/* +Copyright © 2020 The k3d Author(s) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +package v1alpha3 + +import ( + "encoding/json" + + configtypes "github.com/rancher/k3d/v4/pkg/config/types" + "github.com/rancher/k3d/v4/pkg/config/v1alpha2" + log "github.com/sirupsen/logrus" +) + +var Migrations = map[string]func(configtypes.Config) (configtypes.Config, error){ + v1alpha2.ApiVersion: MigrateV1Alpha2, +} + +func MigrateV1Alpha2(input configtypes.Config) (configtypes.Config, error) { + log.Debugln("Migrating v1alpha2 to v1alpha3") + + injson, err := json.Marshal(input) + if err != nil { + return nil, err + } + + if input.GetKind() == "Simple" { + cfg := SimpleConfig{} + + if err := json.Unmarshal(injson, &cfg); err != nil { + return nil, err + } + + cfg.Options.K3sOptions.ExtraArgs = []K3sArgWithNodeFilters{} + + for _, arg := range input.(v1alpha2.SimpleConfig).Options.K3sOptions.ExtraServerArgs { + cfg.Options.K3sOptions.ExtraArgs = append(cfg.Options.K3sOptions.ExtraArgs, K3sArgWithNodeFilters{ + Arg: arg, + NodeFilters: []string{ + "server[*]", + }, + }) + } + + for _, arg := range input.(v1alpha2.SimpleConfig).Options.K3sOptions.ExtraAgentArgs { + cfg.Options.K3sOptions.ExtraArgs = append(cfg.Options.K3sOptions.ExtraArgs, K3sArgWithNodeFilters{ + Arg: arg, + NodeFilters: []string{ + "agent[*]", + }, + }) + } + + cfg.APIVersion = ApiVersion + + log.Debugf("Migrated config: %+v", cfg) + + return cfg, nil + + } + + log.Debugf("No migration needed for %s#%s -> %s#%s", input.GetAPIVersion(), input.GetKind(), ApiVersion, input.GetKind()) + + return input, nil + +} diff --git a/pkg/config/v1alpha3/schema.json b/pkg/config/v1alpha3/schema.json new file mode 100644 index 000000000..2b07c02cc --- /dev/null +++ b/pkg/config/v1alpha3/schema.json @@ -0,0 +1,254 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "SimpleConfig", + "type": "object", + "required": [ + "apiVersion", + "kind" + ], + "properties": { + "apiVersion": { + "type": "string", + "enum": [ + "k3d.io/v1alpha3" + ], + "default": "k3d.io/v1alpha3" + }, + "kind": { + "type": "string", + "enum": [ + "Simple" + ], + "default": "Simple" + }, + "name": { + "description": "Name of the cluster (must be a valid hostname and will be prefixed with 'k3d-'). Example: 'mycluster'.", + "type": "string", + "format": "hostname" + }, + "servers": { + "type": "number", + "minimum": 1 + }, + "agents": { + "type": "number", + "minimum": 0 + }, + "kubeAPI": { + "type": "object", + "properties": { + "host": { + "type": "string", + "format": "hostname" + }, + "hostIP": { + "type": "string", + "format": "ipv4", + "examples": [ + "0.0.0.0", + "192.168.178.55" + ] + }, + "hostPort": { + "type":"string", + "examples": [ + "6443" + ] + } + }, + "additionalProperties": false + }, + "image": { + "type": "string", + "examples": [ + "rancher/k3s:latest" + ] + }, + "network": { + "type": "string" + }, + "subnet": { + "type": "string", + "default": "auto", + "examples": [ + "172.28.0.0/16", + "192.162.0.0/16" + ] + }, + "token": { + "type": "string" + }, + "volumes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "volume": { + "type": "string" + }, + "nodeFilters": { + "$ref": "#/definitions/nodeFilters" + } + }, + "additionalProperties": false + } + }, + "ports": { + "type": "array", + "items": { + "type": "object", + "properties": { + "port": { + "type": "string" + }, + "nodeFilters": { + "$ref": "#/definitions/nodeFilters" + } + }, + "additionalProperties": false + } + }, + "labels": { + "type": "array", + "items": { + "type": "object", + "properties": { + "label": { + "type": "string" + }, + "nodeFilters": { + "$ref": "#/definitions/nodeFilters" + } + }, + "additionalProperties": false + } + }, + "options": { + "type": "object", + "properties": { + "k3d": { + "type": "object", + "properties": { + "wait": { + "type": "boolean", + "default": true + }, + "timeout": { + "type": "string", + "examples": [ + "60s", + "1m", + "1m30s" + ] + }, + "disableLoadbalancer": { + "type": "boolean", + "default": false + }, + "disableImageVolume": { + "type": "boolean", + "default": false + }, + "disableRollback": { + "type": "boolean", + "default": false + }, + "disableHostIPInjection": { + "type": "boolean", + "default": false + } + }, + "additionalProperties": false + }, + "k3s": { + "type": "object", + "properties": { + "extraArgs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "arg": { + "type": "string", + "examples": [ + "--tls-san=127.0.0.1", + "--disable=traefik" + ] + }, + "nodeFilters": { + "$ref": "#/definitions/nodeFilters" + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + }, + "kubeconfig": { + "type": "object", + "properties": { + "updateDefaultKubeconfig": { + "type": "boolean", + "default": true + }, + "switchCurrentContext": { + "type": "boolean", + "default": true + } + }, + "additionalProperties": false + }, + "runtime": { + "type": "object", + "properties": { + "gpuRequest": { + "type": "string" + }, + "serversMemory": { + "type": "string" + }, + "agentsMemory": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + "env": { + "type": "array", + "items": { + "type": "object", + "properties": { + "envVar": { + "type": "string" + }, + "nodeFilters": { + "$ref": "#/definitions/nodeFilters" + } + }, + "additionalProperties": false + } + }, + "registries": { + "type": "object" + } + }, + "additionalProperties": false, + "definitions": { + "nodeFilters": { + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + "loadbalancer", + "server[*]", + "server[0]", + "agent[1]", + "all" + ] + } + } +} \ No newline at end of file diff --git a/pkg/config/v1alpha3/types.go b/pkg/config/v1alpha3/types.go new file mode 100644 index 000000000..22465c71c --- /dev/null +++ b/pkg/config/v1alpha3/types.go @@ -0,0 +1,203 @@ +/* +Copyright © 2020 The k3d Author(s) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +package v1alpha3 + +import ( + _ "embed" + "fmt" + "strings" + "time" + + config "github.com/rancher/k3d/v4/pkg/config/types" + k3d "github.com/rancher/k3d/v4/pkg/types" + "github.com/rancher/k3d/v4/version" +) + +const ApiVersion = "k3d.io/v1alpha3" + +// JSONSchema describes the schema used to validate config files +//go:embed schema.json +var JSONSchema string + +// DefaultConfigTpl for printing +const DefaultConfigTpl = `--- +apiVersion: k3d.io/v1alpha3 +kind: Simple +name: %s +servers: 1 +agents: 0 +image: %s +` + +// DefaultConfig templated DefaultConfigTpl +var DefaultConfig = fmt.Sprintf( + DefaultConfigTpl, + k3d.DefaultClusterName, + fmt.Sprintf("%s:%s", k3d.DefaultK3sImageRepo, version.GetK3sVersion(false)), +) + +type VolumeWithNodeFilters struct { + Volume string `mapstructure:"volume" yaml:"volume" json:"volume,omitempty"` + NodeFilters []string `mapstructure:"nodeFilters" yaml:"nodeFilters" json:"nodeFilters,omitempty"` +} + +type PortWithNodeFilters struct { + Port string `mapstructure:"port" yaml:"port" json:"port,omitempty"` + NodeFilters []string `mapstructure:"nodeFilters" yaml:"nodeFilters" json:"nodeFilters,omitempty"` +} + +type LabelWithNodeFilters struct { + Label string `mapstructure:"label" yaml:"label" json:"label,omitempty"` + NodeFilters []string `mapstructure:"nodeFilters" yaml:"nodeFilters" json:"nodeFilters,omitempty"` +} + +type EnvVarWithNodeFilters struct { + EnvVar string `mapstructure:"envVar" yaml:"envVar" json:"envVar,omitempty"` + NodeFilters []string `mapstructure:"nodeFilters" yaml:"nodeFilters" json:"nodeFilters,omitempty"` +} + +type K3sArgWithNodeFilters struct { + Arg string `mapstructure:"arg" yaml:"arg" json:"arg,omitempty"` + NodeFilters []string `mapstructure:"nodeFilters" yaml:"nodeFilters" json:"nodeFilters,omitempty"` +} + +// SimpleConfigOptionsKubeconfig describes the set of options referring to the kubeconfig during cluster creation. +type SimpleConfigOptionsKubeconfig struct { + UpdateDefaultKubeconfig bool `mapstructure:"updateDefaultKubeconfig" yaml:"updateDefaultKubeconfig" json:"updateDefaultKubeconfig,omitempty"` // default: true + SwitchCurrentContext bool `mapstructure:"switchCurrentContext" yaml:"switchCurrentContext" json:"switchCurrentContext,omitempty"` //nolint:lll // default: true +} + +type SimpleConfigOptions struct { + K3dOptions SimpleConfigOptionsK3d `mapstructure:"k3d" yaml:"k3d"` + K3sOptions SimpleConfigOptionsK3s `mapstructure:"k3s" yaml:"k3s"` + KubeconfigOptions SimpleConfigOptionsKubeconfig `mapstructure:"kubeconfig" yaml:"kubeconfig"` + Runtime SimpleConfigOptionsRuntime `mapstructure:"runtime" yaml:"runtime"` +} + +type SimpleConfigOptionsRuntime struct { + GPURequest string `mapstructure:"gpuRequest" yaml:"gpuRequest"` + ServersMemory string `mapstructure:"serversMemory" yaml:"serversMemory"` + AgentsMemory string `mapstructure:"agentsMemory" yaml:"agentsMemory"` +} + +type SimpleConfigOptionsK3d struct { + Wait bool `mapstructure:"wait" yaml:"wait"` + Timeout time.Duration `mapstructure:"timeout" yaml:"timeout"` + DisableLoadbalancer bool `mapstructure:"disableLoadbalancer" yaml:"disableLoadbalancer"` + DisableImageVolume bool `mapstructure:"disableImageVolume" yaml:"disableImageVolume"` + NoRollback bool `mapstructure:"disableRollback" yaml:"disableRollback"` + PrepDisableHostIPInjection bool `mapstructure:"disableHostIPInjection" yaml:"disableHostIPInjection"` + NodeHookActions []k3d.NodeHookAction `mapstructure:"nodeHookActions" yaml:"nodeHookActions,omitempty"` +} + +type SimpleConfigOptionsK3s struct { + ExtraArgs []K3sArgWithNodeFilters `mapstructure:"extraArgs" yaml:"extraArgs"` +} + +// SimpleConfig describes the toplevel k3d configuration file. +type SimpleConfig struct { + config.TypeMeta `mapstructure:",squash" yaml:",inline"` + Name string `mapstructure:"name" yaml:"name" json:"name,omitempty"` + Servers int `mapstructure:"servers" yaml:"servers" json:"servers,omitempty"` //nolint:lll // default 1 + Agents int `mapstructure:"agents" yaml:"agents" json:"agents,omitempty"` //nolint:lll // default 0 + ExposeAPI SimpleExposureOpts `mapstructure:"kubeAPI" yaml:"kubeAPI" json:"kubeAPI,omitempty"` + Image string `mapstructure:"image" yaml:"image" json:"image,omitempty"` + Network string `mapstructure:"network" yaml:"network" json:"network,omitempty"` + Subnet string `mapstructure:"subnet" yaml:"subnet" json:"subnet,omitempty"` + ClusterToken string `mapstructure:"token" yaml:"clusterToken" json:"clusterToken,omitempty"` // default: auto-generated + Volumes []VolumeWithNodeFilters `mapstructure:"volumes" yaml:"volumes" json:"volumes,omitempty"` + Ports []PortWithNodeFilters `mapstructure:"ports" yaml:"ports" json:"ports,omitempty"` + Labels []LabelWithNodeFilters `mapstructure:"labels" yaml:"labels" json:"labels,omitempty"` + Options SimpleConfigOptions `mapstructure:"options" yaml:"options" json:"options,omitempty"` + Env []EnvVarWithNodeFilters `mapstructure:"env" yaml:"env" json:"env,omitempty"` + Registries struct { + Use []string `mapstructure:"use" yaml:"use,omitempty" json:"use,omitempty"` + Create bool `mapstructure:"create" yaml:"create,omitempty" json:"create,omitempty"` + Config string `mapstructure:"config" yaml:"config,omitempty" json:"config,omitempty"` // registries.yaml (k3s config for containerd registry override) + } `mapstructure:"registries" yaml:"registries,omitempty" json:"registries,omitempty"` +} + +// SimpleExposureOpts provides a simplified syntax compared to the original k3d.ExposureOpts +type SimpleExposureOpts struct { + Host string `mapstructure:"host" yaml:"host,omitempty" json:"host,omitempty"` + HostIP string `mapstructure:"hostIP" yaml:"hostIP,omitempty" json:"hostIP,omitempty"` + HostPort string `mapstructure:"hostPort" yaml:"hostPort,omitempty" json:"hostPort,omitempty"` +} + +// GetKind implements Config.GetKind +func (c SimpleConfig) GetKind() string { + return "Simple" +} + +func (c SimpleConfig) GetAPIVersion() string { + return ApiVersion +} + +// ClusterConfig describes a single cluster config +type ClusterConfig struct { + config.TypeMeta `mapstructure:",squash" yaml:",inline"` + Cluster k3d.Cluster `mapstructure:",squash" yaml:",inline"` + ClusterCreateOpts k3d.ClusterCreateOpts `mapstructure:"options" yaml:"options"` + KubeconfigOpts SimpleConfigOptionsKubeconfig `mapstructure:"kubeconfig" yaml:"kubeconfig"` +} + +// GetKind implements Config.GetKind +func (c ClusterConfig) GetKind() string { + return "Simple" +} + +func (c ClusterConfig) GetAPIVersion() string { + return ApiVersion +} + +// ClusterListConfig describes a list of clusters +type ClusterListConfig struct { + config.TypeMeta `mapstructure:",squash" yaml:",inline"` + Clusters []k3d.Cluster `mapstructure:"clusters" yaml:"clusters"` +} + +func (c ClusterListConfig) GetKind() string { + return "Simple" +} + +func (c ClusterListConfig) GetAPIVersion() string { + return ApiVersion +} + +func GetConfigByKind(kind string) (config.Config, error) { + + // determine config kind + switch strings.ToLower(kind) { + case "simple": + return SimpleConfig{}, nil + case "cluster": + return ClusterConfig{}, nil + case "clusterlist": + return ClusterListConfig{}, nil + case "": + return nil, fmt.Errorf("missing `kind` in config file") + default: + return nil, fmt.Errorf("unknown `kind` '%s' in config file", kind) + } + +} diff --git a/pkg/config/validate.go b/pkg/config/validate.go index bb81ba417..c647e4f5d 100644 --- a/pkg/config/validate.go +++ b/pkg/config/validate.go @@ -27,7 +27,7 @@ import ( "time" k3dc "github.com/rancher/k3d/v4/pkg/client" - conf "github.com/rancher/k3d/v4/pkg/config/v1alpha2" + conf "github.com/rancher/k3d/v4/pkg/config/v1alpha3" "github.com/rancher/k3d/v4/pkg/runtimes" runtimeutil "github.com/rancher/k3d/v4/pkg/runtimes/util" k3d "github.com/rancher/k3d/v4/pkg/types" diff --git a/pkg/config/validate_test.go b/pkg/config/validate_test.go index 45e736e36..881778081 100644 --- a/pkg/config/validate_test.go +++ b/pkg/config/validate_test.go @@ -26,7 +26,7 @@ import ( "context" "testing" - conf "github.com/rancher/k3d/v4/pkg/config/v1alpha2" + conf "github.com/rancher/k3d/v4/pkg/config/v1alpha3" "github.com/rancher/k3d/v4/pkg/runtimes" "github.com/spf13/viper" ) diff --git a/pkg/types/types.go b/pkg/types/types.go index 655047ad2..e31141ec2 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -184,8 +184,6 @@ type ClusterCreateOpts struct { WaitForServer bool `yaml:"waitForServer" json:"waitForServer,omitempty"` Timeout time.Duration `yaml:"timeout" json:"timeout,omitempty"` DisableLoadBalancer bool `yaml:"disableLoadbalancer" json:"disableLoadbalancer,omitempty"` - K3sServerArgs []string `yaml:"k3sServerArgs" json:"k3sServerArgs,omitempty"` - K3sAgentArgs []string `yaml:"k3sAgentArgs" json:"k3sAgentArgs,omitempty"` GPURequest string `yaml:"gpuRequest" json:"gpuRequest,omitempty"` ServersMemory string `yaml:"serversMemory" json:"serversMemory,omitempty"` AgentsMemory string `yaml:"agentsMemory" json:"agentsMemory,omitempty"` diff --git a/tests/assets/config_test_simple.yaml b/tests/assets/config_test_simple.yaml index 4d9f72556..c1d05e2bc 100755 --- a/tests/assets/config_test_simple.yaml +++ b/tests/assets/config_test_simple.yaml @@ -1,4 +1,4 @@ -apiVersion: k3d.io/v1alpha2 +apiVersion: k3d.io/v1alpha3 kind: Simple name: test servers: 3 @@ -43,9 +43,10 @@ options: disableLoadbalancer: false disableImageVolume: false k3s: - extraServerArgs: - - --tls-san=127.0.0.1 - extraAgentArgs: [] + extraArgs: + - arg: --tls-san=127.0.0.1 + nodeFilters: + - server[*] kubeconfig: updateDefaultKubeconfig: true switchCurrentContext: true \ No newline at end of file diff --git a/tests/assets/config_test_simple_migration_v1alpha2.yaml b/tests/assets/config_test_simple_migration_v1alpha2.yaml new file mode 100755 index 000000000..4d9f72556 --- /dev/null +++ b/tests/assets/config_test_simple_migration_v1alpha2.yaml @@ -0,0 +1,51 @@ +apiVersion: k3d.io/v1alpha2 +kind: Simple +name: test +servers: 3 +agents: 2 +kubeAPI: + hostIP: "0.0.0.0" + hostPort: "6446" +image: rancher/k3s:latest +volumes: + - volume: /my/path:/some/path + nodeFilters: + - all +ports: + - port: 80:80 + nodeFilters: + - loadbalancer + - port: 0.0.0.0:443:443 + nodeFilters: + - loadbalancer +env: + - envVar: bar=baz,bob + nodeFilters: + - all +labels: + - label: foo=bar + nodeFilters: + - server[0] + - loadbalancer +registries: + create: true + use: [] + config: | + mirrors: + "my.company.registry": + endpoint: + - http://my.company.registry:5000 + +options: + k3d: + wait: true + timeout: "360s" # should be pretty high for multi-server clusters to allow for a proper startup routine + disableLoadbalancer: false + disableImageVolume: false + k3s: + extraServerArgs: + - --tls-san=127.0.0.1 + extraAgentArgs: [] + kubeconfig: + updateDefaultKubeconfig: true + switchCurrentContext: true \ No newline at end of file diff --git a/tests/assets/config_test_simple_migration_v1alpha3.yaml b/tests/assets/config_test_simple_migration_v1alpha3.yaml new file mode 100755 index 000000000..c1d05e2bc --- /dev/null +++ b/tests/assets/config_test_simple_migration_v1alpha3.yaml @@ -0,0 +1,52 @@ +apiVersion: k3d.io/v1alpha3 +kind: Simple +name: test +servers: 3 +agents: 2 +kubeAPI: + hostIP: "0.0.0.0" + hostPort: "6446" +image: rancher/k3s:latest +volumes: + - volume: /my/path:/some/path + nodeFilters: + - all +ports: + - port: 80:80 + nodeFilters: + - loadbalancer + - port: 0.0.0.0:443:443 + nodeFilters: + - loadbalancer +env: + - envVar: bar=baz,bob + nodeFilters: + - all +labels: + - label: foo=bar + nodeFilters: + - server[0] + - loadbalancer +registries: + create: true + use: [] + config: | + mirrors: + "my.company.registry": + endpoint: + - http://my.company.registry:5000 + +options: + k3d: + wait: true + timeout: "360s" # should be pretty high for multi-server clusters to allow for a proper startup routine + disableLoadbalancer: false + disableImageVolume: false + k3s: + extraArgs: + - arg: --tls-san=127.0.0.1 + nodeFilters: + - server[*] + kubeconfig: + updateDefaultKubeconfig: true + switchCurrentContext: true \ No newline at end of file diff --git a/tests/test_config_file_migration.sh b/tests/test_config_file_migration.sh new file mode 100755 index 000000000..8f4be0939 --- /dev/null +++ b/tests/test_config_file_migration.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +CURR_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +[ -d "$CURR_DIR" ] || { echo "FATAL: no current dir (maybe running in zsh?)"; exit 1; } + +# shellcheck source=./common.sh +source "$CURR_DIR/common.sh" + + +export CURRENT_STAGE="Test | config-file-migration" + + + +highlight "[START] ConfigMigrateTest" + +tempdir=$(mktemp -d) +$EXE config migrate "$CURR_DIR/assets/config_test_simple_migration_v1alpha2.yaml" "$tempdir/expected.yaml" || failed "failed on $CURR_DIR/assets/config_test_simple.yaml" +$EXE config migrate "$CURR_DIR/assets/config_test_simple_migration_v1alpha3.yaml" "$tempdir/actual.yaml" || failed "failed on $CURR_DIR/assets/config_test_simple_migrate.yaml" + +diff "$tempdir/actual.yaml" "$tempdir/expected.yaml" || failed "config migration failed" && passed "config migration succeeded" + + +highlight "[DONE] ConfigMigrateTest" + +exit 0 + +