backend/remote: use `state.v2` for remote state only

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).
This commit is contained in:
Sander van Harmelen 2019-02-18 16:01:06 +01:00
parent e6777105b7
commit 54736b068b
3 changed files with 21 additions and 9 deletions

View File

@ -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<Paste>",
Description: "A space-separated list of the etcd endpoints",
},
"username": &schema.Schema{
Type: schema.TypeString,

View File

@ -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
}

View File

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