From a5437561ebfc1aa1c39221572fa9434f24ce17fa Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Sat, 16 Jul 2022 20:47:55 +0000 Subject: [PATCH] art: Refactor out config sections Now there is a `install` section in the config that has the fields that previously where in `c3os` but were actually only used during install phase. Also the k3s and c3os config were moved to the provider instead that in the global config. --- pkg/config/config.go | 97 ++++++++++++++++++++++++++------------- pkg/config/config_test.go | 24 ++++++++-- 2 files changed, 83 insertions(+), 38 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 88db684..167e0d0 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "strings" + "unicode" retry "github.com/avast/retry-go" "github.com/c3os-io/c3os/internal/machine" @@ -16,34 +17,19 @@ import ( "gopkg.in/yaml.v2" ) -type C3OS struct { - NetworkToken string `yaml:"network_token,omitempty"` - Offline bool `yaml:"offline,omitempty"` - Reboot bool `yaml:"reboot,omitempty"` - Device string `yaml:"device,omitempty"` - Poweroff bool `yaml:"poweroff,omitempty"` - Role string `yaml:"role,omitempty"` - NetworkID string `yaml:"network_id,omitempty"` - DNS bool `yaml:"dns,omitempty"` - LogLevel string `yaml:"loglevel,omitempty"` -} - -type K3s struct { - Env map[string]string `yaml:"env,omitempty"` - ReplaceEnv bool `yaml:"replace_env,omitempty"` - ReplaceArgs bool `yaml:"replace_args,omitempty"` - Args []string `yaml:"args,omitempty"` - Enabled bool `yaml:"enabled,omitempty"` +type Install struct { + Auto bool `yaml:"auto,omitempty"` + Reboot bool `yaml:"reboot,omitempty"` + Device string `yaml:"device,omitempty"` + Poweroff bool `yaml:"poweroff,omitempty"` } type Config struct { - C3OS *C3OS `yaml:"c3os,omitempty"` - K3sAgent K3s `yaml:"k3s-agent,omitempty"` - K3s K3s `yaml:"k3s,omitempty"` - VPN map[string]string `yaml:"vpn,omitempty"` + Install *Install `yaml:"install,omitempty"` //cloudFileContent string originalData map[string]interface{} location string + header string ConfigURL string `yaml:"config_url,omitempty"` Options map[string]string `yaml:"options,omitempty"` IgnoreBundleErrors bool `yaml:"ignore_bundles_errors,omitempty"` @@ -60,6 +46,18 @@ type Bundle struct { Targets []string `yaml:"targets,omitempty"` } +func HasHeader(userdata, head string) (bool, string) { + header := strings.SplitN(userdata, "\n", 2)[0] + + // Trim trailing whitespaces + header = strings.TrimRightFunc(header, unicode.IsSpace) + + if head != "" { + return head == header, header + } + return (header == "#cloud-config") || (header == "#c3os-config") || (header == "#node-config"), header +} + func (b Bundles) Options() (res [][]machine.BundleOption) { for _, bundle := range b { for _, t := range bundle.Targets { @@ -76,6 +74,10 @@ func (b Bundles) Options() (res [][]machine.BundleOption) { return } +func (c Config) Unmarshal(o interface{}) error { + return yaml.Unmarshal([]byte(c.String()), o) +} + func (c Config) Data() map[string]interface{} { return c.originalData } @@ -89,16 +91,16 @@ func (c Config) String() string { } dat, _ := yaml.Marshal(c.originalData) + if c.header != "" { + return AddHeader(c.header, string(dat)) + } return string(dat) } func (c Config) IsValid() bool { - return c.C3OS != nil || - c.K3s.Enabled || - c.K3sAgent.Enabled || + return c.Install != nil || c.ConfigURL != "" || - len(c.Bundles) != 0 || - len(c.VPN) != 0 + len(c.Bundles) != 0 } func Scan(opts ...Option) (c *Config, err error) { @@ -129,11 +131,13 @@ func Scan(opts ...Option) (c *Config, err error) { b, err := ioutil.ReadFile(f) if err == nil { yaml.Unmarshal(b, c) - if c.IsValid() { - // c.cloudFileContent = string(b) + if exists, header := HasHeader(string(b), ""); c.IsValid() || exists { c.location = f yaml.Unmarshal(b, &c.originalData) configFound = true + if exists { + c.header = header + } break } @@ -240,11 +244,13 @@ func listFiles(dir string) ([]string, error) { func ReplaceToken(dir []string, token string) (err error) { c, err := Scan(Directories(dir...)) if err != nil { - return err + return fmt.Errorf("no config file found: %w", err) } - if c.C3OS == nil { - return errors.New("no config file found") + header := "#node-config" + + if hasHeader, head := HasHeader(c.String(), ""); hasHeader { + header = head } content := map[interface{}]interface{}{} @@ -282,7 +288,7 @@ func ReplaceToken(dir []string, token string) (err error) { return err } - return ioutil.WriteFile(c.location, d, fi.Mode().Perm()) + return ioutil.WriteFile(c.location, []byte(AddHeader(header, string(d))), fi.Mode().Perm()) } type Stage string @@ -306,3 +312,28 @@ func SaveCloudConfig(name Stage, yc yip.YipConfig) error { func FromString(s string, o interface{}) error { return yaml.Unmarshal([]byte(s), o) } + +func MergeYAML(objs ...interface{}) ([]byte, error) { + content := [][]byte{} + for _, o := range objs { + dat, err := yaml.Marshal(o) + if err != nil { + return []byte{}, err + } + content = append(content, dat) + } + + finalData := make(map[string]interface{}) + + for _, c := range content { + if err := yaml.Unmarshal(c, &finalData); err != nil { + return []byte{}, err + } + } + + return yaml.Marshal(finalData) +} + +func AddHeader(header, data string) string { + return fmt.Sprintf("%s\n%s", header, data) +} diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index bb92445..d9494ce 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -20,6 +20,8 @@ import ( "os" "path/filepath" + providerConfig "github.com/c3os-io/c3os/internal/provider/config" + "github.com/c3os-io/c3os/pkg/config" . "github.com/c3os-io/c3os/pkg/config" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -30,7 +32,8 @@ var _ = Describe("Get config", func() { Context("directory", func() { It("reads config file greedly", func() { - var cc string = `baz: bar + var cc string = `#c3os-config +baz: bar c3os: network_token: foo ` @@ -47,13 +50,17 @@ fooz: c, err := Scan(Directories(d)) Expect(err).ToNot(HaveOccurred()) Expect(c).ToNot(BeNil()) - Expect(c.C3OS.NetworkToken).To(Equal("foo")) + providerCfg := &providerConfig.Config{} + err = c.Unmarshal(providerCfg) + Expect(err).ToNot(HaveOccurred()) + Expect(providerCfg.C3OS).ToNot(BeNil()) + Expect(providerCfg.C3OS.NetworkToken).To(Equal("foo")) Expect(c.String()).To(Equal(cc)) }) It("replace token in config files", func() { - var cc string = ` + var cc string = `#node-config c3os: network_token: "foo" @@ -80,6 +87,8 @@ fooz: err = yaml.Unmarshal(content, &res) Expect(err).ToNot(HaveOccurred()) + hasHeader, _ := config.HasHeader(string(content), "#node-config") + Expect(hasHeader).To(BeTrue()) Expect(res).To(Equal(map[interface{}]interface{}{ "c3os": map[interface{}]interface{}{"network_token": "baz"}, @@ -89,7 +98,7 @@ fooz: It("merges with bootargs", func() { - var cc string = ` + var cc string = `#c3os-config c3os: network_token: "foo" @@ -107,7 +116,12 @@ bb: c, err := Scan(Directories(d), MergeBootLine, WithBootCMDLineFile(filepath.Join(d, "b"))) Expect(err).ToNot(HaveOccurred()) Expect(c.Options["foo"]).To(Equal("bar")) - Expect(c.C3OS.NetworkToken).To(Equal("foo")) + + providerCfg := &providerConfig.Config{} + err = c.Unmarshal(providerCfg) + Expect(err).ToNot(HaveOccurred()) + Expect(providerCfg.C3OS).ToNot(BeNil()) + Expect(providerCfg.C3OS.NetworkToken).To(Equal("foo")) _, exists := c.Data()["zz"] Expect(exists).To(BeFalse()) })