Skip to content

Commit 626a79d

Browse files
authored
Config factories (#3)
1 parent b437596 commit 626a79d

File tree

6 files changed

+128
-16
lines changed

6 files changed

+128
-16
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
Automate your review process with Rivi the review bot
66

77
## Usage
8+
Rivi can be run as a service which listens to incoming repository webhooks. This service must be internet facing to accept incoming requests (e.g. GitHub).
89
```
910
Usage of rivi:
1011
-config string
@@ -19,6 +20,21 @@ Usage of rivi:
1920
$ rivi -port 9000 -config repo-x.yaml -config repo-y.yaml
2021
```
2122

23+
### Docker
24+
25+
It is also possible to run Rivi as Docker container. Rivi's images are published to Docker Hub as `bivas/rivi`.
26+
27+
You should visit [bivas/rivi](https://hub.docker.com/r/bivas/rivi/) Docker Hub page and check for published [tags](https://hub.docker.com/r/bivas/rivi/tags/).
28+
29+
```
30+
$ docker run --detach \
31+
--name rivi \
32+
--publish 8080:8080 \
33+
--env RIVI_CONFIG_TOKEN=<rivi oath token> \
34+
--volume /path/to/config/files:/config \
35+
bivas/rivi rivi -config /config/repo-x.yaml
36+
```
37+
2238
## Requirements
2339

2440
- Create a token with `repo` permissions

bot/config.go

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,53 @@ func (c *clientConfig) GetSecret() string {
3030
return c.internal.GetString("secret")
3131
}
3232

33+
type ActionConfig interface {
34+
Name() string
35+
}
36+
37+
type ActionConfigBuilder interface {
38+
Build(config map[string]interface{}) (ActionConfig, error)
39+
}
40+
3341
type Configuration interface {
3442
GetClientConfig() ClientConfig
3543
GetRoleMembers(role ...string) []string
3644
GetRoles() []string
3745
GetRules() []Rule
46+
GetActionConfig(kind string) (ActionConfig, error)
3847
}
3948

4049
var (
41-
configSections = []string{"config", "roles", "rules"}
50+
configSections = []string{"config", "roles", "rules"}
51+
actionConfigBuilders = make(map[string]ActionConfigBuilder)
4252
)
4353

54+
func RegisterActionConfigBuilder(name string, builder ActionConfigBuilder) {
55+
search := strings.ToLower(name)
56+
_, exists := actionConfigBuilders[search]
57+
if exists {
58+
util.Logger.Error("action config build for %s exists!", name)
59+
} else {
60+
util.Logger.Debug("registering action config builder %s", name)
61+
actionConfigBuilders[search] = builder
62+
}
63+
}
64+
4465
type config struct {
45-
internal map[string]*viper.Viper
46-
clientConfig ClientConfig
47-
rules []Rule
48-
roles map[string][]string
49-
rolesKeys []string
66+
internal map[string]*viper.Viper
67+
clientConfig ClientConfig
68+
rules []Rule
69+
roles map[string][]string
70+
rolesKeys []string
71+
actionConfigs map[string]ActionConfig
72+
}
73+
74+
func (c *config) GetActionConfig(kind string) (ActionConfig, error) {
75+
config, exists := c.actionConfigs[kind]
76+
if !exists {
77+
return nil, fmt.Errorf("No such action config %s", kind)
78+
}
79+
return config, nil
5080
}
5181

5282
func (c *config) getSection(path string) (string, *viper.Viper) {
@@ -70,7 +100,7 @@ func (c *config) GetRoleMembers(roles ...string) []string {
70100
result = append(result, members...)
71101
}
72102
}
73-
set := util.StringSet{}
103+
set := util.StringSet{Transformer: strings.ToLower}
74104
set.AddAll(result)
75105
return set.Values()
76106
}
@@ -121,14 +151,30 @@ func (c *config) readRulesSection() error {
121151
}
122152

123153
func (c *config) readSections() error {
124-
if err := c.readConfigSection(); err != nil {
125-
return err
154+
sections := []func() error{
155+
c.readConfigSection,
156+
c.readRolesSection,
157+
c.readRulesSection,
126158
}
127-
if err := c.readRolesSection(); err != nil {
128-
return err
159+
for _, section := range sections {
160+
if err := section(); err != nil {
161+
return err
162+
}
129163
}
130-
if err := c.readRulesSection(); err != nil {
131-
return err
164+
c.actionConfigs = make(map[string]ActionConfig)
165+
for kind, builder := range actionConfigBuilders {
166+
util.Logger.Debug("Building configuration for %s", kind)
167+
actionConfig := c.internal["root"].GetStringMap(kind)
168+
if actionConfig == nil || len(actionConfig) == 0 {
169+
util.Logger.Warning("No matching section for %s", kind)
170+
continue
171+
}
172+
config, err := builder.Build(actionConfig)
173+
if err != nil {
174+
util.Logger.Error("Error while building %s config. %s", kind, err)
175+
continue
176+
}
177+
c.actionConfigs[kind] = config
132178
}
133179
return nil
134180
}

bot/config_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package bot
22

33
import (
4+
"fmt"
45
"github.com/stretchr/testify/assert"
56
"os"
67
. "testing"
@@ -82,3 +83,38 @@ func TestEmptyConfigTest(t *T) {
8283
assert.Equal(t, "token-from-env", c.GetClientConfig().GetOAuthToken(), "token from env")
8384
assert.Equal(t, "secret-from-env", c.GetClientConfig().GetSecret(), "secret from env")
8485
}
86+
87+
type testActionConfig struct {
88+
key, value string
89+
}
90+
91+
func (t *testActionConfig) Name() string {
92+
return "test-section"
93+
}
94+
95+
type testBuilder struct {
96+
}
97+
98+
func (*testBuilder) Build(config map[string]interface{}) (ActionConfig, error) {
99+
if len(config) != 1 {
100+
return nil, fmt.Errorf("Wrong number of values")
101+
}
102+
for key, value := range config {
103+
return &testActionConfig{key, value.(string)}, nil
104+
}
105+
return nil, fmt.Errorf("Should not reach here")
106+
}
107+
108+
func TestActionConfigBuilder(t *T) {
109+
RegisterActionConfigBuilder("test-section", &testBuilder{})
110+
c, err := newConfiguration("config_test.yml")
111+
if err != nil {
112+
t.Fatalf("Got error during config read. %s", err)
113+
}
114+
result, err := c.GetActionConfig("test-section")
115+
assert.Nil(t, err, "should contain section")
116+
exact, ok := result.(*testActionConfig)
117+
assert.True(t, ok, "should be of test action config type")
118+
assert.Equal(t, "key", exact.key, "key")
119+
assert.Equal(t, "value", exact.value, "value")
120+
}

bot/config_test.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ roles:
1515
testers:
1616
- user2
1717

18+
test-section:
19+
key: value
20+
1821
rules:
1922
rule1:
2023
condition:

bot/mock/configuration.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ type MockConfiguration struct {
2323
RoleMembers map[string][]string
2424
}
2525

26+
func (m *MockConfiguration) GetActionConfig(kind string) (bot.ActionConfig, error) {
27+
panic("implement me")
28+
}
29+
2630
func (m *MockConfiguration) GetClientConfig() bot.ClientConfig {
2731
return m.MockClientConfig
2832
}

bot/rule.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"github.com/bivas/rivi/util"
66
"github.com/spf13/viper"
7+
"strings"
78
)
89

910
type Action interface {
@@ -69,9 +70,15 @@ var actions map[string]ActionFactory = make(map[string]ActionFactory)
6970
var supportedActions []string = make([]string, 0)
7071

7172
func RegisterAction(kind string, action ActionFactory) {
72-
actions[kind] = action
73-
supportedActions = append(supportedActions, kind)
74-
util.Logger.Debug("running with support for %s", kind)
73+
search := strings.ToLower(kind)
74+
_, exists := actions[search]
75+
if exists {
76+
util.Logger.Error("action %s exists!", kind)
77+
} else {
78+
util.Logger.Debug("registering action %s", kind)
79+
actions[search] = action
80+
supportedActions = append(supportedActions, kind)
81+
}
7582
}
7683

7784
func buildActionsFromConfiguration(config *viper.Viper) []Action {

0 commit comments

Comments
 (0)