From aa48af6e18950b54b6d9371adeb05ba79aee00cd Mon Sep 17 00:00:00 2001 From: James Bardin Date: Tue, 30 Aug 2022 12:50:04 -0400 Subject: [PATCH 1/2] remove deprecated backends --- go.mod | 5 - go.sum | 16 - internal/backend/init/init.go | 38 +- internal/backend/init/init_test.go | 5 - .../remote-state/artifactory/backend.go | 102 ---- .../remote-state/artifactory/client.go | 63 --- .../remote-state/artifactory/client_test.go | 55 -- .../backend/remote-state/manta/backend.go | 206 ------- .../remote-state/manta/backend_state.go | 145 ----- .../remote-state/manta/backend_test.go | 130 ----- internal/backend/remote-state/manta/client.go | 200 ------- .../backend/remote-state/manta/client_test.go | 68 --- .../backend/remote-state/swift/backend.go | 485 ---------------- .../remote-state/swift/backend_state.go | 208 ------- .../remote-state/swift/backend_test.go | 115 ---- internal/backend/remote-state/swift/client.go | 535 ------------------ .../backend/remote-state/swift/client_test.go | 38 -- 17 files changed, 6 insertions(+), 2408 deletions(-) delete mode 100644 internal/backend/remote-state/artifactory/backend.go delete mode 100644 internal/backend/remote-state/artifactory/client.go delete mode 100644 internal/backend/remote-state/artifactory/client_test.go delete mode 100644 internal/backend/remote-state/manta/backend.go delete mode 100644 internal/backend/remote-state/manta/backend_state.go delete mode 100644 internal/backend/remote-state/manta/backend_test.go delete mode 100644 internal/backend/remote-state/manta/client.go delete mode 100644 internal/backend/remote-state/manta/client_test.go delete mode 100644 internal/backend/remote-state/swift/backend.go delete mode 100644 internal/backend/remote-state/swift/backend_state.go delete mode 100644 internal/backend/remote-state/swift/backend_test.go delete mode 100644 internal/backend/remote-state/swift/client.go delete mode 100644 internal/backend/remote-state/swift/client_test.go diff --git a/go.mod b/go.mod index e66e875b56ba..e19740ee32f2 100644 --- a/go.mod +++ b/go.mod @@ -26,8 +26,6 @@ require ( github.com/golang/mock v1.6.0 github.com/google/go-cmp v0.5.8 github.com/google/uuid v1.2.0 - github.com/gophercloud/gophercloud v0.10.1-0.20200424014253-c3bfe50899e5 - github.com/gophercloud/utils v0.0.0-20200423144003-7c72efc7435d github.com/hashicorp/aws-sdk-go-base v0.7.1 github.com/hashicorp/consul/api v1.9.1 github.com/hashicorp/consul/sdk v0.8.0 @@ -49,10 +47,8 @@ require ( github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 github.com/jmespath/go-jmespath v0.4.0 - github.com/joyent/triton-go v0.0.0-20180313100802-d8f9c0314926 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 github.com/lib/pq v1.10.3 - github.com/lusis/go-artifactory v0.0.0-20160115162124-7e4ce345df82 github.com/manicminer/hamilton v0.44.0 github.com/masterzen/winrm v0.0.0-20200615185753-c42b5136ff88 github.com/mattn/go-isatty v0.0.12 @@ -117,7 +113,6 @@ require ( github.com/Masterminds/semver/v3 v3.1.1 // indirect github.com/Masterminds/sprig/v3 v3.2.0 // indirect github.com/Microsoft/go-winio v0.5.0 // indirect - github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af // indirect github.com/antchfx/xmlquery v1.3.5 // indirect github.com/antchfx/xpath v1.1.10 // indirect github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect diff --git a/go.sum b/go.sum index 3a2c9803f62e..8dfef2a87547 100644 --- a/go.sum +++ b/go.sum @@ -98,8 +98,6 @@ github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDe github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= -github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14= -github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw= github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= @@ -318,11 +316,6 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= -github.com/gophercloud/gophercloud v0.6.1-0.20191122030953-d8ac278c1c9d/go.mod h1:ozGNgr9KYOVATV5jsgHl/ceCDXGuguqOZAzoQ/2vcNM= -github.com/gophercloud/gophercloud v0.10.1-0.20200424014253-c3bfe50899e5 h1:Ciwp7ro4LyptUOkili/TX/ecuYr7vGtEIFnOOOKUjD8= -github.com/gophercloud/gophercloud v0.10.1-0.20200424014253-c3bfe50899e5/go.mod h1:gmC5oQqMDOMO1t1gq5DquX/yAU808e/4mzjjDA76+Ss= -github.com/gophercloud/utils v0.0.0-20200423144003-7c72efc7435d h1:fduaPzWwIfvOMLuHk2Al3GZH0XbUqG8MbElPop+Igzs= -github.com/gophercloud/utils v0.0.0-20200423144003-7c72efc7435d/go.mod h1:ehWUbLQJPqS0Ep+CxeD559hsm9pthPXadJNKwZkp43w= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= @@ -432,8 +425,6 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/joyent/triton-go v0.0.0-20180313100802-d8f9c0314926 h1:kie3qOosvRKqwij2HGzXWffwpXvcqfPPXRUw8I4F/mg= -github.com/joyent/triton-go v0.0.0-20180313100802-d8f9c0314926/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -460,8 +451,6 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg= github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lusis/go-artifactory v0.0.0-20160115162124-7e4ce345df82 h1:wnfcqULT+N2seWf6y4yHzmi7GD2kNx4Ute0qArktD48= -github.com/lusis/go-artifactory v0.0.0-20160115162124-7e4ce345df82/go.mod h1:y54tfGmO3NKssKveTEFFzH8C/akrSOy/iW9qEAUDV84= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/manicminer/hamilton v0.43.0/go.mod h1:lbVyngC+/nCWuDp8UhC6Bw+bh7jcP/E+YwqzHTmzemk= @@ -653,7 +642,6 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -724,7 +712,6 @@ golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -802,7 +789,6 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -881,7 +867,6 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -1055,7 +1040,6 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/internal/backend/init/init.go b/internal/backend/init/init.go index a7be1d552a21..b437b3035147 100644 --- a/internal/backend/init/init.go +++ b/internal/backend/init/init.go @@ -12,7 +12,6 @@ import ( backendLocal "github.com/hashicorp/terraform/internal/backend/local" backendRemote "github.com/hashicorp/terraform/internal/backend/remote" - backendArtifactory "github.com/hashicorp/terraform/internal/backend/remote-state/artifactory" backendAzure "github.com/hashicorp/terraform/internal/backend/remote-state/azure" backendConsul "github.com/hashicorp/terraform/internal/backend/remote-state/consul" backendCos "github.com/hashicorp/terraform/internal/backend/remote-state/cos" @@ -20,11 +19,9 @@ import ( backendHTTP "github.com/hashicorp/terraform/internal/backend/remote-state/http" backendInmem "github.com/hashicorp/terraform/internal/backend/remote-state/inmem" backendKubernetes "github.com/hashicorp/terraform/internal/backend/remote-state/kubernetes" - backendManta "github.com/hashicorp/terraform/internal/backend/remote-state/manta" backendOSS "github.com/hashicorp/terraform/internal/backend/remote-state/oss" backendPg "github.com/hashicorp/terraform/internal/backend/remote-state/pg" backendS3 "github.com/hashicorp/terraform/internal/backend/remote-state/s3" - backendSwift "github.com/hashicorp/terraform/internal/backend/remote-state/swift" backendCloud "github.com/hashicorp/terraform/internal/cloud" ) @@ -70,38 +67,15 @@ func Init(services *disco.Disco) { // Terraform Cloud 'backend' // This is an implementation detail only, used for the cloud package "cloud": func() backend.Backend { return backendCloud.New(services) }, - - // FIXME: remove deprecated backends for v1.3 - // Deprecated backends. - "azure": func() backend.Backend { - return deprecateBackend( - backendAzure.New(), - `Warning: "azure" name is deprecated, please use "azurerm"`, - ) - }, - "artifactory": func() backend.Backend { - return deprecateBackend( - backendArtifactory.New(), - `Warning: "artifactory" backend is deprecated, and will be removed in a future release."`, - ) - }, - "manta": func() backend.Backend { - return deprecateBackend( - backendManta.New(), - `Warning: "manta" backend is deprecated, and will be removed in a future release."`, - ) - }, - "swift": func() backend.Backend { - return deprecateBackend( - backendSwift.New(), - `Warning: "swift" backend is deprecated, and will be removed in a future release."`, - ) - }, } RemovedBackends = map[string]string{ - "etcd": `The "etcd" backend is not supported in Terraform v1.3 or later.`, - "etcdv3": `The "etcdv3" backend is not supported in Terraform v1.3 or later.`, + "artifactory": `The "artifactory" backend is not supported in Terraform v1.3 or later.`, + "azure": `The "azure" backend name has been removed, please use "azurerm".`, + "etcd": `The "etcd" backend is not supported in Terraform v1.3 or later.`, + "etcdv3": `The "etcdv3" backend is not supported in Terraform v1.3 or later.`, + "manta": `The "manta" backend is not supported in Terraform v1.3 or later.`, + "swift": `The "swift" backend is not supported in Terraform v1.3 or later.`, } } diff --git a/internal/backend/init/init_test.go b/internal/backend/init/init_test.go index 609e97ce4fa8..e6d36506506e 100644 --- a/internal/backend/init/init_test.go +++ b/internal/backend/init/init_test.go @@ -22,11 +22,6 @@ func TestInit_backend(t *testing.T) { {"inmem", "*inmem.Backend"}, {"pg", "*pg.Backend"}, {"s3", "*s3.Backend"}, - - {"azure", "init.deprecatedBackendShim"}, - {"artifactory", "init.deprecatedBackendShim"}, - {"manta", "init.deprecatedBackendShim"}, - {"swift", "init.deprecatedBackendShim"}, } // Make sure we get the requested backend diff --git a/internal/backend/remote-state/artifactory/backend.go b/internal/backend/remote-state/artifactory/backend.go deleted file mode 100644 index bf2bfcf7e5ce..000000000000 --- a/internal/backend/remote-state/artifactory/backend.go +++ /dev/null @@ -1,102 +0,0 @@ -package artifactory - -import ( - "context" - - cleanhttp "github.com/hashicorp/go-cleanhttp" - "github.com/hashicorp/terraform/internal/backend" - "github.com/hashicorp/terraform/internal/legacy/helper/schema" - "github.com/hashicorp/terraform/internal/states/remote" - "github.com/hashicorp/terraform/internal/states/statemgr" - artifactory "github.com/lusis/go-artifactory/src/artifactory.v401" -) - -func New() backend.Backend { - s := &schema.Backend{ - Schema: map[string]*schema.Schema{ - "username": &schema.Schema{ - Type: schema.TypeString, - Required: true, - DefaultFunc: schema.EnvDefaultFunc("ARTIFACTORY_USERNAME", nil), - Description: "Username", - }, - "password": &schema.Schema{ - Type: schema.TypeString, - Required: true, - DefaultFunc: schema.EnvDefaultFunc("ARTIFACTORY_PASSWORD", nil), - Description: "Password", - }, - "url": &schema.Schema{ - Type: schema.TypeString, - Required: true, - DefaultFunc: schema.EnvDefaultFunc("ARTIFACTORY_URL", nil), - Description: "Artfactory base URL", - }, - "repo": &schema.Schema{ - Type: schema.TypeString, - Required: true, - Description: "The repository name", - }, - "subpath": &schema.Schema{ - Type: schema.TypeString, - Required: true, - Description: "Path within the repository", - }, - }, - } - - b := &Backend{Backend: s} - b.Backend.ConfigureFunc = b.configure - return b -} - -type Backend struct { - *schema.Backend - - client *ArtifactoryClient -} - -func (b *Backend) configure(ctx context.Context) error { - data := schema.FromContextBackendConfig(ctx) - - userName := data.Get("username").(string) - password := data.Get("password").(string) - url := data.Get("url").(string) - repo := data.Get("repo").(string) - subpath := data.Get("subpath").(string) - - clientConf := &artifactory.ClientConfig{ - BaseURL: url, - Username: userName, - Password: password, - Transport: cleanhttp.DefaultPooledTransport(), - } - nativeClient := artifactory.NewClient(clientConf) - - b.client = &ArtifactoryClient{ - nativeClient: &nativeClient, - userName: userName, - password: password, - url: url, - repo: repo, - subpath: subpath, - } - return nil -} - -func (b *Backend) Workspaces() ([]string, error) { - return nil, backend.ErrWorkspacesNotSupported -} - -func (b *Backend) DeleteWorkspace(string) error { - return backend.ErrWorkspacesNotSupported -} - -func (b *Backend) StateMgr(name string) (statemgr.Full, error) { - if name != backend.DefaultStateName { - return nil, backend.ErrWorkspacesNotSupported - } - return &remote.State{ - Client: b.client, - }, nil -} diff --git a/internal/backend/remote-state/artifactory/client.go b/internal/backend/remote-state/artifactory/client.go deleted file mode 100644 index 672e1b7f46e8..000000000000 --- a/internal/backend/remote-state/artifactory/client.go +++ /dev/null @@ -1,63 +0,0 @@ -package artifactory - -import ( - "crypto/md5" - "fmt" - "strings" - - "github.com/hashicorp/terraform/internal/states/remote" - artifactory "github.com/lusis/go-artifactory/src/artifactory.v401" -) - -const ARTIF_TFSTATE_NAME = "terraform.tfstate" - -type ArtifactoryClient struct { - nativeClient *artifactory.ArtifactoryClient - userName string - password string - url string - repo string - subpath string -} - -func (c *ArtifactoryClient) Get() (*remote.Payload, error) { - p := fmt.Sprintf("%s/%s/%s", c.repo, c.subpath, ARTIF_TFSTATE_NAME) - output, err := c.nativeClient.Get(p, make(map[string]string)) - if err != nil { - if strings.Contains(err.Error(), "404") { - return nil, nil - } - return nil, err - } - - // TODO: migrate to using X-Checksum-Md5 header from artifactory - // needs to be exposed by go-artifactory first - - hash := md5.Sum(output) - payload := &remote.Payload{ - Data: output, - MD5: hash[:md5.Size], - } - - // If there was no data, then return nil - if len(payload.Data) == 0 { - return nil, nil - } - - return payload, nil -} - -func (c *ArtifactoryClient) Put(data []byte) error { - p := fmt.Sprintf("%s/%s/%s", c.repo, c.subpath, ARTIF_TFSTATE_NAME) - if _, err := c.nativeClient.Put(p, string(data), make(map[string]string)); err == nil { - return nil - } else { - return fmt.Errorf("Failed to upload state: %v", err) - } -} - -func (c *ArtifactoryClient) Delete() error { - p := fmt.Sprintf("%s/%s/%s", c.repo, c.subpath, ARTIF_TFSTATE_NAME) - err := c.nativeClient.Delete(p) - return err -} diff --git a/internal/backend/remote-state/artifactory/client_test.go b/internal/backend/remote-state/artifactory/client_test.go deleted file mode 100644 index d34aff53887c..000000000000 --- a/internal/backend/remote-state/artifactory/client_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package artifactory - -import ( - "testing" - - "github.com/hashicorp/terraform/internal/backend" - "github.com/hashicorp/terraform/internal/configs" - "github.com/hashicorp/terraform/internal/states/remote" - "github.com/zclconf/go-cty/cty" -) - -func TestArtifactoryClient_impl(t *testing.T) { - var _ remote.Client = new(ArtifactoryClient) -} - -func TestArtifactoryFactory(t *testing.T) { - // This test just instantiates the client. Shouldn't make any actual - // requests nor incur any costs. - - config := make(map[string]cty.Value) - config["url"] = cty.StringVal("http://artifactory.local:8081/artifactory") - config["repo"] = cty.StringVal("terraform-repo") - config["subpath"] = cty.StringVal("myproject") - - // For this test we'll provide the credentials as config. The - // acceptance tests implicitly test passing credentials as - // environment variables. - config["username"] = cty.StringVal("test") - config["password"] = cty.StringVal("testpass") - - b := backend.TestBackendConfig(t, New(), configs.SynthBody("synth", config)) - - state, err := b.StateMgr(backend.DefaultStateName) - if err != nil { - t.Fatalf("Error for valid config: %s", err) - } - - artifactoryClient := state.(*remote.State).Client.(*ArtifactoryClient) - - if artifactoryClient.nativeClient.Config.BaseURL != "http://artifactory.local:8081/artifactory" { - t.Fatalf("Incorrect url was populated") - } - if artifactoryClient.nativeClient.Config.Username != "test" { - t.Fatalf("Incorrect username was populated") - } - if artifactoryClient.nativeClient.Config.Password != "testpass" { - t.Fatalf("Incorrect password was populated") - } - if artifactoryClient.repo != "terraform-repo" { - t.Fatalf("Incorrect repo was populated") - } - if artifactoryClient.subpath != "myproject" { - t.Fatalf("Incorrect subpath was populated") - } -} diff --git a/internal/backend/remote-state/manta/backend.go b/internal/backend/remote-state/manta/backend.go deleted file mode 100644 index 3a7a21bc5a6c..000000000000 --- a/internal/backend/remote-state/manta/backend.go +++ /dev/null @@ -1,206 +0,0 @@ -package manta - -import ( - "context" - "encoding/pem" - "errors" - "fmt" - "io/ioutil" - "os" - - "github.com/hashicorp/errwrap" - "github.com/hashicorp/go-multierror" - "github.com/hashicorp/terraform/internal/backend" - "github.com/hashicorp/terraform/internal/legacy/helper/schema" - triton "github.com/joyent/triton-go" - "github.com/joyent/triton-go/authentication" - "github.com/joyent/triton-go/storage" -) - -func New() backend.Backend { - s := &schema.Backend{ - Schema: map[string]*schema.Schema{ - "account": { - Type: schema.TypeString, - Required: true, - DefaultFunc: schema.MultiEnvDefaultFunc([]string{"TRITON_ACCOUNT", "SDC_ACCOUNT"}, ""), - }, - - "user": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.MultiEnvDefaultFunc([]string{"TRITON_USER", "SDC_USER"}, ""), - }, - - "url": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.MultiEnvDefaultFunc([]string{"MANTA_URL"}, "https://us-east.manta.joyent.com"), - }, - - "key_material": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.MultiEnvDefaultFunc([]string{"TRITON_KEY_MATERIAL", "SDC_KEY_MATERIAL"}, ""), - }, - - "key_id": { - Type: schema.TypeString, - Required: true, - DefaultFunc: schema.MultiEnvDefaultFunc([]string{"TRITON_KEY_ID", "SDC_KEY_ID"}, ""), - }, - - "insecure_skip_tls_verify": { - Type: schema.TypeBool, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("TRITON_SKIP_TLS_VERIFY", false), - }, - - "path": { - Type: schema.TypeString, - Required: true, - }, - - "object_name": { - Type: schema.TypeString, - Optional: true, - Default: "terraform.tfstate", - }, - }, - } - - result := &Backend{Backend: s} - result.Backend.ConfigureFunc = result.configure - return result -} - -type Backend struct { - *schema.Backend - data *schema.ResourceData - - // The fields below are set from configure - storageClient *storage.StorageClient - path string - objectName string -} - -type BackendConfig struct { - AccountId string - Username string - KeyId string - AccountUrl string - KeyMaterial string - SkipTls bool -} - -func (b *Backend) configure(ctx context.Context) error { - if b.path != "" { - return nil - } - - data := schema.FromContextBackendConfig(ctx) - - config := &BackendConfig{ - AccountId: data.Get("account").(string), - AccountUrl: data.Get("url").(string), - KeyId: data.Get("key_id").(string), - SkipTls: data.Get("insecure_skip_tls_verify").(bool), - } - - if v, ok := data.GetOk("user"); ok { - config.Username = v.(string) - } - - if v, ok := data.GetOk("key_material"); ok { - config.KeyMaterial = v.(string) - } - - b.path = data.Get("path").(string) - b.objectName = data.Get("object_name").(string) - - // If object_name is not set, try the deprecated objectName. - if b.objectName == "" { - b.objectName = data.Get("objectName").(string) - } - - var validationError *multierror.Error - - if data.Get("account").(string) == "" { - validationError = multierror.Append(validationError, errors.New("`Account` must be configured for the Triton provider")) - } - if data.Get("key_id").(string) == "" { - validationError = multierror.Append(validationError, errors.New("`Key ID` must be configured for the Triton provider")) - } - if b.path == "" { - validationError = multierror.Append(validationError, errors.New("`Path` must be configured for the Triton provider")) - } - - if validationError != nil { - return validationError - } - - var signer authentication.Signer - var err error - - if config.KeyMaterial == "" { - input := authentication.SSHAgentSignerInput{ - KeyID: config.KeyId, - AccountName: config.AccountId, - Username: config.Username, - } - signer, err = authentication.NewSSHAgentSigner(input) - if err != nil { - return errwrap.Wrapf("Error Creating SSH Agent Signer: {{err}}", err) - } - } else { - var keyBytes []byte - if _, err = os.Stat(config.KeyMaterial); err == nil { - keyBytes, err = ioutil.ReadFile(config.KeyMaterial) - if err != nil { - return fmt.Errorf("Error reading key material from %s: %s", - config.KeyMaterial, err) - } - block, _ := pem.Decode(keyBytes) - if block == nil { - return fmt.Errorf( - "Failed to read key material '%s': no key found", config.KeyMaterial) - } - - if block.Headers["Proc-Type"] == "4,ENCRYPTED" { - return fmt.Errorf( - "Failed to read key '%s': password protected keys are\n"+ - "not currently supported. Please decrypt the key prior to use.", config.KeyMaterial) - } - - } else { - keyBytes = []byte(config.KeyMaterial) - } - - input := authentication.PrivateKeySignerInput{ - KeyID: config.KeyId, - PrivateKeyMaterial: keyBytes, - AccountName: config.AccountId, - Username: config.Username, - } - - signer, err = authentication.NewPrivateKeySigner(input) - if err != nil { - return errwrap.Wrapf("Error Creating SSH Private Key Signer: {{err}}", err) - } - } - - clientConfig := &triton.ClientConfig{ - MantaURL: config.AccountUrl, - AccountName: config.AccountId, - Username: config.Username, - Signers: []authentication.Signer{signer}, - } - triton, err := storage.NewClient(clientConfig) - if err != nil { - return err - } - - b.storageClient = triton - - return nil -} diff --git a/internal/backend/remote-state/manta/backend_state.go b/internal/backend/remote-state/manta/backend_state.go deleted file mode 100644 index b30b250dc73a..000000000000 --- a/internal/backend/remote-state/manta/backend_state.go +++ /dev/null @@ -1,145 +0,0 @@ -package manta - -import ( - "context" - "errors" - "fmt" - "path" - "sort" - "strings" - - tritonErrors "github.com/joyent/triton-go/errors" - "github.com/joyent/triton-go/storage" - - "github.com/hashicorp/terraform/internal/backend" - "github.com/hashicorp/terraform/internal/states" - "github.com/hashicorp/terraform/internal/states/remote" - "github.com/hashicorp/terraform/internal/states/statemgr" -) - -func (b *Backend) Workspaces() ([]string, error) { - result := []string{backend.DefaultStateName} - - objs, err := b.storageClient.Dir().List(context.Background(), &storage.ListDirectoryInput{ - DirectoryName: path.Join(mantaDefaultRootStore, b.path), - }) - if err != nil { - if tritonErrors.IsResourceNotFound(err) { - return result, nil - } - return nil, err - } - - for _, obj := range objs.Entries { - if obj.Type == "directory" && obj.Name != "" { - result = append(result, obj.Name) - } - } - - sort.Strings(result[1:]) - return result, nil -} - -func (b *Backend) DeleteWorkspace(name string) error { - if name == backend.DefaultStateName || name == "" { - return fmt.Errorf("can't delete default state") - } - - //firstly we need to delete the state file - err := b.storageClient.Objects().Delete(context.Background(), &storage.DeleteObjectInput{ - ObjectPath: path.Join(mantaDefaultRootStore, b.statePath(name), b.objectName), - }) - if err != nil { - return err - } - - //then we need to delete the state folder - err = b.storageClient.Objects().Delete(context.Background(), &storage.DeleteObjectInput{ - ObjectPath: path.Join(mantaDefaultRootStore, b.statePath(name)), - }) - if err != nil { - return err - } - - return nil -} - -func (b *Backend) StateMgr(name string) (statemgr.Full, error) { - if name == "" { - return nil, errors.New("missing state name") - } - - client := &RemoteClient{ - storageClient: b.storageClient, - directoryName: b.statePath(name), - keyName: b.objectName, - } - - stateMgr := &remote.State{Client: client} - - //if this isn't the default state name, we need to create the object so - //it's listed by States. - if name != backend.DefaultStateName { - // take a lock on this state while we write it - lockInfo := statemgr.NewLockInfo() - lockInfo.Operation = "init" - lockId, err := client.Lock(lockInfo) - if err != nil { - return nil, fmt.Errorf("failed to lock manta state: %s", err) - } - - // Local helper function so we can call it multiple places - lockUnlock := func(parent error) error { - if err := stateMgr.Unlock(lockId); err != nil { - return fmt.Errorf(strings.TrimSpace(errStateUnlock), lockId, err) - } - return parent - } - - // Grab the value - if err := stateMgr.RefreshState(); err != nil { - err = lockUnlock(err) - return nil, err - } - - // If we have no state, we have to create an empty state - if v := stateMgr.State(); v == nil { - if err := stateMgr.WriteState(states.NewState()); err != nil { - err = lockUnlock(err) - return nil, err - } - if err := stateMgr.PersistState(nil); err != nil { - err = lockUnlock(err) - return nil, err - } - } - - // Unlock, the state should now be initialized - if err := lockUnlock(nil); err != nil { - return nil, err - } - - } - - return stateMgr, nil -} - -func (b *Backend) client() *RemoteClient { - return &RemoteClient{} -} - -func (b *Backend) statePath(name string) string { - if name == backend.DefaultStateName { - return b.path - } - - return path.Join(b.path, name) -} - -const errStateUnlock = ` -Error unlocking Manta state. Lock ID: %s - -Error: %s - -You may have to force-unlock this state in order to use it again. -` diff --git a/internal/backend/remote-state/manta/backend_test.go b/internal/backend/remote-state/manta/backend_test.go deleted file mode 100644 index 180b2aece37c..000000000000 --- a/internal/backend/remote-state/manta/backend_test.go +++ /dev/null @@ -1,130 +0,0 @@ -package manta - -import ( - "context" - "fmt" - "os" - "path" - "testing" - "time" - - "github.com/hashicorp/terraform/internal/backend" - "github.com/joyent/triton-go/storage" -) - -func testACC(t *testing.T) { - skip := os.Getenv("TF_ACC") == "" && os.Getenv("TF_MANTA_TEST") == "" - if skip { - t.Log("Manta backend tests require setting TF_ACC or TF_MANTA_TEST") - t.Skip() - } - skip = os.Getenv("TRITON_ACCOUNT") == "" && os.Getenv("SDC_ACCOUNT") == "" - if skip { - t.Fatal("Manta backend tests require setting TRITON_ACCOUNT or SDC_ACCOUNT") - } - skip = os.Getenv("TRITON_KEY_ID") == "" && os.Getenv("SDC_KEY_ID") == "" - if skip { - t.Fatal("Manta backend tests require setting TRITON_KEY_ID or SDC_KEY_ID") - } -} - -func TestBackend_impl(t *testing.T) { - var _ backend.Backend = new(Backend) -} - -func TestBackend(t *testing.T) { - testACC(t) - - directory := fmt.Sprintf("terraform-remote-manta-test-%x", time.Now().Unix()) - keyName := "testState" - - b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ - "path": directory, - "object_name": keyName, - })).(*Backend) - - createMantaFolder(t, b.storageClient, directory) - defer deleteMantaFolder(t, b.storageClient, directory) - - backend.TestBackendStates(t, b) -} - -func TestBackendLocked(t *testing.T) { - testACC(t) - - directory := fmt.Sprintf("terraform-remote-manta-test-%x", time.Now().Unix()) - keyName := "testState" - - b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ - "path": directory, - "object_name": keyName, - })).(*Backend) - - b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ - "path": directory, - "object_name": keyName, - })).(*Backend) - - createMantaFolder(t, b1.storageClient, directory) - defer deleteMantaFolder(t, b1.storageClient, directory) - - backend.TestBackendStateLocks(t, b1, b2) - backend.TestBackendStateForceUnlock(t, b1, b2) -} - -func createMantaFolder(t *testing.T, mantaClient *storage.StorageClient, directoryName string) { - // Be clear about what we're doing in case the user needs to clean - // this up later. - //t.Logf("creating Manta directory %s", directoryName) - err := mantaClient.Dir().Put(context.Background(), &storage.PutDirectoryInput{ - DirectoryName: path.Join(mantaDefaultRootStore, directoryName), - }) - if err != nil { - t.Fatal("failed to create test Manta directory:", err) - } -} - -func deleteMantaFolder(t *testing.T, mantaClient *storage.StorageClient, directoryName string) { - //warning := "WARNING: Failed to delete the test Manta directory. It may have been left in your Manta account and may incur storage charges. (error was %s)" - - // first we have to get rid of the env objects, or we can't delete the directory - objs, err := mantaClient.Dir().List(context.Background(), &storage.ListDirectoryInput{ - DirectoryName: path.Join(mantaDefaultRootStore, directoryName), - }) - if err != nil { - t.Fatal("failed to retrieve directory listing") - } - - for _, obj := range objs.Entries { - if obj.Type == "directory" { - ojs, err := mantaClient.Dir().List(context.Background(), &storage.ListDirectoryInput{ - DirectoryName: path.Join(mantaDefaultRootStore, directoryName, obj.Name), - }) - if err != nil { - t.Fatal("failed to retrieve directory listing") - } - for _, oj := range ojs.Entries { - err := mantaClient.Objects().Delete(context.Background(), &storage.DeleteObjectInput{ - ObjectPath: path.Join(mantaDefaultRootStore, directoryName, obj.Name, oj.Name), - }) - if err != nil { - t.Fatal(err) - } - } - } - - err := mantaClient.Objects().Delete(context.Background(), &storage.DeleteObjectInput{ - ObjectPath: path.Join(mantaDefaultRootStore, directoryName, obj.Name), - }) - if err != nil { - t.Fatal(err) - } - } - - err = mantaClient.Dir().Delete(context.Background(), &storage.DeleteDirectoryInput{ - DirectoryName: path.Join(mantaDefaultRootStore, directoryName), - }) - if err != nil { - t.Fatal("failed to delete manta directory") - } -} diff --git a/internal/backend/remote-state/manta/client.go b/internal/backend/remote-state/manta/client.go deleted file mode 100644 index 29d36595146b..000000000000 --- a/internal/backend/remote-state/manta/client.go +++ /dev/null @@ -1,200 +0,0 @@ -package manta - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "log" - "path" - - uuid "github.com/hashicorp/go-uuid" - "github.com/hashicorp/terraform/internal/states/remote" - "github.com/hashicorp/terraform/internal/states/statemgr" - tritonErrors "github.com/joyent/triton-go/errors" - "github.com/joyent/triton-go/storage" -) - -const ( - mantaDefaultRootStore = "/stor" - lockFileName = "tflock" -) - -type RemoteClient struct { - storageClient *storage.StorageClient - directoryName string - keyName string - statePath string -} - -func (c *RemoteClient) Get() (*remote.Payload, error) { - output, err := c.storageClient.Objects().Get(context.Background(), &storage.GetObjectInput{ - ObjectPath: path.Join(mantaDefaultRootStore, c.directoryName, c.keyName), - }) - if err != nil { - if tritonErrors.IsResourceNotFound(err) { - return nil, nil - } - return nil, err - } - defer output.ObjectReader.Close() - - buf := bytes.NewBuffer(nil) - if _, err := io.Copy(buf, output.ObjectReader); err != nil { - return nil, fmt.Errorf("Failed to read remote state: %s", err) - } - - payload := &remote.Payload{ - Data: buf.Bytes(), - } - - // If there was no data, then return nil - if len(payload.Data) == 0 { - return nil, nil - } - - return payload, nil - -} - -func (c *RemoteClient) Put(data []byte) error { - contentType := "application/json" - contentLength := int64(len(data)) - - params := &storage.PutObjectInput{ - ContentType: contentType, - ContentLength: uint64(contentLength), - ObjectPath: path.Join(mantaDefaultRootStore, c.directoryName, c.keyName), - ObjectReader: bytes.NewReader(data), - } - - log.Printf("[DEBUG] Uploading remote state to Manta: %#v", params) - err := c.storageClient.Objects().Put(context.Background(), params) - if err != nil { - return err - } - - return nil -} - -func (c *RemoteClient) Delete() error { - err := c.storageClient.Objects().Delete(context.Background(), &storage.DeleteObjectInput{ - ObjectPath: path.Join(mantaDefaultRootStore, c.directoryName, c.keyName), - }) - - return err -} - -func (c *RemoteClient) Lock(info *statemgr.LockInfo) (string, error) { - //At Joyent, we want to make sure that the State directory exists before we interact with it - //We don't expect users to have to create it in advance - //The order of operations of Backend State as follows: - // * Get - if this doesn't exist then we continue as though it's new - // * Lock - we make sure that the state directory exists as it's the entrance to writing to Manta - // * Put - put the state up there - // * Unlock - unlock the directory - //We can always guarantee that the user can put their state in the specified location because of this - err := c.storageClient.Dir().Put(context.Background(), &storage.PutDirectoryInput{ - DirectoryName: path.Join(mantaDefaultRootStore, c.directoryName), - }) - if err != nil { - return "", err - } - - //firstly we want to check that a lock doesn't already exist - lockErr := &statemgr.LockError{} - lockInfo, err := c.getLockInfo() - if err != nil { - if !tritonErrors.IsResourceNotFound(err) { - lockErr.Err = fmt.Errorf("failed to retrieve lock info: %s", err) - return "", lockErr - } - } - - if lockInfo != nil { - lockErr := &statemgr.LockError{ - Err: fmt.Errorf("A lock is already acquired"), - Info: lockInfo, - } - return "", lockErr - } - - info.Path = path.Join(c.directoryName, lockFileName) - - if info.ID == "" { - lockID, err := uuid.GenerateUUID() - if err != nil { - return "", err - } - - info.ID = lockID - } - - data := info.Marshal() - - contentType := "application/json" - contentLength := int64(len(data)) - - params := &storage.PutObjectInput{ - ContentType: contentType, - ContentLength: uint64(contentLength), - ObjectPath: path.Join(mantaDefaultRootStore, c.directoryName, lockFileName), - ObjectReader: bytes.NewReader(data), - ForceInsert: true, - } - - log.Printf("[DEBUG] Creating manta state lock: %#v", params) - err = c.storageClient.Objects().Put(context.Background(), params) - if err != nil { - return "", err - } - - return info.ID, nil -} - -func (c *RemoteClient) Unlock(id string) error { - lockErr := &statemgr.LockError{} - - lockInfo, err := c.getLockInfo() - if err != nil { - lockErr.Err = fmt.Errorf("failed to retrieve lock info: %s", err) - return lockErr - } - lockErr.Info = lockInfo - - if lockInfo.ID != id { - lockErr.Err = fmt.Errorf("lock id %q does not match existing lock", id) - return lockErr - } - - err = c.storageClient.Objects().Delete(context.Background(), &storage.DeleteObjectInput{ - ObjectPath: path.Join(mantaDefaultRootStore, c.directoryName, lockFileName), - }) - - return err -} - -func (c *RemoteClient) getLockInfo() (*statemgr.LockInfo, error) { - output, err := c.storageClient.Objects().Get(context.Background(), &storage.GetObjectInput{ - ObjectPath: path.Join(mantaDefaultRootStore, c.directoryName, lockFileName), - }) - if err != nil { - return nil, err - } - - defer output.ObjectReader.Close() - - buf := bytes.NewBuffer(nil) - if _, err := io.Copy(buf, output.ObjectReader); err != nil { - return nil, fmt.Errorf("Failed to read lock info: %s", err) - } - - lockInfo := &statemgr.LockInfo{} - err = json.Unmarshal(buf.Bytes(), lockInfo) - if err != nil { - return nil, err - } - - return lockInfo, nil -} diff --git a/internal/backend/remote-state/manta/client_test.go b/internal/backend/remote-state/manta/client_test.go deleted file mode 100644 index f5b9a516f51b..000000000000 --- a/internal/backend/remote-state/manta/client_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package manta - -import ( - "testing" - - "fmt" - "time" - - "github.com/hashicorp/terraform/internal/backend" - "github.com/hashicorp/terraform/internal/states/remote" -) - -func TestRemoteClient_impl(t *testing.T) { - var _ remote.Client = new(RemoteClient) - var _ remote.ClientLocker = new(RemoteClient) -} - -func TestRemoteClient(t *testing.T) { - testACC(t) - directory := fmt.Sprintf("terraform-remote-manta-test-%x", time.Now().Unix()) - keyName := "testState" - - b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ - "path": directory, - "object_name": keyName, - })).(*Backend) - - createMantaFolder(t, b.storageClient, directory) - defer deleteMantaFolder(t, b.storageClient, directory) - - state, err := b.StateMgr(backend.DefaultStateName) - if err != nil { - t.Fatal(err) - } - - remote.TestClient(t, state.(*remote.State).Client) -} - -func TestRemoteClientLocks(t *testing.T) { - testACC(t) - directory := fmt.Sprintf("terraform-remote-manta-test-%x", time.Now().Unix()) - keyName := "testState" - - b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ - "path": directory, - "object_name": keyName, - })).(*Backend) - - b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ - "path": directory, - "object_name": keyName, - })).(*Backend) - - createMantaFolder(t, b1.storageClient, directory) - defer deleteMantaFolder(t, b1.storageClient, directory) - - s1, err := b1.StateMgr(backend.DefaultStateName) - if err != nil { - t.Fatal(err) - } - - s2, err := b2.StateMgr(backend.DefaultStateName) - if err != nil { - t.Fatal(err) - } - - remote.TestRemoteLocks(t, s1.(*remote.State).Client, s2.(*remote.State).Client) -} diff --git a/internal/backend/remote-state/swift/backend.go b/internal/backend/remote-state/swift/backend.go deleted file mode 100644 index 6084131338c9..000000000000 --- a/internal/backend/remote-state/swift/backend.go +++ /dev/null @@ -1,485 +0,0 @@ -package swift - -import ( - "context" - "fmt" - "log" - "strconv" - "strings" - "time" - - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/utils/terraform/auth" - - "github.com/hashicorp/terraform/internal/backend" - "github.com/hashicorp/terraform/internal/legacy/helper/schema" - "github.com/hashicorp/terraform/version" -) - -// Use openstackbase.Config as the base/foundation of this provider's -// Config struct. -type Config struct { - auth.Config -} - -// New creates a new backend for Swift remote state. -func New() backend.Backend { - s := &schema.Backend{ - Schema: map[string]*schema.Schema{ - "auth_url": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("OS_AUTH_URL", ""), - Description: descriptions["auth_url"], - }, - - "region_name": { - Type: schema.TypeString, - Optional: true, - Description: descriptions["region_name"], - DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""), - }, - - "user_name": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("OS_USERNAME", ""), - Description: descriptions["user_name"], - }, - - "user_id": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("OS_USER_ID", ""), - Description: descriptions["user_name"], - }, - - "application_credential_id": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("OS_APPLICATION_CREDENTIAL_ID", ""), - Description: descriptions["application_credential_id"], - }, - - "application_credential_name": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("OS_APPLICATION_CREDENTIAL_NAME", ""), - Description: descriptions["application_credential_name"], - }, - - "application_credential_secret": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("OS_APPLICATION_CREDENTIAL_SECRET", ""), - Description: descriptions["application_credential_secret"], - }, - - "tenant_id": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.MultiEnvDefaultFunc([]string{ - "OS_TENANT_ID", - "OS_PROJECT_ID", - }, ""), - Description: descriptions["tenant_id"], - }, - - "tenant_name": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.MultiEnvDefaultFunc([]string{ - "OS_TENANT_NAME", - "OS_PROJECT_NAME", - }, ""), - Description: descriptions["tenant_name"], - }, - - "password": { - Type: schema.TypeString, - Optional: true, - Sensitive: true, - DefaultFunc: schema.EnvDefaultFunc("OS_PASSWORD", ""), - Description: descriptions["password"], - }, - - "token": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.MultiEnvDefaultFunc([]string{ - "OS_TOKEN", - "OS_AUTH_TOKEN", - }, ""), - Description: descriptions["token"], - }, - - "user_domain_name": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("OS_USER_DOMAIN_NAME", ""), - Description: descriptions["user_domain_name"], - }, - - "user_domain_id": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("OS_USER_DOMAIN_ID", ""), - Description: descriptions["user_domain_id"], - }, - - "project_domain_name": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("OS_PROJECT_DOMAIN_NAME", ""), - Description: descriptions["project_domain_name"], - }, - - "project_domain_id": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("OS_PROJECT_DOMAIN_ID", ""), - Description: descriptions["project_domain_id"], - }, - - "domain_id": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("OS_DOMAIN_ID", ""), - Description: descriptions["domain_id"], - }, - - "domain_name": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("OS_DOMAIN_NAME", ""), - Description: descriptions["domain_name"], - }, - - "default_domain": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("OS_DEFAULT_DOMAIN", "default"), - Description: descriptions["default_domain"], - }, - - "insecure": { - Type: schema.TypeBool, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("OS_INSECURE", nil), - Description: descriptions["insecure"], - }, - - "endpoint_type": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("OS_ENDPOINT_TYPE", ""), - }, - - "cacert_file": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("OS_CACERT", ""), - Description: descriptions["cacert_file"], - }, - - "cert": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("OS_CERT", ""), - Description: descriptions["cert"], - }, - - "key": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("OS_KEY", ""), - Description: descriptions["key"], - }, - - "swauth": { - Type: schema.TypeBool, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("OS_SWAUTH", false), - Description: descriptions["swauth"], - }, - - "allow_reauth": { - Type: schema.TypeBool, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("OS_ALLOW_REAUTH", false), - Description: descriptions["allow_reauth"], - }, - - "cloud": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("OS_CLOUD", ""), - Description: descriptions["cloud"], - }, - - "max_retries": { - Type: schema.TypeInt, - Optional: true, - Default: 0, - Description: descriptions["max_retries"], - }, - - "disable_no_cache_header": { - Type: schema.TypeBool, - Optional: true, - Default: false, - Description: descriptions["disable_no_cache_header"], - }, - - "path": { - Type: schema.TypeString, - Optional: true, - Description: descriptions["path"], - Deprecated: "Use container instead", - ConflictsWith: []string{"container"}, - }, - - "container": { - Type: schema.TypeString, - Optional: true, - Description: descriptions["container"], - }, - - "archive_path": { - Type: schema.TypeString, - Optional: true, - Description: descriptions["archive_path"], - Deprecated: "Use archive_container instead", - ConflictsWith: []string{"archive_container"}, - }, - - "archive_container": { - Type: schema.TypeString, - Optional: true, - Description: descriptions["archive_container"], - }, - - "expire_after": { - Type: schema.TypeString, - Optional: true, - Description: descriptions["expire_after"], - }, - - "lock": { - Type: schema.TypeBool, - Optional: true, - Description: "Lock state access", - Default: true, - }, - - "state_name": { - Type: schema.TypeString, - Optional: true, - Description: descriptions["state_name"], - Default: "tfstate.tf", - }, - }, - } - - result := &Backend{Backend: s} - result.Backend.ConfigureFunc = result.configure - return result -} - -var descriptions map[string]string - -func init() { - descriptions = map[string]string{ - "auth_url": "The Identity authentication URL.", - - "region_name": "The name of the Region to use.", - - "user_name": "Username to login with.", - - "user_id": "User ID to login with.", - - "application_credential_id": "Application Credential ID to login with.", - - "application_credential_name": "Application Credential name to login with.", - - "application_credential_secret": "Application Credential secret to login with.", - - "tenant_id": "The ID of the Tenant (Identity v2) or Project (Identity v3)\n" + - "to login with.", - - "tenant_name": "The name of the Tenant (Identity v2) or Project (Identity v3)\n" + - "to login with.", - - "password": "Password to login with.", - - "token": "Authentication token to use as an alternative to username/password.", - - "user_domain_name": "The name of the domain where the user resides (Identity v3).", - - "user_domain_id": "The ID of the domain where the user resides (Identity v3).", - - "project_domain_name": "The name of the domain where the project resides (Identity v3).", - - "project_domain_id": "The ID of the domain where the proejct resides (Identity v3).", - - "domain_id": "The ID of the Domain to scope to (Identity v3).", - - "domain_name": "The name of the Domain to scope to (Identity v3).", - - "default_domain": "The name of the Domain ID to scope to if no other domain is specified. Defaults to `default` (Identity v3).", - - "insecure": "Trust self-signed certificates.", - - "cacert_file": "A Custom CA certificate.", - - "endpoint_type": "The catalog endpoint type to use.", - - "cert": "A client certificate to authenticate with.", - - "key": "A client private key to authenticate with.", - - "swauth": "Use Swift's authentication system instead of Keystone.", - - "allow_reauth": "If set to `true`, OpenStack authorization will be perfomed\n" + - "automatically, if the initial auth token get expired. This is useful,\n" + - "when the token TTL is low or the overall Terraform provider execution\n" + - "time expected to be greater than the initial token TTL.", - - "cloud": "An entry in a `clouds.yaml` file to use.", - - "max_retries": "How many times HTTP connection should be retried until giving up.", - - "disable_no_cache_header": "If set to `true`, the HTTP `Cache-Control: no-cache` header will not be added by default to all API requests.", - - "path": "Swift container path to use.", - - "container": "Swift container to create", - - "archive_path": "Swift container path to archive state to.", - - "archive_container": "Swift container to archive state to.", - - "expire_after": "Archive object expiry duration.", - - "state_name": "Name of state object in container", - } -} - -type Backend struct { - *schema.Backend - - // Fields below are set from configure - client *gophercloud.ServiceClient - archive bool - archiveContainer string - expireSecs int - container string - lock bool - stateName string -} - -func (b *Backend) configure(ctx context.Context) error { - if b.client != nil { - return nil - } - - // Grab the resource data - data := schema.FromContextBackendConfig(ctx) - config := &Config{ - auth.Config{ - CACertFile: data.Get("cacert_file").(string), - ClientCertFile: data.Get("cert").(string), - ClientKeyFile: data.Get("key").(string), - Cloud: data.Get("cloud").(string), - DefaultDomain: data.Get("default_domain").(string), - DomainID: data.Get("domain_id").(string), - DomainName: data.Get("domain_name").(string), - EndpointType: data.Get("endpoint_type").(string), - IdentityEndpoint: data.Get("auth_url").(string), - Password: data.Get("password").(string), - ProjectDomainID: data.Get("project_domain_id").(string), - ProjectDomainName: data.Get("project_domain_name").(string), - Region: data.Get("region_name").(string), - Swauth: data.Get("swauth").(bool), - Token: data.Get("token").(string), - TenantID: data.Get("tenant_id").(string), - TenantName: data.Get("tenant_name").(string), - UserDomainID: data.Get("user_domain_id").(string), - UserDomainName: data.Get("user_domain_name").(string), - Username: data.Get("user_name").(string), - UserID: data.Get("user_id").(string), - ApplicationCredentialID: data.Get("application_credential_id").(string), - ApplicationCredentialName: data.Get("application_credential_name").(string), - ApplicationCredentialSecret: data.Get("application_credential_secret").(string), - AllowReauth: data.Get("allow_reauth").(bool), - MaxRetries: data.Get("max_retries").(int), - DisableNoCacheHeader: data.Get("disable_no_cache_header").(bool), - TerraformVersion: version.Version, - }, - } - - if v, ok := data.GetOkExists("insecure"); ok { - insecure := v.(bool) - config.Insecure = &insecure - } - - if err := config.LoadAndValidate(); err != nil { - return err - } - - // Assign state name - b.stateName = data.Get("state_name").(string) - - // Assign Container - b.container = data.Get("container").(string) - if b.container == "" { - // Check deprecated field - b.container = data.Get("path").(string) - } - - // Store the lock information - b.lock = data.Get("lock").(bool) - - // Enable object archiving? - if archiveContainer, ok := data.GetOk("archive_container"); ok { - log.Printf("[DEBUG] Archive_container set, enabling object versioning") - b.archive = true - b.archiveContainer = archiveContainer.(string) - } else if archivePath, ok := data.GetOk("archive_path"); ok { - log.Printf("[DEBUG] Archive_path set, enabling object versioning") - b.archive = true - b.archiveContainer = archivePath.(string) - } - - // Enable object expiry? - if expireRaw, ok := data.GetOk("expire_after"); ok { - expire := expireRaw.(string) - log.Printf("[DEBUG] Requested that remote state expires after %s", expire) - - if strings.HasSuffix(expire, "d") { - log.Printf("[DEBUG] Got a days expire after duration. Converting to hours") - days, err := strconv.Atoi(expire[:len(expire)-1]) - if err != nil { - return fmt.Errorf("Error converting expire_after value %s to int: %s", expire, err) - } - - expire = fmt.Sprintf("%dh", days*24) - log.Printf("[DEBUG] Expire after %s hours", expire) - } - - expireDur, err := time.ParseDuration(expire) - if err != nil { - log.Printf("[DEBUG] Error parsing duration %s: %s", expire, err) - return fmt.Errorf("Error parsing expire_after duration '%s': %s", expire, err) - } - log.Printf("[DEBUG] Seconds duration = %d", int(expireDur.Seconds())) - b.expireSecs = int(expireDur.Seconds()) - } - - var err error - if b.client, err = config.ObjectStorageV1Client(config.Region); err != nil { - return err - } - - return nil -} diff --git a/internal/backend/remote-state/swift/backend_state.go b/internal/backend/remote-state/swift/backend_state.go deleted file mode 100644 index 719585d855f7..000000000000 --- a/internal/backend/remote-state/swift/backend_state.go +++ /dev/null @@ -1,208 +0,0 @@ -package swift - -import ( - "fmt" - "strings" - - "github.com/hashicorp/terraform/internal/backend" - "github.com/hashicorp/terraform/internal/states" - "github.com/hashicorp/terraform/internal/states/remote" - "github.com/hashicorp/terraform/internal/states/statemgr" -) - -const ( - objectEnvPrefix = "env-" - delimiter = "/" -) - -func (b *Backend) Workspaces() ([]string, error) { - client := &RemoteClient{ - client: b.client, - container: b.container, - archive: b.archive, - archiveContainer: b.archiveContainer, - expireSecs: b.expireSecs, - lockState: b.lock, - } - - // List our container objects - objectNames, err := client.ListObjectsNames(objectEnvPrefix, delimiter) - - if err != nil { - return nil, err - } - - // Find the envs, we use a map since we can get duplicates with - // path suffixes. - envs := map[string]struct{}{} - for _, object := range objectNames { - object = strings.TrimPrefix(object, objectEnvPrefix) - object = strings.TrimSuffix(object, delimiter) - - // Ignore objects that still contain a "/" - // as we dont store states in subdirectories - if idx := strings.Index(object, delimiter); idx >= 0 { - continue - } - - // swift is eventually consistent, thus a deleted object may - // be listed in objectList. To ensure consistency, we query - // each object with a "newest" arg set to true - payload, err := client.get(b.objectName(object)) - if err != nil { - return nil, err - } - if payload == nil { - // object doesn't exist anymore. skipping. - continue - } - - envs[object] = struct{}{} - } - - result := make([]string, 1, len(envs)+1) - result[0] = backend.DefaultStateName - - for k := range envs { - result = append(result, k) - } - - return result, nil -} - -func (b *Backend) DeleteWorkspace(name string) error { - if name == backend.DefaultStateName || name == "" { - return fmt.Errorf("can't delete default state") - } - - client := &RemoteClient{ - client: b.client, - container: b.container, - archive: b.archive, - archiveContainer: b.archiveContainer, - expireSecs: b.expireSecs, - objectName: b.objectName(name), - lockState: b.lock, - } - - // Delete our object - err := client.Delete() - - return err -} - -func (b *Backend) StateMgr(name string) (statemgr.Full, error) { - if name == "" { - return nil, fmt.Errorf("missing state name") - } - - client := &RemoteClient{ - client: b.client, - container: b.container, - archive: b.archive, - archiveContainer: b.archiveContainer, - expireSecs: b.expireSecs, - objectName: b.objectName(name), - lockState: b.lock, - } - - var stateMgr statemgr.Full = &remote.State{Client: client} - - // If we're not locking, disable it - if !b.lock { - stateMgr = &statemgr.LockDisabled{Inner: stateMgr} - } - - // Check to see if this state already exists. - // If we're trying to force-unlock a state, we can't take the lock before - // fetching the state. If the state doesn't exist, we have to assume this - // is a normal create operation, and take the lock at that point. - // - // If we need to force-unlock, but for some reason the state no longer - // exists, the user will have to use openstack tools to manually fix the - // situation. - existing, err := b.Workspaces() - if err != nil { - return nil, err - } - - exists := false - for _, s := range existing { - if s == name { - exists = true - break - } - } - - // We need to create the object so it's listed by States. - if !exists { - // the default state always exists - if name == backend.DefaultStateName { - return stateMgr, nil - } - - // Grab a lock, we use this to write an empty state if one doesn't - // exist already. We have to write an empty state as a sentinel value - // so States() knows it exists. - lockInfo := statemgr.NewLockInfo() - lockInfo.Operation = "init" - lockId, err := stateMgr.Lock(lockInfo) - if err != nil { - return nil, fmt.Errorf("failed to lock state in Swift: %s", err) - } - - // Local helper function so we can call it multiple places - lockUnlock := func(parent error) error { - if err := stateMgr.Unlock(lockId); err != nil { - return fmt.Errorf(strings.TrimSpace(errStateUnlock), lockId, err) - } - - return parent - } - - // Grab the value - if err := stateMgr.RefreshState(); err != nil { - err = lockUnlock(err) - return nil, err - } - - // If we have no state, we have to create an empty state - if v := stateMgr.State(); v == nil { - if err := stateMgr.WriteState(states.NewState()); err != nil { - err = lockUnlock(err) - return nil, err - } - if err := stateMgr.PersistState(nil); err != nil { - err = lockUnlock(err) - return nil, err - } - } - - // Unlock, the state should now be initialized - if err := lockUnlock(nil); err != nil { - return nil, err - } - } - - return stateMgr, nil -} - -func (b *Backend) objectName(name string) string { - if name != backend.DefaultStateName { - name = fmt.Sprintf("%s%s/%s", objectEnvPrefix, name, b.stateName) - } else { - name = b.stateName - } - - return name -} - -const errStateUnlock = ` -Error unlocking Swift state. Lock ID: %s - -Error: %s - -You may have to force-unlock this state in order to use it again. -The Swift backend acquires a lock during initialization to ensure -the minimum required keys are prepared. -` diff --git a/internal/backend/remote-state/swift/backend_test.go b/internal/backend/remote-state/swift/backend_test.go deleted file mode 100644 index 864e3963b1cf..000000000000 --- a/internal/backend/remote-state/swift/backend_test.go +++ /dev/null @@ -1,115 +0,0 @@ -package swift - -import ( - "fmt" - "os" - "testing" - "time" - - "github.com/hashicorp/terraform/internal/backend" -) - -// verify that we are doing ACC tests or the Swift tests specifically -func testACC(t *testing.T) { - skip := os.Getenv("TF_ACC") == "" && os.Getenv("TF_SWIFT_TEST") == "" - if skip { - t.Log("swift backend tests require setting TF_ACC or TF_SWIFT_TEST") - t.Skip() - } - t.Log("swift backend acceptance tests enabled") -} - -func TestBackend_impl(t *testing.T) { - var _ backend.Backend = new(Backend) -} - -func testAccPreCheck(t *testing.T) { - v := os.Getenv("OS_AUTH_URL") - if v == "" { - t.Fatal("OS_AUTH_URL must be set for acceptance tests") - } -} - -func TestBackendConfig(t *testing.T) { - testACC(t) - - // Build config - container := fmt.Sprintf("terraform-state-swift-testconfig-%x", time.Now().Unix()) - archiveContainer := fmt.Sprintf("%s_archive", container) - - config := map[string]interface{}{ - "archive_container": archiveContainer, - "container": container, - } - - b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(config)).(*Backend) - - if b.container != container { - t.Fatal("Incorrect container was provided.") - } - if b.archiveContainer != archiveContainer { - t.Fatal("Incorrect archive_container was provided.") - } -} - -func TestBackend(t *testing.T) { - testACC(t) - - container := fmt.Sprintf("terraform-state-swift-testbackend-%x", time.Now().Unix()) - - be0 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ - "container": container, - })).(*Backend) - - be1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ - "container": container, - })).(*Backend) - - client := &RemoteClient{ - client: be0.client, - container: be0.container, - } - - defer client.deleteContainer() - - backend.TestBackendStates(t, be0) - backend.TestBackendStateLocks(t, be0, be1) - backend.TestBackendStateForceUnlock(t, be0, be1) -} - -func TestBackendArchive(t *testing.T) { - testACC(t) - - container := fmt.Sprintf("terraform-state-swift-testarchive-%x", time.Now().Unix()) - archiveContainer := fmt.Sprintf("%s_archive", container) - - be0 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ - "archive_container": archiveContainer, - "container": container, - })).(*Backend) - - be1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ - "archive_container": archiveContainer, - "container": container, - })).(*Backend) - - defer func() { - client := &RemoteClient{ - client: be0.client, - container: be0.container, - } - - aclient := &RemoteClient{ - client: be0.client, - container: be0.archiveContainer, - } - - defer client.deleteContainer() - client.deleteContainer() - aclient.deleteContainer() - }() - - backend.TestBackendStates(t, be0) - backend.TestBackendStateLocks(t, be0, be1) - backend.TestBackendStateForceUnlock(t, be0, be1) -} diff --git a/internal/backend/remote-state/swift/client.go b/internal/backend/remote-state/swift/client.go deleted file mode 100644 index 603554af2161..000000000000 --- a/internal/backend/remote-state/swift/client.go +++ /dev/null @@ -1,535 +0,0 @@ -package swift - -import ( - "bytes" - "context" - "crypto/md5" - "encoding/json" - "fmt" - "log" - "sync" - "time" - - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers" - "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects" - "github.com/gophercloud/gophercloud/pagination" - "github.com/hashicorp/terraform/internal/states/remote" - "github.com/hashicorp/terraform/internal/states/statemgr" -) - -const ( - consistencyTimeout = 15 - - // Suffix that will be appended to state file paths - // when locking - lockSuffix = ".lock" - - // The TTL associated with this lock. - lockTTL = 60 * time.Second - - // The Interval associated with this lock periodic renew. - lockRenewInterval = 30 * time.Second - - // The amount of time we will retry to delete a container waiting for - // the objects to be deleted. - deleteRetryTimeout = 60 * time.Second - - // delay when polling the objects - deleteRetryPollInterval = 5 * time.Second -) - -// RemoteClient implements the Client interface for an Openstack Swift server. -// Implements "state/remote".ClientLocker -type RemoteClient struct { - client *gophercloud.ServiceClient - container string - archive bool - archiveContainer string - expireSecs int - objectName string - - mu sync.Mutex - // lockState is true if we're using locks - lockState bool - - info *statemgr.LockInfo - - // lockCancel cancels the Context use for lockRenewPeriodic, and is - // called when unlocking, or before creating a new lock if the lock is - // lost. - lockCancel context.CancelFunc -} - -func (c *RemoteClient) ListObjectsNames(prefix string, delim string) ([]string, error) { - if err := c.ensureContainerExists(); err != nil { - return nil, err - } - - // List our raw path - listOpts := objects.ListOpts{ - Full: false, - Prefix: prefix, - Delimiter: delim, - } - - result := []string{} - pager := objects.List(c.client, c.container, listOpts) - // Define an anonymous function to be executed on each page's iteration - err := pager.EachPage(func(page pagination.Page) (bool, error) { - objectList, err := objects.ExtractNames(page) - if err != nil { - return false, fmt.Errorf("Error extracting names from objects from page %+v", err) - } - for _, object := range objectList { - result = append(result, object) - } - return true, nil - }) - - if err != nil { - return nil, err - } - - return result, nil - -} - -func (c *RemoteClient) Get() (*remote.Payload, error) { - payload, err := c.get(c.objectName) - - // 404 response is to be expected if the object doesn't already exist! - if _, ok := err.(gophercloud.ErrDefault404); ok { - log.Println("[DEBUG] Object doesn't exist to download.") - return nil, nil - } - - return payload, err -} - -// swift is eventually constistent. Consistency -// is ensured by the Get func which will always try -// to retrieve the most recent object -func (c *RemoteClient) Put(data []byte) error { - if c.expireSecs != 0 { - log.Printf("[DEBUG] ExpireSecs = %d", c.expireSecs) - return c.put(c.objectName, data, c.expireSecs, "") - } - - return c.put(c.objectName, data, -1, "") - -} - -func (c *RemoteClient) Delete() error { - return c.delete(c.objectName) -} - -func (c *RemoteClient) Lock(info *statemgr.LockInfo) (string, error) { - c.mu.Lock() - defer c.mu.Unlock() - - if !c.lockState { - return "", nil - } - - log.Printf("[DEBUG] Acquiring Lock %#v on %s/%s", info, c.container, c.objectName) - - // This check only is to ensure we strictly follow the specification. - // Terraform shouldn't ever re-lock, so provide errors for the possible - // states if this is called. - if c.info != nil { - // we have an active lock already - return "", fmt.Errorf("state %q already locked", c.lockFilePath()) - } - - // update the path we're using - info.Path = c.lockFilePath() - - if err := c.writeLockInfo(info, lockTTL, "*"); err != nil { - return "", err - } - - log.Printf("[DEBUG] Acquired Lock %s on %s", info.ID, c.objectName) - - c.info = info - - ctx, cancel := context.WithCancel(context.Background()) - c.lockCancel = cancel - - // keep the lock renewed - go c.lockRenewPeriodic(ctx, info) - - return info.ID, nil -} - -func (c *RemoteClient) Unlock(id string) error { - c.mu.Lock() - - if !c.lockState { - return nil - } - - defer func() { - // The periodic lock renew is canceled - // the lockCancel func may not be nil in most usecases - // but can typically be nil when using a second client - // to ForceUnlock the state based on the same lock Id - if c.lockCancel != nil { - c.lockCancel() - } - c.info = nil - c.mu.Unlock() - }() - - log.Printf("[DEBUG] Releasing Lock %s on %s", id, c.objectName) - - info, err := c.lockInfo() - if err != nil { - return c.lockError(fmt.Errorf("failed to retrieve lock info: %s", err), nil) - } - - c.info = info - - // conflicting lock - if info.ID != id { - return c.lockError(fmt.Errorf("lock id %q does not match existing lock", id), info) - } - - // before the lock object deletion is ordered, we shall - // stop periodic renew - if c.lockCancel != nil { - c.lockCancel() - } - - if err = c.delete(c.lockFilePath()); err != nil { - return c.lockError(fmt.Errorf("error deleting lock with %q: %s", id, err), info) - } - - // Swift is eventually consistent; we have to wait until - // the lock is effectively deleted to return, or raise - // an error if deadline is reached. - - warning := ` -WARNING: Waiting for lock deletion timed out. -Swift has accepted the deletion order of the lock %s/%s. -But as it is eventually consistent, complete deletion -may happen later. -` - deadline := time.Now().Add(deleteRetryTimeout) - for { - if time.Now().Before(deadline) { - info, err := c.lockInfo() - - // 404 response is to be expected if the lock deletion - // has been processed - if _, ok := err.(gophercloud.ErrDefault404); ok { - log.Println("[DEBUG] Lock has been deleted.") - return nil - } - - if err != nil { - return err - } - - // conflicting lock - if info.ID != id { - log.Printf("[DEBUG] Someone else has acquired a lock: %v.", info) - return nil - } - - log.Printf("[DEBUG] Lock is still there, delete again and wait %v.", deleteRetryPollInterval) - c.delete(c.lockFilePath()) - time.Sleep(deleteRetryPollInterval) - continue - } - - return fmt.Errorf(warning, c.container, c.lockFilePath()) - } - -} - -func (c *RemoteClient) get(object string) (*remote.Payload, error) { - log.Printf("[DEBUG] Getting object %s/%s", c.container, object) - result := objects.Download(c.client, c.container, object, objects.DownloadOpts{Newest: true}) - - // Extract any errors from result - _, err := result.Extract() - if err != nil { - return nil, err - } - - bytes, err := result.ExtractContent() - if err != nil { - return nil, err - } - - hash := md5.Sum(bytes) - payload := &remote.Payload{ - Data: bytes, - MD5: hash[:md5.Size], - } - - return payload, nil -} - -func (c *RemoteClient) put(object string, data []byte, deleteAfter int, ifNoneMatch string) error { - log.Printf("[DEBUG] Writing object in %s/%s", c.container, object) - if err := c.ensureContainerExists(); err != nil { - return err - } - - contentType := "application/json" - contentLength := int64(len(data)) - - createOpts := objects.CreateOpts{ - Content: bytes.NewReader(data), - ContentType: contentType, - ContentLength: int64(contentLength), - } - - if deleteAfter >= 0 { - createOpts.DeleteAfter = deleteAfter - } - - if ifNoneMatch != "" { - createOpts.IfNoneMatch = ifNoneMatch - } - - result := objects.Create(c.client, c.container, object, createOpts) - if result.Err != nil { - return result.Err - } - - return nil -} - -func (c *RemoteClient) deleteContainer() error { - log.Printf("[DEBUG] Deleting container %s", c.container) - - warning := ` -WARNING: Waiting for container %s deletion timed out. -It may have been left in your Openstack account and may incur storage charges. -error was: %s -` - - deadline := time.Now().Add(deleteRetryTimeout) - - // Swift is eventually consistent; we have to retry until - // all objects are effectively deleted to delete the container - // If we still have objects in the container, or raise - // an error if deadline is reached - for { - if time.Now().Before(deadline) { - // Remove any objects - c.cleanObjects() - - // Delete the container - log.Printf("[DEBUG] Deleting container %s", c.container) - deleteResult := containers.Delete(c.client, c.container) - if deleteResult.Err != nil { - // container is not found, thus has been deleted - if _, ok := deleteResult.Err.(gophercloud.ErrDefault404); ok { - return nil - } - - // 409 http error is raised when deleting a container with - // remaining objects - if respErr, ok := deleteResult.Err.(gophercloud.ErrUnexpectedResponseCode); ok && respErr.Actual == 409 { - time.Sleep(deleteRetryPollInterval) - log.Printf("[DEBUG] Remaining objects, failed to delete container, retrying...") - continue - } - - return fmt.Errorf(warning, deleteResult.Err) - } - return nil - } - - return fmt.Errorf(warning, c.container, "timeout reached") - } - -} - -// Helper function to delete Swift objects within a container -func (c *RemoteClient) cleanObjects() error { - // Get a slice of object names - objectNames, err := c.objectNames(c.container) - if err != nil { - return err - } - - for _, object := range objectNames { - log.Printf("[DEBUG] Deleting object %s from container %s", object, c.container) - result := objects.Delete(c.client, c.container, object, nil) - if result.Err == nil { - continue - } - - // if object is not found, it has already been deleted - if _, ok := result.Err.(gophercloud.ErrDefault404); !ok { - return fmt.Errorf("Error deleting object %s from container %s: %v", object, c.container, result.Err) - } - } - return nil - -} - -func (c *RemoteClient) delete(object string) error { - log.Printf("[DEBUG] Deleting object %s/%s", c.container, object) - - result := objects.Delete(c.client, c.container, object, nil) - - if result.Err != nil { - return result.Err - } - return nil -} - -func (c *RemoteClient) writeLockInfo(info *statemgr.LockInfo, deleteAfter time.Duration, ifNoneMatch string) error { - err := c.put(c.lockFilePath(), info.Marshal(), int(deleteAfter.Seconds()), ifNoneMatch) - - if httpErr, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok && httpErr.Actual == 412 { - log.Printf("[DEBUG] Couldn't write lock %s. One already exists.", info.ID) - info2, err2 := c.lockInfo() - if err2 != nil { - return fmt.Errorf("Couldn't read lock info: %v", err2) - } - - return c.lockError(err, info2) - } - - if err != nil { - return c.lockError(err, nil) - } - - return nil -} - -func (c *RemoteClient) lockError(err error, conflictingLock *statemgr.LockInfo) *statemgr.LockError { - lockErr := &statemgr.LockError{ - Err: err, - Info: conflictingLock, - } - - return lockErr -} - -// lockInfo reads the lock file, parses its contents and returns the parsed -// LockInfo struct. -func (c *RemoteClient) lockInfo() (*statemgr.LockInfo, error) { - raw, err := c.get(c.lockFilePath()) - if err != nil { - return nil, err - } - - info := &statemgr.LockInfo{} - - if err := json.Unmarshal(raw.Data, info); err != nil { - return nil, err - } - - return info, nil -} - -func (c *RemoteClient) lockRenewPeriodic(ctx context.Context, info *statemgr.LockInfo) error { - log.Printf("[DEBUG] Renew lock %v", info) - - waitDur := lockRenewInterval - lastRenewTime := time.Now() - var lastErr error - for { - if time.Since(lastRenewTime) > lockTTL { - return lastErr - } - select { - case <-time.After(waitDur): - c.mu.Lock() - // Unlock may have released the mu.Lock - // in which case we shouldn't renew the lock - select { - case <-ctx.Done(): - log.Printf("[DEBUG] Stopping Periodic renew of lock %v", info) - return nil - default: - } - - info2, err := c.lockInfo() - if _, ok := err.(gophercloud.ErrDefault404); ok { - log.Println("[DEBUG] Lock has expired trying to reacquire.") - err = nil - } - - if err == nil && (info2 == nil || info.ID == info2.ID) { - info2 = info - log.Printf("[DEBUG] Renewing lock %v.", info) - err = c.writeLockInfo(info, lockTTL, "") - } - - c.mu.Unlock() - - if err != nil { - log.Printf("[ERROR] could not reacquire lock (%v): %s", info, err) - waitDur = time.Second - lastErr = err - continue - } - - // conflicting lock - if info2.ID != info.ID { - return c.lockError(fmt.Errorf("lock id %q does not match existing lock %q", info.ID, info2.ID), info2) - } - - waitDur = lockRenewInterval - lastRenewTime = time.Now() - - case <-ctx.Done(): - log.Printf("[DEBUG] Stopping Periodic renew of lock %s", info.ID) - return nil - } - } -} - -func (c *RemoteClient) lockFilePath() string { - return c.objectName + lockSuffix -} - -func (c *RemoteClient) ensureContainerExists() error { - containerOpts := &containers.CreateOpts{} - - if c.archive { - log.Printf("[DEBUG] Creating archive container %s", c.archiveContainer) - result := containers.Create(c.client, c.archiveContainer, nil) - if result.Err != nil { - log.Printf("[DEBUG] Error creating archive container %s: %s", c.archiveContainer, result.Err) - return result.Err - } - - log.Printf("[DEBUG] Enabling Versioning on container %s", c.container) - containerOpts.VersionsLocation = c.archiveContainer - } - - log.Printf("[DEBUG] Creating container %s", c.container) - result := containers.Create(c.client, c.container, containerOpts) - if result.Err != nil { - return result.Err - } - - return nil -} - -// Helper function to get a list of objects in a Swift container -func (c *RemoteClient) objectNames(container string) (objectNames []string, err error) { - _ = objects.List(c.client, container, nil).EachPage(func(page pagination.Page) (bool, error) { - // Get a slice of object names - names, err := objects.ExtractNames(page) - if err != nil { - return false, fmt.Errorf("Error extracting object names from page: %s", err) - } - for _, object := range names { - objectNames = append(objectNames, object) - } - - return true, nil - }) - return -} diff --git a/internal/backend/remote-state/swift/client_test.go b/internal/backend/remote-state/swift/client_test.go deleted file mode 100644 index 76fcf798e25d..000000000000 --- a/internal/backend/remote-state/swift/client_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package swift - -import ( - "fmt" - "testing" - "time" - - "github.com/hashicorp/terraform/internal/backend" - "github.com/hashicorp/terraform/internal/states/remote" -) - -func TestRemoteClient_impl(t *testing.T) { - var _ remote.Client = new(RemoteClient) -} - -func TestRemoteClient(t *testing.T) { - testACC(t) - - container := fmt.Sprintf("terraform-state-swift-testclient-%x", time.Now().Unix()) - - b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ - "container": container, - })).(*Backend) - - state, err := b.StateMgr(backend.DefaultStateName) - if err != nil { - t.Fatal(err) - } - - client := &RemoteClient{ - client: b.client, - container: b.container, - } - - defer client.deleteContainer() - - remote.TestClient(t, state.(*remote.State).Client) -} From 67711c2acfdc28d08fede8688aa5fcc4e07d4f87 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Tue, 30 Aug 2022 12:58:43 -0400 Subject: [PATCH 2/2] remove backend docs Remove references to deprecated backends from docs. --- website/data/language-nav-data.json | 45 ----- .../settings/backends/artifactory.mdx | 60 ------ .../docs/language/settings/backends/etcd.mdx | 46 ----- .../language/settings/backends/etcdv3.mdx | 56 ------ .../docs/language/settings/backends/manta.mdx | 53 ------ .../docs/language/settings/backends/swift.mdx | 176 ------------------ website/docs/language/state/workspaces.mdx | 3 - website/layouts/language.erb | 15 -- 8 files changed, 454 deletions(-) delete mode 100644 website/docs/language/settings/backends/artifactory.mdx delete mode 100644 website/docs/language/settings/backends/etcd.mdx delete mode 100644 website/docs/language/settings/backends/etcdv3.mdx delete mode 100644 website/docs/language/settings/backends/manta.mdx delete mode 100644 website/docs/language/settings/backends/swift.mdx diff --git a/website/data/language-nav-data.json b/website/data/language-nav-data.json index 876ee26733c7..06358abd1d62 100644 --- a/website/data/language-nav-data.json +++ b/website/data/language-nav-data.json @@ -933,10 +933,6 @@ "title": "remote", "href": "/language/settings/backends/remote" }, - { - "title": "artifactory", - "href": "/language/settings/backends/artifactory" - }, { "title": "azurerm", "href": "/language/settings/backends/azurerm" @@ -949,14 +945,6 @@ "title": "cos", "href": "/language/settings/backends/cos" }, - { - "title": "etcd", - "href": "/language/settings/backends/etcd" - }, - { - "title": "etcdv3", - "href": "/language/settings/backends/etcdv3" - }, { "title": "gcs", "href": "/language/settings/backends/gcs" @@ -969,10 +957,6 @@ "title": "Kubernetes", "href": "/language/settings/backends/kubernetes" }, - { - "title": "manta", - "href": "/language/settings/backends/manta" - }, { "title": "oss", "href": "/language/settings/backends/oss" @@ -984,10 +968,6 @@ { "title": "s3", "href": "/language/settings/backends/s3" - }, - { - "title": "swift", - "href": "/language/settings/backends/swift" } ] }, @@ -1001,11 +981,6 @@ "hidden": true, "path": "settings/backends/remote" }, - { - "title": "artifactory", - "hidden": true, - "path": "settings/backends/artifactory" - }, { "title": "azurerm", "hidden": true, @@ -1021,16 +996,6 @@ "hidden": true, "path": "settings/backends/cos" }, - { - "title": "etcd", - "hidden": true, - "path": "settings/backends/etcd" - }, - { - "title": "etcdv3", - "hidden": true, - "path": "settings/backends/etcdv3" - }, { "title": "gcs", "hidden": true, @@ -1046,11 +1011,6 @@ "hidden": true, "path": "settings/backends/kubernetes" }, - { - "title": "manta", - "hidden": true, - "path": "settings/backends/manta" - }, { "title": "oss", "hidden": true, @@ -1065,11 +1025,6 @@ "title": "s3", "hidden": true, "path": "settings/backends/s3" - }, - { - "title": "swift", - "hidden": true, - "path": "settings/backends/swift" } ] } diff --git a/website/docs/language/settings/backends/artifactory.mdx b/website/docs/language/settings/backends/artifactory.mdx deleted file mode 100644 index 574c5b352513..000000000000 --- a/website/docs/language/settings/backends/artifactory.mdx +++ /dev/null @@ -1,60 +0,0 @@ ---- -page_title: 'Backend Type: artifactory' -description: Terraform can store state in artifactory. ---- - -# artifactory (deprecated) - --> **Note:** The `artifactory` backend is deprecated and will be removed in a future Terraform release. - -Stores the state as an artifact in a given repository in -[Artifactory](https://www.jfrog.com/artifactory/). - -Generic HTTP repositories are supported, and state from different -configurations may be kept at different subpaths within the repository. - --> **Note:** The URL must include the path to the Artifactory installation. -It will likely end in `/artifactory`. - -This backend does **not** support [state locking](/language/state/locking). - -## Example Configuration - -```hcl -terraform { - backend "artifactory" { - username = "SheldonCooper" - password = "AmyFarrahFowler" - url = "https://custom.artifactoryonline.com/artifactory" - repo = "foo" - subpath = "terraform-bar" - } -} -``` - -## Data Source Configuration - -```hcl -data "terraform_remote_state" "foo" { - backend = "artifactory" - config = { - username = "SheldonCooper" - password = "AmyFarrahFowler" - url = "https://custom.artifactoryonline.com/artifactory" - repo = "foo" - subpath = "terraform-bar" - } -} -``` - -## Configuration Variables - -!> **Warning:** We recommend using environment variables to supply credentials and other sensitive data. If you use `-backend-config` or hardcode these values directly in your configuration, Terraform will include these values in both the `.terraform` subdirectory and in plan files. Refer to [Credentials and Sensitive Data](/language/settings/backends/configuration#credentials-and-sensitive-data) for details. - -The following configuration options / environment variables are supported: - -- `username` / `ARTIFACTORY_USERNAME` (Required) - The username -- `password` / `ARTIFACTORY_PASSWORD` (Required) - The password -- `url` / `ARTIFACTORY_URL` (Required) - The URL. Note that this is the base url to artifactory not the full repo and subpath. -- `repo` (Required) - The repository name -- `subpath` (Required) - Path within the repository diff --git a/website/docs/language/settings/backends/etcd.mdx b/website/docs/language/settings/backends/etcd.mdx deleted file mode 100644 index 9304d6486535..000000000000 --- a/website/docs/language/settings/backends/etcd.mdx +++ /dev/null @@ -1,46 +0,0 @@ ---- -page_title: 'Backend Type: etcd' -description: Terraform can store state remotely in etcd 2.x. ---- - -# etcd (deprecated) - --> **Note:** The `etcd` backend is deprecated and will be removed in a future Terraform release. - -Stores the state in [etcd 2.x](https://coreos.com/etcd/docs/latest/v2/README.html) at a given path. - -This backend does **not** support [state locking](/language/state/locking). - -## Example Configuration - -```hcl -terraform { - backend "etcd" { - path = "path/to/terraform.tfstate" - endpoints = "http://one:4001 http://two:4001" - } -} -``` - -## Data Source Configuration - -```hcl -data "terraform_remote_state" "foo" { - backend = "etcd" - config = { - path = "path/to/terraform.tfstate" - endpoints = "http://one:4001 http://two:4001" - } -} -``` - -## Configuration Variables - -!> **Warning:** We recommend using environment variables to supply credentials and other sensitive data. If you use `-backend-config` or hardcode these values directly in your configuration, Terraform will include these values in both the `.terraform` subdirectory and in plan files. Refer to [Credentials and Sensitive Data](/language/settings/backends/configuration#credentials-and-sensitive-data) for details. - -The following configuration options are supported: - -- `path` - (Required) The path where to store the state -- `endpoints` - (Required) A space-separated list of the etcd endpoints -- `username` - (Optional) The username -- `password` - (Optional) The password diff --git a/website/docs/language/settings/backends/etcdv3.mdx b/website/docs/language/settings/backends/etcdv3.mdx deleted file mode 100644 index 04f46b381fae..000000000000 --- a/website/docs/language/settings/backends/etcdv3.mdx +++ /dev/null @@ -1,56 +0,0 @@ ---- -page_title: 'Backend Type: etcdv3' -description: Terraform can store state remotely in etcd 3.x. ---- - -# etcdv3 (deprecated) - --> **Note:** The `etcdv3` backend is deprecated and will be removed in a future Terraform release. - -Stores the state in the [etcd](https://etcd.io/) KV store with a given prefix. - -This backend supports [state locking](/language/state/locking). - -## Example Configuration - -```hcl -terraform { - backend "etcdv3" { - endpoints = ["etcd-1:2379", "etcd-2:2379", "etcd-3:2379"] - lock = true - prefix = "terraform-state/" - } -} -``` - -Note that for the access credentials we recommend using a -[partial configuration](/language/settings/backends/configuration#partial-configuration). - -## Data Source Configuration - -```hcl -data "terraform_remote_state" "foo" { - backend = "etcdv3" - config = { - endpoints = ["etcd-1:2379", "etcd-2:2379", "etcd-3:2379"] - lock = true - prefix = "terraform-state/" - } -} -``` - -## Configuration Variables - -!> **Warning:** We recommend using environment variables to supply credentials and other sensitive data. If you use `-backend-config` or hardcode these values directly in your configuration, Terraform will include these values in both state and plan files. Refer to [Credentials and Sensitive Data](/language/settings/backends/configuration#credentials-and-sensitive-data) for details. - -The following configuration options / environment variables are supported: - -- `endpoints` - (Required) The list of 'etcd' endpoints which to connect to. -- `username` / `ETCDV3_USERNAME` - (Optional) Username used to connect to the etcd cluster. -- `password` / `ETCDV3_PASSWORD` - (Optional) Password used to connect to the etcd cluster. -- `prefix` - (Optional) An optional prefix to be added to keys when to storing state in etcd. Defaults to `""`. -- `lock` - (Optional) Whether to lock state access. Defaults to `true`. -- `cacert_path` - (Optional) The path to a PEM-encoded CA bundle with which to verify certificates of TLS-enabled etcd servers. -- `cert_path` - (Optional) The path to a PEM-encoded certificate to provide to etcd for secure client identification. -- `key_path` - (Optional) The path to a PEM-encoded key to provide to etcd for secure client identification. -- `max_request_bytes` - (Optional) The max request size to send to etcd. This can be increased to enable storage of larger state. You must set the corresponding server-side flag [--max-request-bytes](https://etcd.io/docs/current/dev-guide/limit/#request-size-limit) as well and the value should be less than the client setting. Defaults to `2097152` (2.0 MiB). **Please Note:** Increasing etcd's request size limit may negatively impact overall latency. diff --git a/website/docs/language/settings/backends/manta.mdx b/website/docs/language/settings/backends/manta.mdx deleted file mode 100644 index f4b751cf3fe5..000000000000 --- a/website/docs/language/settings/backends/manta.mdx +++ /dev/null @@ -1,53 +0,0 @@ ---- -page_title: 'Backend Type: manta' -description: Terraform can store state in manta. ---- - -# manta (deprecated) - --> **Note:** The `manta` backend is deprecated and will be removed in a future Terraform release. - -Stores the state as an artifact in [Manta](https://www.joyent.com/manta). - -This backend supports [state locking](/language/state/locking), with locking within Manta. - -## Example Configuration - -```hcl -terraform { - backend "manta" { - path = "random/path" - object_name = "terraform.tfstate" - } -} -``` - -Note that for the access credentials we recommend using a -[partial configuration](/language/settings/backends/configuration#partial-configuration). - -## Data Source Configuration - -```hcl -data "terraform_remote_state" "foo" { - backend = "manta" - config = { - path = "random/path" - object_name = "terraform.tfstate" - } -} -``` - -## Configuration Variables - -!> **Warning:** We recommend using environment variables to supply credentials and other sensitive data. If you use `-backend-config` or hardcode these values directly in your configuration, Terraform will include these values in both the `.terraform` subdirectory and in plan files. Refer to [Credentials and Sensitive Data](/language/settings/backends/configuration#credentials-and-sensitive-data) for details. - -The following configuration options are supported: - -- `account` - (Required) This is the name of the Manta account. It can also be provided via the `SDC_ACCOUNT` or `TRITON_ACCOUNT` environment variables. -- `user` - (Optional) The username of the Triton account used to authenticate with the Triton API. It can also be provided via the `SDC_USER` or `TRITON_USER` environment variables. -- `url` - (Optional) The Manta API Endpoint. It can also be provided via the `MANTA_URL` environment variable. Defaults to `https://us-east.manta.joyent.com`. -- `key_material` - (Optional) This is the private key of an SSH key associated with the Triton account to be used. If this is not set, the private key corresponding to the fingerprint in key_id must be available via an SSH Agent. Can be set via the `SDC_KEY_MATERIAL` or `TRITON_KEY_MATERIAL` environment variables. -- `key_id` - (Required) This is the fingerprint of the public key matching the key specified in key_path. It can be obtained via the command ssh-keygen -l -E md5 -f /path/to/key. Can be set via the `SDC_KEY_ID` or `TRITON_KEY_ID` environment variables. -- `insecure_skip_tls_verify` - (Optional) This allows skipping TLS verification of the Triton endpoint. It is useful when connecting to a temporary Triton installation such as Cloud-On-A-Laptop which does not generally use a certificate signed by a trusted root CA. Defaults to `false`. -- `path` - (Required) The path relative to your private storage directory (`/$MANTA_USER/stor`) where the state file will be stored. **Please Note:** If this path does not exist, then the backend will create this folder location as part of backend creation. -- `object_name` - (Optional) The name of the state file (defaults to `terraform.tfstate`) diff --git a/website/docs/language/settings/backends/swift.mdx b/website/docs/language/settings/backends/swift.mdx deleted file mode 100644 index eb20bae88c5a..000000000000 --- a/website/docs/language/settings/backends/swift.mdx +++ /dev/null @@ -1,176 +0,0 @@ ---- -page_title: 'Backend Type: swift' -description: Terraform can store state remotely in Swift. ---- - -# swift (deprecated) - --> **Note:** The `swift` backend is deprecated and will be removed in a future Terraform release. - -Stores the state as an artifact in [Swift](http://docs.openstack.org/developer/swift/latest/). - -This backend supports [state locking](/language/state/locking). - -~> Warning! It is highly recommended that you enable [Object Versioning](https://docs.openstack.org/developer/swift/latest/overview_object_versioning.html) by setting the [`archive_container`](/language/settings/backends/swift#archive_container) configuration. This allows for state recovery in the case of accidental deletions and human error. - -## Example Configuration - -```hcl -terraform { - backend "swift" { - container = "terraform-state" - archive_container = "terraform-state-archive" - } -} -``` - -This will create a container called `terraform-state` and an object within that container called `tfstate.tf`. It will enable versioning using the `terraform-state-archive` container to contain the older version. - -For the access credentials we recommend using a -[partial configuration](/language/settings/backends/configuration#partial-configuration). - -## Data Source Configuration - -```hcl -data "terraform_remote_state" "foo" { - backend = "swift" - config = { - container = "terraform_state" - archive_container = "terraform_state-archive" - } -} -``` - -## Configuration Variables - -!> **Warning:** We recommend using environment variables to supply credentials and other sensitive data. If you use `-backend-config` or hardcode these values directly in your configuration, Terraform will include these values in both the `.terraform` subdirectory and in plan files. Refer to [Credentials and Sensitive Data](/language/settings/backends/configuration#credentials-and-sensitive-data) for details. - -The following configuration options are supported: - -* `auth_url` - (Optional) The Identity authentication URL. If omitted, the - `OS_AUTH_URL` environment variable is used. - -* `cloud` - (Optional; required if `auth_url` is not specified) An entry in a - `clouds.yaml` file. See the OpenStack `os-client-config` - [documentation](https://docs.openstack.org/os-client-config/latest/user/configuration.html) - for more information about `clouds.yaml` files. If omitted, the `OS_CLOUD` - environment variable is used. - -* `region_name` - (Optional) - The region in which to store `terraform.tfstate`. If - omitted, the `OS_REGION_NAME` environment variable is used. - -* `container` - (Required) The name of the container to create for storing - the Terraform state file. - -* `state_name` - (Optional) The name of the state file in the container. - Defaults to `tfstate.tf`. - -* `path` - (Optional) DEPRECATED: Use `container` instead. - The name of the container to create in order to store the state file. - -* `user_name` - (Optional) The Username to login with. If omitted, the - `OS_USERNAME` environment variable is used. - -* `user_id` - (Optional) The User ID to login with. If omitted, the - `OS_USER_ID` environment variable is used. - -* `application_credential_id` - (Optional) (Identity v3 only) The ID of an - application credential to authenticate with. An - `application_credential_secret` has to bet set along with this parameter. - -* `application_credential_name` - (Optional) (Identity v3 only) The name of an - application credential to authenticate with. Requires `user_id`, or - `user_name` and `user_domain_name` (or `user_domain_id`) to be set. - -* `application_credential_secret` - (Optional) (Identity v3 only) The secret of an - application credential to authenticate with. Required by - `application_credential_id` or `application_credential_name`. - -* `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. - -* `password` - (Optional) The Password to login with. If omitted, the - `OS_PASSWORD` environment variable is used. - -* `token` - (Optional; Required if not using `user_name` and `password`) - A token is an expiring, temporary means of access issued via the Keystone - service. By specifying a token, you do not have to specify a username/password - combination, since the token was already created by a username/password out of - band of Terraform. If omitted, the `OS_TOKEN` or `OS_AUTH_TOKEN` environment - variables are used. - -* `user_domain_name` - (Optional) The domain name where the user is located. If - omitted, the `OS_USER_DOMAIN_NAME` environment variable is checked. - -* `user_domain_id` - (Optional) The domain ID where the user is located. If - omitted, the `OS_USER_DOMAIN_ID` environment variable is checked. - -* `project_domain_name` - (Optional) The domain name where the project is - located. If omitted, the `OS_PROJECT_DOMAIN_NAME` environment variable is - checked. - -* `project_domain_id` - (Optional) The domain ID where the project is located - If omitted, the `OS_PROJECT_DOMAIN_ID` environment variable is checked. - -* `domain_id` - (Optional) The ID of the Domain to scope to (Identity v3). 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`. - -* `default_domain` - (Optional) The ID of the Domain to scope to if no other - domain is specified (Identity v3). If omitted, the environment variable - `OS_DEFAULT_DOMAIN` is checked or a default value of "default" will be - used. - -* `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. 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 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. - -* `endpoint_type` - (Optional) Specify which type of endpoint to use from the - service catalog. It can be set using the OS_ENDPOINT_TYPE environment - variable. If not set, public endpoints is used. - -* `swauth` - (Optional) Set to `true` to authenticate against Swauth, a - Swift-native authentication system. If omitted, the `OS_SWAUTH` environment - variable is used. You must also set `username` to the Swauth/Swift username - such as `username:project`. Set the `password` to the Swauth/Swift key. This feature supports v1.0 of the Swauth system. - Finally, set `auth_url` as the location of the Swift service. - -* `disable_no_cache_header` - (Optional) If set to `true`, the HTTP - `Cache-Control: no-cache` header will not be added by default to all API requests. - If omitted this header is added to all API requests to force HTTP caches (if any) - to go upstream instead of serving cached responses. - -* `allow_reauth` - (Optional) If set to `true`, OpenStack authorization will be - perfomed automatically, if the initial auth token get expired. This is useful, - when the token TTL is low or the overall Terraform provider execution time - expected to be greater than the initial token TTL. - -* `archive_container` - (Optional) The container to create to store archived copies - of the Terraform state file. If specified, Swift [object versioning](https://docs.openstack.org/developer/swift/latest/overview_object_versioning.html) is enabled on the container created at `container`. - -* `archive_path` - (Optional) DEPRECATED: Use `archive_container` instead. - The path to store archived copied of `terraform.tfstate`. If specified, - Swift [object versioning](https://docs.openstack.org/developer/swift/latest/overview_object_versioning.html) is enabled on the container created at `path`. - -* `expire_after` - (Optional) How long should the `terraform.tfstate` created at `container` - be retained for? If specified, Swift [expiring object support](https://docs.openstack.org/developer/swift/latest/overview_expiring_objects.html) is enabled on the state. Supported durations: `m` - Minutes, `h` - Hours, `d` - Days. - ~> **NOTE:** Since Terraform is inherently stateful - we'd strongly recommend against auto-expiring Statefiles. diff --git a/website/docs/language/state/workspaces.mdx b/website/docs/language/state/workspaces.mdx index 424aaed64587..ecb3421751e6 100644 --- a/website/docs/language/state/workspaces.mdx +++ b/website/docs/language/state/workspaces.mdx @@ -27,16 +27,13 @@ Multiple workspaces are currently supported by the following backends: * [AzureRM](/language/settings/backends/azurerm) * [Consul](/language/settings/backends/consul) * [COS](/language/settings/backends/cos) -* [etcdv3](/language/settings/backends/etcdv3) * [GCS](/language/settings/backends/gcs) * [Kubernetes](/language/settings/backends/kubernetes) * [Local](/language/settings/backends/local) -* [Manta](/language/settings/backends/manta) * [OSS](/language/settings/backends/oss) * [Postgres](/language/settings/backends/pg) * [Remote](/language/settings/backends/remote) * [S3](/language/settings/backends/s3) -* [Swift](/language/settings/backends/swift) In the 0.9 line of Terraform releases, this concept was known as "environment". It was renamed in 0.10 based on feedback about confusion caused by the diff --git a/website/layouts/language.erb b/website/layouts/language.erb index f2bf83dede52..0d5c25e8c449 100644 --- a/website/layouts/language.erb +++ b/website/layouts/language.erb @@ -878,9 +878,6 @@
  • remote
  • -
  • - artifactory -
  • azurerm
  • @@ -890,12 +887,6 @@
  • cos
  • -
  • - etcd -
  • -
  • - etcdv3 -
  • gcs
  • @@ -905,9 +896,6 @@
  • kubernetes
  • -
  • - manta -
  • oss
  • @@ -917,9 +905,6 @@
  • s3
  • -
  • - swift -