From 86a644f31fc27c6ee7da3e105b1fb07f306f3597 Mon Sep 17 00:00:00 2001 From: justusbunsi <61625851+justusbunsi@users.noreply.github.com> Date: Tue, 29 Jun 2021 10:29:20 +0200 Subject: [PATCH] Use OOP-ish style for configuration loading Signed-off-by: Steven Kriegler <61625851+justusbunsi@users.noreply.github.com> --- .gitignore | 2 + README.md | 1 + internal/settings/settings.go | 66 ++++++++++-------------------- internal/settings/settings_test.go | 32 +++++++-------- internal/settings/token.go | 38 +++++++++++++++++ internal/settings/webhook.go | 38 +++++++++++++++++ 6 files changed, 116 insertions(+), 61 deletions(-) create mode 100644 internal/settings/token.go create mode 100644 internal/settings/webhook.go diff --git a/.gitignore b/.gitignore index bb63e77..e30c4d3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ /config/ /vendor/ /gitea-sonarqube-bot +/coverage.html +/*.log diff --git a/README.md b/README.md index 4ae98ab..6d22846 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Luckily, both endpoints have a proper REST API to communicate with each others. ## TODOs +- [ ] Validate configuration on startup - [ ] Maybe drop `PRBOT_CONFIG_PATH` environment variable in favor of `--config path/to/config.yaml` cli attribute - [ ] Configure SonarQube PR branch naming pattern for more flexibility (currently focused on Jenkins with [Gitea Plugin](https://github.com/jenkinsci/gitea-plugin)) - [ ] Configuration live reloading diff --git a/internal/settings/settings.go b/internal/settings/settings.go index 7b4a0f8..13f5eab 100644 --- a/internal/settings/settings.go +++ b/internal/settings/settings.go @@ -2,7 +2,6 @@ package settings import ( "fmt" - "io/ioutil" "strings" "github.com/spf13/viper" @@ -13,26 +12,16 @@ type giteaRepository struct { Name string } -type token struct { - Value string - File string -} - -type webhook struct { - Secret string - SecretFile string -} - type giteaConfig struct { Url string - Token token - Webhook webhook + Token *token + Webhook *webhook } type sonarQubeConfig struct { Url string - Token token - Webhook webhook + Token *token + Webhook *webhook } type Project struct { @@ -42,31 +31,12 @@ type Project struct { Gitea giteaRepository } -type fullConfig struct { - Gitea giteaConfig - SonarQube sonarQubeConfig `mapstructure:"sonarqube"` - Projects []Project -} - var ( Gitea giteaConfig SonarQube sonarQubeConfig Projects []Project ) -func readSecretFile(file string, defaultValue string) (string) { - if file == "" { - return defaultValue - } - - content, err := ioutil.ReadFile(file) - if err != nil { - panic(fmt.Errorf("Cannot read '%s' or it is no regular file. %w", file, err)) - } - - return string(content) -} - func newConfigReader() *viper.Viper { v := viper.New() v.SetConfigName("config.yaml") @@ -100,23 +70,29 @@ func Load(configPath string) { panic(fmt.Errorf("Fatal error while reading config file: %w \n", err)) } - var configuration fullConfig + var projects []Project - err = r.Unmarshal(&configuration) + err = r.UnmarshalKey("projects", &projects) if err != nil { - panic(fmt.Errorf("Unable to load config into struct, %v", err)) + panic(fmt.Errorf("Unable to load project mapping: %s", err.Error())) } - if len(configuration.Projects) == 0 { + if len(projects) == 0 { panic("Invalid configuration. At least one project mapping is necessary.") } - Gitea = configuration.Gitea - SonarQube = configuration.SonarQube - Projects = configuration.Projects + Projects = projects - Gitea.Webhook.Secret = readSecretFile(Gitea.Webhook.SecretFile, Gitea.Webhook.Secret) - Gitea.Token.Value = readSecretFile(Gitea.Token.File, Gitea.Token.Value) - SonarQube.Webhook.Secret = readSecretFile(SonarQube.Webhook.SecretFile, SonarQube.Webhook.Secret) - SonarQube.Token.Value = readSecretFile(SonarQube.Token.File, SonarQube.Token.Value) + errCallback := func(msg string) {panic(msg)} + + Gitea = giteaConfig{ + Url: r.GetString("gitea.url"), + Token: NewToken(r, "gitea", errCallback), + Webhook: NewWebhook(r, "gitea", errCallback), + } + SonarQube = sonarQubeConfig{ + Url: r.GetString("sonarqube.url"), + Token: NewToken(r, "sonarqube", errCallback), + Webhook: NewWebhook(r, "sonarqube", errCallback), + } } diff --git a/internal/settings/settings_test.go b/internal/settings/settings_test.go index 20f8672..14f2154 100644 --- a/internal/settings/settings_test.go +++ b/internal/settings/settings_test.go @@ -57,10 +57,10 @@ func TestLoadGiteaStructure(t *testing.T) { expected := giteaConfig{ Url: "https://example.com/gitea", - Token: token{ + Token: &token{ Value: "d0fcdeb5eaa99c506831f9eb4e63fc7cc484a565", }, - Webhook: webhook{ + Webhook: &webhook{ Secret: "haxxor-gitea-secret", }, } @@ -76,10 +76,10 @@ func TestLoadGiteaStructureInjectedEnvs(t *testing.T) { expected := giteaConfig{ Url: "https://example.com/gitea", - Token: token{ + Token: &token{ Value: "injected-token", }, - Webhook: webhook{ + Webhook: &webhook{ Secret: "injected-webhook-secret", }, } @@ -98,10 +98,10 @@ func TestLoadSonarQubeStructure(t *testing.T) { expected := sonarQubeConfig{ Url: "https://example.com/sonarqube", - Token: token{ + Token: &token{ Value: "a09eb5785b25bb2cbacf48808a677a0709f02d8e", }, - Webhook: webhook{ + Webhook: &webhook{ Secret: "haxxor-sonarqube-secret", }, } @@ -117,10 +117,10 @@ func TestLoadSonarQubeStructureInjectedEnvs(t *testing.T) { expected := sonarQubeConfig{ Url: "https://example.com/sonarqube", - Token: token{ + Token: &token{ Value: "injected-token", }, - Webhook: webhook{ + Webhook: &webhook{ Secret: "injected-webhook-secret", }, } @@ -169,25 +169,25 @@ projects: expectedGitea := giteaConfig{ Url: "https://example.com/gitea", - Token: token{ + Token: &token{ Value: "d0fcdeb5eaa99c506831f9eb4e63fc7cc484a565", - File: giteaTokenFile, + file: giteaTokenFile, }, - Webhook: webhook{ + Webhook: &webhook{ Secret: "gitea-totally-secret", - SecretFile: giteaWebhookSecretFile, + secretFile: giteaWebhookSecretFile, }, } expectedSonarQube := sonarQubeConfig{ Url: "https://example.com/sonarqube", - Token: token{ + Token: &token{ Value: "a09eb5785b25bb2cbacf48808a677a0709f02d8e", - File: sonarqubeTokenFile, + file: sonarqubeTokenFile, }, - Webhook: webhook{ + Webhook: &webhook{ Secret: "sonarqube-totally-secret", - SecretFile: sonarqubeWebhookSecretFile, + secretFile: sonarqubeWebhookSecretFile, }, } diff --git a/internal/settings/token.go b/internal/settings/token.go new file mode 100644 index 0000000..447c1dc --- /dev/null +++ b/internal/settings/token.go @@ -0,0 +1,38 @@ +package settings + +import ( + "fmt" + "io/ioutil" + + "github.com/spf13/viper" +) + +type token struct { + Value string + file string +} + +func (t *token) lookupSecret(errCallback func(string)) { + if t.file == "" { + return + } + + content, err := ioutil.ReadFile(t.file) + if err != nil { + errCallback(fmt.Sprintf("Cannot read '%s' or it is no regular file: %s", t.file, err.Error())) + return + } + + t.Value = string(content) +} + +func NewToken(v *viper.Viper, confContainer string, errCallback func(string)) *token { + t := &token{ + Value: v.GetString(fmt.Sprintf("%s.token.value", confContainer)), + file: v.GetString(fmt.Sprintf("%s.token.file", confContainer)), + } + + t.lookupSecret(errCallback) + + return t +} diff --git a/internal/settings/webhook.go b/internal/settings/webhook.go new file mode 100644 index 0000000..a52d495 --- /dev/null +++ b/internal/settings/webhook.go @@ -0,0 +1,38 @@ +package settings + +import ( + "fmt" + "io/ioutil" + + "github.com/spf13/viper" +) + +type webhook struct { + Secret string + secretFile string +} + +func (w *webhook) lookupSecret(errCallback func(string)) { + if w.secretFile == "" { + return + } + + content, err := ioutil.ReadFile(w.secretFile) + if err != nil { + errCallback(fmt.Sprintf("Cannot read '%s' or it is no regular file: %s", w.secretFile, err.Error())) + return + } + + w.Secret = string(content) +} + +func NewWebhook(v *viper.Viper, confContainer string, errCallback func(string)) *webhook { + w := &webhook{ + Secret: v.GetString(fmt.Sprintf("%s.webhook.secret", confContainer)), + secretFile: v.GetString(fmt.Sprintf("%s.webhook.secretFile", confContainer)), + } + + w.lookupSecret(errCallback) + + return w +}