Use OOP-ish style for configuration loading
Signed-off-by: Steven Kriegler <61625851+justusbunsi@users.noreply.github.com>
This commit is contained in:
parent
4ba781d74f
commit
86a644f31f
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -3,3 +3,5 @@
|
|||
/config/
|
||||
/vendor/
|
||||
/gitea-sonarqube-bot
|
||||
/coverage.html
|
||||
/*.log
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
38
internal/settings/token.go
Normal file
38
internal/settings/token.go
Normal file
|
@ -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
|
||||
}
|
38
internal/settings/webhook.go
Normal file
38
internal/settings/webhook.go
Normal file
|
@ -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
|
||||
}
|
Loading…
Reference in a new issue