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), }