1package backends
2
3import (
4	"context"
5	"fmt"
6	"io/ioutil"
7	"os"
8
9	"github.com/araddon/gou"
10	"github.com/cloudfoundry/bbl-state-resource/storage"
11	"github.com/lytics/cloudstorage"
12	"github.com/lytics/cloudstorage/awss3"
13	"github.com/mholt/archiver"
14)
15
16type Config struct {
17	AWSAccessKeyID       string
18	AWSSecretAccessKey   string
19	GCPServiceAccountKey string
20	Bucket               string
21	Region               string
22	Dest                 string
23}
24
25type Provider interface {
26	Client(string) (Backend, error)
27}
28
29func NewProvider() Provider {
30	return provider{}
31}
32
33type provider struct{}
34
35func (p provider) Client(iaas string) (Backend, error) {
36	switch iaas {
37	case "aws":
38		return cloudStorageBackend{}, nil
39	case "gcp":
40		return gcsStateBackend{}, nil
41	default:
42		return nil, fmt.Errorf("remote state storage is unsupported for %s environments", iaas)
43	}
44}
45
46type Backend interface {
47	GetState(Config, string) error
48}
49
50type cloudStorageBackend struct{}
51
52func (c cloudStorageBackend) GetState(config Config, name string) error {
53	awsAuthSettings := make(gou.JsonHelper)
54	awsAuthSettings[awss3.ConfKeyAccessKey] = config.AWSAccessKeyID
55	awsAuthSettings[awss3.ConfKeyAccessSecret] = config.AWSSecretAccessKey
56
57	csConfig := cloudstorage.Config{
58		Type:       awss3.StoreType,
59		AuthMethod: awss3.AuthAccessKey,
60		Bucket:     config.Bucket,
61		Settings:   awsAuthSettings,
62		Region:     config.Region,
63	}
64
65	store, err := cloudstorage.NewStore(&csConfig)
66	if err != nil {
67		return err
68	}
69
70	tarball, err := store.Get(context.Background(), name)
71	if err != nil {
72		return err
73	}
74
75	stateTar, err := tarball.Open(cloudstorage.ReadOnly)
76	if err != nil {
77		return err
78	}
79
80	err = archiver.TarGz.Read(stateTar, config.Dest)
81	if err != nil {
82		return fmt.Errorf("unable to untar state dir: %s", err)
83	}
84
85	return nil
86}
87
88type gcsStateBackend struct{}
89
90func (g gcsStateBackend) GetState(config Config, name string) error {
91	key, err := g.getGCPServiceAccountKey(config.GCPServiceAccountKey)
92	if err != nil {
93		return fmt.Errorf("could not read GCP service account key: %s", err)
94	}
95
96	gcsClient, err := storage.NewStorageClient(key, name, config.Bucket)
97	if err != nil {
98		return fmt.Errorf("could not create GCS client: %s", err)
99	}
100
101	_, err = gcsClient.Download(config.Dest)
102	if err != nil {
103		return fmt.Errorf("downloading remote state from GCS: %s", err)
104	}
105
106	return nil
107}
108
109func (g gcsStateBackend) getGCPServiceAccountKey(key string) (string, error) {
110	if _, err := os.Stat(key); err != nil {
111		return key, nil
112	}
113
114	keyBytes, err := ioutil.ReadFile(key)
115	if err != nil {
116		return "", fmt.Errorf("Reading key: %v", err)
117	}
118
119	return string(keyBytes), nil
120}
121