From 54736b068bfb424fbddf2c94c56ff631e284a2c4 Mon Sep 17 00:00:00 2001 From: Sander van Harmelen Date: Mon, 18 Feb 2019 16:01:06 +0100 Subject: [PATCH] backend/remote: use `state.v2` for remote state only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The API surface area is much smaller when we use the remote backend for remote state only. So in order to try and prevent any backwards incompatibilities when TF runs inside of TFE, we’ve split up the discovery services into `state.v2` (which can be used for remote state only configurations, so when running in TFE) and `tfe.v2.1` (which can be used for all remote configurations). --- backend/remote-state/etcdv2/backend.go | 2 +- backend/remote/backend.go | 19 ++++++++++++++----- backend/remote/testing.go | 9 ++++++--- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/backend/remote-state/etcdv2/backend.go b/backend/remote-state/etcdv2/backend.go index fed0d2a1b..aa0305632 100644 --- a/backend/remote-state/etcdv2/backend.go +++ b/backend/remote-state/etcdv2/backend.go @@ -24,7 +24,7 @@ func New() backend.Backend { "endpoints": &schema.Schema{ Type: schema.TypeString, Required: true, - Description: "A space-separated list of the etcd endpoints", + Description: "A space-separated list of the etcd endpoints", }, "username": &schema.Schema{ Type: schema.TypeString, diff --git a/backend/remote/backend.go b/backend/remote/backend.go index 93a78d52b..f4fd3eb5b 100644 --- a/backend/remote/backend.go +++ b/backend/remote/backend.go @@ -33,6 +33,7 @@ import ( const ( defaultHostname = "app.terraform.io" defaultParallelism = 10 + stateServiceID = "state.v2" tfeServiceID = "tfe.v2.1" ) @@ -211,9 +212,17 @@ func (b *Remote) Configure(obj cty.Value) tfdiags.Diagnostics { } } + // Determine if we are forced to use the local backend. + b.forceLocal = os.Getenv("TF_FORCE_LOCAL_BACKEND") != "" + + serviceID := tfeServiceID + if b.forceLocal { + serviceID = stateServiceID + } + // Discover the service URL for this host to confirm that it provides // a remote backend API and to get the version constraints. - service, constraints, err := b.discover() + service, constraints, err := b.discover(serviceID) // First check any contraints we might have received. if constraints != nil { @@ -313,13 +322,13 @@ func (b *Remote) Configure(obj cty.Value) tfdiags.Diagnostics { // Configure a local backend for when we need to run operations locally. b.local = backendLocal.NewWithBackend(b) - b.forceLocal = !entitlements.Operations || os.Getenv("TF_FORCE_LOCAL_BACKEND") != "" + b.forceLocal = b.forceLocal || !entitlements.Operations return diags } // discover the remote backend API service URL and version constraints. -func (b *Remote) discover() (*url.URL, *disco.Constraints, error) { +func (b *Remote) discover(serviceID string) (*url.URL, *disco.Constraints, error) { hostname, err := svchost.ForComparison(b.hostname) if err != nil { return nil, nil, err @@ -330,7 +339,7 @@ func (b *Remote) discover() (*url.URL, *disco.Constraints, error) { return nil, nil, err } - service, err := host.ServiceURL(tfeServiceID) + service, err := host.ServiceURL(serviceID) // Return the error, unless its a disco.ErrVersionNotSupported error. if _, ok := err.(*disco.ErrVersionNotSupported); !ok && err != nil { return nil, nil, err @@ -338,7 +347,7 @@ func (b *Remote) discover() (*url.URL, *disco.Constraints, error) { // We purposefully ignore the error and return the previous error, as // checking for version constraints is considered optional. - constraints, _ := host.VersionConstraints(tfeServiceID, "terraform") + constraints, _ := host.VersionConstraints(serviceID, "terraform") return service, constraints, err } diff --git a/backend/remote/testing.go b/backend/remote/testing.go index 729cd0ace..7d05f64dc 100644 --- a/backend/remote/testing.go +++ b/backend/remote/testing.go @@ -6,6 +6,7 @@ import ( "io" "net/http" "net/http/httptest" + "path" "testing" tfe "github.com/hashicorp/go-tfe" @@ -184,6 +185,7 @@ func testServer(t *testing.T) *httptest.Server { mux.HandleFunc("/well-known/terraform.json", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") io.WriteString(w, `{ + "state.v2": "/api/v2/", "tfe.v2.1": "/api/v2/", "versions.v1": "/v1/versions/" }`) @@ -192,12 +194,12 @@ func testServer(t *testing.T) *httptest.Server { // Respond to service version constraints calls. mux.HandleFunc("/v1/versions/", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") - io.WriteString(w, `{ - "service": "tfe.v2.1", + io.WriteString(w, fmt.Sprintf(`{ + "service": "%s", "product": "terraform", "minimum": "0.1.0", "maximum": "10.0.0" -}`) +}`, path.Base(r.URL.Path))) }) // Respond to the initial query to read the hashicorp org entitlements. @@ -259,6 +261,7 @@ func testServer(t *testing.T) *httptest.Server { // localhost to a local test server. func testDisco(s *httptest.Server) *disco.Disco { services := map[string]interface{}{ + "state.v2": fmt.Sprintf("%s/api/v2/", s.URL), "tfe.v2.1": fmt.Sprintf("%s/api/v2/", s.URL), "versions.v1": fmt.Sprintf("%s/v1/versions/", s.URL), }