seedling: Return configuration data from the agent (#567)

* 🌱 Return configuration data from the agent

Signed-off-by: Ettore Di Giacinto <mudler@users.noreply.github.com>

* 🤖 Cover query by tests

Signed-off-by: Ettore Di Giacinto <mudler@users.noreply.github.com>

Signed-off-by: Ettore Di Giacinto <mudler@users.noreply.github.com>
This commit is contained in:
Ettore Di Giacinto
2022-12-19 17:01:57 +01:00
committed by Itxaka
parent 2bf4793c41
commit a909c44620
3 changed files with 57 additions and 9 deletions

View File

@@ -10,12 +10,13 @@ import (
"unicode" "unicode"
retry "github.com/avast/retry-go" retry "github.com/avast/retry-go"
"github.com/itchyny/gojq"
"github.com/kairos-io/kairos/pkg/machine" "github.com/kairos-io/kairos/pkg/machine"
"github.com/kairos-io/kairos/sdk/bundles" "github.com/kairos-io/kairos/sdk/bundles"
"github.com/kairos-io/kairos/sdk/unstructured" "github.com/kairos-io/kairos/sdk/unstructured"
yip "github.com/mudler/yip/pkg/schema" yip "github.com/mudler/yip/pkg/schema"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v3"
) )
type Install struct { type Install struct {
@@ -106,6 +107,38 @@ func (c Config) String() string {
return string(dat) return string(dat)
} }
func (c Config) Query(s string) (res string, err error) {
s = fmt.Sprintf(".%s", s)
jsondata := map[string]interface{}{}
err = yaml.Unmarshal([]byte(c.String()), &jsondata)
if err != nil {
return
}
query, err := gojq.Parse(s)
if err != nil {
return res, err
}
iter := query.Run(jsondata) // or query.RunWithContext
for {
v, ok := iter.Next()
if !ok {
break
}
if err, ok := v.(error); ok {
return res, fmt.Errorf("failed parsing, error: %w", err)
}
dat, err := yaml.Marshal(v)
if err != nil {
break
}
res += string(dat)
}
return
}
func allFiles(dir []string) []string { func allFiles(dir []string) []string {
files := []string{} files := []string{}
for _, d := range dir { for _, d := range dir {
@@ -122,7 +155,7 @@ func Scan(opts ...Option) (c *Config, err error) {
return nil, err return nil, err
} }
c = parseConfig(o.ScanDir) c = parseConfig(o.ScanDir, o.NoLogs)
if o.MergeBootCMDLine { if o.MergeBootCMDLine {
d, err := machine.DotToYAML(o.BootCMDLineFile) d, err := machine.DotToYAML(o.BootCMDLineFile)
@@ -291,18 +324,22 @@ func FindYAMLWithKey(s string, opts ...Option) ([]string, error) {
} }
// parseConfig merges all config back in one structure. // parseConfig merges all config back in one structure.
func parseConfig(dir []string) *Config { func parseConfig(dir []string, nologs bool) *Config {
files := allFiles(dir) files := allFiles(dir)
c := &Config{} c := &Config{}
for _, f := range files { for _, f := range files {
if fileSize(f) > 1.0 { if fileSize(f) > 1.0 {
fmt.Printf("warning: skipping %s. too big (>1MB)\n", f) if !nologs {
fmt.Printf("warning: skipping %s. too big (>1MB)\n", f)
}
continue continue
} }
if strings.Contains(f, "userdata") || filepath.Ext(f) == ".yml" || filepath.Ext(f) == ".yaml" { if strings.Contains(f, "userdata") || filepath.Ext(f) == ".yml" || filepath.Ext(f) == ".yaml" {
b, err := os.ReadFile(f) b, err := os.ReadFile(f)
if err != nil { if err != nil {
fmt.Printf("warning: skipping %s. %s\n", f, err.Error()) if !nologs {
fmt.Printf("warning: skipping %s. %s\n", f, err.Error())
}
continue continue
} }
yaml.Unmarshal(b, c) //nolint:errcheck yaml.Unmarshal(b, c) //nolint:errcheck
@@ -311,7 +348,9 @@ func parseConfig(dir []string) *Config {
c.header = header c.header = header
} }
} else { } else {
fmt.Printf("warning: skipping %s (extension).\n", f) if !nologs {
fmt.Printf("warning: skipping %s (extension).\n", f)
}
} }
} }

View File

@@ -50,7 +50,7 @@ var _ = Describe("Config", func() {
ExpectWithOffset(1, header).To(Equal(DefaultHeader)) ExpectWithOffset(1, header).To(Equal(DefaultHeader))
} }
It("reads from bootargs", func() { It("reads from bootargs and can query", func() {
err := os.WriteFile(filepath.Join(d, "b"), []byte(`zz.foo="baa" options.foo=bar`), os.ModePerm) err := os.WriteFile(filepath.Join(d, "b"), []byte(`zz.foo="baa" options.foo=bar`), os.ModePerm)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
@@ -58,6 +58,8 @@ var _ = Describe("Config", func() {
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
headerCheck(c) headerCheck(c)
Expect(c.Options["foo"]).To(Equal("bar")) Expect(c.Options["foo"]).To(Equal("bar"))
Expect(c.Query("options")).To(Equal("foo: bar\n"))
Expect(c.Query("options.foo")).To(Equal("bar\n"))
}) })
It("reads multiple config files", func() { It("reads multiple config files", func() {
@@ -96,7 +98,7 @@ c: d
var cc string = `#kairos-config var cc string = `#kairos-config
baz: bar baz: bar
kairos: kairos:
network_token: foo network_token: foo
` `
err := os.WriteFile(filepath.Join(d, "test.yaml"), []byte(cc), os.ModePerm) err := os.WriteFile(filepath.Join(d, "test.yaml"), []byte(cc), os.ModePerm)
@@ -114,7 +116,8 @@ fooz:
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(providerCfg.Kairos).ToNot(BeNil()) Expect(providerCfg.Kairos).ToNot(BeNil())
Expect(providerCfg.Kairos.NetworkToken).To(Equal("foo")) Expect(providerCfg.Kairos.NetworkToken).To(Equal("foo"))
Expect(c.String()).To(Equal(cc)) Expect(c.String()).To(Equal(cc), c.String(), cc)
}) })
It("merges with bootargs", func() { It("merges with bootargs", func() {

View File

@@ -4,10 +4,16 @@ type Options struct {
ScanDir []string ScanDir []string
BootCMDLineFile string BootCMDLineFile string
MergeBootCMDLine bool MergeBootCMDLine bool
NoLogs bool
} }
type Option func(o *Options) error type Option func(o *Options) error
var NoLogs Option = func(o *Options) error {
o.NoLogs = true
return nil
}
func (o *Options) Apply(opts ...Option) error { func (o *Options) Apply(opts ...Option) error {
for _, oo := range opts { for _, oo := range opts {
if err := oo(o); err != nil { if err := oo(o); err != nil {