Skip to content

Commit

Permalink
config refactoring: fix singlenode config option
Browse files Browse the repository at this point in the history
Signed-off-by: Karen Almog <[email protected]>
  • Loading branch information
Karen Almog committed Jan 13, 2022
1 parent 5b20613 commit 1eac183
Show file tree
Hide file tree
Showing 11 changed files with 176 additions and 69 deletions.
6 changes: 1 addition & 5 deletions cmd/controller/certificates.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,7 @@ func (c *Certificates) Init(ctx context.Context) error {
if err != nil {
return err
}
if err := kubeConfig(c.K0sVars.KonnectivityKubeConfigPath, kubeConfigAPIUrl, c.CACert, konnectivityCert.Cert, konnectivityCert.Key, constant.KonnectivityServerUser); err != nil {
return err
}

return nil
return kubeConfig(c.K0sVars.KonnectivityKubeConfigPath, kubeConfigAPIUrl, c.CACert, konnectivityCert.Cert, konnectivityCert.Key, constant.KonnectivityServerUser)
})

eg.Go(func() error {
Expand Down
2 changes: 1 addition & 1 deletion cmd/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func (c *CmdOpts) startController(ctx context.Context) error {

// initialize runtime config
loadingRules := config.ClientConfigLoadingRules{Nodeconfig: true}
if err := loadingRules.InitRuntimeConfig(); err != nil {
if err := loadingRules.InitRuntimeConfig(c.K0sVars); err != nil {
logrus.Fatalf("failed to initialize k0s runtime config: %s", err.Error())
}

Expand Down
27 changes: 9 additions & 18 deletions cmd/restore/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,17 @@ func NewRestoreCmd() *cobra.Command {
}
return c.restore(args[0])
},
PreRunE: preRunValidateConfig,
}

cmd.SilenceUsage = true
cmd.Flags().StringVar(&restoredConfigPath, "config-out", "", "Specify desired name and full path for the restored k0s.yaml file (default: ${cwd}/k0s_<archive timestamp>.yaml)")
cmd.Flags().AddFlagSet(config.FileInputFlag())

cwd, err := os.Getwd()
if err != nil {
return nil
}

restoredConfigPathDescription := fmt.Sprintf("Specify desired name and full path for the restored k0s.yaml file (default: %s/k0s_<archive timestamp>.yaml", cwd)
cmd.Flags().StringVar(&restoredConfigPath, "config-out", "", restoredConfigPathDescription)
cmd.PersistentFlags().AddFlagSet(config.GetPersistentFlagSet())
return cmd
}
Expand Down Expand Up @@ -86,32 +91,18 @@ func (c *CmdOpts) restore(path string) error {
if err != nil {
return err
}
// c.CfgFile, c.ClusterConfig.Spec, c.K0sVars

if restoredConfigPath == "" {
restoredConfigPath = defaultConfigFileOutputPath(path)
}
return mgr.RunRestore(path, c.K0sVars, restoredConfigPath)
}

// 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())

loadingRules := config.ClientConfigLoadingRules{K0sVars: c.K0sVars}
_, err := loadingRules.ParseRuntimeConfig()
if err != nil {
return fmt.Errorf("failed to get config: %v", err)
}
return nil
}

// set output config file name and path according to input archive Timestamps
// the default location for the restore operation is the currently running cwd
// this can be override, by using the --config-out flag
func defaultConfigFileOutputPath(archivePath string) string {
if archivePath == "-" {
return "-"
return constant.K0sConfigPathDefault
}
f := filepath.Base(archivePath)
nameWithoutExt := strings.Split(f, ".")[0]
Expand Down
1 change: 1 addition & 0 deletions inttest/common/footloosesuite.go
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,7 @@ func (s *FootlooseSuite) GetKubeClientConfig(node string, k0sKubeconfigArgs ...s
kubeConfigCmd := fmt.Sprintf("%s kubeconfig admin %s 2>/dev/null", s.K0sFullPath, strings.Join(k0sKubeconfigArgs, " "))
kubeConf, err := ssh.ExecWithOutput(kubeConfigCmd)
if err != nil {
fmt.Println(string(kubeConf))
return nil, err
}
cfg, err := clientcmd.Load([]byte(kubeConf))
Expand Down
6 changes: 1 addition & 5 deletions pkg/apis/k0s.k0sproject.io/v1beta1/calico.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,5 @@ func (c *Calico) UnmarshalJSON(data []byte) error {

type calico Calico
jc := (*calico)(c)
if err := json.Unmarshal(data, jc); err != nil {
return err
}

return nil
return json.Unmarshal(data, jc)
}
13 changes: 7 additions & 6 deletions pkg/apis/k0s.k0sproject.io/v1beta1/clusterconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,14 @@ func ConfigFromReader(r io.Reader, defaultStorage ...*StorageSpec) (*ClusterConf

// DefaultClusterConfig sets the default ClusterConfig values, when none are given
func DefaultClusterConfig(defaultStorage ...*StorageSpec) *ClusterConfig {
clusterSpec := DefaultClusterSpec(defaultStorage...)
return &ClusterConfig{
ObjectMeta: metav1.ObjectMeta{Name: "k0s"},
TypeMeta: metav1.TypeMeta{
APIVersion: "k0s.k0sproject.io/v1beta1",
Kind: "ClusterConfig",
},
Spec: DefaultClusterSpec(defaultStorage...),
Spec: clusterSpec,
}
}

Expand Down Expand Up @@ -279,12 +280,12 @@ 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 {
var etcdConfig *EtcdConfig
if c.Spec.Storage.Type == EtcdStorageType {
if storageSpec.Type == EtcdStorageType {
etcdConfig = &EtcdConfig{
ExternalCluster: c.Spec.Storage.Etcd.ExternalCluster,
PeerAddress: c.Spec.Storage.Etcd.PeerAddress,
ExternalCluster: storageSpec.Etcd.ExternalCluster,
PeerAddress: storageSpec.Etcd.PeerAddress,
}
c.Spec.Storage.Etcd = etcdConfig
}
Expand All @@ -293,7 +294,7 @@ func (c *ClusterConfig) GetBootstrappingConfig() *ClusterConfig {
TypeMeta: c.TypeMeta,
Spec: &ClusterSpec{
API: c.Spec.API,
Storage: c.Spec.Storage,
Storage: storageSpec,
Network: &Network{
ServiceCIDR: c.Spec.Network.ServiceCIDR,
DualStack: c.Spec.Network.DualStack,
Expand Down
5 changes: 5 additions & 0 deletions pkg/backup/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ limitations under the License.
package backup

import (
"fmt"
"io"
"os"
"path"
Expand Down Expand Up @@ -61,8 +62,12 @@ func (c configurationStep) Restore(restoreFrom, restoreTo string) error {
if err != nil {
return err
}
if f == nil {
return fmt.Errorf("couldn't get a file handle for %s", c.restoredConfigPath)
}
defer f.Close()
_, err = io.Copy(f, os.Stdout)
logrus.Errorf("got error: %v", err)
return err
}
logrus.Infof("restoring from `%s` to `%s`", objectPathInArchive, c.restoredConfigPath)
Expand Down
4 changes: 2 additions & 2 deletions pkg/config/api_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (rules *ClientConfigLoadingRules) fetchNodeConfig() (*v1beta1.ClusterConfig
logrus.Errorf("failed to read config from file: %v", err)
return nil, err
}
return cfg.GetBootstrappingConfig(), nil
return cfg.GetBootstrappingConfig(cfg.Spec.Storage), nil
}

// when API config is enabled, but only node config is needed (for bootstrapping commands)
Expand All @@ -77,7 +77,7 @@ func (rules *ClientConfigLoadingRules) mergeNodeAndClusterconfig(nodeConfig *v1b
return nil, err
}

err = mergo.Merge(clusterConfig, nodeConfig.GetBootstrappingConfig(), mergo.WithOverride)
err = mergo.Merge(clusterConfig, nodeConfig.GetBootstrappingConfig(nodeConfig.Spec.Storage), mergo.WithOverride)
if err != nil {
return nil, err
}
Expand Down
5 changes: 1 addition & 4 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,7 @@ func (rules *ClientConfigLoadingRules) IsAPIConfig() bool {
func (rules *ClientConfigLoadingRules) IsDefaultConfig() bool {
// if no custom-value is provided as a config file, and no config-file exists in the default location
// we assume we need to generate configuration defaults
if CfgFile == constant.K0sConfigPathDefault && !file.Exists(constant.K0sConfigPathDefault) {
return true
}
return false
return CfgFile == constant.K0sConfigPathDefault && !file.Exists(constant.K0sConfigPathDefault)
}

func (rules *ClientConfigLoadingRules) Load() (*v1beta1.ClusterConfig, error) {
Expand Down
132 changes: 129 additions & 3 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"context"
"fmt"
"os"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -64,7 +65,7 @@ func TestGetConfigFromFile(t *testing.T) {
defer os.Remove(configPathRuntimeTest)

loadingRules := ClientConfigLoadingRules{RuntimeConfigPath: configPathRuntimeTest}
err := loadingRules.InitRuntimeConfig()
err := loadingRules.InitRuntimeConfig(constant.GetConfig(""))
if err != nil {
t.Fatalf("failed to initialize k0s config: %s", err.Error())
}
Expand Down Expand Up @@ -95,6 +96,52 @@ func TestGetConfigFromFile(t *testing.T) {
}
}

func TestExternalEtcdConfig(t *testing.T) {
yamlData := `
spec:
storage:
type: etcd
etcd:
externalCluster:
endpoints:
- http://etcd0:2379
etcdPrefix: k0s-tenant`
cfgFilePath := writeConfigFile(yamlData)
CfgFile = cfgFilePath
defer os.Remove(configPathRuntimeTest)

loadingRules := ClientConfigLoadingRules{RuntimeConfigPath: configPathRuntimeTest}
err := loadingRules.InitRuntimeConfig(constant.GetConfig(""))
if err != nil {
t.Fatalf("failed to initialize k0s config: %s", err.Error())
}

cfg, err := loadingRules.Load()
if err != nil {
t.Fatalf("failed to load config: %s", err.Error())
}
if cfg == nil {
t.Fatal("received an empty config! failing")
}
testCases := []struct {
name string
got string
expected string
}{
{"Storage_Type", cfg.Spec.Storage.Type, "etcd"},
{"External_Cluster_Endpoint", cfg.Spec.Storage.Etcd.ExternalCluster.Endpoints[0], "http://etcd0:2379"},
{"External_Cluster_Prefix", cfg.Spec.Storage.Etcd.ExternalCluster.EtcdPrefix, "k0s-tenant"},
}

for _, tc := range testCases {
t.Run(fmt.Sprintf("%s eq %s", tc.name, tc.expected), func(t *testing.T) {
if tc.got != tc.expected {
t.Fatalf("expected to read '%s' for the %s test value. Got: %s", tc.expected, tc.name, tc.got)
}
})
}
}

// Test using config from a yaml file
func TestConfigFromDefaults(t *testing.T) {
CfgFile = constant.K0sConfigPathDefault // this path doesn't exist, so default values should be generated
Expand Down Expand Up @@ -138,7 +185,7 @@ func TestNodeConfigWithAPIConfig(t *testing.T) {

loadingRules := ClientConfigLoadingRules{Nodeconfig: true, RuntimeConfigPath: configPathRuntimeTest}

err := loadingRules.InitRuntimeConfig()
err := loadingRules.InitRuntimeConfig(constant.GetConfig(""))
if err != nil {
t.Fatalf("failed to initialize k0s config: %s", err.Error())
}
Expand Down Expand Up @@ -167,6 +214,85 @@ func TestNodeConfigWithAPIConfig(t *testing.T) {
}
}

func TestSingleNodeConfig(t *testing.T) {
CfgFile = constant.K0sConfigPathDefault // this path doesn't exist, so default values should be generated
defer os.Remove(configPathRuntimeTest)

loadingRules := ClientConfigLoadingRules{RuntimeConfigPath: configPathRuntimeTest, Nodeconfig: true}
k0sVars := constant.GetConfig("")
k0sVars.DefaultStorageType = "kine"

err := loadingRules.InitRuntimeConfig(k0sVars)
if err != nil {
t.Fatalf("failed to initialize k0s config: %s", err.Error())
}

cfg, err := loadingRules.Load()
if err != nil {
t.Fatalf("failed to load config: %s", err.Error())
}
if cfg == nil {
t.Fatal("received an empty config! failing")
}
testCases := []struct {
name string
got string
expected string
}{
{"Storage_Type", cfg.Spec.Storage.Type, "kine"},
{"Kine_DataSource", cfg.Spec.Storage.Kine.DataSource, "sqlite:///var/lib/k0s/db/state.db"},
}

for _, tc := range testCases {
t.Run(fmt.Sprintf("%s eq %s", tc.name, tc.expected), func(t *testing.T) {
if !strings.Contains(tc.got, tc.expected) {
t.Fatalf("expected to read '%s' for the %s test value. Got: %s", tc.expected, tc.name, tc.got)
}
})
}
}

func TestSingleNodeConfigWithEtcd(t *testing.T) {
yamlData := `
spec:
storage:
type: etcd`

cfgFilePath := writeConfigFile(yamlData)
CfgFile = cfgFilePath
defer os.Remove(configPathRuntimeTest)

loadingRules := ClientConfigLoadingRules{RuntimeConfigPath: configPathRuntimeTest, Nodeconfig: true}
k0sVars := constant.GetConfig("")
k0sVars.DefaultStorageType = "kine"

err := loadingRules.InitRuntimeConfig(k0sVars)
if err != nil {
t.Fatalf("failed to initialize k0s config: %s", err.Error())
}

cfg, err := loadingRules.Load()
if err != nil {
t.Fatalf("failed to load config: %s", err.Error())
}
if cfg == nil {
t.Fatal("received an empty config! failing")
}
testCases := []struct {
name string
got string
expected string
}{{"Storage_Type", cfg.Spec.Storage.Type, "etcd"}} // config file storage type trumps k0sVars.DefaultStorageType

for _, tc := range testCases {
t.Run(fmt.Sprintf("%s eq %s", tc.name, tc.expected), func(t *testing.T) {
if tc.got != tc.expected {
t.Fatalf("expected to read '%s' for the %s test value. Got: %s", tc.expected, tc.name, tc.got)
}
})
}
}

// when a component requests an API config,
// the merged node and cluster config should be returned
func TestAPIConfig(t *testing.T) {
Expand All @@ -183,7 +309,7 @@ func TestAPIConfig(t *testing.T) {
defer os.Remove(configPathRuntimeTest)

loadingRules := ClientConfigLoadingRules{RuntimeConfigPath: configPathRuntimeTest, APIClient: client.K0sV1beta1()}
err = loadingRules.InitRuntimeConfig()
err = loadingRules.InitRuntimeConfig(constant.GetConfig(""))
if err != nil {
t.Fatalf("failed to initialize k0s config: %s", err.Error())
}
Expand Down
Loading

0 comments on commit 1eac183

Please sign in to comment.