Skip to content

Commit 661d29d

Browse files
AndresCidoncharaphink
authored andcommitted
feat: Add short CLI param to config-file
* Move Port to Web param group * Add ENV option to Port * Remove default value from config-file doc: Update readme * Add parameters to README Add LoadConfigFromYaml tests * db_port as uint16 * remove parameter from LoadConfigFromYaml method refactor: Use config.DBConfig as parameter in db.Init Sync interval configuration doc: Update README for sync-interval config * Use sync-interval in retry sleep too
1 parent fa635d3 commit 661d29d

File tree

7 files changed

+159
-51
lines changed

7 files changed

+159
-51
lines changed

README.md

+34
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,38 @@ requires:
7878
you need to make sure the provided AWS credentials have `dynamodb:Scan` access to that
7979
table.
8080

81+
## Configuration
82+
83+
Terraboard currently supports configuration in three different ways:
84+
85+
1. Environment variables
86+
2. CLI parameters
87+
3. Configuration file (YAML). A configuration file example can be found in the root directory of this repository.
88+
89+
The precedence of configurations is as described below.
90+
91+
### Available parameters
92+
93+
|CLI|ENV|YAML|Description|Default|
94+
|---|---|----|-----------|-------|
95+
|`-V | --version`| - | - | Prints version | - |
96+
|`-p | --port`|`PORT`|`web.port`|Port to listen on| 8080 |
97+
|`-c | --config-file`|`CONFIG_FILE`|-|Config File path| - |
98+
|`-l | --log-level` | `TERRABOARD_LOG_LEVEL` | `log.level` | Set log level (debug, info, warn, error, fatal, panic) | info |
99+
|`--log-format` | `TERRABOARD_LOG_FORMAT` | `log.format` | Set log format (plain, json) | plain |
100+
|`--db-host` | `DB_HOST` | `db.host` | Database host | db |
101+
|`--db-port` | `DB_PORT` | `db.port` | Database port | 5432 |
102+
|`--db-user` | `DB_USER` | `db.user` | Database user | gorm |
103+
|`--db-password` | `DB_PASSWORD` | `db.password` | Database password | - |
104+
|`--db-name` | `DB_NAME` | `db.name` | Database name | gorm |
105+
|`--no-sync` | - | `db.no-sync` | Do not sync database | false |
106+
|`--sync-interval` | - | `db.sync-interval` | DB sync interval (in minutes) | 1 |
107+
|`--dynamodb-table` | `AWS_DYNAMODB_TABLE` | `aws.dynamodb-table` | AWS DynamoDB table for locks | - |
108+
|`--s3-bucket` | `AWS_BUCKET` | `aws.bucket` | AWS S3 bucket | - |
109+
|`--key-prefix` | `AWS_KEY_PREFIX` | `aws.key-prefix` | AWS Key Prefix | - |
110+
|`--file-extension` | `AWS_FILE_EXTENSION` | `aws.file-extension` | File extension of state files | .tfstate |
111+
|`--base-url` | `TERRABOARD_BASE_URL` | `web.base-url` | Base URL | / |
112+
|`--logout-url` | `TERRABOARD_LOGOUT_URL` | `web.logout-url` | Logout URL | - |
81113

82114
## Use with Docker
83115

@@ -111,6 +143,8 @@ Then point your browser to http://localhost:8080.
111143

112144
To use the included compose file, you will need to configure an [OAuth application](https://developer.github.com/apps/building-oauth-apps/).
113145

146+
Configuration file can be provided to the container using a [volume](https://docs.docker.com/compose/compose-file/#volumes) or a [configuration](https://docs.docker.com/compose/compose-file/#configs).
147+
114148
## Use with Rancher
115149

116150
[Camptocamp's Rancher Catalog](https://github.com/camptocamp/camptocamp-rancher-catalog)

config/config.go

+49-35
Original file line numberDiff line numberDiff line change
@@ -11,48 +11,62 @@ import (
1111
"gopkg.in/yaml.v2"
1212
)
1313

14+
// LogConfig stores the log configuration
15+
type LogConfig struct {
16+
Level string `short:"l" long:"log-level" env:"TERRABOARD_LOG_LEVEL" yaml:"level" description:"Set log level ('debug', 'info', 'warn', 'error', 'fatal', 'panic')." default:"info"`
17+
Format string `long:"log-format" yaml:"format" env:"TERRABOARD_LOG_FORMAT" description:"Set log format ('plain', 'json')." default:"plain"`
18+
}
19+
20+
// DBConfig stores the database configuration
21+
type DBConfig struct {
22+
Host string `long:"db-host" env:"DB_HOST" yaml:"host" description:"Database host." default:"db"`
23+
Port uint16 `long:"db-port" env:"DB_PORT" yaml:"port" description:"Database port." default:"5432"`
24+
User string `long:"db-user" env:"DB_USER" yaml:"user" description:"Database user." default:"gorm"`
25+
Password string `long:"db-password" env:"DB_PASSWORD" yaml:"password" description:"Database password."`
26+
Name string `long:"db-name" env:"DB_NAME" yaml:"name" description:"Database name." default:"gorm"`
27+
NoSync bool `long:"no-sync" yaml:"no-sync" description:"Do not sync database."`
28+
SyncInterval uint16 `long:"sync-interval" yaml:"sync-interval" description:"DB sync interval (in minutes)" default:"1"`
29+
}
30+
31+
// S3BucketConfig stores the S3 bucket configuration
32+
type S3BucketConfig struct {
33+
Bucket string `long:"s3-bucket" env:"AWS_BUCKET" yaml:"bucket" description:"AWS S3 bucket."`
34+
KeyPrefix string `long:"key-prefix" env:"AWS_KEY_PREFIX" yaml:"key-prefix" description:"AWS Key Prefix."`
35+
FileExtension string `long:"file-extension" env:"AWS_FILE_EXTENSION" yaml:"file-extension" description:"File extension of state files." default:".tfstate"`
36+
}
37+
38+
// AWSConfig stores the DynamoDB table and S3 Bucket configuration
39+
type AWSConfig struct {
40+
DynamoDBTable string `long:"dynamodb-table" env:"AWS_DYNAMODB_TABLE" yaml:"dynamodb-table" description:"AWS DynamoDB table for locks."`
41+
S3 S3BucketConfig `group:"S3 Options" yaml:"s3"`
42+
}
43+
44+
// WebConfig stores the UI interface parameters
45+
type WebConfig struct {
46+
Port uint16 `short:"p" long:"port" env:"TERRABOARD_PORT" yaml:"port" description:"Port to listen on." default:"8080"`
47+
BaseURL string `long:"base-url" env:"TERRABOARD_BASE_URL" yaml:"base-url" description:"Base URL." default:"/"`
48+
LogoutURL string `long:"logout-url" env:"TERRABOARD_LOGOUT_URL" yaml:"logout-url" description:"Logout URL."`
49+
}
50+
1451
// Config stores the handler's configuration and UI interface parameters
1552
type Config struct {
1653
Version bool `short:"V" long:"version" description:"Display version."`
1754

18-
Port int `short:"p" long:"port" yaml:"port" description:"Port to listen on." default:"8080"`
19-
20-
ConfigFilePath string `long:"config-file" env:"CONFIG_FILE" description:"Config File path" default:""`
21-
22-
Log struct {
23-
Level string `short:"l" long:"log-level" yaml:"level" description:"Set log level ('debug', 'info', 'warn', 'error', 'fatal', 'panic')." env:"TERRABOARD_LOG_LEVEL" default:"info"`
24-
Format string `long:"log-format" yaml:"format" description:"Set log format ('plain', 'json')." env:"TERRABOARD_LOG_FORMAT" default:"plain"`
25-
} `group:"Logging Options" yaml:"log"`
26-
27-
DB struct {
28-
Host string `long:"db-host" env:"DB_HOST" yaml:"host" description:"Database host." default:"db"`
29-
Port string `long:"db-port" env:"DB_PORT" yaml:"port" description:"Database port." default:"5432"`
30-
User string `long:"db-user" env:"DB_USER" yaml:"user" description:"Database user." default:"gorm"`
31-
Password string `long:"db-password" env:"DB_PASSWORD" yaml:"password" description:"Database password."`
32-
Name string `long:"db-name" env:"DB_NAME" yaml:"name" description:"Database name." default:"gorm"`
33-
NoSync bool `long:"no-sync" yaml:"no-sync" description:"Do not sync database."`
34-
} `group:"Database Options" yaml:"database"`
35-
36-
AWS struct {
37-
DynamoDBTable string `long:"dynamodb-table" env:"AWS_DYNAMODB_TABLE" yaml:"dynamodb-table" description:"AWS DynamoDB table for locks."`
38-
39-
S3 struct {
40-
Bucket string `long:"s3-bucket" env:"AWS_BUCKET" yaml:"bucket" description:"AWS S3 bucket."`
41-
KeyPrefix string `long:"key-prefix" env:"AWS_KEY_PREFIX" yaml:"key-prefix" description:"AWS Key Prefix."`
42-
FileExtension string `long:"file-extension" env:"AWS_FILE_EXTENSION" yaml:"file-extension" description:"File extension of state files." default:".tfstate"`
43-
} `group:"S3 Options" yaml:"s3"`
44-
} `group:"AWS Options" yaml:"aws"`
45-
46-
Web struct {
47-
BaseURL string `long:"base-url" env:"TERRABOARD_BASE_URL" yaml:"base-url" description:"Base URL." default:"/"`
48-
LogoutURL string `long:"logout-url" env:"TERRABOARD_LOGOUT_URL" yaml:"logout-url" description:"Logout URL."`
49-
} `group:"Web" yaml:"web"`
55+
ConfigFilePath string `short:"c" long:"config-file" env:"CONFIG_FILE" description:"Config File path"`
56+
57+
Log LogConfig `group:"Logging Options" yaml:"log"`
58+
59+
DB DBConfig `group:"Database Options" yaml:"database"`
60+
61+
AWS AWSConfig `group:"AWS Options" yaml:"aws"`
62+
63+
Web WebConfig `group:"Web" yaml:"web"`
5064
}
5165

5266
// LoadConfigFromYaml loads the config from config file
53-
func (c *Config) LoadConfigFromYaml(configFilePath string) *Config {
67+
func (c *Config) LoadConfigFromYaml() *Config {
5468
fmt.Printf("Loading config from %s\n", c.ConfigFilePath)
55-
yamlFile, err := ioutil.ReadFile(configFilePath)
69+
yamlFile, err := ioutil.ReadFile(c.ConfigFilePath)
5670
if err != nil {
5771
log.Printf("yamlFile.Get err #%v ", err)
5872
}
@@ -75,7 +89,7 @@ func LoadConfig(version string) *Config {
7589

7690
if c.ConfigFilePath != "" {
7791
if _, err := os.Stat(c.ConfigFilePath); err == nil {
78-
c.LoadConfigFromYaml(c.ConfigFilePath)
92+
c.LoadConfigFromYaml()
7993
} else {
8094
fmt.Printf("File %s doesn't exists!\n", c.ConfigFilePath)
8195
os.Exit(1)

config/config_test.go

+37-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,46 @@
11
package config
22

33
import (
4+
"testing"
5+
46
log "github.com/sirupsen/logrus"
57
)
68

7-
import "testing"
9+
func TestLoadConfigFromYaml(t *testing.T) {
10+
compareConfig := Config{
11+
Log: LogConfig{
12+
Level: "error",
13+
Format: "json",
14+
},
15+
ConfigFilePath: "config_test.yml",
16+
DB: DBConfig{
17+
Host: "postgres",
18+
Port: 15432,
19+
User: "terraboard-user",
20+
Password: "terraboard-pass",
21+
Name: "terraboard-db",
22+
NoSync: true,
23+
},
24+
AWS: AWSConfig{
25+
DynamoDBTable: "terraboard-dynamodb",
26+
S3: S3BucketConfig{
27+
Bucket: "terraboard-bucket",
28+
KeyPrefix: "test/",
29+
FileExtension: ".tfstate",
30+
},
31+
},
32+
Web: WebConfig{
33+
Port: 39090,
34+
BaseURL: "/test/",
35+
LogoutURL: "/test-logout",
36+
},
37+
}
38+
c := Config{ConfigFilePath: "config_test.yml"}
39+
c.LoadConfigFromYaml()
40+
if c != compareConfig {
41+
t.Fatalf("Expected: %v\nGot: %v", compareConfig, c)
42+
}
43+
}
844

945
func TestSetLogging_debug(t *testing.T) {
1046
c := Config{}

config/config_test.yml

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
log:
2+
level: error
3+
format: json
4+
5+
database:
6+
host: postgres
7+
port: 15432
8+
user: terraboard-user
9+
password: terraboard-pass
10+
name: terraboard-db
11+
no-sync: true
12+
13+
aws:
14+
dynamodb-table: terraboard-dynamodb
15+
s3:
16+
bucket: terraboard-bucket
17+
key-prefix: test/
18+
file-extension: .tfstate
19+
20+
web:
21+
port: 39090
22+
base-url: /test/
23+
logout-url: /test-logout

db/db.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"strings"
99

1010
"github.com/aws/aws-sdk-go/service/s3"
11+
"github.com/camptocamp/terraboard/config"
1112
"github.com/camptocamp/terraboard/types"
1213
"github.com/hashicorp/terraform/states"
1314
"github.com/hashicorp/terraform/states/statefile"
@@ -26,9 +27,9 @@ type Database struct {
2627
var pageSize = 20
2728

2829
// Init setups up the Database and a pointer to it
29-
func Init(host, port, user, dbname, password, logLevel string) *Database {
30+
func Init(config config.DBConfig, debug bool) *Database {
3031
var err error
31-
connString := fmt.Sprintf("host=%s port=%s user=%s dbname=%s sslmode=disable password=%s", host, port, user, dbname, password)
32+
connString := fmt.Sprintf("host=%s port=%d user=%s dbname=%s sslmode=disable password=%s", config.Host, config.Port, config.User, config.Name, config.Password)
3233
db, err := gorm.Open("postgres", connString)
3334
if err != nil {
3435
log.Fatal(err)
@@ -37,7 +38,7 @@ func Init(host, port, user, dbname, password, logLevel string) *Database {
3738
log.Infof("Automigrate")
3839
db.AutoMigrate(&types.Version{}, &types.State{}, &types.Module{}, &types.Resource{}, &types.Attribute{})
3940

40-
if logLevel == "debug" {
41+
if debug {
4142
db.LogMode(true)
4243
}
4344
return &Database{db}

example.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
port: 9090
2-
31
log:
42
level: info
53
format: plain
@@ -10,7 +8,8 @@ database:
108
user: terraboard
119
password: terraboard
1210
name: terraboard
13-
no-sync: true
11+
no-sync: false
12+
sync-interval: 5
1413

1514
aws:
1615
dynamodb-table: terraboard
@@ -20,5 +19,6 @@ aws:
2019
file-extension: .tfstate
2120

2221
web:
22+
port: 9090
2323
base-url: /terraboard/
2424
logout-url: /test

main.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,16 @@ func isKnownStateVersion(statesVersions map[string][]string, versionID, path str
5454

5555
// Refresh the DB from S3
5656
// This should be the only direct bridge between S3 and the DB
57-
func refreshDB(d *db.Database) {
57+
func refreshDB(syncInterval uint16, d *db.Database) {
58+
interval := time.Duration(syncInterval) * time.Minute
5859
for {
5960
log.Infof("Refreshing DB from S3")
6061
states, err := s3.GetStates()
6162
if err != nil {
6263
log.WithFields(log.Fields{
6364
"error": err,
6465
}).Error("Failed to retrieve states from S3. Retrying in 1 minute.")
65-
time.Sleep(1 * time.Minute)
66+
time.Sleep(interval)
6667
continue
6768
}
6869

@@ -105,7 +106,8 @@ func refreshDB(d *db.Database) {
105106
}
106107
}
107108

108-
time.Sleep(1 * time.Minute)
109+
log.Debugf("Waiting %d minutes until next DB sync", syncInterval)
110+
time.Sleep(interval)
109111
}
110112
}
111113

@@ -145,14 +147,11 @@ func main() {
145147
auth.Setup(c)
146148

147149
// Set up the DB and start S3->DB sync
148-
database := db.Init(
149-
c.DB.Host, c.DB.Port,
150-
c.DB.User, c.DB.Password,
151-
c.DB.Name, c.Log.Level)
150+
database := db.Init(c.DB, c.Log.Level == "debug")
152151
if c.DB.NoSync {
153152
log.Infof("Not syncing database, as requested.")
154153
} else {
155-
go refreshDB(database)
154+
go refreshDB(c.DB.SyncInterval, database)
156155
}
157156
defer database.Close()
158157

@@ -181,5 +180,6 @@ func main() {
181180
http.HandleFunc(util.GetFullPath("api/tf_versions"), handleWithDB(api.ListTfVersions, database))
182181

183182
// Start server
184-
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", c.Port), nil))
183+
log.Debugf("Listening on port %d\n", c.Web.Port)
184+
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", c.Web.Port), nil))
185185
}

0 commit comments

Comments
 (0)