mirror of
https://github.com/k3s-io/k3s.git
synced 2024-06-07 19:41:36 +00:00
Support .d directory for k3s config file (#3162)
Configuration will be loaded from config.yaml and then config.yaml.d/*.(yaml|yml) in alphanumeric order. The merging is done by just taking the last value of a key found, so LIFO for keys. Slices are not merged but replaced. Signed-off-by: Darren Shepherd <darren@rancher.com>
This commit is contained in:
parent
601c4984f5
commit
a0a1071aa5
@ -6,8 +6,10 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/rancher/k3s/pkg/agent/util"
|
||||
"github.com/rancher/wrangler/pkg/data/convert"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
@ -19,7 +21,7 @@ type Parser struct {
|
||||
DefaultConfig string
|
||||
}
|
||||
|
||||
// Parser will parse an os.Args style slice looking for Parser.FlagNames after Parse.After.
|
||||
// Parse will parse an os.Args style slice looking for Parser.FlagNames after Parse.After.
|
||||
// It will read the parameter value of Parse.FlagNames and read the file, appending all flags directly after
|
||||
// the Parser.After value. This means a the non-config file flags will override, or if a slice append to, the config
|
||||
// file values.
|
||||
@ -110,31 +112,69 @@ func (p *Parser) findStart(args []string) ([]string, []string, bool) {
|
||||
return args, nil, false
|
||||
}
|
||||
|
||||
func dotDFiles(basefile string) (result []string, _ error) {
|
||||
files, err := ioutil.ReadDir(basefile + ".d")
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, file := range files {
|
||||
if file.IsDir() || !util.HasSuffixI(file.Name(), ".yaml", ".yml") {
|
||||
continue
|
||||
}
|
||||
result = append(result, filepath.Join(basefile+".d", file.Name()))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func readConfigFile(file string) (result []string, _ error) {
|
||||
bytes, err := readConfigFileData(file)
|
||||
files, err := dotDFiles(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := yaml.MapSlice{}
|
||||
if err := yaml.Unmarshal(bytes, &data); err != nil {
|
||||
_, err = os.Stat(file)
|
||||
if os.IsNotExist(err) && len(files) > 0 {
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
files = append([]string{file}, files...)
|
||||
}
|
||||
|
||||
for _, i := range data {
|
||||
k, v := convert.ToString(i.Key), i.Value
|
||||
prefix := "--"
|
||||
if len(k) == 1 {
|
||||
prefix = "-"
|
||||
keySeen := map[string]bool{}
|
||||
for i := len(files) - 1; i >= 0; i-- {
|
||||
file := files[i]
|
||||
bytes, err := readConfigFileData(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if slice, ok := v.([]interface{}); ok {
|
||||
for _, v := range slice {
|
||||
result = append(result, prefix+k+"="+convert.ToString(v))
|
||||
data := yaml.MapSlice{}
|
||||
if err := yaml.Unmarshal(bytes, &data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, i := range data {
|
||||
k, v := convert.ToString(i.Key), i.Value
|
||||
if keySeen[k] {
|
||||
continue
|
||||
}
|
||||
keySeen[k] = true
|
||||
|
||||
prefix := "--"
|
||||
if len(k) == 1 {
|
||||
prefix = "-"
|
||||
}
|
||||
|
||||
if slice, ok := v.([]interface{}); ok {
|
||||
for _, v := range slice {
|
||||
result = append(result, prefix+k+"="+convert.ToString(v))
|
||||
}
|
||||
} else {
|
||||
str := convert.ToString(v)
|
||||
result = append(result, prefix+k+"="+str)
|
||||
}
|
||||
} else {
|
||||
str := convert.ToString(v)
|
||||
result = append(result, prefix+k+"="+str)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,8 +150,9 @@ func TestConfigFile(t *testing.T) {
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
testDataOutput := []string{
|
||||
"--foo-bar=baz",
|
||||
"--foo-bar=bar-foo",
|
||||
"--a-slice=1",
|
||||
"--a-slice=1.5",
|
||||
"--a-slice=2",
|
||||
"--a-slice=",
|
||||
"--a-slice=three",
|
||||
@ -205,7 +206,7 @@ func TestParse(t *testing.T) {
|
||||
input: []string{"server", "-c=missing"},
|
||||
output: []string{"server", "-c=missing"},
|
||||
what: "fail when missing config",
|
||||
err: "open missing: no such file or directory",
|
||||
err: "stat missing: no such file or directory",
|
||||
},
|
||||
{
|
||||
parser: Parser{
|
||||
@ -217,6 +218,16 @@ func TestParse(t *testing.T) {
|
||||
output: append(append([]string{"before", "server"}, testDataOutput...), "before", "-c", "./testdata/data.yaml", "after"),
|
||||
what: "read config file",
|
||||
},
|
||||
{
|
||||
parser: Parser{
|
||||
After: []string{"server", "agent"},
|
||||
FlagNames: []string{"-c", "--config"},
|
||||
DefaultConfig: "missing",
|
||||
},
|
||||
input: []string{"before", "server", "before", "-c", "./testdata/data.yaml.d/02-data.yaml", "after"},
|
||||
output: []string{"before", "server", "--foo-bar=bar-foo", "before", "-c", "./testdata/data.yaml.d/02-data.yaml", "after"},
|
||||
what: "read single config file",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
|
7
pkg/configfilearg/testdata/data.yaml.d/01-data.yml
vendored
Normal file
7
pkg/configfilearg/testdata/data.yaml.d/01-data.yml
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
foo-bar: get-overriden
|
||||
a-slice:
|
||||
- 1
|
||||
- "1.5"
|
||||
- "2"
|
||||
- ""
|
||||
- three
|
1
pkg/configfilearg/testdata/data.yaml.d/02-data-ignore-this.txt
vendored
Normal file
1
pkg/configfilearg/testdata/data.yaml.d/02-data-ignore-this.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
foo-bar: ignored
|
1
pkg/configfilearg/testdata/data.yaml.d/02-data.yaml
vendored
Normal file
1
pkg/configfilearg/testdata/data.yaml.d/02-data.yaml
vendored
Normal file
@ -0,0 +1 @@
|
||||
foo-bar: bar-foo
|
Loading…
Reference in New Issue
Block a user