From c2e35ae3b5aba93a029e7c7cd8b73d3a224094d3 Mon Sep 17 00:00:00 2001 From: Matt Kasa Date: Wed, 2 Sep 2020 05:35:08 -0700 Subject: [PATCH] backend/http: add support for configuration by environment variable (#25439) --- backend/remote-state/http/backend.go | 17 ++++-- backend/remote-state/http/backend_test.go | 74 +++++++++++++++++++++++ website/docs/backends/types/http.html.md | 41 +++++++------ 3 files changed, 107 insertions(+), 25 deletions(-) diff --git a/backend/remote-state/http/backend.go b/backend/remote-state/http/backend.go index 4446ff67c..12076e01a 100644 --- a/backend/remote-state/http/backend.go +++ b/backend/remote-state/http/backend.go @@ -22,44 +22,49 @@ func New() backend.Backend { "address": &schema.Schema{ Type: schema.TypeString, Required: true, + DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_ADDRESS", nil), Description: "The address of the REST endpoint", }, "update_method": &schema.Schema{ Type: schema.TypeString, Optional: true, - Default: "POST", + DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_UPDATE_METHOD", "POST"), Description: "HTTP method to use when updating state", }, "lock_address": &schema.Schema{ Type: schema.TypeString, Optional: true, + DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_LOCK_ADDRESS", nil), Description: "The address of the lock REST endpoint", }, "unlock_address": &schema.Schema{ Type: schema.TypeString, Optional: true, + DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_UNLOCK_ADDRESS", nil), Description: "The address of the unlock REST endpoint", }, "lock_method": &schema.Schema{ Type: schema.TypeString, Optional: true, - Default: "LOCK", + DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_LOCK_METHOD", "LOCK"), Description: "The HTTP method to use when locking", }, "unlock_method": &schema.Schema{ Type: schema.TypeString, Optional: true, - Default: "UNLOCK", + DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_UNLOCK_METHOD", "UNLOCK"), Description: "The HTTP method to use when unlocking", }, "username": &schema.Schema{ Type: schema.TypeString, Optional: true, + DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_USERNAME", nil), Description: "The username for HTTP basic authentication", }, "password": &schema.Schema{ Type: schema.TypeString, Optional: true, + DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_PASSWORD", nil), Description: "The password for HTTP basic authentication", }, "skip_cert_verification": &schema.Schema{ @@ -71,19 +76,19 @@ func New() backend.Backend { "retry_max": &schema.Schema{ Type: schema.TypeInt, Optional: true, - Default: 2, + DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_RETRY_MAX", 2), Description: "The number of HTTP request retries.", }, "retry_wait_min": &schema.Schema{ Type: schema.TypeInt, Optional: true, - Default: 1, + DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_RETRY_WAIT_MIN", 1), Description: "The minimum time in seconds to wait between HTTP request attempts.", }, "retry_wait_max": &schema.Schema{ Type: schema.TypeInt, Optional: true, - Default: 30, + DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_RETRY_WAIT_MAX", 30), Description: "The maximum time in seconds to wait between HTTP request attempts.", }, }, diff --git a/backend/remote-state/http/backend_test.go b/backend/remote-state/http/backend_test.go index dc91129c7..a03ebb319 100644 --- a/backend/remote-state/http/backend_test.go +++ b/backend/remote-state/http/backend_test.go @@ -1,6 +1,7 @@ package http import ( + "os" "testing" "time" @@ -88,3 +89,76 @@ func TestHTTPClientFactory(t *testing.T) { t.Fatalf("Expected retry_wait_max \"%s\", got \"%s\"", 150*time.Second, client.Client.RetryWaitMax) } } + +func TestHTTPClientFactoryWithEnv(t *testing.T) { + // env + conf := map[string]string{ + "address": "http://127.0.0.1:8888/foo", + "update_method": "BLAH", + "lock_address": "http://127.0.0.1:8888/bar", + "lock_method": "BLIP", + "unlock_address": "http://127.0.0.1:8888/baz", + "unlock_method": "BLOOP", + "username": "user", + "password": "pass", + "retry_max": "999", + "retry_wait_min": "15", + "retry_wait_max": "150", + } + + defer testWithEnv(t, "TF_HTTP_ADDRESS", conf["address"])() + defer testWithEnv(t, "TF_HTTP_UPDATE_METHOD", conf["update_method"])() + defer testWithEnv(t, "TF_HTTP_LOCK_ADDRESS", conf["lock_address"])() + defer testWithEnv(t, "TF_HTTP_UNLOCK_ADDRESS", conf["unlock_address"])() + defer testWithEnv(t, "TF_HTTP_LOCK_METHOD", conf["lock_method"])() + defer testWithEnv(t, "TF_HTTP_UNLOCK_METHOD", conf["unlock_method"])() + defer testWithEnv(t, "TF_HTTP_USERNAME", conf["username"])() + defer testWithEnv(t, "TF_HTTP_PASSWORD", conf["password"])() + defer testWithEnv(t, "TF_HTTP_RETRY_MAX", conf["retry_max"])() + defer testWithEnv(t, "TF_HTTP_RETRY_WAIT_MIN", conf["retry_wait_min"])() + defer testWithEnv(t, "TF_HTTP_RETRY_WAIT_MAX", conf["retry_wait_max"])() + + b := backend.TestBackendConfig(t, New(), nil).(*Backend) + client := b.client + + if client == nil { + t.Fatal("Unexpected failure, EnvDefaultFunc") + } + if client.UpdateMethod != "BLAH" { + t.Fatalf("Expected update_method \"%s\", got \"%s\"", "BLAH", client.UpdateMethod) + } + if client.LockURL.String() != conf["lock_address"] || client.LockMethod != "BLIP" { + t.Fatalf("Unexpected lock_address \"%s\" vs \"%s\" or lock_method \"%s\" vs \"%s\"", client.LockURL.String(), + conf["lock_address"], client.LockMethod, conf["lock_method"]) + } + if client.UnlockURL.String() != conf["unlock_address"] || client.UnlockMethod != "BLOOP" { + t.Fatalf("Unexpected unlock_address \"%s\" vs \"%s\" or unlock_method \"%s\" vs \"%s\"", client.UnlockURL.String(), + conf["unlock_address"], client.UnlockMethod, conf["unlock_method"]) + } + if client.Username != "user" || client.Password != "pass" { + t.Fatalf("Unexpected username \"%s\" vs \"%s\" or password \"%s\" vs \"%s\"", client.Username, conf["username"], + client.Password, conf["password"]) + } + if client.Client.RetryMax != 999 { + t.Fatalf("Expected retry_max \"%d\", got \"%d\"", 999, client.Client.RetryMax) + } + if client.Client.RetryWaitMin != 15*time.Second { + t.Fatalf("Expected retry_wait_min \"%s\", got \"%s\"", 15*time.Second, client.Client.RetryWaitMin) + } + if client.Client.RetryWaitMax != 150*time.Second { + t.Fatalf("Expected retry_wait_max \"%s\", got \"%s\"", 150*time.Second, client.Client.RetryWaitMax) + } +} + +// testWithEnv sets an environment variable and returns a deferable func to clean up +func testWithEnv(t *testing.T, key string, value string) func() { + if err := os.Setenv(key, value); err != nil { + t.Fatalf("err: %v", err) + } + + return func() { + if err := os.Unsetenv(key); err != nil { + t.Fatalf("err: %v", err) + } + } +} diff --git a/website/docs/backends/types/http.html.md b/website/docs/backends/types/http.html.md index f4a97aceb..e0f3e7488 100644 --- a/website/docs/backends/types/http.html.md +++ b/website/docs/backends/types/http.html.md @@ -43,25 +43,28 @@ data "terraform_remote_state" "foo" { ## Configuration variables -The following configuration options are supported: +The following configuration options / environment variables are supported: - * `address` - (Required) The address of the REST endpoint - * `update_method` - (Optional) HTTP method to use when updating state. - Defaults to `POST`. - * `lock_address` - (Optional) The address of the lock REST endpoint. - Defaults to disabled. - * `lock_method` - (Optional) The HTTP method to use when locking. - Defaults to `LOCK`. - * `unlock_address` - (Optional) The address of the unlock REST endpoint. - Defaults to disabled. - * `unlock_method` - (Optional) The HTTP method to use when unlocking. - Defaults to `UNLOCK`. - * `username` - (Optional) The username for HTTP basic authentication - * `password` - (Optional) The password for HTTP basic authentication + * `address` / `TF_HTTP_ADDRESS` - (Required) The address of the REST endpoint + * `update_method` / `TF_HTTP_UPDATE_METHOD` - (Optional) HTTP method to use + when updating state. Defaults to `POST`. + * `lock_address` / `TF_HTTP_LOCK_ADDRESS` - (Optional) The address of the lock + REST endpoint. Defaults to disabled. + * `lock_method` / `TF_HTTP_LOCK_METHOD` - (Optional) The HTTP method to use + when locking. Defaults to `LOCK`. + * `unlock_address` / `TF_HTTP_UNLOCK_ADDRESS` - (Optional) The address of the + unlock REST endpoint. Defaults to disabled. + * `unlock_method` / `TF_HTTP_UNLOCK_METHOD` - (Optional) The HTTP method to use + when unlocking. Defaults to `UNLOCK`. + * `username` / `TF_HTTP_USERNAME` - (Optional) The username for HTTP basic + authentication + * `password` / `TF_HTTP_PASSWORD` - (Optional) The password for HTTP basic + authentication * `skip_cert_verification` - (Optional) Whether to skip TLS verification. Defaults to `false`. - * `retry_max` – (Optional) The number of HTTP request retries. Defaults to `2`. - * `retry_wait_min` – (Optional) The minimum time in seconds to wait between HTTP request attempts. - Defaults to `1`. - * `retry_wait_max` – (Optional) The maximum time in seconds to wait between HTTP request attempts. - Defaults to `30`. + * `retry_max` / `TF_HTTP_RETRY_MAX` – (Optional) The number of HTTP request + retries. Defaults to `2`. + * `retry_wait_min` / `TF_HTTP_RETRY_WAIT_MIN` – (Optional) The minimum time in + seconds to wait between HTTP request attempts. Defaults to `1`. + * `retry_wait_max` / `TF_HTTP_RETRY_WAIT_MAX` – (Optional) The maximum time in + seconds to wait between HTTP request attempts. Defaults to `30`.