diff --git a/pkg/config/abra.go b/pkg/config/abra.go new file mode 100644 index 00000000..ecd799af --- /dev/null +++ b/pkg/config/abra.go @@ -0,0 +1,104 @@ +package config + +import ( + "os" + "path" + "path/filepath" + + "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" +) + +// LoadAbraConfig returns the abra configuration. It tries to find a abra +// configuration file (see findAbraConfig for lookup logic). When no +// configuration was found it returns the default config. +func LoadAbraConfig() Abra { + wd, _ := os.Getwd() + configFile := findAbraConfig(wd) + if configFile == "" { + logrus.Debugf("no config file found") + return Abra{} + } + data, err := os.ReadFile(configFile) + if err != nil { + // Do nothing, when an error occurs + logrus.Debugf("error reading config file: %s", err) + return Abra{} + } + + config := Abra{} + err = yaml.Unmarshal(data, &config) + if err != nil { + // Do nothing, when an error occurs + logrus.Debugf("error loading config file: %s", err) + return Abra{} + } + logrus.Debugf("config file loaded from: %s", configFile) + config.configPath = configFile + return config +} + +// findAbraConfig recursively looks for a abra.y(a)ml file in the given directory. +// When the file was not found it calls the function again with the parent +// directory until the home directory is hit. When no abra config was found it +// returns an empty string. +func findAbraConfig(dir string) string { + dir, err := filepath.Abs(dir) + if err != nil { + return "" + } + if dir == os.ExpandEnv("$HOME") || dir == "/" { + return "" + } + p := path.Join(dir, "abra.yaml") + if _, err := os.Stat(p); err == nil { + return p + } + p = path.Join(dir, "abra.yml") + if _, err := os.Stat(p); err == nil { + return p + } + return findAbraConfig(filepath.Dir(dir)) +} + +// Abra defines the configuration file for abra. +type Abra struct { + configPath string + AbraDir string `yaml:"abraDir"` +} + +// GetAbraDir returns the abra dir. It has the following logic: +// 1. check if $ABRA_DIR is set +// 2. check if abraDir was set in a config file +// 3. use $HOME/.abra when above two options failed +func (a Abra) GetAbraDir() string { + if dir, exists := os.LookupEnv("ABRA_DIR"); exists && dir != "" { + logrus.Debug("read abra dir from $ABRA_DIR") + return dir + } + if a.AbraDir != "" { + logrus.Debug("read abra dir from config file") + if path.IsAbs(a.AbraDir) { + return a.AbraDir + } + // Make the path absolute + return path.Join(a.configPath, a.AbraDir) + } + logrus.Debug("using default abra dir") + return os.ExpandEnv("$HOME/.abra") +} + +var config = LoadAbraConfig() + +var ( + ABRA_DIR = config.GetAbraDir() + SERVERS_DIR = path.Join(ABRA_DIR, "servers") + RECIPES_DIR = path.Join(ABRA_DIR, "recipes") + VENDOR_DIR = path.Join(ABRA_DIR, "vendor") + BACKUP_DIR = path.Join(ABRA_DIR, "backups") + CATALOGUE_DIR = path.Join(ABRA_DIR, "catalogue") + RECIPES_JSON = path.Join(ABRA_DIR, "catalogue", "recipes.json") + REPOS_BASE_URL = "https://git.coopcloud.tech/coop-cloud" + CATALOGUE_JSON_REPO_NAME = "recipes-catalogue-json" + SSH_URL_TEMPLATE = "ssh://git@git.coopcloud.tech:2222/coop-cloud/%s.git" +) diff --git a/pkg/config/abra_test.go b/pkg/config/abra_test.go new file mode 100644 index 00000000..f60ad6a9 --- /dev/null +++ b/pkg/config/abra_test.go @@ -0,0 +1,103 @@ +package config + +import ( + "log" + "os" + "path/filepath" + "testing" +) + +func TestFindAbraConfig(t *testing.T) { + wd, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + tests := []struct { + Dir string + Config string + }{ + { + Dir: "testdata/abraconfig1", + Config: filepath.Join(wd, "testdata/abraconfig1/abra.yaml"), + }, + { + Dir: "testdata/abraconfig1/subdir", + Config: filepath.Join(wd, "testdata/abraconfig1/abra.yaml"), + }, + { + Dir: "testdata/abraconfig2", + Config: filepath.Join(wd, "testdata/abraconfig2/abra.yml"), + }, + { + Dir: "testdata/abraconfig2/subdir", + Config: filepath.Join(wd, "testdata/abraconfig2/abra.yml"), + }, + { + Dir: "testdata", + Config: "", + }, + } + + for _, tc := range tests { + t.Run(tc.Dir, func(t *testing.T) { + config := findAbraConfig(tc.Dir) + if config != tc.Config { + t.Errorf("\nwant: %s\ngot: %s", tc.Config, config) + } + }) + } +} + +func TestLoadAbraConfigGetAbraDir(t *testing.T) { + wd, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + t.Setenv("ABRA_DIR", "") + + t.Run("default", func(t *testing.T) { + cfg := LoadAbraConfig() + wantAbraDir := os.ExpandEnv("$HOME/.abra") + if cfg.GetAbraDir() != wantAbraDir { + t.Errorf("\nwant: %s\ngot: %s", wantAbraDir, cfg.GetAbraDir()) + } + }) + + t.Run("from config file", func(t *testing.T) { + t.Cleanup(func() { os.Chdir(wd) }) + err = os.Chdir(filepath.Join(wd, "testdata/abraconfig1")) + if err != nil { + log.Fatal(err) + } + + cfg := LoadAbraConfig() + log.Println(cfg.GetAbraDir()) + wantAbraDir := filepath.Join(wd, "testdata/abraconfig1/abra.yaml/foobar") + if cfg.GetAbraDir() != wantAbraDir { + t.Errorf("\nwant: %s\ngot: %s", wantAbraDir, cfg.GetAbraDir()) + } + }) + + t.Run("default when config file is empty", func(t *testing.T) { + t.Cleanup(func() { os.Chdir(wd) }) + err := os.Chdir(filepath.Join(wd, "testdata/abraconfig2")) + if err != nil { + log.Fatal(err) + } + + cfg := LoadAbraConfig() + wantAbraDir := os.ExpandEnv("$HOME/.abra") + if cfg.GetAbraDir() != wantAbraDir { + t.Errorf("\nwant: %s\ngot: %s", wantAbraDir, cfg.GetAbraDir()) + } + }) + + t.Run("from env variable", func(t *testing.T) { + t.Setenv("ABRA_DIR", "foo") + cfg := LoadAbraConfig() + wantAbraDir := "foo" + if cfg.GetAbraDir() != wantAbraDir { + t.Errorf("\nwant: %s\ngot: %s", wantAbraDir, cfg.GetAbraDir()) + } + }) +} diff --git a/pkg/config/env.go b/pkg/config/env.go index 45bd9757..d2ba1598 100644 --- a/pkg/config/env.go +++ b/pkg/config/env.go @@ -16,26 +16,6 @@ import ( "github.com/sirupsen/logrus" ) -// getBaseDir retrieves the Abra base directory. -func getBaseDir() string { - home := os.ExpandEnv("$HOME/.abra") - if customAbraDir, exists := os.LookupEnv("ABRA_DIR"); exists && customAbraDir != "" { - home = customAbraDir - } - return home -} - -var ABRA_DIR = getBaseDir() -var SERVERS_DIR = path.Join(ABRA_DIR, "servers") -var RECIPES_DIR = path.Join(ABRA_DIR, "recipes") -var VENDOR_DIR = path.Join(ABRA_DIR, "vendor") -var BACKUP_DIR = path.Join(ABRA_DIR, "backups") -var CATALOGUE_DIR = path.Join(ABRA_DIR, "catalogue") -var RECIPES_JSON = path.Join(ABRA_DIR, "catalogue", "recipes.json") -var REPOS_BASE_URL = "https://git.coopcloud.tech/coop-cloud" -var CATALOGUE_JSON_REPO_NAME = "recipes-catalogue-json" -var SSH_URL_TEMPLATE = "ssh://git@git.coopcloud.tech:2222/coop-cloud/%s.git" - const MAX_SANITISED_APP_NAME_LENGTH = 45 const MAX_DOCKER_SECRET_LENGTH = 64 diff --git a/pkg/config/testdata/abraconfig1/abra.yaml b/pkg/config/testdata/abraconfig1/abra.yaml new file mode 100644 index 00000000..b125d5b2 --- /dev/null +++ b/pkg/config/testdata/abraconfig1/abra.yaml @@ -0,0 +1 @@ +abraDir: foobar diff --git a/pkg/config/testdata/abraconfig1/subdir/.gitkeep b/pkg/config/testdata/abraconfig1/subdir/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/pkg/config/testdata/abraconfig2/abra.yml b/pkg/config/testdata/abraconfig2/abra.yml new file mode 100644 index 00000000..e69de29b diff --git a/pkg/config/testdata/abraconfig2/subdir/.gitkeep b/pkg/config/testdata/abraconfig2/subdir/.gitkeep new file mode 100644 index 00000000..e69de29b