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