Skip to content

Commit 279bba8

Browse files
mvisonneauraphink
authored andcommitted
Implemented support for Terraform Enterprise as state provider
1 parent 775b8cc commit 279bba8

File tree

10 files changed

+229
-51
lines changed

10 files changed

+229
-51
lines changed

api/api.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,9 @@ func StateCompare(w http.ResponseWriter, r *http.Request, d *db.Database) {
134134
}
135135

136136
// GetLocks returns information on locked States
137-
func GetLocks(w http.ResponseWriter, r *http.Request) {
137+
func GetLocks(w http.ResponseWriter, r *http.Request, sp state.Provider) {
138138
w.Header().Set("Access-Control-Allow-Origin", "*")
139-
locks, err := state.GetLocks()
139+
locks, err := sp.GetLocks()
140140
if err != nil {
141141
JSONError(w, "Failed to get locks", err)
142142
return

config/config.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ type AWSConfig struct {
4343

4444
// TFEConfig stores the Terraform Enterprise configuration
4545
type TFEConfig struct {
46-
Token string `long:"tfe-token" env:"TFE_TOKEN" yaml:"tfe-token" description:"Terraform Enterprise Token for state access"`
46+
Address string `long:"tfe-address" env:"TFE_ADDRESS" yaml:"tfe-address" description:"Terraform Enterprise address for states access"`
47+
Token string `long:"tfe-token" env:"TFE_TOKEN" yaml:"tfe-token" description:"Terraform Enterprise Token for states access"`
48+
Organization string `long:"tfe-organization" env:"TFE_ORGANIZATION" yaml:"tfe-organization" description:"Terraform Enterprise organization for states access"`
4749
}
4850

4951
// WebConfig stores the UI interface parameters

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.13
55
require (
66
github.com/aws/aws-sdk-go v1.26.6
77
github.com/denisenkom/go-mssqldb v0.0.0-20190820223206-44cdfe8d8ba9 // indirect
8+
github.com/hashicorp/go-tfe v0.3.27
89
github.com/hashicorp/hcl/v2 v2.2.0 // indirect
910
github.com/hashicorp/hil v0.0.0-20190212132231-97b3a9cdfa93 // indirect
1011
github.com/hashicorp/terraform v0.12.18

go.sum

+7
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
121121
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
122122
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
123123
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
124+
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
124125
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
125126
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
126127
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -145,6 +146,7 @@ github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv
145146
github.com/hashicorp/go-azure-helpers v0.10.0/go.mod h1:YuAtHxm2v74s+IjQwUG88dHBJPd5jL+cXr5BGVzSKhE=
146147
github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg=
147148
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
149+
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
148150
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
149151
github.com/hashicorp/go-getter v1.4.0/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY=
150152
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
@@ -155,11 +157,14 @@ github.com/hashicorp/go-multierror v0.0.0-20180717150148-3d5d8f294aa0/go.mod h1:
155157
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
156158
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
157159
github.com/hashicorp/go-plugin v1.0.1-0.20190610192547-a1bc61569a26/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
160+
github.com/hashicorp/go-retryablehttp v0.5.2 h1:AoISa4P4IsW0/m4T6St8Yw38gTl5GtBAgfkhYh1xAz4=
158161
github.com/hashicorp/go-retryablehttp v0.5.2/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
159162
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
160163
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
164+
github.com/hashicorp/go-slug v0.4.1 h1:/jAo8dNuLgSImoLXaX7Od7QB4TfYCVPam+OpAt5bZqc=
161165
github.com/hashicorp/go-slug v0.4.1/go.mod h1:I5tq5Lv0E2xcNXNkmx7BSfzi1PsJ2cNjs3cC3LwyhK8=
162166
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
167+
github.com/hashicorp/go-tfe v0.3.27 h1:7XZ/ZoPyYoeuNXaWWW0mJOq016y0qb7I4Q0P/cagyu8=
163168
github.com/hashicorp/go-tfe v0.3.27/go.mod h1:DVPSW2ogH+M9W1/i50ASgMht8cHP7NxxK0nrY9aFikQ=
164169
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
165170
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
@@ -314,6 +319,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
314319
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
315320
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
316321
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
322+
github.com/svanharmelen/jsonapi v0.0.0-20180618144545-0c0828c3f16d h1:Z4EH+5EffvBEhh37F0C0DnpklTMh00JOkjW5zK3ofBI=
317323
github.com/svanharmelen/jsonapi v0.0.0-20180618144545-0c0828c3f16d/go.mod h1:BSTlc8jOjh0niykqEGVXOLXdi9o0r0kR8tCYiMvjFgw=
318324
github.com/terraform-providers/terraform-provider-openstack v1.15.0/go.mod h1:2aQ6n/BtChAl1y2S60vebhyJyZXBsuAI5G4+lHrT1Ew=
319325
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@@ -416,6 +422,7 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
416422
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
417423
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
418424
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
425+
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
419426
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
420427
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
421428
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

main.go

+20-11
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ func handleWithDB(apiF func(w http.ResponseWriter, r *http.Request, d *db.Databa
4141
})
4242
}
4343

44+
func handleWithStateProvider(apiF func(w http.ResponseWriter, r *http.Request, sp state.Provider), sp state.Provider) func(http.ResponseWriter, *http.Request) {
45+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
46+
apiF(w, r, sp)
47+
})
48+
}
49+
4450
func isKnownStateVersion(statesVersions map[string][]string, versionID, path string) bool {
4551
if v, ok := statesVersions[versionID]; ok {
4652
for _, s := range v {
@@ -52,24 +58,24 @@ func isKnownStateVersion(statesVersions map[string][]string, versionID, path str
5258
return false
5359
}
5460

55-
// Refresh the DB from S3
56-
// This should be the only direct bridge between S3 and the DB
57-
func refreshDB(syncInterval uint16, d *db.Database) {
61+
// Refresh the DB
62+
// This should be the only direct bridge between the state provider and the DB
63+
func refreshDB(syncInterval uint16, d *db.Database, sp state.Provider) {
5864
interval := time.Duration(syncInterval) * time.Minute
5965
for {
60-
log.Infof("Refreshing DB from S3")
61-
states, err := state.GetStates()
66+
log.Infof("Refreshing DB")
67+
states, err := sp.GetStates()
6268
if err != nil {
6369
log.WithFields(log.Fields{
6470
"error": err,
65-
}).Error("Failed to retrieve states from S3. Retrying in 1 minute.")
71+
}).Error("Failed to retrieve states. Retrying in 1 minute.")
6672
time.Sleep(interval)
6773
continue
6874
}
6975

7076
statesVersions := d.ListStatesVersions()
7177
for _, st := range states {
72-
versions, _ := state.GetVersions(st)
78+
versions, _ := sp.GetVersions(st)
7379
for _, v := range versions {
7480
if _, ok := statesVersions[v.ID]; ok {
7581
log.WithFields(log.Fields{
@@ -86,7 +92,7 @@ func refreshDB(syncInterval uint16, d *db.Database) {
8692
}).Debug("State is already in the database, skipping")
8793
continue
8894
}
89-
state, err := state.GetState(st, v.ID)
95+
state, err := sp.GetState(st, v.ID)
9096
if err != nil {
9197
log.WithFields(log.Fields{
9298
"path": st,
@@ -141,7 +147,10 @@ func main() {
141147
}
142148

143149
// Set up the state provider
144-
state.Configure(c)
150+
sp, err := state.Configure(c)
151+
if err != nil {
152+
log.Fatal(err)
153+
}
145154

146155
// Set up auth
147156
auth.Setup(c)
@@ -151,7 +160,7 @@ func main() {
151160
if c.DB.NoSync {
152161
log.Infof("Not syncing database, as requested.")
153162
} else {
154-
go refreshDB(c.DB.SyncInterval, database)
163+
go refreshDB(c.DB.SyncInterval, database, sp)
155164
}
156165
defer database.Close()
157166

@@ -171,7 +180,7 @@ func main() {
171180
http.HandleFunc(util.GetFullPath("api/state/"), handleWithDB(api.GetState, database))
172181
http.HandleFunc(util.GetFullPath("api/state/activity/"), handleWithDB(api.GetStateActivity, database))
173182
http.HandleFunc(util.GetFullPath("api/state/compare/"), handleWithDB(api.StateCompare, database))
174-
http.HandleFunc(util.GetFullPath("api/locks"), api.GetLocks)
183+
http.HandleFunc(util.GetFullPath("api/locks"), handleWithStateProvider(api.GetLocks, sp))
175184
http.HandleFunc(util.GetFullPath("api/search/attribute"), handleWithDB(api.SearchAttribute, database))
176185
http.HandleFunc(util.GetFullPath("api/resource/types"), handleWithDB(api.ListResourceTypes, database))
177186
http.HandleFunc(util.GetFullPath("api/resource/types/count"), handleWithDB(api.ListResourceTypesWithCount, database))

state/aws.go

+12-12
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,25 @@ import (
1818

1919
// AWS is a state provider type, leveraging S3 and DynamoDB
2020
type AWS struct {
21-
svc *s3.S3
22-
dynamoSvc *dynamodb.DynamoDB
23-
bucket string
24-
dynamoTable string
25-
keyPrefix string
21+
svc *s3.S3
22+
dynamoSvc *dynamodb.DynamoDB
23+
bucket string
24+
dynamoTable string
25+
keyPrefix string
2626
fileExtension string
2727
}
2828

2929
// NewAWS creates an AWS object
3030
func NewAWS(c *config.Config) AWS {
3131
sess := session.Must(session.NewSession())
32-
32+
3333
return AWS{
34-
svc: s3.New(sess, &aws_sdk.Config{}),
35-
bucket: c.AWS.S3.Bucket,
36-
keyPrefix: c.AWS.S3.KeyPrefix,
34+
svc: s3.New(sess, &aws_sdk.Config{}),
35+
bucket: c.AWS.S3.Bucket,
36+
keyPrefix: c.AWS.S3.KeyPrefix,
3737
fileExtension: c.AWS.S3.FileExtension,
38-
dynamoSvc: dynamodb.New(sess, &aws_sdk.Config{}),
39-
dynamoTable: c.AWS.DynamoDBTable,
38+
dynamoSvc: dynamodb.New(sess, &aws_sdk.Config{}),
39+
dynamoTable: c.AWS.DynamoDBTable,
4040
}
4141
}
4242

@@ -146,7 +146,7 @@ func (a *AWS) GetVersions(state string) (versions []Version, err error) {
146146

147147
for _, v := range result.Versions {
148148
versions = append(versions, Version{
149-
ID: *v.VersionId,
149+
ID: *v.VersionId,
150150
LastModified: *v.LastModified,
151151
})
152152
}

state/state.go

+12-24
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ type Lock struct {
2727

2828
// Version is a handler for state versions
2929
type Version struct {
30-
ID string
30+
ID string
3131
LastModified time.Time
3232
}
3333

@@ -39,30 +39,18 @@ type Provider interface {
3939
GetState(string, string) (*statefile.File, error)
4040
}
4141

42-
// Provider is a handler for the configured one
43-
var p Provider
42+
// Configure the state provider
43+
func Configure(c *config.Config) (Provider, error) {
44+
if len(c.TFE.Token) > 0 {
45+
log.Info("Using Terraform Enterprise as the state/locks provider")
46+
provider, err := NewTFE(c)
47+
if err != nil {
48+
return nil, err
49+
}
50+
return &provider, nil
51+
}
4452

45-
// Configure returns the configured provider
46-
func Configure(c *config.Config) {
4753
log.Info("Using AWS (S3+DynamoDB) as the state/locks provider")
4854
provider := NewAWS(c)
49-
p = &provider
50-
}
51-
52-
// Functions wrappers
53-
//
54-
func GetLocks() (map[string]LockInfo, error) {
55-
return p.GetLocks()
56-
}
57-
58-
func GetVersions(state string) ([]Version, error) {
59-
return p.GetVersions(state)
60-
}
61-
62-
func GetStates() ([]string, error) {
63-
return p.GetStates()
64-
}
65-
66-
func GetState(state, versionID string) (*statefile.File, error) {
67-
return p.GetState(state, versionID)
55+
return &provider, nil
6856
}

state/state_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
package state
22

3-
// TODO: write!
3+
// TODO: write!

0 commit comments

Comments
 (0)