github.com/ns1/terraform@v0.7.10-0.20161109153551-8949419bef40/state/remote/swift.go (about) 1 package remote 2 3 import ( 4 "bytes" 5 "crypto/md5" 6 "crypto/tls" 7 "crypto/x509" 8 "fmt" 9 "io/ioutil" 10 "log" 11 "net/http" 12 "os" 13 "strconv" 14 15 "github.com/gophercloud/gophercloud" 16 "github.com/gophercloud/gophercloud/openstack" 17 "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers" 18 "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects" 19 ) 20 21 const TFSTATE_NAME = "tfstate.tf" 22 23 // SwiftClient implements the Client interface for an Openstack Swift server. 24 type SwiftClient struct { 25 client *gophercloud.ServiceClient 26 authurl string 27 cacert string 28 cert string 29 domainid string 30 domainname string 31 insecure bool 32 key string 33 password string 34 path string 35 region string 36 tenantid string 37 tenantname string 38 userid string 39 username string 40 } 41 42 func swiftFactory(conf map[string]string) (Client, error) { 43 client := &SwiftClient{} 44 45 if err := client.validateConfig(conf); err != nil { 46 return nil, err 47 } 48 49 return client, nil 50 } 51 52 func (c *SwiftClient) validateConfig(conf map[string]string) (err error) { 53 authUrl, ok := conf["auth_url"] 54 if !ok { 55 authUrl = os.Getenv("OS_AUTH_URL") 56 if authUrl == "" { 57 return fmt.Errorf("missing 'auth_url' configuration or OS_AUTH_URL environment variable") 58 } 59 } 60 c.authurl = authUrl 61 62 username, ok := conf["user_name"] 63 if !ok { 64 username = os.Getenv("OS_USERNAME") 65 } 66 c.username = username 67 68 userID, ok := conf["user_id"] 69 if !ok { 70 userID = os.Getenv("OS_USER_ID") 71 } 72 c.userid = userID 73 74 password, ok := conf["password"] 75 if !ok { 76 password = os.Getenv("OS_PASSWORD") 77 if password == "" { 78 return fmt.Errorf("missing 'password' configuration or OS_PASSWORD environment variable") 79 } 80 } 81 c.password = password 82 83 region, ok := conf["region_name"] 84 if !ok { 85 region = os.Getenv("OS_REGION_NAME") 86 } 87 c.region = region 88 89 tenantID, ok := conf["tenant_id"] 90 if !ok { 91 tenantID = multiEnv([]string{ 92 "OS_TENANT_ID", 93 "OS_PROJECT_ID", 94 }) 95 } 96 c.tenantid = tenantID 97 98 tenantName, ok := conf["tenant_name"] 99 if !ok { 100 tenantName = multiEnv([]string{ 101 "OS_TENANT_NAME", 102 "OS_PROJECT_NAME", 103 }) 104 } 105 c.tenantname = tenantName 106 107 domainID, ok := conf["domain_id"] 108 if !ok { 109 domainID = multiEnv([]string{ 110 "OS_USER_DOMAIN_ID", 111 "OS_PROJECT_DOMAIN_ID", 112 "OS_DOMAIN_ID", 113 }) 114 } 115 c.domainid = domainID 116 117 domainName, ok := conf["domain_name"] 118 if !ok { 119 domainName = multiEnv([]string{ 120 "OS_USER_DOMAIN_NAME", 121 "OS_PROJECT_DOMAIN_NAME", 122 "OS_DOMAIN_NAME", 123 "DEFAULT_DOMAIN", 124 }) 125 } 126 c.domainname = domainName 127 128 path, ok := conf["path"] 129 if !ok || path == "" { 130 return fmt.Errorf("missing 'path' configuration") 131 } 132 c.path = path 133 134 c.insecure = false 135 raw, ok := conf["insecure"] 136 if !ok { 137 raw = os.Getenv("OS_INSECURE") 138 } 139 if raw != "" { 140 v, err := strconv.ParseBool(raw) 141 if err != nil { 142 return fmt.Errorf("'insecure' and 'OS_INSECURE' could not be parsed as bool: %s", err) 143 } 144 c.insecure = v 145 } 146 147 cacertFile, ok := conf["cacert_file"] 148 if !ok { 149 cacertFile = os.Getenv("OS_CACERT") 150 } 151 c.cacert = cacertFile 152 153 cert, ok := conf["cert"] 154 if !ok { 155 cert = os.Getenv("OS_CERT") 156 } 157 c.cert = cert 158 159 key, ok := conf["key"] 160 if !ok { 161 key = os.Getenv("OS_KEY") 162 } 163 c.key = key 164 165 ao := gophercloud.AuthOptions{ 166 IdentityEndpoint: c.authurl, 167 UserID: c.userid, 168 Username: c.username, 169 TenantID: c.tenantid, 170 TenantName: c.tenantname, 171 Password: c.password, 172 DomainID: c.domainid, 173 DomainName: c.domainname, 174 } 175 176 provider, err := openstack.NewClient(ao.IdentityEndpoint) 177 if err != nil { 178 return err 179 } 180 181 config := &tls.Config{} 182 183 if c.cacert != "" { 184 caCert, err := ioutil.ReadFile(c.cacert) 185 if err != nil { 186 return err 187 } 188 189 caCertPool := x509.NewCertPool() 190 caCertPool.AppendCertsFromPEM(caCert) 191 config.RootCAs = caCertPool 192 } 193 194 if c.insecure { 195 log.Printf("[DEBUG] Insecure mode set") 196 config.InsecureSkipVerify = true 197 } 198 199 if c.cert != "" && c.key != "" { 200 cert, err := tls.LoadX509KeyPair(c.cert, c.key) 201 if err != nil { 202 return err 203 } 204 205 config.Certificates = []tls.Certificate{cert} 206 config.BuildNameToCertificate() 207 } 208 209 transport := &http.Transport{Proxy: http.ProxyFromEnvironment, TLSClientConfig: config} 210 provider.HTTPClient.Transport = transport 211 212 err = openstack.Authenticate(provider, ao) 213 if err != nil { 214 return err 215 } 216 217 c.client, err = openstack.NewObjectStorageV1(provider, gophercloud.EndpointOpts{ 218 Region: c.region, 219 }) 220 221 return err 222 } 223 224 func (c *SwiftClient) Get() (*Payload, error) { 225 result := objects.Download(c.client, c.path, TFSTATE_NAME, nil) 226 227 // Extract any errors from result 228 _, err := result.Extract() 229 230 // 404 response is to be expected if the object doesn't already exist! 231 if _, ok := err.(gophercloud.ErrDefault404); ok { 232 log.Printf("[DEBUG] Container doesn't exist to download.") 233 return nil, nil 234 } 235 236 bytes, err := result.ExtractContent() 237 if err != nil { 238 return nil, err 239 } 240 241 hash := md5.Sum(bytes) 242 payload := &Payload{ 243 Data: bytes, 244 MD5: hash[:md5.Size], 245 } 246 247 return payload, nil 248 } 249 250 func (c *SwiftClient) Put(data []byte) error { 251 if err := c.ensureContainerExists(); err != nil { 252 return err 253 } 254 255 reader := bytes.NewReader(data) 256 createOpts := objects.CreateOpts{ 257 Content: reader, 258 } 259 result := objects.Create(c.client, c.path, TFSTATE_NAME, createOpts) 260 261 return result.Err 262 } 263 264 func (c *SwiftClient) Delete() error { 265 result := objects.Delete(c.client, c.path, TFSTATE_NAME, nil) 266 return result.Err 267 } 268 269 func (c *SwiftClient) ensureContainerExists() error { 270 result := containers.Create(c.client, c.path, nil) 271 if result.Err != nil { 272 return result.Err 273 } 274 275 return nil 276 } 277 278 func multiEnv(ks []string) string { 279 for _, k := range ks { 280 if v := os.Getenv(k); v != "" { 281 return v 282 } 283 } 284 return "" 285 }