Allow OpenStack SSL fields to be specified by contents
- OpenStack provider now supports either a path or the file contents for `cacert_file`, `cert`, and `key` - Makes it easier to automate TF by passing in certs as environment variables - set `OS_SSL_TESTS=true` to run the acceptance tests
This commit is contained in:
parent
2d894bae48
commit
3b7cf41b83
|
@ -4,12 +4,12 @@ import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gophercloud/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/gophercloud/gophercloud/openstack"
|
"github.com/gophercloud/gophercloud/openstack"
|
||||||
"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/swauth"
|
"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/swauth"
|
||||||
|
"github.com/hashicorp/terraform/helper/pathorcontents"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
@ -70,13 +70,13 @@ func (c *Config) loadAndValidate() error {
|
||||||
|
|
||||||
config := &tls.Config{}
|
config := &tls.Config{}
|
||||||
if c.CACertFile != "" {
|
if c.CACertFile != "" {
|
||||||
caCert, err := ioutil.ReadFile(c.CACertFile)
|
caCert, _, err := pathorcontents.Read(c.CACertFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("Error reading CA Cert: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
caCertPool := x509.NewCertPool()
|
caCertPool := x509.NewCertPool()
|
||||||
caCertPool.AppendCertsFromPEM(caCert)
|
caCertPool.AppendCertsFromPEM([]byte(caCert))
|
||||||
config.RootCAs = caCertPool
|
config.RootCAs = caCertPool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +85,16 @@ func (c *Config) loadAndValidate() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.ClientCertFile != "" && c.ClientKeyFile != "" {
|
if c.ClientCertFile != "" && c.ClientKeyFile != "" {
|
||||||
cert, err := tls.LoadX509KeyPair(c.ClientCertFile, c.ClientKeyFile)
|
clientCert, _, err := pathorcontents.Read(c.ClientCertFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error reading Client Cert: %s", err)
|
||||||
|
}
|
||||||
|
clientKey, _, err := pathorcontents.Read(c.ClientKeyFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error reading Client Key: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err := tls.X509KeyPair([]byte(clientCert), []byte(clientKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
package openstack
|
package openstack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/config"
|
||||||
|
"github.com/hashicorp/terraform/helper/pathorcontents"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
|
@ -23,16 +27,6 @@ func init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProvider(t *testing.T) {
|
|
||||||
if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProvider_impl(t *testing.T) {
|
|
||||||
var _ terraform.ResourceProvider = Provider()
|
|
||||||
}
|
|
||||||
|
|
||||||
func testAccPreCheck(t *testing.T) {
|
func testAccPreCheck(t *testing.T) {
|
||||||
v := os.Getenv("OS_AUTH_URL")
|
v := os.Getenv("OS_AUTH_URL")
|
||||||
if v == "" {
|
if v == "" {
|
||||||
|
@ -73,3 +67,172 @@ func testAccPreCheck(t *testing.T) {
|
||||||
t.Fatal("OS_EXTGW_ID must be set for acceptance tests")
|
t.Fatal("OS_EXTGW_ID must be set for acceptance tests")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProvider(t *testing.T) {
|
||||||
|
if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProvider_impl(t *testing.T) {
|
||||||
|
var _ terraform.ResourceProvider = Provider()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Steps for configuring OpenStack with SSL validation are here:
|
||||||
|
// https://github.com/hashicorp/terraform/pull/6279#issuecomment-219020144
|
||||||
|
func TestAccProvider_caCertFile(t *testing.T) {
|
||||||
|
if os.Getenv("TF_ACC") == "" || os.Getenv("OS_SSL_TESTS") == "" {
|
||||||
|
t.Skip("TF_ACC or OS_SSL_TESTS not set, skipping OpenStack SSL test.")
|
||||||
|
}
|
||||||
|
if os.Getenv("OS_CACERT") == "" {
|
||||||
|
t.Skip("OS_CACERT is not set; skipping OpenStack CA test.")
|
||||||
|
}
|
||||||
|
|
||||||
|
p := Provider()
|
||||||
|
|
||||||
|
caFile, err := envVarFile("OS_CACERT")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.Remove(caFile)
|
||||||
|
|
||||||
|
raw := map[string]interface{}{
|
||||||
|
"cacert_file": caFile,
|
||||||
|
}
|
||||||
|
rawConfig, err := config.NewRawConfig(raw)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = p.Configure(terraform.NewResourceConfig(rawConfig))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected err when specifying OpenStack CA by file: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccProvider_caCertString(t *testing.T) {
|
||||||
|
if os.Getenv("TF_ACC") == "" || os.Getenv("OS_SSL_TESTS") == "" {
|
||||||
|
t.Skip("TF_ACC or OS_SSL_TESTS not set, skipping OpenStack SSL test.")
|
||||||
|
}
|
||||||
|
if os.Getenv("OS_CACERT") == "" {
|
||||||
|
t.Skip("OS_CACERT is not set; skipping OpenStack CA test.")
|
||||||
|
}
|
||||||
|
|
||||||
|
p := Provider()
|
||||||
|
|
||||||
|
caContents, err := envVarContents("OS_CACERT")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
raw := map[string]interface{}{
|
||||||
|
"cacert_file": caContents,
|
||||||
|
}
|
||||||
|
rawConfig, err := config.NewRawConfig(raw)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = p.Configure(terraform.NewResourceConfig(rawConfig))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected err when specifying OpenStack CA by string: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccProvider_clientCertFile(t *testing.T) {
|
||||||
|
if os.Getenv("TF_ACC") == "" || os.Getenv("OS_SSL_TESTS") == "" {
|
||||||
|
t.Skip("TF_ACC or OS_SSL_TESTS not set, skipping OpenStack SSL test.")
|
||||||
|
}
|
||||||
|
if os.Getenv("OS_CERT") == "" || os.Getenv("OS_KEY") == "" {
|
||||||
|
t.Skip("OS_CERT or OS_KEY is not set; skipping OpenStack client SSL auth test.")
|
||||||
|
}
|
||||||
|
|
||||||
|
p := Provider()
|
||||||
|
|
||||||
|
certFile, err := envVarFile("OS_CERT")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.Remove(certFile)
|
||||||
|
keyFile, err := envVarFile("OS_KEY")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.Remove(keyFile)
|
||||||
|
|
||||||
|
raw := map[string]interface{}{
|
||||||
|
"cert": certFile,
|
||||||
|
"key": keyFile,
|
||||||
|
}
|
||||||
|
rawConfig, err := config.NewRawConfig(raw)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = p.Configure(terraform.NewResourceConfig(rawConfig))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected err when specifying OpenStack Client keypair by file: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccProvider_clientCertString(t *testing.T) {
|
||||||
|
if os.Getenv("TF_ACC") == "" || os.Getenv("OS_SSL_TESTS") == "" {
|
||||||
|
t.Skip("TF_ACC or OS_SSL_TESTS not set, skipping OpenStack SSL test.")
|
||||||
|
}
|
||||||
|
if os.Getenv("OS_CERT") == "" || os.Getenv("OS_KEY") == "" {
|
||||||
|
t.Skip("OS_CERT or OS_KEY is not set; skipping OpenStack client SSL auth test.")
|
||||||
|
}
|
||||||
|
|
||||||
|
p := Provider()
|
||||||
|
|
||||||
|
certContents, err := envVarContents("OS_CERT")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
keyContents, err := envVarContents("OS_KEY")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw := map[string]interface{}{
|
||||||
|
"cert": certContents,
|
||||||
|
"key": keyContents,
|
||||||
|
}
|
||||||
|
rawConfig, err := config.NewRawConfig(raw)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = p.Configure(terraform.NewResourceConfig(rawConfig))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected err when specifying OpenStack Client keypair by contents: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func envVarContents(varName string) (string, error) {
|
||||||
|
contents, _, err := pathorcontents.Read(os.Getenv(varName))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Error reading %s: %s", varName, err)
|
||||||
|
}
|
||||||
|
return contents, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func envVarFile(varName string) (string, error) {
|
||||||
|
contents, err := envVarContents(varName)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpFile, err := ioutil.TempFile("", varName)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Error creating temp file: %s", err)
|
||||||
|
}
|
||||||
|
if _, err := tmpFile.Write([]byte(contents)); err != nil {
|
||||||
|
_ = os.Remove(tmpFile.Name())
|
||||||
|
return "", fmt.Errorf("Error writing temp file: %s", err)
|
||||||
|
}
|
||||||
|
if err := tmpFile.Close(); err != nil {
|
||||||
|
_ = os.Remove(tmpFile.Name())
|
||||||
|
return "", fmt.Errorf("Error closing temp file: %s", err)
|
||||||
|
}
|
||||||
|
return tmpFile.Name(), nil
|
||||||
|
}
|
||||||
|
|
|
@ -74,13 +74,16 @@ The following arguments are supported:
|
||||||
`OS_INSECURE` environment variable is used.
|
`OS_INSECURE` environment variable is used.
|
||||||
|
|
||||||
* `cacert_file` - (Optional) Specify a custom CA certificate when communicating
|
* `cacert_file` - (Optional) Specify a custom CA certificate when communicating
|
||||||
over SSL. If omitted, the `OS_CACERT` environment variable is used.
|
over SSL. You can specify either a path to the file or the contents of the
|
||||||
|
certificate. If omitted, the `OS_CACERT` environment variable is used.
|
||||||
|
|
||||||
* `cert` - (Optional) Specify client certificate file for SSL client
|
* `cert` - (Optional) Specify client certificate file for SSL client
|
||||||
authentication. If omitted the `OS_CERT` environment variable is used.
|
authentication. You can specify either a path to the file or the contents of
|
||||||
|
the certificate. If omitted the `OS_CERT` environment variable is used.
|
||||||
|
|
||||||
* `key` - (Optional) Specify client private key file for SSL client
|
* `key` - (Optional) Specify client private key file for SSL client
|
||||||
authentication. If omitted the `OS_KEY` environment variable is used.
|
authentication. You can specify either a path to the file or the contents of
|
||||||
|
the key. If omitted the `OS_KEY` environment variable is used.
|
||||||
|
|
||||||
* `endpoint_type` - (Optional) Specify which type of endpoint to use from the
|
* `endpoint_type` - (Optional) Specify which type of endpoint to use from the
|
||||||
service catalog. It can be set using the OS_ENDPOINT_TYPE environment
|
service catalog. It can be set using the OS_ENDPOINT_TYPE environment
|
||||||
|
|
Loading…
Reference in New Issue