Skip to content

Commit

Permalink
Fall back to $HOME/.minder.yaml when reading configuration
Browse files Browse the repository at this point in the history
This allows us to have a predictable configuration file when reading the
client minder configuration. The idea is that we could set up
`$HOME/.minder.yaml` and that would serve as an entrypoint.

In the future, we could modify this if we want to dynamically change
the current working project.

Signed-off-by: Juan Antonio Osorio <ozz@stacklok.com>
  • Loading branch information
JAORMX committed Jun 18, 2024
1 parent 31e0ca3 commit f27d63c
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 69 deletions.
44 changes: 28 additions & 16 deletions cmd/cli/app/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ Configuration options include:
- identity.cli.issuer_url
- identity.cli.client_id
By default, we look for the file as $PWD/config.yaml. You can specify a custom path via the --config flag, or by setting the MINDER_CONFIG environment variable.`,
By default, we look for the file as $PWD/config.yaml and $HOME/.minder.yaml. You can specify a custom path via the --config flag, or by setting the MINDER_CONFIG environment variable.`,
}
)

Expand Down Expand Up @@ -116,24 +116,36 @@ func initConfig() {
viper.SetEnvPrefix("minder")
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))

cfgFile := viper.GetString("config")
cfgFileData, err := config.GetConfigFileData(cfgFile, filepath.Join(".", "config.yaml"))
if err != nil {
RootCmd.PrintErrln(err)
os.Exit(1)
//nolint:errcheck // ignore error as we are just checking if the file exists
hdirname, _ := os.UserHomeDir()

var homeCfg string
if hdirname != "" {
homeCfg = filepath.Join(hdirname, ".minder.yaml")
}

keysWithNullValue := config.GetKeysWithNullValueFromYAML(cfgFileData, "")
if len(keysWithNullValue) > 0 {
RootCmd.PrintErrln("Error: The following configuration keys are missing values:")
for _, key := range keysWithNullValue {
RootCmd.PrintErrln("Null Value at: " + key)
cfgFile := viper.GetString("config")
cfgFilePath := config.GetRelevantCfgPath(append([]string{cfgFile},
filepath.Join(".", "config.yaml"),
homeCfg,
))
if cfgFilePath != "" {
cfgFileData, err := config.GetConfigFileData(cfgFilePath)
if err != nil {
RootCmd.PrintErrln(err)
os.Exit(1)
}

keysWithNullValue := config.GetKeysWithNullValueFromYAML(cfgFileData, "")
if len(keysWithNullValue) > 0 {
RootCmd.PrintErrln("Error: The following configuration keys are missing values:")
for _, key := range keysWithNullValue {
RootCmd.PrintErrln("Null Value at: " + key)
}
os.Exit(1)
}
os.Exit(1)
}

if cfgFile != "" {
viper.SetConfigFile(cfgFile)
viper.SetConfigFile(cfgFilePath)
} else {
// use defaults
viper.SetConfigName("config")
Expand All @@ -142,7 +154,7 @@ func initConfig() {
viper.SetConfigType("yaml")
viper.AutomaticEnv()

if err = viper.ReadInConfig(); err != nil {
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
// Config file not found; use default values
RootCmd.PrintErrln("No config file present, using default values.")
Expand Down
31 changes: 17 additions & 14 deletions cmd/reminder/app/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,22 +67,25 @@ func init() {

func initConfig() {
cfgFile := viper.GetString("config")
cfgFileData, err := config.GetConfigFileData(cfgFile, filepath.Join(".", configFileName))
if err != nil {
log.Fatal().Err(err).Msg("Error reading config file")
}
cfgFilePath := config.GetRelevantCfgPath(append([]string{cfgFile},
filepath.Join(".", configFileName),
))
if cfgFilePath != "" {
cfgFileData, err := config.GetConfigFileData(cfgFilePath)
if err != nil {
log.Fatal().Err(err).Msg("Error reading config file")
}

keysWithNullValue := config.GetKeysWithNullValueFromYAML(cfgFileData, "")
if len(keysWithNullValue) > 0 {
RootCmd.PrintErrln("Error: The following configuration keys are missing values:")
for _, key := range keysWithNullValue {
RootCmd.PrintErrln("Null Value at: " + key)
keysWithNullValue := config.GetKeysWithNullValueFromYAML(cfgFileData, "")
if len(keysWithNullValue) > 0 {
RootCmd.PrintErrln("Error: The following configuration keys are missing values:")
for _, key := range keysWithNullValue {
RootCmd.PrintErrln("Null Value at: " + key)
}
os.Exit(1)
}
os.Exit(1)
}

if cfgFile != "" {
viper.SetConfigFile(cfgFile)
viper.SetConfigFile(cfgFilePath)
} else {
// use defaults
viper.SetConfigName(strings.TrimSuffix(configFileName, filepath.Ext(configFileName)))
Expand All @@ -91,7 +94,7 @@ func initConfig() {
viper.SetConfigType("yaml")
viper.AutomaticEnv()

if err = viper.ReadInConfig(); err != nil {
if err := viper.ReadInConfig(); err != nil {
fmt.Println("Error reading config file:", err)
}
}
33 changes: 18 additions & 15 deletions cmd/server/app/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,23 +67,26 @@ func initConfig() {
serverconfig.SetViperDefaults(viper.GetViper())

cfgFile := viper.GetString("config")
cfgFileData, err := config.GetConfigFileData(cfgFile, filepath.Join(".", "server-config.yaml"))
if err != nil {
RootCmd.PrintErrln(err)
os.Exit(1)
}
cfgFilePath := config.GetRelevantCfgPath(append([]string{cfgFile},
filepath.Join(".", "server-config.yaml"),
))
if cfgFilePath != "" {
cfgFileData, err := config.GetConfigFileData(cfgFilePath)
if err != nil {
RootCmd.PrintErrln(err)
os.Exit(1)
}

keysWithNullValue := config.GetKeysWithNullValueFromYAML(cfgFileData, "")
if len(keysWithNullValue) > 0 {
RootCmd.PrintErrln("Error: The following configuration keys are missing values:")
for _, key := range keysWithNullValue {
RootCmd.PrintErrln("Null Value at: " + key)
keysWithNullValue := config.GetKeysWithNullValueFromYAML(cfgFileData, "")
if len(keysWithNullValue) > 0 {
RootCmd.PrintErrln("Error: The following configuration keys are missing values:")
for _, key := range keysWithNullValue {
RootCmd.PrintErrln("Null Value at: " + key)
}
os.Exit(1)
}
os.Exit(1)
}

if cfgFile != "" {
viper.SetConfigFile(cfgFile)
viper.SetConfigFile(cfgFilePath)
} else {
// use defaults
viper.SetConfigName("server-config")
Expand All @@ -92,7 +95,7 @@ func initConfig() {
viper.SetConfigType("yaml")
viper.AutomaticEnv()

if err = viper.ReadInConfig(); err != nil {
if err := viper.ReadInConfig(); err != nil {
fmt.Println("Error reading config file:", err)
}
}
42 changes: 18 additions & 24 deletions internal/config/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,30 +102,8 @@ func doViperBind[V any](
}

// GetConfigFileData returns the data from the given configuration file.
func GetConfigFileData(cfgFile, defaultCfgPath string) (interface{}, error) {
var cfgFilePath string
var err error
if cfgFile != "" {
cfgFilePath, err = filepath.Abs(cfgFile)
if err != nil {
return nil, err
}
} else {
cfgFilePath, err = filepath.Abs(defaultCfgPath)
if err != nil {
return nil, err
}
}

cleanCfgFilePath := filepath.Clean(cfgFilePath)

// If no local config file is present during mounting, Docker will create an empty directory in the container.
// If no config file is present, system will revert to default values.
if info, err := os.Stat(cleanCfgFilePath); err == nil && info.IsDir() || err != nil && os.IsNotExist(err) {
return nil, nil
}

cfgFileBytes, err := os.ReadFile(cleanCfgFilePath)
func GetConfigFileData(cfgFilePath string) (interface{}, error) {
cfgFileBytes, err := os.ReadFile(filepath.Clean(cfgFilePath))
if err != nil {
return nil, err
}
Expand All @@ -139,6 +117,22 @@ func GetConfigFileData(cfgFile, defaultCfgPath string) (interface{}, error) {
return cfgFileData, nil
}

// GetRelevantCfgPath returns the first path that exists (and is a config file).
func GetRelevantCfgPath(paths []string) string {
for _, path := range paths {
if path == "" {
continue
}

cleanPath := filepath.Clean(path)
if info, err := os.Stat(cleanPath); err == nil && !info.IsDir() {
return cleanPath
}
}

return ""
}

// GetKeysWithNullValueFromYAML returns a list of paths to null values in the given configuration data.
func GetKeysWithNullValueFromYAML(data interface{}, currentPath string) []string {
var keysWithNullValue []string
Expand Down
106 changes: 106 additions & 0 deletions internal/config/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@
package config

import (
"os"
"path/filepath"
"regexp"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
)

Expand Down Expand Up @@ -148,3 +152,105 @@ key3: [value1, null, value2]
})
}
}

func TestGetRelevantCfgPath(t *testing.T) {
t.Parallel()

type args struct {
paths []string
}
tests := []struct {
name string
args args
want string
}{
{
name: "Test with empty paths",
args: args{
paths: []string{},
},
want: "",
},
{
name: "Test with one empty path",
args: args{
paths: []string{""},
},
want: "",
},
{
name: "Test with one non-empty path",
args: args{
paths: []string{"config.yaml"},
},
want: "config.yaml",
},
{
name: "Test with multiple paths",
args: args{
paths: []string{"", "config.yaml", "config.yml", "config.json"},
},
want: "config.yaml",
},
{
name: "Test with multiple paths with empty path in the middle",
args: args{
paths: []string{"config.yaml", "", "config.yml", "config.json"},
},
want: "config.yaml",
},
{
name: "Test with multiple paths with empty path at the end",
args: args{
paths: []string{"config.yaml", "config.yml", "config.json", ""},
},
want: "config.yaml",
},
{
name: "Test with multiple paths with empty path at the beginning",
args: args{
paths: []string{"", "config.yaml", "config.yml", "config.json"},
},
want: "config.yaml",
},
{
name: "Test with multiple paths with all empty paths",
args: args{
paths: []string{"", "", "", ""},
},
want: "",
},
{
name: "Test with multiple paths with all non-empty paths",
args: args{
paths: []string{"config.yaml", "config.yml", "config.json"},
},
want: "config.yaml",
},
{
name: "Test with one non-empty path and all empty paths",
args: args{
paths: []string{"", "", "", "config.yaml"},
},
want: "config.yaml",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

baseDir := t.TempDir()
createdpaths := []string{}
for _, path := range tt.args.paths {
if path != "" {
f, err := os.Create(filepath.Clean(filepath.Join(baseDir, path)))
require.NoError(t, err)
createdpaths = append(createdpaths, f.Name())
}
}

got := GetRelevantCfgPath(createdpaths)
assert.Regexp(t, regexp.MustCompile("^.*"+tt.want+"$"), got)
})
}
}

0 comments on commit f27d63c

Please sign in to comment.