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>
pull/3219/head
Darren Shepherd 4 years ago committed by GitHub
parent 601c4984f5
commit a0a1071aa5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -6,8 +6,10 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"path/filepath"
"strings" "strings"
"github.com/rancher/k3s/pkg/agent/util"
"github.com/rancher/wrangler/pkg/data/convert" "github.com/rancher/wrangler/pkg/data/convert"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
@ -19,7 +21,7 @@ type Parser struct {
DefaultConfig string 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 // 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 // the Parser.After value. This means a the non-config file flags will override, or if a slice append to, the config
// file values. // file values.
@ -110,7 +112,39 @@ func (p *Parser) findStart(args []string) ([]string, []string, bool) {
return args, nil, false 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) { func readConfigFile(file string) (result []string, _ error) {
files, err := dotDFiles(file)
if err != nil {
return nil, err
}
_, err = os.Stat(file)
if os.IsNotExist(err) && len(files) > 0 {
} else if err != nil {
return nil, err
} else {
files = append([]string{file}, files...)
}
keySeen := map[string]bool{}
for i := len(files) - 1; i >= 0; i-- {
file := files[i]
bytes, err := readConfigFileData(file) bytes, err := readConfigFileData(file)
if err != nil { if err != nil {
return nil, err return nil, err
@ -123,6 +157,11 @@ func readConfigFile(file string) (result []string, _ error) {
for _, i := range data { for _, i := range data {
k, v := convert.ToString(i.Key), i.Value k, v := convert.ToString(i.Key), i.Value
if keySeen[k] {
continue
}
keySeen[k] = true
prefix := "--" prefix := "--"
if len(k) == 1 { if len(k) == 1 {
prefix = "-" prefix = "-"
@ -137,6 +176,7 @@ func readConfigFile(file string) (result []string, _ error) {
result = append(result, prefix+k+"="+str) result = append(result, prefix+k+"="+str)
} }
} }
}
return return
} }

@ -150,8 +150,9 @@ func TestConfigFile(t *testing.T) {
func TestParse(t *testing.T) { func TestParse(t *testing.T) {
testDataOutput := []string{ testDataOutput := []string{
"--foo-bar=baz", "--foo-bar=bar-foo",
"--a-slice=1", "--a-slice=1",
"--a-slice=1.5",
"--a-slice=2", "--a-slice=2",
"--a-slice=", "--a-slice=",
"--a-slice=three", "--a-slice=three",
@ -205,7 +206,7 @@ func TestParse(t *testing.T) {
input: []string{"server", "-c=missing"}, input: []string{"server", "-c=missing"},
output: []string{"server", "-c=missing"}, output: []string{"server", "-c=missing"},
what: "fail when missing config", what: "fail when missing config",
err: "open missing: no such file or directory", err: "stat missing: no such file or directory",
}, },
{ {
parser: Parser{ parser: Parser{
@ -217,6 +218,16 @@ func TestParse(t *testing.T) {
output: append(append([]string{"before", "server"}, testDataOutput...), "before", "-c", "./testdata/data.yaml", "after"), output: append(append([]string{"before", "server"}, testDataOutput...), "before", "-c", "./testdata/data.yaml", "after"),
what: "read config file", 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 { for _, testCase := range testCases {

@ -0,0 +1,7 @@
foo-bar: get-overriden
a-slice:
- 1
- "1.5"
- "2"
- ""
- three
Loading…
Cancel
Save