github.com/hooklift/terraform@v0.11.0-beta1.0.20171117000744-6786c1361ffe/state/remote/gcs.go (about) 1 package remote 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "log" 9 "net/http" 10 "os" 11 "runtime" 12 "strings" 13 14 "github.com/hashicorp/terraform/helper/pathorcontents" 15 "golang.org/x/net/context" 16 "golang.org/x/oauth2" 17 "golang.org/x/oauth2/google" 18 "golang.org/x/oauth2/jwt" 19 "google.golang.org/api/googleapi" 20 "google.golang.org/api/storage/v1" 21 22 version "github.com/hashicorp/terraform/version" 23 ) 24 25 // accountFile represents the structure of the credentials JSON 26 type accountFile struct { 27 PrivateKeyId string `json:"private_key_id"` 28 PrivateKey string `json:"private_key"` 29 ClientEmail string `json:"client_email"` 30 ClientId string `json:"client_id"` 31 } 32 33 func parseJSON(result interface{}, contents string) error { 34 r := strings.NewReader(contents) 35 dec := json.NewDecoder(r) 36 37 return dec.Decode(result) 38 } 39 40 type GCSClient struct { 41 bucket string 42 path string 43 clientStorage *storage.Service 44 context context.Context 45 } 46 47 func gcsFactory(conf map[string]string) (Client, error) { 48 var account accountFile 49 var client *http.Client 50 clientScopes := []string{ 51 "https://www.googleapis.com/auth/devstorage.full_control", 52 } 53 54 bucketName, ok := conf["bucket"] 55 if !ok { 56 return nil, fmt.Errorf("missing 'bucket' configuration") 57 } 58 59 pathName, ok := conf["path"] 60 if !ok { 61 return nil, fmt.Errorf("missing 'path' configuration") 62 } 63 64 credentials, ok := conf["credentials"] 65 if !ok { 66 credentials = os.Getenv("GOOGLE_CREDENTIALS") 67 } 68 69 if credentials != "" { 70 contents, _, err := pathorcontents.Read(credentials) 71 if err != nil { 72 return nil, fmt.Errorf("Error loading credentials: %s", err) 73 } 74 75 // Assume account_file is a JSON string 76 if err := parseJSON(&account, contents); err != nil { 77 return nil, fmt.Errorf("Error parsing credentials '%s': %s", contents, err) 78 } 79 80 // Get the token for use in our requests 81 log.Printf("[INFO] Requesting Google token...") 82 log.Printf("[INFO] -- Email: %s", account.ClientEmail) 83 log.Printf("[INFO] -- Scopes: %s", clientScopes) 84 log.Printf("[INFO] -- Private Key Length: %d", len(account.PrivateKey)) 85 86 conf := jwt.Config{ 87 Email: account.ClientEmail, 88 PrivateKey: []byte(account.PrivateKey), 89 Scopes: clientScopes, 90 TokenURL: "https://accounts.google.com/o/oauth2/token", 91 } 92 93 client = conf.Client(oauth2.NoContext) 94 95 } else { 96 log.Printf("[INFO] Authenticating using DefaultClient") 97 err := error(nil) 98 client, err = google.DefaultClient(oauth2.NoContext, clientScopes...) 99 if err != nil { 100 return nil, err 101 } 102 } 103 versionString := version.Version 104 userAgent := fmt.Sprintf( 105 "(%s %s) Terraform/%s", runtime.GOOS, runtime.GOARCH, versionString) 106 107 log.Printf("[INFO] Instantiating Google Storage Client...") 108 clientStorage, err := storage.New(client) 109 if err != nil { 110 return nil, err 111 } 112 clientStorage.UserAgent = userAgent 113 114 return &GCSClient{ 115 clientStorage: clientStorage, 116 bucket: bucketName, 117 path: pathName, 118 }, nil 119 120 } 121 122 func (c *GCSClient) Get() (*Payload, error) { 123 // Read the object from bucket. 124 log.Printf("[INFO] Reading %s/%s", c.bucket, c.path) 125 126 resp, err := c.clientStorage.Objects.Get(c.bucket, c.path).Download() 127 if err != nil { 128 if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { 129 log.Printf("[INFO] %s/%s not found", c.bucket, c.path) 130 131 return nil, nil 132 } 133 134 return nil, fmt.Errorf("[WARN] Error retrieving object %s/%s: %s", c.bucket, c.path, err) 135 } 136 defer resp.Body.Close() 137 138 var buf []byte 139 w := bytes.NewBuffer(buf) 140 n, err := io.Copy(w, resp.Body) 141 if err != nil { 142 log.Fatalf("[WARN] error buffering %q: %v", c.path, err) 143 } 144 log.Printf("[INFO] Downloaded %d bytes", n) 145 146 payload := &Payload{ 147 Data: w.Bytes(), 148 } 149 150 // If there was no data, then return nil 151 if len(payload.Data) == 0 { 152 return nil, nil 153 } 154 155 return payload, nil 156 } 157 158 func (c *GCSClient) Put(data []byte) error { 159 log.Printf("[INFO] Writing %s/%s", c.bucket, c.path) 160 161 r := bytes.NewReader(data) 162 _, err := c.clientStorage.Objects.Insert(c.bucket, &storage.Object{Name: c.path}).Media(r).Do() 163 if err != nil { 164 return err 165 } 166 167 return nil 168 } 169 170 func (c *GCSClient) Delete() error { 171 log.Printf("[INFO] Deleting %s/%s", c.bucket, c.path) 172 173 err := c.clientStorage.Objects.Delete(c.bucket, c.path).Do() 174 return err 175 176 }