cmd: make config initialization more general
- move viper initialization from k3d config file to separate util sub-package in cmd/ - use that new subpackage init function to leverage the config file in `k3d cluster delete` - cover that with an e2e test case
This commit is contained in:
parent
78738058c8
commit
91426eabd1
@ -24,9 +24,7 @@ package cluster
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -40,6 +38,7 @@ import (
|
|||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
cliutil "github.com/rancher/k3d/v4/cmd/util"
|
cliutil "github.com/rancher/k3d/v4/cmd/util"
|
||||||
|
cliconfig "github.com/rancher/k3d/v4/cmd/util/config"
|
||||||
k3dCluster "github.com/rancher/k3d/v4/pkg/client"
|
k3dCluster "github.com/rancher/k3d/v4/pkg/client"
|
||||||
"github.com/rancher/k3d/v4/pkg/config"
|
"github.com/rancher/k3d/v4/pkg/config"
|
||||||
conf "github.com/rancher/k3d/v4/pkg/config/v1alpha3"
|
conf "github.com/rancher/k3d/v4/pkg/config/v1alpha3"
|
||||||
@ -59,74 +58,30 @@ Every cluster will consist of one or more containers:
|
|||||||
- (optionally) 1 (or more) agent node containers (k3s)
|
- (optionally) 1 (or more) agent node containers (k3s)
|
||||||
`
|
`
|
||||||
|
|
||||||
var cfgViper = viper.New()
|
/*
|
||||||
var ppViper = viper.New()
|
* Viper for configuration handling
|
||||||
|
* we use two different instances of Viper here to handle
|
||||||
|
* - cfgViper: "static" configuration
|
||||||
|
* - ppViper: "pre-processed" configuration, where CLI input has to be pre-processed
|
||||||
|
* to be treated as part of the SImpleConfig
|
||||||
|
*/
|
||||||
|
var (
|
||||||
|
cfgViper = viper.New()
|
||||||
|
ppViper = viper.New()
|
||||||
|
)
|
||||||
|
|
||||||
func initConfig() {
|
func initConfig() error {
|
||||||
|
|
||||||
// Viper for pre-processed config options
|
// Viper for pre-processed config options
|
||||||
ppViper.SetEnvPrefix("K3D")
|
ppViper.SetEnvPrefix("K3D")
|
||||||
|
|
||||||
// viper for the general config (file, env and non pre-processed flags)
|
|
||||||
cfgViper.SetEnvPrefix("K3D")
|
|
||||||
cfgViper.AutomaticEnv()
|
|
||||||
|
|
||||||
cfgViper.SetConfigType("yaml")
|
|
||||||
|
|
||||||
// Set config file, if specified
|
|
||||||
if configFile != "" {
|
|
||||||
|
|
||||||
if _, err := os.Stat(configFile); err != nil {
|
|
||||||
l.Log().Fatalf("Failed to stat config file %s: %+v", configFile, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// create temporary file to expand environment variables in the config without writing that back to the original file
|
|
||||||
// we're doing it here, because this happens just before absolutely all other processing
|
|
||||||
tmpfile, err := os.CreateTemp(os.TempDir(), fmt.Sprintf("k3d-config-tmp-%s", filepath.Base(configFile)))
|
|
||||||
if err != nil {
|
|
||||||
l.Log().Fatalf("error creating temp copy of configfile %s for variable expansion: %v", configFile, err)
|
|
||||||
}
|
|
||||||
defer tmpfile.Close()
|
|
||||||
|
|
||||||
originalcontent, err := ioutil.ReadFile(configFile)
|
|
||||||
if err != nil {
|
|
||||||
l.Log().Fatalf("error reading config file %s: %v", configFile, err)
|
|
||||||
}
|
|
||||||
expandedcontent := os.ExpandEnv(string(originalcontent))
|
|
||||||
if _, err := tmpfile.WriteString(expandedcontent); err != nil {
|
|
||||||
l.Log().Fatalf("error writing expanded config file contents to temp file %s: %v", tmpfile.Name(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// use temp file with expanded variables
|
|
||||||
cfgViper.SetConfigFile(tmpfile.Name())
|
|
||||||
|
|
||||||
// try to read config into memory (viper map structure)
|
|
||||||
if err := cfgViper.ReadInConfig(); err != nil {
|
|
||||||
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
|
||||||
l.Log().Fatalf("Config file %s not found: %+v", configFile, err)
|
|
||||||
}
|
|
||||||
// config file found but some other error happened
|
|
||||||
l.Log().Fatalf("Failed to read config file %s: %+v", configFile, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
schema, err := config.GetSchemaByVersion(cfgViper.GetString("apiVersion"))
|
|
||||||
if err != nil {
|
|
||||||
l.Log().Fatalf("Cannot validate config file %s: %+v", configFile, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := config.ValidateSchemaFile(configFile, schema); err != nil {
|
|
||||||
l.Log().Fatalf("Schema Validation failed for config file %s: %+v", configFile, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Log().Infof("Using config file %s (%s#%s)", configFile, strings.ToLower(cfgViper.GetString("apiVersion")), strings.ToLower(cfgViper.GetString("kind")))
|
|
||||||
}
|
|
||||||
if l.Log().GetLevel() >= logrus.DebugLevel {
|
if l.Log().GetLevel() >= logrus.DebugLevel {
|
||||||
c, _ := yaml.Marshal(cfgViper.AllSettings())
|
|
||||||
l.Log().Debugf("Configuration:\n%s", c)
|
|
||||||
|
|
||||||
c, _ = yaml.Marshal(ppViper.AllSettings())
|
c, _ := yaml.Marshal(ppViper.AllSettings())
|
||||||
l.Log().Debugf("Additional CLI Configuration:\n%s", c)
|
l.Log().Debugf("Additional CLI Configuration:\n%s", c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return cliconfig.InitViperWithConfigFile(cfgViper, configFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCmdClusterCreate returns a new cobra command
|
// NewCmdClusterCreate returns a new cobra command
|
||||||
@ -139,8 +94,7 @@ func NewCmdClusterCreate() *cobra.Command {
|
|||||||
Long: clusterCreateDescription,
|
Long: clusterCreateDescription,
|
||||||
Args: cobra.RangeArgs(0, 1), // exactly one cluster name can be set (default: k3d.DefaultClusterName)
|
Args: cobra.RangeArgs(0, 1), // exactly one cluster name can be set (default: k3d.DefaultClusterName)
|
||||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
initConfig()
|
return initConfig()
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/rancher/k3d/v4/cmd/util"
|
"github.com/rancher/k3d/v4/cmd/util"
|
||||||
|
cliconfig "github.com/rancher/k3d/v4/cmd/util/config"
|
||||||
"github.com/rancher/k3d/v4/pkg/client"
|
"github.com/rancher/k3d/v4/pkg/client"
|
||||||
l "github.com/rancher/k3d/v4/pkg/logger"
|
l "github.com/rancher/k3d/v4/pkg/logger"
|
||||||
"github.com/rancher/k3d/v4/pkg/runtimes"
|
"github.com/rancher/k3d/v4/pkg/runtimes"
|
||||||
@ -34,8 +35,12 @@ import (
|
|||||||
k3dutil "github.com/rancher/k3d/v4/pkg/util"
|
k3dutil "github.com/rancher/k3d/v4/pkg/util"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var clusterDeleteConfigFile string
|
||||||
|
var clusterDeleteCfgViper = viper.New()
|
||||||
|
|
||||||
// NewCmdClusterDelete returns a new cobra command
|
// NewCmdClusterDelete returns a new cobra command
|
||||||
func NewCmdClusterDelete() *cobra.Command {
|
func NewCmdClusterDelete() *cobra.Command {
|
||||||
|
|
||||||
@ -47,6 +52,9 @@ func NewCmdClusterDelete() *cobra.Command {
|
|||||||
Long: `Delete cluster(s).`,
|
Long: `Delete cluster(s).`,
|
||||||
Args: cobra.MinimumNArgs(0), // 0 or n arguments; 0 = default cluster name
|
Args: cobra.MinimumNArgs(0), // 0 or n arguments; 0 = default cluster name
|
||||||
ValidArgsFunction: util.ValidArgsAvailableClusters,
|
ValidArgsFunction: util.ValidArgsAvailableClusters,
|
||||||
|
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return cliconfig.InitViperWithConfigFile(clusterDeleteCfgViper, clusterDeleteConfigFile)
|
||||||
|
},
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
clusters := parseDeleteClusterCmd(cmd, args)
|
clusters := parseDeleteClusterCmd(cmd, args)
|
||||||
|
|
||||||
@ -87,6 +95,15 @@ func NewCmdClusterDelete() *cobra.Command {
|
|||||||
// add flags
|
// add flags
|
||||||
cmd.Flags().BoolP("all", "a", false, "Delete all existing clusters")
|
cmd.Flags().BoolP("all", "a", false, "Delete all existing clusters")
|
||||||
|
|
||||||
|
/***************
|
||||||
|
* Config File *
|
||||||
|
***************/
|
||||||
|
|
||||||
|
cmd.Flags().StringVarP(&clusterDeleteConfigFile, "config", "c", "", "Path of a config file to use")
|
||||||
|
if err := cmd.MarkFlagFilename("config", "yaml", "yml"); err != nil {
|
||||||
|
l.Log().Fatalln("Failed to mark flag 'config' as filename flag")
|
||||||
|
}
|
||||||
|
|
||||||
// done
|
// done
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
@ -94,12 +111,36 @@ func NewCmdClusterDelete() *cobra.Command {
|
|||||||
// parseDeleteClusterCmd parses the command input into variables required to delete clusters
|
// parseDeleteClusterCmd parses the command input into variables required to delete clusters
|
||||||
func parseDeleteClusterCmd(cmd *cobra.Command, args []string) []*k3d.Cluster {
|
func parseDeleteClusterCmd(cmd *cobra.Command, args []string) []*k3d.Cluster {
|
||||||
|
|
||||||
// --all
|
|
||||||
var clusters []*k3d.Cluster
|
var clusters []*k3d.Cluster
|
||||||
|
|
||||||
if all, err := cmd.Flags().GetBool("all"); err != nil {
|
// --all
|
||||||
|
all, err := cmd.Flags().GetBool("all")
|
||||||
|
if err != nil {
|
||||||
l.Log().Fatalln(err)
|
l.Log().Fatalln(err)
|
||||||
} else if all {
|
}
|
||||||
|
|
||||||
|
// --config
|
||||||
|
if clusterDeleteConfigFile != "" {
|
||||||
|
// not allowed with --all or more args
|
||||||
|
if len(args) > 0 || all {
|
||||||
|
l.Log().Fatalln("failed to delete cluster: cannot use `--config` flag with additional arguments or `--all`")
|
||||||
|
}
|
||||||
|
|
||||||
|
if clusterDeleteCfgViper.GetString("name") == "" {
|
||||||
|
l.Log().Fatalln("failed to delete cluster via config file: no name in config file")
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := client.ClusterGet(cmd.Context(), runtimes.SelectedRuntime, &k3d.Cluster{Name: clusterDeleteCfgViper.GetString("name")})
|
||||||
|
if err != nil {
|
||||||
|
l.Log().Fatalf("failed to delete cluster '%s': %v", clusterDeleteCfgViper.GetString("name"), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
clusters = append(clusters, c)
|
||||||
|
return clusters
|
||||||
|
}
|
||||||
|
|
||||||
|
// --all was set
|
||||||
|
if all {
|
||||||
l.Log().Infoln("Deleting all clusters...")
|
l.Log().Infoln("Deleting all clusters...")
|
||||||
clusters, err = client.ClusterList(cmd.Context(), runtimes.SelectedRuntime)
|
clusters, err = client.ClusterList(cmd.Context(), runtimes.SelectedRuntime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -108,6 +149,7 @@ func parseDeleteClusterCmd(cmd *cobra.Command, args []string) []*k3d.Cluster {
|
|||||||
return clusters
|
return clusters
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// args only
|
||||||
clusternames := []string{k3d.DefaultClusterName}
|
clusternames := []string{k3d.DefaultClusterName}
|
||||||
if len(args) != 0 {
|
if len(args) != 0 {
|
||||||
clusternames = args
|
clusternames = args
|
||||||
|
98
cmd/util/config/config.go
Normal file
98
cmd/util/config/config.go
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2020-2021 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"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/rancher/k3d/v4/pkg/config"
|
||||||
|
l "github.com/rancher/k3d/v4/pkg/logger"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitViperWithConfigFile(cfgViper *viper.Viper, configFile string) error {
|
||||||
|
|
||||||
|
// viper for the general config (file, env and non pre-processed flags)
|
||||||
|
cfgViper.SetEnvPrefix("K3D")
|
||||||
|
cfgViper.AutomaticEnv()
|
||||||
|
|
||||||
|
cfgViper.SetConfigType("yaml")
|
||||||
|
|
||||||
|
// Set config file, if specified
|
||||||
|
if configFile != "" {
|
||||||
|
|
||||||
|
if _, err := os.Stat(configFile); err != nil {
|
||||||
|
l.Log().Fatalf("Failed to stat config file %s: %+v", configFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create temporary file to expand environment variables in the config without writing that back to the original file
|
||||||
|
// we're doing it here, because this happens just before absolutely all other processing
|
||||||
|
tmpfile, err := os.CreateTemp(os.TempDir(), fmt.Sprintf("k3d-config-tmp-%s", filepath.Base(configFile)))
|
||||||
|
if err != nil {
|
||||||
|
l.Log().Fatalf("error creating temp copy of configfile %s for variable expansion: %v", configFile, err)
|
||||||
|
}
|
||||||
|
defer tmpfile.Close()
|
||||||
|
|
||||||
|
originalcontent, err := ioutil.ReadFile(configFile)
|
||||||
|
if err != nil {
|
||||||
|
l.Log().Fatalf("error reading config file %s: %v", configFile, err)
|
||||||
|
}
|
||||||
|
expandedcontent := os.ExpandEnv(string(originalcontent))
|
||||||
|
if _, err := tmpfile.WriteString(expandedcontent); err != nil {
|
||||||
|
l.Log().Fatalf("error writing expanded config file contents to temp file %s: %v", tmpfile.Name(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// use temp file with expanded variables
|
||||||
|
cfgViper.SetConfigFile(tmpfile.Name())
|
||||||
|
|
||||||
|
// try to read config into memory (viper map structure)
|
||||||
|
if err := cfgViper.ReadInConfig(); err != nil {
|
||||||
|
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
||||||
|
l.Log().Fatalf("Config file %s not found: %+v", configFile, err)
|
||||||
|
}
|
||||||
|
// config file found but some other error happened
|
||||||
|
l.Log().Fatalf("Failed to read config file %s: %+v", configFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
schema, err := config.GetSchemaByVersion(cfgViper.GetString("apiVersion"))
|
||||||
|
if err != nil {
|
||||||
|
l.Log().Fatalf("Cannot validate config file %s: %+v", configFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := config.ValidateSchemaFile(configFile, schema); err != nil {
|
||||||
|
l.Log().Fatalf("Schema Validation failed for config file %s: %+v", configFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Log().Infof("Using config file %s (%s#%s)", configFile, strings.ToLower(cfgViper.GetString("apiVersion")), strings.ToLower(cfgViper.GetString("kind")))
|
||||||
|
}
|
||||||
|
if l.Log().GetLevel() >= logrus.DebugLevel {
|
||||||
|
c, _ := yaml.Marshal(cfgViper.AllSettings())
|
||||||
|
l.Log().Debugf("Configuration:\n%s", c)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -17,13 +17,16 @@ fi
|
|||||||
|
|
||||||
export CURRENT_STAGE="Test | config-file | $K3S_IMAGE_TAG"
|
export CURRENT_STAGE="Test | config-file | $K3S_IMAGE_TAG"
|
||||||
|
|
||||||
|
configfileoriginal="$CURR_DIR/assets/config_test_simple.yaml"
|
||||||
|
configfile="/tmp/config_test_simple-tmp_$(date -u +'%Y%m%dT%H%M%SZ').yaml"
|
||||||
clustername="configtest"
|
clustername="configtest"
|
||||||
|
|
||||||
|
sed -E "s/name:.+/name: $clustername/g" < "$configfileoriginal" > "$configfile" # replace cluster name in config file so we can use it in this script without running into override issues
|
||||||
|
|
||||||
highlight "[START] ConfigTest $EXTRA_TITLE"
|
highlight "[START] ConfigTest $EXTRA_TITLE"
|
||||||
|
|
||||||
info "Creating cluster $clustername..."
|
info "Creating cluster $clustername..."
|
||||||
$EXE cluster create "$clustername" --config "$CURR_DIR/assets/config_test_simple.yaml" $EXTRA_FLAG || failed "could not create cluster $clustername $EXTRA_TITLE"
|
$EXE cluster create "$clustername" --config "$configfile" $EXTRA_FLAG || failed "could not create cluster $clustername $EXTRA_TITLE"
|
||||||
|
|
||||||
info "Sleeping for 5 seconds to give the cluster enough time to get ready..."
|
info "Sleeping for 5 seconds to give the cluster enough time to get ready..."
|
||||||
sleep 5
|
sleep 5
|
||||||
@ -60,8 +63,10 @@ exec_in_node "k3d-$clustername-server-0" "cat /etc/rancher/k3s/registries.yaml"
|
|||||||
|
|
||||||
# Cleanup
|
# Cleanup
|
||||||
|
|
||||||
info "Deleting cluster $clustername..."
|
info "Deleting cluster $clustername (using config file)..."
|
||||||
$EXE cluster delete "$clustername" || failed "could not delete the cluster $clustername"
|
$EXE cluster delete --config "$configfile" || failed "could not delete the cluster $clustername"
|
||||||
|
|
||||||
|
rm "$configfile"
|
||||||
|
|
||||||
highlight "[DONE] ConfigTest $EXTRA_TITLE"
|
highlight "[DONE] ConfigTest $EXTRA_TITLE"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user