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 fed0d2a1b7..aa03056329 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 93a78d52b3..f4fd3eb5b7 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 729cd0ace2..7d05f64dca 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), }