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