From 63c545f37e91ba51ed9c53cb25d942703759a786 Mon Sep 17 00:00:00 2001 From: Karen Almog Date: Thu, 23 Dec 2021 11:21:42 +0000 Subject: [PATCH 1/3] remove --config as a global flag this commit removes config as a global flag so that it will not show up in commands that don't use it (for example, worker). it also sets the default config path to /etc/k0s/k0s.yaml since it is already the de-facto location for k0sctl and also appears in our documenttation. Signed-off-by: Karen Almog --- cmd/airgap/airgap.go | 1 + cmd/airgap/listimages.go | 1 + cmd/api/api.go | 1 + cmd/backup/backup.go | 1 + cmd/config/validate.go | 1 + cmd/etcd/etcd.go | 1 + cmd/etcd/leave.go | 1 + cmd/etcd/list.go | 1 + cmd/install/install.go | 5 - cmd/kubeconfig/create.go | 2 +- cmd/reset/reset.go | 1 + cmd/restore/restore.go | 1 + cmd/token/create.go | 2 +- cmd/token/token.go | 1 - cmd/validate/validate.go | 3 - .../v1beta1/clusterconfig_types.go | 85 +++++++++--- pkg/component/controller/clusterConfig.go | 2 +- pkg/config/cli.go | 11 +- pkg/config/config.go | 125 +++++++----------- pkg/constant/constant_posix.go | 1 + pkg/constant/constant_windows.go | 1 + 21 files changed, 141 insertions(+), 107 deletions(-) diff --git a/cmd/airgap/airgap.go b/cmd/airgap/airgap.go index 7f0cf19affa3..2f597edcaa42 100644 --- a/cmd/airgap/airgap.go +++ b/cmd/airgap/airgap.go @@ -29,6 +29,7 @@ func NewAirgapCmd() *cobra.Command { cmd.SilenceUsage = true cmd.AddCommand(NewAirgapListImagesCmd()) + cmd.PersistentFlags().AddFlagSet(config.FileInputFlag()) cmd.PersistentFlags().AddFlagSet(config.GetPersistentFlagSet()) return cmd } diff --git a/cmd/airgap/listimages.go b/cmd/airgap/listimages.go index 9f501deb8417..398b00793bf5 100644 --- a/cmd/airgap/listimages.go +++ b/cmd/airgap/listimages.go @@ -44,6 +44,7 @@ func NewAirgapListImagesCmd() *cobra.Command { return nil }, } + cmd.Flags().AddFlagSet(config.FileInputFlag()) cmd.PersistentFlags().AddFlagSet(config.GetPersistentFlagSet()) return cmd } diff --git a/cmd/api/api.go b/cmd/api/api.go index 26b992a63e47..571a3780dbea 100644 --- a/cmd/api/api.go +++ b/cmd/api/api.go @@ -72,6 +72,7 @@ func NewAPICmd() *cobra.Command { }, } cmd.SilenceUsage = true + cmd.Flags().AddFlagSet(config.FileInputFlag()) cmd.PersistentFlags().AddFlagSet(config.GetPersistentFlagSet()) return cmd } diff --git a/cmd/backup/backup.go b/cmd/backup/backup.go index eabca14b1e91..dedeed2170dc 100644 --- a/cmd/backup/backup.go +++ b/cmd/backup/backup.go @@ -54,6 +54,7 @@ func NewBackupCmd() *cobra.Command { } cmd.Flags().StringVar(&savePath, "save-path", "", "destination directory path for backup assets") cmd.SilenceUsage = true + cmd.Flags().AddFlagSet(config.FileInputFlag()) cmd.PersistentFlags().AddFlagSet(config.GetPersistentFlagSet()) return cmd } diff --git a/cmd/config/validate.go b/cmd/config/validate.go index faaf0d3574de..d7ca7802d979 100644 --- a/cmd/config/validate.go +++ b/cmd/config/validate.go @@ -36,5 +36,6 @@ func NewValidateCmd() *cobra.Command { } cmd.PersistentFlags().AddFlagSet(config.GetPersistentFlagSet()) + cmd.Flags().AddFlagSet(config.FileInputFlag()) return cmd } diff --git a/cmd/etcd/etcd.go b/cmd/etcd/etcd.go index 50b9fec1a9cd..e12fa156ea7a 100644 --- a/cmd/etcd/etcd.go +++ b/cmd/etcd/etcd.go @@ -46,6 +46,7 @@ func NewEtcdCmd() *cobra.Command { cmd.SilenceUsage = true cmd.AddCommand(etcdLeaveCmd()) cmd.AddCommand(etcdListCmd()) + cmd.Flags().AddFlagSet(config.FileInputFlag()) cmd.PersistentFlags().AddFlagSet(config.GetPersistentFlagSet()) return cmd } diff --git a/cmd/etcd/leave.go b/cmd/etcd/leave.go index 32e26ce717a7..4e90033b204a 100644 --- a/cmd/etcd/leave.go +++ b/cmd/etcd/leave.go @@ -75,5 +75,6 @@ func etcdLeaveCmd() *cobra.Command { cmd.Flags().StringVar(&etcdPeerAddress, "peer-address", "", "etcd peer address") cmd.PersistentFlags().AddFlagSet(config.GetPersistentFlagSet()) + cmd.Flags().AddFlagSet(config.FileInputFlag()) return cmd } diff --git a/cmd/etcd/list.go b/cmd/etcd/list.go index e85ac002c856..d66c387d7756 100644 --- a/cmd/etcd/list.go +++ b/cmd/etcd/list.go @@ -45,6 +45,7 @@ func etcdListCmd() *cobra.Command { return json.NewEncoder(os.Stdout).Encode(map[string]interface{}{"members": members}) }, } + cmd.Flags().AddFlagSet(config.FileInputFlag()) cmd.PersistentFlags().AddFlagSet(config.GetPersistentFlagSet()) return cmd } diff --git a/cmd/install/install.go b/cmd/install/install.go index e7fcf6a01956..584771a3861e 100644 --- a/cmd/install/install.go +++ b/cmd/install/install.go @@ -22,7 +22,6 @@ import ( "github.com/spf13/cobra" - "github.com/k0sproject/k0s/internal/pkg/dir" "github.com/k0sproject/k0s/internal/pkg/file" "github.com/k0sproject/k0s/pkg/config" "github.com/k0sproject/k0s/pkg/install" @@ -50,10 +49,6 @@ func (c *CmdOpts) setup(role string, args []string) error { return fmt.Errorf("this command must be run as root") } - // if cfgFile is not provided k0s will handle this so no need to check if the file exists. - if c.CfgFile != "" && !dir.IsDirectory(c.CfgFile) && !file.Exists(c.CfgFile) { - return fmt.Errorf("file %s does not exist", c.CfgFile) - } if role == "controller" { cfg, err := config.GetNodeConfig(c.CfgFile, c.K0sVars) if err != nil { diff --git a/cmd/kubeconfig/create.go b/cmd/kubeconfig/create.go index 4548b6f32b0c..5aeabf0b953b 100644 --- a/cmd/kubeconfig/create.go +++ b/cmd/kubeconfig/create.go @@ -130,6 +130,7 @@ Note: A certificate once signed cannot be revoked for a particular user`, }, } cmd.Flags().StringVar(&groups, "groups", "", "Specify groups") + cmd.Flags().AddFlagSet(config.FileInputFlag()) cmd.PersistentFlags().AddFlagSet(config.GetPersistentFlagSet()) return cmd } @@ -137,7 +138,6 @@ Note: A certificate once signed cannot be revoked for a particular user`, func (c *CmdOpts) getAPIURL() (string, error) { // Disable logrus logrus.SetLevel(logrus.WarnLevel) - cfg, err := config.GetNodeConfig(c.CfgFile, c.K0sVars) if err != nil { return "", err diff --git a/cmd/reset/reset.go b/cmd/reset/reset.go index 7e288f54f73a..a51d6b51ceeb 100644 --- a/cmd/reset/reset.go +++ b/cmd/reset/reset.go @@ -46,6 +46,7 @@ func NewResetCmd() *cobra.Command { cmd.SilenceUsage = true cmd.PersistentFlags().AddFlagSet(config.GetPersistentFlagSet()) cmd.Flags().AddFlagSet(config.GetCriSocketFlag()) + cmd.Flags().AddFlagSet(config.FileInputFlag()) return cmd } diff --git a/cmd/restore/restore.go b/cmd/restore/restore.go index f5909d9548b7..5d7cd6e90871 100644 --- a/cmd/restore/restore.go +++ b/cmd/restore/restore.go @@ -56,6 +56,7 @@ func NewRestoreCmd() *cobra.Command { cmd.SilenceUsage = true cmd.Flags().StringVar(&restoredConfigPath, "config-out", "", "Specify desired name and full path for the restored k0s.yaml file (default: ${cwd}/k0s_.yaml)") + cmd.Flags().AddFlagSet(config.FileInputFlag()) cmd.PersistentFlags().AddFlagSet(config.GetPersistentFlagSet()) return cmd } diff --git a/cmd/token/create.go b/cmd/token/create.go index 41183eedd7a3..aad58cb7b7f1 100644 --- a/cmd/token/create.go +++ b/cmd/token/create.go @@ -76,7 +76,7 @@ k0s token create --role worker --expiry 10m //sets expiration time to 10 minute } // append flags cmd.PersistentFlags().AddFlagSet(config.GetPersistentFlagSet()) - + cmd.Flags().AddFlagSet(config.FileInputFlag()) cmd.Flags().StringVar(&tokenExpiry, "expiry", "0s", "Expiration time of the token. Format 1.5h, 2h45m or 300ms.") cmd.Flags().StringVar(&createTokenRole, "role", "worker", "Either worker or controller") cmd.Flags().BoolVar(&waitCreate, "wait", false, "wait forever (default false)") diff --git a/cmd/token/token.go b/cmd/token/token.go index f59d1b5893c4..798e6beb078c 100644 --- a/cmd/token/token.go +++ b/cmd/token/token.go @@ -43,6 +43,5 @@ func NewTokenCmd() *cobra.Command { cmd.AddCommand(tokenCreateCmd()) cmd.AddCommand(tokenListCmd()) cmd.AddCommand(tokenInvalidateCmd()) - cmd.PersistentFlags().AddFlagSet(config.GetPersistentFlagSet()) return cmd } diff --git a/cmd/validate/validate.go b/cmd/validate/validate.go index 4139865a4123..c36152a15f95 100644 --- a/cmd/validate/validate.go +++ b/cmd/validate/validate.go @@ -1,12 +1,9 @@ /* Copyright 2021 k0s authors - Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/pkg/apis/k0s.k0sproject.io/v1beta1/clusterconfig_types.go b/pkg/apis/k0s.k0sproject.io/v1beta1/clusterconfig_types.go index 5fc908a02a19..0f1096e8e71a 100644 --- a/pkg/apis/k0s.k0sproject.io/v1beta1/clusterconfig_types.go +++ b/pkg/apis/k0s.k0sproject.io/v1beta1/clusterconfig_types.go @@ -18,9 +18,7 @@ package v1beta1 import ( "bytes" "encoding/json" - "fmt" "io" - "os" "reflect" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -162,24 +160,6 @@ func (s *SchedulerSpec) IsZero() bool { return len(s.ExtraArgs) == 0 } -// ConfigFromFile takes a file path as Input, and parses it into a ClusterConfig -func ConfigFromFile(filename string, defaultStorage ...*StorageSpec) (*ClusterConfig, error) { - buf, err := os.ReadFile(filename) - if err != nil { - return nil, fmt.Errorf("failed to read config file at %s: %w", filename, err) - } - return ConfigFromString(string(buf), defaultStorage...) -} - -// ConfigFromStdin tries to read k0s.yaml config from stdin -func ConfigFromStdin(defaultStorage ...*StorageSpec) (*ClusterConfig, error) { - input, err := io.ReadAll(os.Stdin) - if err != nil { - return nil, fmt.Errorf("can't read configration from stdin: %v", err) - } - return ConfigFromString(string(input), defaultStorage...) -} - func ConfigFromString(yml string, defaultStorage ...*StorageSpec) (*ClusterConfig, error) { config := DefaultClusterConfig(defaultStorage...) err := strictyaml.YamlUnmarshalStrictIgnoringFields([]byte(yml), config, "interval") @@ -192,6 +172,15 @@ func ConfigFromString(yml string, defaultStorage ...*StorageSpec) (*ClusterConfi return config, nil } +// ConfigFromReader reads the configuration from any reader (can be stdin, file reader, etc) +func ConfigFromReader(r io.Reader, defaultStorage ...*StorageSpec) (*ClusterConfig, error) { + input, err := io.ReadAll(r) + if err != nil { + return nil, err + } + return ConfigFromString(string(input), defaultStorage...) +} + // DefaultClusterConfig sets the default ClusterConfig values, when none are given func DefaultClusterConfig(defaultStorage ...*StorageSpec) *ClusterConfig { return &ClusterConfig{ @@ -289,6 +278,62 @@ func (c *ClusterConfig) Validate() []error { return errors } +// GetBootstrappingConfig returns a ClusterConfig object stripped of Cluster-Wide Settings +func (c *ClusterConfig) GetBootstrappingConfig() *ClusterConfig { + return &ClusterConfig{ + ObjectMeta: c.ObjectMeta, + TypeMeta: c.TypeMeta, + Spec: &ClusterSpec{ + API: c.Spec.API, + Storage: &StorageSpec{ + Type: c.Spec.Storage.Type, + Etcd: &EtcdConfig{ + PeerAddress: c.Spec.Storage.Etcd.PeerAddress, + }, + Kine: c.Spec.Storage.Kine, + }, + Network: &Network{ + ServiceCIDR: c.Spec.Network.ServiceCIDR, + DualStack: c.Spec.Network.DualStack, + }, + Install: c.Spec.Install, + }, + Status: c.Status, + } +} + +// HACK: the current ClusterConfig struct holds both bootstrapping config & cluster-wide config +// this hack strips away the node-specific bootstrapping config so that we write a "clean" config to the CR +// This function accepts a standard ClusterConfig and returns the same config minus the node specific info: +// - APISpec +// - StorageSpec +// - Network.ServiceCIDR +// - Install +func (c *ClusterConfig) GetClusterWideConfig() *ClusterConfig { + return &ClusterConfig{ + ObjectMeta: c.ObjectMeta, + TypeMeta: c.TypeMeta, + Spec: &ClusterSpec{ + ControllerManager: c.Spec.ControllerManager, + Scheduler: c.Spec.Scheduler, + Network: &Network{ + Calico: c.Spec.Network.Calico, + KubeProxy: c.Spec.Network.KubeProxy, + KubeRouter: c.Spec.Network.KubeRouter, + PodCIDR: c.Spec.Network.PodCIDR, + Provider: c.Spec.Network.Provider, + }, + PodSecurityPolicy: c.Spec.PodSecurityPolicy, + WorkerProfiles: c.Spec.WorkerProfiles, + Telemetry: c.Spec.Telemetry, + Images: c.Spec.Images, + Extensions: c.Spec.Extensions, + Konnectivity: c.Spec.Konnectivity, + }, + Status: c.Status, + } +} + // CRValidator is used to make sure a config CR is created with correct values func (c *ClusterConfig) CRValidator() *ClusterConfig { copy := c.DeepCopy() diff --git a/pkg/component/controller/clusterConfig.go b/pkg/component/controller/clusterConfig.go index 9a6c811eca2f..338549bb2ccf 100644 --- a/pkg/component/controller/clusterConfig.go +++ b/pkg/component/controller/clusterConfig.go @@ -201,7 +201,7 @@ func (r *ClusterConfigReconciler) reportStatus(config *v1beta1.ClusterConfig, re func (r *ClusterConfigReconciler) copyRunningConfigToCR(baseCtx context.Context) (*v1beta1.ClusterConfig, error) { ctx, cancel := context.WithTimeout(baseCtx, 5*time.Second) defer cancel() - clusterWideConfig := config.ClusterConfigMinusNodeConfig(r.YamlConfig).StripDefaults().CRValidator() + clusterWideConfig := r.YamlConfig.GetClusterWideConfig().StripDefaults().CRValidator() clusterConfig, err := r.configClient.Create(ctx, clusterWideConfig, cOpts) if err != nil { return nil, err diff --git a/pkg/config/cli.go b/pkg/config/cli.go index eb2439840c3d..72d1ea35dec1 100644 --- a/pkg/config/cli.go +++ b/pkg/config/cli.go @@ -104,7 +104,6 @@ func DefaultLogLevels() map[string]string { func GetPersistentFlagSet() *pflag.FlagSet { flagset := &pflag.FlagSet{} - flagset.StringVarP(&CfgFile, "config", "c", "", "config file, use '-' to read the config from stdin") flagset.BoolVarP(&Debug, "debug", "d", false, "Debug logging (default: false)") flagset.BoolVarP(&Verbose, "verbose", "v", false, "Verbose logging (default: false)") flagset.StringVar(&DataDir, "data-dir", "", "Data Directory for k0s (default: /var/lib/k0s). DO NOT CHANGE for an existing setup, things will break!") @@ -179,6 +178,16 @@ func GetControllerFlags() *pflag.FlagSet { flagset.IntVar(&controllerOpts.K0sCloudProviderPort, "k0s-cloud-provider-port", cloudprovider.CloudControllerManagerPort, "the port that k0s-cloud-provider binds on") flagset.AddFlagSet(GetCriSocketFlag()) flagset.BoolVar(&controllerOpts.EnableDynamicConfig, "enable-dynamic-config", false, "enable cluster-wide dynamic config based on custom resource") + flagset.AddFlagSet(FileInputFlag()) + return flagset +} + +// The config flag used to be a persistent, joint flag to all commands +// now only a few commands use it. This function helps to share the flag with multiple commands without needing to define +// it in multiple places +func FileInputFlag() *pflag.FlagSet { + flagset := &pflag.FlagSet{} + flagset.StringVarP(&CfgFile, "config", "c", constant.K0sConfigPathDefault, "config file, use '-' to read the config from stdin") return flagset } diff --git a/pkg/config/config.go b/pkg/config/config.go index 378f312fe66f..b9d6315f38be 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -17,7 +17,10 @@ package config import ( "context" + "errors" "fmt" + "io" + "os" "strings" "time" @@ -26,6 +29,7 @@ import ( v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/clientcmd" + "github.com/k0sproject/k0s/internal/pkg/file" cfgClient "github.com/k0sproject/k0s/pkg/apis/k0s.k0sproject.io/clientset" "github.com/k0sproject/k0s/pkg/apis/k0s.k0sproject.io/v1beta1" "github.com/k0sproject/k0s/pkg/constant" @@ -35,8 +39,13 @@ import ( var ( resourceType = v1.TypeMeta{APIVersion: "k0s.k0sproject.io/v1beta1", Kind: "clusterconfigs"} getOpts = v1.GetOptions{TypeMeta: resourceType} + errNoConfig = errors.New("no configuration found") ) +func IsErrNoConfig(err error) bool { + return err == errNoConfig +} + func getConfigFromAPI(kubeConfig string) (*v1beta1.ClusterConfig, error) { timeout := time.After(120 * time.Second) ticker := time.NewTicker(3 * time.Second) @@ -117,34 +126,42 @@ func GetYamlFromFile(cfgPath string, k0sVars constant.CfgVars) (clusterConfig *v func ValidateYaml(cfgPath string, k0sVars constant.CfgVars) (clusterConfig *v1beta1.ClusterConfig, err error) { var storage *v1beta1.StorageSpec + var cfg *v1beta1.ClusterConfig + + CfgFile = cfgPath + if k0sVars.DefaultStorageType == "kine" { storage = &v1beta1.StorageSpec{ Type: v1beta1.KineStorageType, Kine: v1beta1.DefaultKineConfig(k0sVars.DataDir), } - } - switch cfgPath { - case "-": - clusterConfig, err = v1beta1.ConfigFromStdin(storage) - case "": - clusterConfig = v1beta1.DefaultClusterConfig(storage) - default: - clusterConfig, err = v1beta1.ConfigFromFile(cfgPath, storage) + if file.Exists(constant.K0sConfigPathDefault) { + logrus.Debugf("found config file in %s", constant.K0sConfigPathDefault) } + cfgReader, err := getConfigReader() if err != nil { - return nil, err + if IsErrNoConfig(err) { + cfg = v1beta1.DefaultClusterConfig(storage) + } else { + return nil, err + } + } else { + cfg, err = v1beta1.ConfigFromReader(cfgReader, storage) + if err != nil { + return nil, err + } } - if clusterConfig.Spec.Storage.Type == v1beta1.KineStorageType && clusterConfig.Spec.Storage.Kine == nil { + if cfg.Spec.Storage.Type == v1beta1.KineStorageType && cfg.Spec.Storage.Kine == nil { logrus.Warn("storage type is kine but no config given, setting up defaults") - clusterConfig.Spec.Storage.Kine = v1beta1.DefaultKineConfig(k0sVars.DataDir) + cfg.Spec.Storage.Kine = v1beta1.DefaultKineConfig(k0sVars.DataDir) } - if clusterConfig.Spec.Install == nil { - clusterConfig.Spec.Install = v1beta1.DefaultInstallSpec() + if cfg.Spec.Install == nil { + cfg.Spec.Install = v1beta1.DefaultInstallSpec() } - errors := clusterConfig.Validate() + errors := cfg.Validate() if len(errors) > 0 { messages := make([]string, len(errors)) for _, e := range errors { @@ -152,47 +169,7 @@ func ValidateYaml(cfgPath string, k0sVars constant.CfgVars) (clusterConfig *v1be } return nil, fmt.Errorf(strings.Join(messages, "\n")) } - return clusterConfig, nil -} - -// HACK: the current ClusterConfig struct holds both bootstrapping config & cluster-wide config -// this hack strips away the node-specific bootstrapping config so that we write a "clean" config to the CR -// This function accepts a standard ClusterConfig and returns the same config minus the node specific info: -// - APISpec -// - StorageSpec -// - Network.ServiceCIDR -// - Install -func ClusterConfigMinusNodeConfig(config *v1beta1.ClusterConfig) *v1beta1.ClusterConfig { - clusterSpec := &v1beta1.ClusterSpec{ - ControllerManager: config.Spec.ControllerManager, - Scheduler: config.Spec.Scheduler, - Network: &v1beta1.Network{ - Calico: config.Spec.Network.Calico, - DualStack: config.Spec.Network.DualStack, - KubeProxy: config.Spec.Network.KubeProxy, - KubeRouter: config.Spec.Network.KubeRouter, - PodCIDR: config.Spec.Network.PodCIDR, - Provider: config.Spec.Network.Provider, - }, - PodSecurityPolicy: config.Spec.PodSecurityPolicy, - WorkerProfiles: config.Spec.WorkerProfiles, - Telemetry: config.Spec.Telemetry, - Images: config.Spec.Images, - Extensions: config.Spec.Extensions, - Konnectivity: config.Spec.Konnectivity, - API: &v1beta1.APISpec{ - ExternalAddress: config.Spec.API.ExternalAddress, - Address: config.Spec.API.Address, - Port: config.Spec.API.Port, - }, - } - - return &v1beta1.ClusterConfig{ - ObjectMeta: config.ObjectMeta, - TypeMeta: config.TypeMeta, - Spec: clusterSpec, - Status: config.Status, - } + return cfg, nil } // GetNodeConfig takes a config-file parameter and returns a ClusterConfig stripped of Cluster-Wide Settings @@ -201,31 +178,31 @@ func GetNodeConfig(cfgPath string, k0sVars constant.CfgVars) (*v1beta1.ClusterCo if err != nil { return nil, err } + nodeConfig := cfg.GetBootstrappingConfig() var etcdConfig *v1beta1.EtcdConfig if cfg.Spec.Storage.Type == v1beta1.EtcdStorageType { etcdConfig = &v1beta1.EtcdConfig{ PeerAddress: cfg.Spec.Storage.Etcd.PeerAddress, } + nodeConfig.Spec.Storage.Etcd = etcdConfig } + return nodeConfig, nil +} - clusterSpec := &v1beta1.ClusterSpec{ - API: cfg.Spec.API, - Storage: &v1beta1.StorageSpec{ - Type: cfg.Spec.Storage.Type, - Etcd: etcdConfig, - Kine: cfg.Spec.Storage.Kine, - }, - Network: &v1beta1.Network{ - ServiceCIDR: cfg.Spec.Network.ServiceCIDR, - DualStack: cfg.Spec.Network.DualStack, - }, - Install: cfg.Spec.Install, - } - nodeConfig := &v1beta1.ClusterConfig{ - ObjectMeta: cfg.ObjectMeta, - TypeMeta: cfg.TypeMeta, - Spec: clusterSpec, - Status: cfg.Status, +func getConfigReader() (io.Reader, error) { + switch CfgFile { + case "-": + return os.Stdin, nil + case "", constant.K0sConfigPathDefault: + f, err := os.Open(constant.K0sConfigPathDefault) + if err == nil { + return f, nil + } + if os.IsNotExist(err) { + return nil, errNoConfig + } + return nil, err + default: + return os.Open(CfgFile) } - return nodeConfig, nil } diff --git a/pkg/constant/constant_posix.go b/pkg/constant/constant_posix.go index 82af82908e4f..03b209ec1adf 100644 --- a/pkg/constant/constant_posix.go +++ b/pkg/constant/constant_posix.go @@ -27,6 +27,7 @@ const ( KineSocket = "kine/kine.sock:2379" KubePauseContainerImage = "k8s.gcr.io/pause" KubePauseContainerImageVersion = "3.5" + K0sConfigPathDefault = "/etc/k0s/k0s.yaml" ) func formatPath(dir string, file string) string { diff --git a/pkg/constant/constant_windows.go b/pkg/constant/constant_windows.go index d946cf908540..b6ca027981c0 100644 --- a/pkg/constant/constant_windows.go +++ b/pkg/constant/constant_windows.go @@ -36,6 +36,7 @@ const ( KineSocket = "kine\\kine.sock:2379" KubePauseContainerImage = "mcr.microsoft.com/oss/kubernetes/pause" KubePauseContainerImageVersion = "1.4.1" + K0sConfigPathDefault = "C:\\etc\\k0s\\k0s.yaml" ) func formatPath(dir string, file string) string { From 901cddf4d2713792503f75717c7f22c4f8926530 Mon Sep 17 00:00:00 2001 From: Karen Almog Date: Fri, 24 Dec 2021 09:17:11 +0000 Subject: [PATCH 2/3] config refactoring: make tests green again Signed-off-by: Karen Almog --- inttest/Makefile | 1 + inttest/customports/customports_test.go | 2 +- .../k0s.k0sproject.io/v1beta1/clusterconfig_types.go | 12 +++--------- pkg/config/config.go | 2 +- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/inttest/Makefile b/inttest/Makefile index 150ad81655af..448d81300a3d 100644 --- a/inttest/Makefile +++ b/inttest/Makefile @@ -40,6 +40,7 @@ check-ctr: TIMEOUT=10m check-byocri: TIMEOUT=5m # readiness check for metric tests takes between around 5 and 6 minutes. check-metrics: TIMEOUT=6m +check-calico: TIMEOUT=6m # Establishing konnectivity tunnels with the LB in place takes a while, thus a bit longer timeout for the smoke check-customports: TIMEOUT=6m diff --git a/inttest/customports/customports_test.go b/inttest/customports/customports_test.go index 5b22b9b49f13..dd1a975b0e38 100644 --- a/inttest/customports/customports_test.go +++ b/inttest/customports/customports_test.go @@ -104,7 +104,7 @@ func (ds *Suite) TestControllerJoinsWithCustomPort() { workerToken, err := ds.GetJoinToken("worker", "", "--config=/tmp/k0s.yaml") ds.Require().NoError(err) - ds.Require().NoError(ds.RunWorkersWithToken("/var/lib/k0s", workerToken, `--config="/tmp/k0s.yaml"`)) + ds.Require().NoError(ds.RunWorkersWithToken("/var/lib/k0s", workerToken)) kc, err := ds.KubeClient("controller0", "") ds.Require().NoError(err) diff --git a/pkg/apis/k0s.k0sproject.io/v1beta1/clusterconfig_types.go b/pkg/apis/k0s.k0sproject.io/v1beta1/clusterconfig_types.go index 0f1096e8e71a..63e34c8641fa 100644 --- a/pkg/apis/k0s.k0sproject.io/v1beta1/clusterconfig_types.go +++ b/pkg/apis/k0s.k0sproject.io/v1beta1/clusterconfig_types.go @@ -279,19 +279,13 @@ func (c *ClusterConfig) Validate() []error { } // GetBootstrappingConfig returns a ClusterConfig object stripped of Cluster-Wide Settings -func (c *ClusterConfig) GetBootstrappingConfig() *ClusterConfig { +func (c *ClusterConfig) GetBootstrappingConfig(storageSpec *StorageSpec) *ClusterConfig { return &ClusterConfig{ ObjectMeta: c.ObjectMeta, TypeMeta: c.TypeMeta, Spec: &ClusterSpec{ - API: c.Spec.API, - Storage: &StorageSpec{ - Type: c.Spec.Storage.Type, - Etcd: &EtcdConfig{ - PeerAddress: c.Spec.Storage.Etcd.PeerAddress, - }, - Kine: c.Spec.Storage.Kine, - }, + API: c.Spec.API, + Storage: storageSpec, Network: &Network{ ServiceCIDR: c.Spec.Network.ServiceCIDR, DualStack: c.Spec.Network.DualStack, diff --git a/pkg/config/config.go b/pkg/config/config.go index b9d6315f38be..2147146cbbc0 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -178,7 +178,7 @@ func GetNodeConfig(cfgPath string, k0sVars constant.CfgVars) (*v1beta1.ClusterCo if err != nil { return nil, err } - nodeConfig := cfg.GetBootstrappingConfig() + nodeConfig := cfg.GetBootstrappingConfig(cfg.Spec.Storage) var etcdConfig *v1beta1.EtcdConfig if cfg.Spec.Storage.Type == v1beta1.EtcdStorageType { etcdConfig = &v1beta1.EtcdConfig{ From 5e2aa110737fbb90d976385032f818413155ef51 Mon Sep 17 00:00:00 2001 From: Karen Almog Date: Mon, 27 Dec 2021 23:29:25 +0000 Subject: [PATCH 3/3] config refactoring: defer close config file Signed-off-by: Karen Almog --- cmd/backup/backup.go | 2 +- cmd/install/install.go | 2 +- cmd/reset/reset.go | 2 +- cmd/restore/restore.go | 2 +- pkg/config/config.go | 78 +++++++++++++++++++++--------------------- 5 files changed, 43 insertions(+), 43 deletions(-) diff --git a/cmd/backup/backup.go b/cmd/backup/backup.go index dedeed2170dc..b68b38d8a9fb 100644 --- a/cmd/backup/backup.go +++ b/cmd/backup/backup.go @@ -90,7 +90,7 @@ func (c *CmdOpts) backup() error { func preRunValidateConfig(cmd *cobra.Command, args []string) error { c := CmdOpts(config.GetCmdOpts()) - _, err := config.ValidateYaml(c.CfgFile, c.K0sVars) + _, err := config.GetConfigFromYAML(c.CfgFile, c.K0sVars) if err != nil { return err } diff --git a/cmd/install/install.go b/cmd/install/install.go index 584771a3861e..7ad8821a5586 100644 --- a/cmd/install/install.go +++ b/cmd/install/install.go @@ -97,7 +97,7 @@ func (c *CmdOpts) convertFileParamsToAbsolute() (err error) { func preRunValidateConfig(_ *cobra.Command, _ []string) error { c := CmdOpts(config.GetCmdOpts()) - _, err := config.ValidateYaml(c.CfgFile, c.K0sVars) + _, err := config.GetConfigFromYAML(c.CfgFile, c.K0sVars) if err != nil { return err } diff --git a/cmd/reset/reset.go b/cmd/reset/reset.go index a51d6b51ceeb..4096d4658ea3 100644 --- a/cmd/reset/reset.go +++ b/cmd/reset/reset.go @@ -75,7 +75,7 @@ func (c *CmdOpts) reset() error { func preRunValidateConfig(_ *cobra.Command, _ []string) error { c := CmdOpts(config.GetCmdOpts()) - _, err := config.ValidateYaml(c.CfgFile, c.K0sVars) + _, err := config.GetConfigFromYAML(c.CfgFile, c.K0sVars) if err != nil { return err } diff --git a/cmd/restore/restore.go b/cmd/restore/restore.go index 5d7cd6e90871..7f8be26da584 100644 --- a/cmd/restore/restore.go +++ b/cmd/restore/restore.go @@ -96,7 +96,7 @@ func (c *CmdOpts) restore(path string) error { // TODO Need to move to some common place, now it's defined in restore and backup commands func preRunValidateConfig(_ *cobra.Command, _ []string) error { c := CmdOpts(config.GetCmdOpts()) - _, err := config.ValidateYaml(c.CfgFile, c.K0sVars) + _, err := config.GetConfigFromYAML(c.CfgFile, c.K0sVars) if err != nil { return err } diff --git a/pkg/config/config.go b/pkg/config/config.go index 2147146cbbc0..6e1779aef42e 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -17,9 +17,7 @@ package config import ( "context" - "errors" "fmt" - "io" "os" "strings" "time" @@ -29,7 +27,6 @@ import ( v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/clientcmd" - "github.com/k0sproject/k0s/internal/pkg/file" cfgClient "github.com/k0sproject/k0s/pkg/apis/k0s.k0sproject.io/clientset" "github.com/k0sproject/k0s/pkg/apis/k0s.k0sproject.io/v1beta1" "github.com/k0sproject/k0s/pkg/constant" @@ -39,13 +36,8 @@ import ( var ( resourceType = v1.TypeMeta{APIVersion: "k0s.k0sproject.io/v1beta1", Kind: "clusterconfigs"} getOpts = v1.GetOptions{TypeMeta: resourceType} - errNoConfig = errors.New("no configuration found") ) -func IsErrNoConfig(err error) bool { - return err == errNoConfig -} - func getConfigFromAPI(kubeConfig string) (*v1beta1.ClusterConfig, error) { timeout := time.After(120 * time.Second) ticker := time.NewTicker(3 * time.Second) @@ -74,7 +66,7 @@ func GetFullConfig(cfgPath string, k0sVars constant.CfgVars) (clusterConfig *v1b // no config file exists, using defaults logrus.Warn("no config file given, using defaults") } - cfg, err := ValidateYaml(cfgPath, k0sVars) + cfg, err := GetConfigFromYAML(cfgPath, k0sVars) if err != nil { return nil, err } @@ -117,40 +109,66 @@ func GetYamlFromFile(cfgPath string, k0sVars constant.CfgVars) (clusterConfig *v // no config file exists, using defaults logrus.Warn("no config file given, using defaults") } - cfg, err := ValidateYaml(cfgPath, k0sVars) + cfg, err := GetConfigFromYAML(cfgPath, k0sVars) if err != nil { return nil, err } return cfg, nil } -func ValidateYaml(cfgPath string, k0sVars constant.CfgVars) (clusterConfig *v1beta1.ClusterConfig, err error) { +// GetConfigFromYAML will attempt to read a config yaml, validate it and return a clusterConfig object +func GetConfigFromYAML(cfgPath string, k0sVars constant.CfgVars) (clusterConfig *v1beta1.ClusterConfig, err error) { var storage *v1beta1.StorageSpec var cfg *v1beta1.ClusterConfig CfgFile = cfgPath + // first, let's set the default storage type if k0sVars.DefaultStorageType == "kine" { storage = &v1beta1.StorageSpec{ Type: v1beta1.KineStorageType, Kine: v1beta1.DefaultKineConfig(k0sVars.DataDir), } } - if file.Exists(constant.K0sConfigPathDefault) { - logrus.Debugf("found config file in %s", constant.K0sConfigPathDefault) - } - cfgReader, err := getConfigReader() - if err != nil { - if IsErrNoConfig(err) { - cfg = v1beta1.DefaultClusterConfig(storage) - } else { + + switch CfgFile { + // read config file flag + default: + f, err := os.Open(CfgFile) + if err != nil { return nil, err } - } else { - cfg, err = v1beta1.ConfigFromReader(cfgReader, storage) + defer f.Close() + + cfg, err = v1beta1.ConfigFromReader(f, storage) if err != nil { return nil, err } + + // stdin input + case "-": + cfg, err = v1beta1.ConfigFromReader(os.Stdin, storage) + + // config file not provided: try to read config from default location. + // if not exists, generate default config + case constant.K0sConfigPathDefault: + f, err := os.Open(constant.K0sConfigPathDefault) + if err != nil { + if os.IsNotExist(err) { + logrus.Debugf("could not find config in %s, using defaults", constant.K0sConfigPathDefault) + cfg = v1beta1.DefaultClusterConfig(storage) + } else { + return nil, err + } + } + if err == nil { + logrus.Debugf("found config file in %s", constant.K0sConfigPathDefault) + cfg, err = v1beta1.ConfigFromReader(f, storage) + if err != nil { + return nil, err + } + defer f.Close() + } } if cfg.Spec.Storage.Type == v1beta1.KineStorageType && cfg.Spec.Storage.Kine == nil { @@ -188,21 +206,3 @@ func GetNodeConfig(cfgPath string, k0sVars constant.CfgVars) (*v1beta1.ClusterCo } return nodeConfig, nil } - -func getConfigReader() (io.Reader, error) { - switch CfgFile { - case "-": - return os.Stdin, nil - case "", constant.K0sConfigPathDefault: - f, err := os.Open(constant.K0sConfigPathDefault) - if err == nil { - return f, nil - } - if os.IsNotExist(err) { - return nil, errNoConfig - } - return nil, err - default: - return os.Open(CfgFile) - } -}