github.com/ctrox/terraform@v0.11.12-beta1/backend/remote/testing.go (about) 1 package remote 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "net/http" 8 "net/http/httptest" 9 "testing" 10 11 tfe "github.com/hashicorp/go-tfe" 12 "github.com/hashicorp/terraform/backend" 13 "github.com/hashicorp/terraform/state/remote" 14 "github.com/hashicorp/terraform/svchost" 15 "github.com/hashicorp/terraform/svchost/auth" 16 "github.com/hashicorp/terraform/svchost/disco" 17 "github.com/mitchellh/cli" 18 19 backendLocal "github.com/hashicorp/terraform/backend/local" 20 ) 21 22 const ( 23 testCred = "test-auth-token" 24 ) 25 26 var ( 27 tfeHost = svchost.Hostname(defaultHostname) 28 credsSrc = auth.StaticCredentialsSource(map[svchost.Hostname]map[string]interface{}{ 29 tfeHost: {"token": testCred}, 30 }) 31 ) 32 33 func testInput(t *testing.T, answers map[string]string) *mockInput { 34 return &mockInput{answers: answers} 35 } 36 37 func testBackendDefault(t *testing.T) *Remote { 38 c := map[string]interface{}{ 39 "organization": "hashicorp", 40 "workspaces": []interface{}{ 41 map[string]interface{}{ 42 "name": "prod", 43 }, 44 }, 45 } 46 return testBackend(t, c) 47 } 48 49 func testBackendNoDefault(t *testing.T) *Remote { 50 c := map[string]interface{}{ 51 "organization": "hashicorp", 52 "workspaces": []interface{}{ 53 map[string]interface{}{ 54 "prefix": "my-app-", 55 }, 56 }, 57 } 58 return testBackend(t, c) 59 } 60 61 func testBackendNoOperations(t *testing.T) *Remote { 62 c := map[string]interface{}{ 63 "organization": "no-operations", 64 "workspaces": []interface{}{ 65 map[string]interface{}{ 66 "name": "prod", 67 }, 68 }, 69 } 70 return testBackend(t, c) 71 } 72 73 func testRemoteClient(t *testing.T) remote.Client { 74 b := testBackendDefault(t) 75 raw, err := b.State(backend.DefaultStateName) 76 if err != nil { 77 t.Fatalf("error: %v", err) 78 } 79 s := raw.(*remote.State) 80 return s.Client 81 } 82 83 func testBackend(t *testing.T, c map[string]interface{}) *Remote { 84 s := testServer(t) 85 b := New(testDisco(s)) 86 87 // Configure the backend so the client is created. 88 backend.TestBackendConfig(t, b, c) 89 90 // Get a new mock client. 91 mc := newMockClient() 92 93 // Replace the services we use with our mock services. 94 b.CLI = cli.NewMockUi() 95 b.client.Applies = mc.Applies 96 b.client.ConfigurationVersions = mc.ConfigurationVersions 97 b.client.Organizations = mc.Organizations 98 b.client.Plans = mc.Plans 99 b.client.PolicyChecks = mc.PolicyChecks 100 b.client.Runs = mc.Runs 101 b.client.StateVersions = mc.StateVersions 102 b.client.Workspaces = mc.Workspaces 103 104 // Set local to a local test backend. 105 b.local = testLocalBackend(t, b) 106 107 ctx := context.Background() 108 109 // Create the organization. 110 _, err := b.client.Organizations.Create(ctx, tfe.OrganizationCreateOptions{ 111 Name: tfe.String(b.organization), 112 }) 113 if err != nil { 114 t.Fatalf("error: %v", err) 115 } 116 117 // Create the default workspace if required. 118 if b.workspace != "" { 119 _, err = b.client.Workspaces.Create(ctx, b.organization, tfe.WorkspaceCreateOptions{ 120 Name: tfe.String(b.workspace), 121 }) 122 if err != nil { 123 t.Fatalf("error: %v", err) 124 } 125 } 126 127 return b 128 } 129 130 func testLocalBackend(t *testing.T, remote *Remote) backend.Enhanced { 131 b := backendLocal.NewWithBackend(remote) 132 b.CLI = remote.CLI 133 134 // Add a test provider to the local backend. 135 backendLocal.TestLocalProvider(t, b, "null") 136 137 return b 138 } 139 140 // testServer returns a *httptest.Server used for local testing. 141 func testServer(t *testing.T) *httptest.Server { 142 mux := http.NewServeMux() 143 144 // Respond to service discovery calls. 145 mux.HandleFunc("/well-known/terraform.json", func(w http.ResponseWriter, r *http.Request) { 146 w.Header().Set("Content-Type", "application/json") 147 io.WriteString(w, `{ 148 "tfe.v2.1": "/api/v2/", 149 "versions.v1": "/v1/versions/" 150 }`) 151 }) 152 153 // Respond to service version constraints calls. 154 mux.HandleFunc("/v1/versions/", func(w http.ResponseWriter, r *http.Request) { 155 w.Header().Set("Content-Type", "application/json") 156 io.WriteString(w, `{ 157 "service": "tfe.v2.1", 158 "product": "terraform", 159 "minimum": "0.11.8", 160 "maximum": "0.11.11" 161 }`) 162 }) 163 164 // Respond to the initial query to read the hashicorp org entitlements. 165 mux.HandleFunc("/api/v2/organizations/hashicorp/entitlement-set", func(w http.ResponseWriter, r *http.Request) { 166 w.Header().Set("Content-Type", "application/vnd.api+json") 167 io.WriteString(w, `{ 168 "data": { 169 "id": "org-GExadygjSbKP8hsY", 170 "type": "entitlement-sets", 171 "attributes": { 172 "operations": true, 173 "private-module-registry": true, 174 "sentinel": true, 175 "state-storage": true, 176 "teams": true, 177 "vcs-integrations": true 178 } 179 } 180 }`) 181 }) 182 183 // Respond to the initial query to read the no-operations org entitlements. 184 mux.HandleFunc("/api/v2/organizations/no-operations/entitlement-set", func(w http.ResponseWriter, r *http.Request) { 185 w.Header().Set("Content-Type", "application/vnd.api+json") 186 io.WriteString(w, `{ 187 "data": { 188 "id": "org-ufxa3y8jSbKP8hsT", 189 "type": "entitlement-sets", 190 "attributes": { 191 "operations": false, 192 "private-module-registry": true, 193 "sentinel": true, 194 "state-storage": true, 195 "teams": true, 196 "vcs-integrations": true 197 } 198 } 199 }`) 200 }) 201 202 // All tests that are assumed to pass will use the hashicorp organization, 203 // so for all other organization requests we will return a 404. 204 mux.HandleFunc("/api/v2/organizations/", func(w http.ResponseWriter, r *http.Request) { 205 w.WriteHeader(404) 206 io.WriteString(w, `{ 207 "errors": [ 208 { 209 "status": "404", 210 "title": "not found" 211 } 212 ] 213 }`) 214 }) 215 216 return httptest.NewServer(mux) 217 } 218 219 // testDisco returns a *disco.Disco mapping app.terraform.io and 220 // localhost to a local test server. 221 func testDisco(s *httptest.Server) *disco.Disco { 222 services := map[string]interface{}{ 223 "tfe.v2.1": fmt.Sprintf("%s/api/v2/", s.URL), 224 "versions.v1": fmt.Sprintf("%s/v1/versions/", s.URL), 225 } 226 d := disco.NewWithCredentialsSource(credsSrc) 227 228 d.ForceHostServices(svchost.Hostname(defaultHostname), services) 229 d.ForceHostServices(svchost.Hostname("localhost"), services) 230 return d 231 }