diff --git a/Dockerfile b/Dockerfile index 91c63e4..69a0ec9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ COPY . /go/src/github.com/nimbolus/terraform-backend WORKDIR /go/src/github.com/nimbolus/terraform-backend -RUN GOOS=linux CGO_ENABLED=0 go build -o terraform-backend +RUN GOOS=linux CGO_ENABLED=0 go build cmd/terraform-backend.go # start clean for final image FROM alpine:3 diff --git a/redisclient/redisclient.go b/client/redis/redis.go similarity index 90% rename from redisclient/redisclient.go rename to client/redis/redis.go index ed39e5b..7287d0f 100644 --- a/redisclient/redisclient.go +++ b/client/redis/redis.go @@ -1,4 +1,4 @@ -package redisclient +package redis import ( redis "github.com/go-redis/redis/v8" diff --git a/vaultclient/vaultclient.go b/client/vault/vautl.go similarity index 98% rename from vaultclient/vaultclient.go rename to client/vault/vautl.go index b21dc3f..3abd5ba 100644 --- a/vaultclient/vaultclient.go +++ b/client/vault/vautl.go @@ -1,4 +1,4 @@ -package vaultclient +package vault import ( "fmt" diff --git a/cmd/terraform-backend.go b/cmd/terraform-backend.go index 3f2ae05..e93185e 100644 --- a/cmd/terraform-backend.go +++ b/cmd/terraform-backend.go @@ -10,7 +10,7 @@ import ( "github.com/nimbolus/terraform-backend/terraform" "github.com/nimbolus/terraform-backend/terraform/auth" "github.com/nimbolus/terraform-backend/terraform/lock" - "github.com/nimbolus/terraform-backend/terraform/store" + "github.com/nimbolus/terraform-backend/terraform/storage" log "github.com/sirupsen/logrus" "github.com/spf13/viper" ) @@ -21,7 +21,7 @@ func httpResponse(w http.ResponseWriter, code int, body string) { fmt.Fprint(w, body) } -func stateHandler(stateStore store.Store, locker lock.Locker, kms kms.KMS) func(http.ResponseWriter, *http.Request) { +func stateHandler(store storage.Storage, locker lock.Locker, kms kms.KMS) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, req *http.Request) { body, err := io.ReadAll(req.Body) defer req.Body.Close() @@ -82,7 +82,7 @@ func stateHandler(stateStore store.Store, locker lock.Locker, kms kms.KMS) func( case http.MethodGet: log.Debugf("get state with id %s", state.ID) stateID := state.ID - state, err = stateStore.GetState(state.ID) + state, err = store.GetState(state.ID) if err != nil { log.Warnf("failed to get state with id %s: %v", stateID, err) httpResponse(w, http.StatusBadRequest, err.Error()) @@ -110,7 +110,7 @@ func stateHandler(stateStore store.Store, locker lock.Locker, kms kms.KMS) func( return } - err := stateStore.SaveState(state) + err := store.SaveState(state) if err != nil { log.Warnf("failed to save state with id %s: %v", state.ID, err) httpResponse(w, http.StatusBadRequest, err.Error()) @@ -143,11 +143,11 @@ func main() { log.Infof("set log level to %s", level.String()) log.SetLevel(level) - stateStore, err := store.GetStore() + store, err := storage.GetStorage() if err != nil { log.Fatal(err.Error()) } - log.Infof("initialized %s store backend", stateStore.GetName()) + log.Infof("initialized %s storage backend", store.GetName()) locker, err := lock.GetLocker() if err != nil { @@ -164,6 +164,6 @@ func main() { addr := viper.GetString("listen_addr") log.Printf("listening on %s", addr) r := mux.NewRouter().StrictSlash(true) - r.HandleFunc("/state/{project}/{id}", stateHandler(stateStore, locker, kms)) + r.HandleFunc("/state/{project}/{id}", stateHandler(store, locker, kms)) log.Fatalf("failed to listen on %s: %v", addr, http.ListenAndServe(addr, r)) } diff --git a/kms/kms.go b/kms/kms.go index 4f55fc2..96df64f 100644 --- a/kms/kms.go +++ b/kms/kms.go @@ -3,9 +3,9 @@ package kms import ( "fmt" + vaultclient "github.com/nimbolus/terraform-backend/client/vault" "github.com/nimbolus/terraform-backend/kms/local" "github.com/nimbolus/terraform-backend/kms/transit" - "github.com/nimbolus/terraform-backend/vaultclient" "github.com/spf13/viper" ) diff --git a/kms/transit/transit.go b/kms/transit/transit.go index cdd9d4e..917ff8c 100644 --- a/kms/transit/transit.go +++ b/kms/transit/transit.go @@ -5,7 +5,7 @@ import ( "fmt" vault "github.com/hashicorp/vault/api" - "github.com/nimbolus/terraform-backend/vaultclient" + vaultclient "github.com/nimbolus/terraform-backend/client/vault" ) type VaultTransit struct { diff --git a/terraform/lock/redis/redis.go b/terraform/lock/redis/redis.go index 4bf3b03..0bd9def 100644 --- a/terraform/lock/redis/redis.go +++ b/terraform/lock/redis/redis.go @@ -9,7 +9,7 @@ import ( "github.com/go-redsync/redsync/v4" "github.com/go-redsync/redsync/v4/redis" goredis "github.com/go-redsync/redsync/v4/redis/goredis/v8" - "github.com/nimbolus/terraform-backend/redisclient" + redisclient "github.com/nimbolus/terraform-backend/client/redis" "github.com/nimbolus/terraform-backend/terraform" ) diff --git a/terraform/store/file/file.go b/terraform/storage/filesystem/filesystem.go similarity index 66% rename from terraform/store/file/file.go rename to terraform/storage/filesystem/filesystem.go index b92095a..e849c2e 100644 --- a/terraform/store/file/file.go +++ b/terraform/storage/filesystem/filesystem.go @@ -1,4 +1,4 @@ -package file +package filesystem import ( "errors" @@ -8,30 +8,30 @@ import ( "github.com/nimbolus/terraform-backend/terraform" ) -type FileStore struct { +type FileSystemStorage struct { directory string } -func NewFileStore(directory string) (*FileStore, error) { +func NewFileSystemStorage(directory string) (*FileSystemStorage, error) { err := os.MkdirAll(directory, 0700) if err != nil { return nil, fmt.Errorf("failed to create directory %s: %v", directory, err) } - return &FileStore{ + return &FileSystemStorage{ directory: directory, }, nil } -func (f *FileStore) GetName() string { +func (f *FileSystemStorage) GetName() string { return "file" } -func (f *FileStore) SaveState(s *terraform.State) error { +func (f *FileSystemStorage) SaveState(s *terraform.State) error { return os.WriteFile(fmt.Sprintf("%s/%s.tfstate", f.directory, s.ID), s.Data, 0600) } -func (f *FileStore) GetState(id string) (*terraform.State, error) { +func (f *FileSystemStorage) GetState(id string) (*terraform.State, error) { if _, err := os.Stat(f.getFileName(id)); errors.Is(err, os.ErrNotExist) { f, err := os.Create(f.getFileName(id)) if err != nil { @@ -52,6 +52,6 @@ func (f *FileStore) GetState(id string) (*terraform.State, error) { }, nil } -func (f *FileStore) getFileName(id string) string { +func (f *FileSystemStorage) getFileName(id string) string { return fmt.Sprintf("%s/%s.tfstate", f.directory, id) } diff --git a/terraform/store/s3/s3.go b/terraform/storage/s3/s3.go similarity index 82% rename from terraform/store/s3/s3.go rename to terraform/storage/s3/s3.go index 1944ded..949e961 100644 --- a/terraform/store/s3/s3.go +++ b/terraform/storage/s3/s3.go @@ -10,12 +10,12 @@ import ( "github.com/nimbolus/terraform-backend/terraform" ) -type S3Store struct { +type S3Storage struct { client *minio.Client bucket string } -func NewS3Store(endpoint, bucket, accessKey, secretKey string, useSSL bool) (*S3Store, error) { +func NewS3Storage(endpoint, bucket, accessKey, secretKey string, useSSL bool) (*S3Storage, error) { client, err := minio.New(endpoint, &minio.Options{ Creds: credentials.NewStaticV4(accessKey, secretKey, ""), Secure: useSSL, @@ -30,17 +30,17 @@ func NewS3Store(endpoint, bucket, accessKey, secretKey string, useSSL bool) (*S3 return nil, fmt.Errorf("bucket does not exist") } - return &S3Store{ + return &S3Storage{ client: client, bucket: bucket, }, nil } -func (s *S3Store) GetName() string { +func (s *S3Storage) GetName() string { return "s3" } -func (s *S3Store) SaveState(state *terraform.State) error { +func (s *S3Storage) SaveState(state *terraform.State) error { r := bytes.NewReader(state.Data) _, err := s.client.PutObject(context.Background(), s.bucket, getObjectName(state.ID), r, r.Size(), minio.PutObjectOptions{ ContentType: "application/octet-stream", @@ -48,7 +48,7 @@ func (s *S3Store) SaveState(state *terraform.State) error { return err } -func (s *S3Store) GetState(id string) (*terraform.State, error) { +func (s *S3Storage) GetState(id string) (*terraform.State, error) { state := &terraform.State{ ID: id, } diff --git a/terraform/storage/storage.go b/terraform/storage/storage.go new file mode 100644 index 0000000..fcc87b1 --- /dev/null +++ b/terraform/storage/storage.go @@ -0,0 +1,47 @@ +package storage + +import ( + "fmt" + + "github.com/nimbolus/terraform-backend/terraform" + "github.com/nimbolus/terraform-backend/terraform/storage/filesystem" + "github.com/nimbolus/terraform-backend/terraform/storage/s3" + "github.com/spf13/viper" +) + +type Storage interface { + GetName() string + SaveState(s *terraform.State) error + GetState(id string) (*terraform.State, error) +} + +func GetStorage() (s Storage, err error) { + viper.SetDefault("storage_backend", "fs") + backend := viper.GetString("storage_backend") + + switch backend { + case "fs": + viper.SetDefault("storage_fs_dir", "./states") + s, err = filesystem.NewFileSystemStorage(viper.GetString("storage_fs_dir")) + case "s3": + viper.SetDefault("storage_s3_endpoint", "s3.amazonaws.com") + viper.SetDefault("storage_s3_use_ssl", true) + viper.SetDefault("storage_s3_access_key", "access-key-id") + viper.SetDefault("storage_s3_secret_key", "secret-access-key") + viper.SetDefault("storage_s3_bucket", "terraform-state") + + endpoint := viper.GetString("storage_s3_endpoint") + useSSL := viper.GetBool("storage_s3_use_ssl") + accessKey := viper.GetString("storage_s3_access_key") + secretKey := viper.GetString("storage_s3_secret_key") + bucket := viper.GetString("storage_s3_bucket") + + s, err = s3.NewS3Storage(endpoint, bucket, accessKey, secretKey, useSSL) + default: + err = fmt.Errorf("backend is not implemented") + } + if err != nil { + return nil, fmt.Errorf("failed to initialize storage backend %s: %v", backend, err) + } + return +} diff --git a/terraform/store/store.go b/terraform/store/store.go deleted file mode 100644 index c932a84..0000000 --- a/terraform/store/store.go +++ /dev/null @@ -1,47 +0,0 @@ -package store - -import ( - "fmt" - - "github.com/nimbolus/terraform-backend/terraform" - "github.com/nimbolus/terraform-backend/terraform/store/file" - "github.com/nimbolus/terraform-backend/terraform/store/s3" - "github.com/spf13/viper" -) - -type Store interface { - GetName() string - SaveState(s *terraform.State) error - GetState(id string) (*terraform.State, error) -} - -func GetStore() (s Store, err error) { - viper.SetDefault("store_backend", "file") - backend := viper.GetString("store_backend") - - switch backend { - case "file": - viper.SetDefault("store_local_dir", "./states") - s, err = file.NewFileStore(viper.GetString("store_local_dir")) - case "s3": - viper.SetDefault("store_s3_endpoint", "s3.amazonaws.com") - viper.SetDefault("store_s3_use_ssl", true) - viper.SetDefault("store_s3_access_key", "access-key-id") - viper.SetDefault("store_s3_secret_key", "secret-access-key") - viper.SetDefault("store_s3_bucket", "terraform-state") - - endpoint := viper.GetString("store_s3_endpoint") - useSSL := viper.GetBool("store_s3_use_ssl") - accessKey := viper.GetString("store_s3_access_key") - secretKey := viper.GetString("store_s3_secret_key") - bucket := viper.GetString("store_s3_bucket") - - s, err = s3.NewS3Store(endpoint, bucket, accessKey, secretKey, useSSL) - default: - err = fmt.Errorf("backend is not implemented") - } - if err != nil { - return nil, fmt.Errorf("failed to initialize store backend %s: %v", backend, err) - } - return -}