state/remote/swift: Enhancements to support full set of Openstack configuration options, plus SSL certs. Documentation updated to support

This commit is contained in:
Gavin Williams 2016-11-01 17:03:00 +00:00
parent 6c801d0386
commit bf8612b9b7
2 changed files with 209 additions and 39 deletions

View File

@ -4,7 +4,9 @@ import (
"bytes"
"crypto/md5"
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
@ -21,7 +23,20 @@ const TFSTATE_NAME = "tfstate.tf"
// SwiftClient implements the Client interface for an Openstack Swift server.
type SwiftClient struct {
client *gophercloud.ServiceClient
authurl string
cacert string
cert string
domainid string
domainname string
insecure bool
key string
password string
path string
region string
tenantid string
tenantname string
userid string
username string
}
func swiftFactory(conf map[string]string) (Client, error) {
@ -35,30 +50,127 @@ func swiftFactory(conf map[string]string) (Client, error) {
}
func (c *SwiftClient) validateConfig(conf map[string]string) (err error) {
if val := os.Getenv("OS_AUTH_URL"); val == "" {
return fmt.Errorf("missing OS_AUTH_URL environment variable")
authUrl, ok := conf["auth_url"]
if !ok {
authUrl = os.Getenv("OS_AUTH_URL")
if authUrl == "" {
return fmt.Errorf("missing 'auth_url' configuration or OS_AUTH_URL environment variable")
}
if val := os.Getenv("OS_USERNAME"); val == "" {
return fmt.Errorf("missing OS_USERNAME environment variable")
}
if val := os.Getenv("OS_TENANT_NAME"); val == "" {
return fmt.Errorf("missing OS_TENANT_NAME environment variable")
c.authurl = authUrl
username, ok := conf["user_name"]
if !ok {
username = os.Getenv("OS_USERNAME")
}
if val := os.Getenv("OS_PASSWORD"); val == "" {
return fmt.Errorf("missing OS_PASSWORD environment variable")
c.username = username
userID, ok := conf["user_id"]
if !ok {
userID = os.Getenv("OS_USER_ID")
}
c.userid = userID
password, ok := conf["password"]
if !ok {
password = os.Getenv("OS_PASSWORD")
if password == "" {
return fmt.Errorf("missing 'password' configuration or OS_PASSWORD environment variable")
}
}
c.password = password
region, ok := conf["region_name"]
if !ok {
region = os.Getenv("OS_REGION_NAME")
}
c.region = region
tenantID, ok := conf["tenant_id"]
if !ok {
tenantID = multiEnv([]string{
"OS_TENANT_ID",
"OS_PROJECT_ID",
})
}
c.tenantid = tenantID
tenantName, ok := conf["tenant_name"]
if !ok {
tenantName = multiEnv([]string{
"OS_TENANT_NAME",
"OS_PROJECT_NAME",
})
}
c.tenantname = tenantName
domainID, ok := conf["domain_id"]
if !ok {
domainID = multiEnv([]string{
"OS_USER_DOMAIN_ID",
"OS_PROJECT_DOMAIN_ID",
"OS_DOMAIN_ID",
})
}
c.domainid = domainID
domainName, ok := conf["domain_name"]
if !ok {
domainName = multiEnv([]string{
"OS_USER_DOMAIN_NAME",
"OS_PROJECT_DOMAIN_NAME",
"OS_DOMAIN_NAME",
"DEFAULT_DOMAIN",
})
}
c.domainname = domainName
path, ok := conf["path"]
if !ok || path == "" {
return fmt.Errorf("missing 'path' configuration")
}
c.path = path
c.insecure = false
raw, ok := conf["insecure"]
if !ok {
raw = os.Getenv("OS_INSECURE")
}
if raw != "" {
v, err := strconv.ParseBool(raw)
if err != nil {
return fmt.Errorf("'insecure' and 'OS_INSECURE' could not be parsed as bool: %s", err)
}
c.insecure = v
}
cacertFile, ok := conf["cacert_file"]
if !ok {
cacertFile = os.Getenv("OS_CACERT")
}
c.cacert = cacertFile
cert, ok := conf["cert"]
if !ok {
cert = os.Getenv("OS_CERT")
}
c.cert = cert
key, ok := conf["key"]
if !ok {
key = os.Getenv("OS_KEY")
}
c.key = key
ao := gophercloud.AuthOptions{
IdentityEndpoint: os.Getenv("OS_AUTH_URL"),
Username: os.Getenv("OS_USERNAME"),
TenantName: os.Getenv("OS_TENANT_NAME"),
Password: os.Getenv("OS_PASSWORD"),
DomainName: os.Getenv("OS_DOMAIN_NAME"),
DomainID: os.Getenv("OS_DOMAIN_ID"),
IdentityEndpoint: c.authurl,
UserID: c.userid,
Username: c.username,
TenantID: c.tenantid,
TenantName: c.tenantname,
Password: c.password,
DomainID: c.domainid,
DomainName: c.domainname,
}
provider, err := openstack.NewClient(ao.IdentityEndpoint)
@ -67,19 +179,33 @@ func (c *SwiftClient) validateConfig(conf map[string]string) (err error) {
}
config := &tls.Config{}
insecure := false
if insecure_env := os.Getenv("OS_INSECURE"); insecure_env != "" {
insecure, err = strconv.ParseBool(insecure_env)
if c.cacert != "" {
caCert, err := ioutil.ReadFile(c.cacert)
if err != nil {
return err
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
config.RootCAs = caCertPool
}
if insecure {
if c.insecure {
log.Printf("[DEBUG] Insecure mode set")
config.InsecureSkipVerify = true
}
if c.cert != "" && c.key != "" {
cert, err := tls.LoadX509KeyPair(c.cert, c.key)
if err != nil {
return err
}
config.Certificates = []tls.Certificate{cert}
config.BuildNameToCertificate()
}
transport := &http.Transport{Proxy: http.ProxyFromEnvironment, TLSClientConfig: config}
provider.HTTPClient.Transport = transport
@ -88,9 +214,8 @@ func (c *SwiftClient) validateConfig(conf map[string]string) (err error) {
return err
}
c.path = path
c.client, err = openstack.NewObjectStorageV1(provider, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
Region: c.region,
})
return err
@ -149,3 +274,12 @@ func (c *SwiftClient) ensureContainerExists() error {
return nil
}
func multiEnv(ks []string) string {
for _, k := range ks {
if v := os.Getenv(k); v != "" {
return v
}
}
return ""
}

View File

@ -10,12 +10,16 @@ description: |-
Stores the state as an artifact in [Swift](http://docs.openstack.org/developer/swift/).
-> **Note:** Passing credentials directly via configuration options will
make them included in cleartext inside the persisted state. Use of
environment variables is recommended.
## Example Usage
```
terraform remote config \
-backend=swift \
-backend-config="path=random/path"
-backend-config="path=terraform_state"
```
## Example Referencing
@ -24,24 +28,56 @@ terraform remote config \
data "terraform_remote_state" "foo" {
backend = "swift"
config {
path = "random/path"
path = "terraform_state"
}
}
```
## Configuration variables
The following configuration option is supported:
The following configuration options are supported:
* `path` - (Required) The path where to store `terraform.tfstate`
* `insecure` - (Optional) Allow "insecure" SSL requests. Defaults to `false`.
* `auth_url` - (Required) The Identity authentication URL. If omitted, the
`OS_AUTH_URL` environment variable is used.
The following environment variables are supported:
* `path` - (Required) The path where to store `terraform.tfstate`.
* `user_name` - (Optional) The Username to login with. If omitted, the
`OS_USERNAME` environment variable is used.
* `OS_AUTH_URL` - (Required) The identity endpoint
* `OS_USERNAME` - (Required) The username
* `OS_PASSWORD` - (Required) The password
* `OS_REGION_NAME` - (Required) The region
* `OS_TENANT_NAME` - (Required) The name of the tenant
* `OS_DOMAIN_ID` - (Optional) The ID of the domain
* `OS_DOMAIN_NAME` - (Optional) The name of the domain
* `user_id` - (Optional) The User ID to login with. If omitted, the
`OS_USER_ID` environment variable is used.
* `password` - (Optional) The Password to login with. If omitted, the
`OS_PASSWORD` environment variable is used.
* `region_name` (Required) - The region in which to store `terraform.tfstate`. If
omitted, the `OS_REGION_NAME` environment variable is used.
* `tenant_id` (Optional) The ID of the Tenant (Identity v2) or Project
(Identity v3) to login with. If omitted, the `OS_TENANT_ID` or
`OS_PROJECT_ID` environment variables are used.
* `tenant_name` - (Optional) The Name of the Tenant (Identity v2) or Project
(Identity v3) to login with. If omitted, the `OS_TENANT_NAME` or
`OS_PROJECT_NAME` environment variable are used.
* `domain_id` - (Optional) The ID of the Domain to scope to (Identity v3). If
If omitted, the following environment variables are checked (in this order):
`OS_USER_DOMAIN_ID`, `OS_PROJECT_DOMAIN_ID`, `OS_DOMAIN_ID`.
* `domain_name` - (Optional) The Name of the Domain to scope to (Identity v3).
If omitted, the following environment variables are checked (in this order):
`OS_USER_DOMAIN_NAME`, `OS_PROJECT_DOMAIN_NAME`, `OS_DOMAIN_NAME`,
`DEFAULT_DOMAIN`.
* `insecure` - (Optional) Trust self-signed SSL certificates. If omitted, the
`OS_INSECURE` environment variable is used.
* `cacert_file` - (Optional) Specify a custom CA certificate when communicating
over SSL. If omitted, the `OS_CACERT` environment variable is used.
* `cert` - (Optional) Specify client certificate file for SSL client
authentication. If omitted the `OS_CERT` environment variable is used.
* `key` - (Optional) Specify client private key file for SSL client
authentication. If omitted the `OS_KEY` environment variable is used.