mirror of
https://github.com/hashicorp/terraform.git
synced 2026-05-14 02:10:10 -04:00
198 lines
5.6 KiB
Go
198 lines
5.6 KiB
Go
// Copyright IBM Corp. 2014, 2026
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package command
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/terraform/internal/backend"
|
|
"github.com/hashicorp/terraform/internal/backend/backendrun"
|
|
"github.com/hashicorp/terraform/internal/command/arguments"
|
|
"github.com/hashicorp/terraform/internal/configs"
|
|
"github.com/hashicorp/terraform/internal/states/statemgr"
|
|
"github.com/hashicorp/terraform/internal/terraform"
|
|
"github.com/hashicorp/terraform/internal/tfdiags"
|
|
|
|
backendLocal "github.com/hashicorp/terraform/internal/backend/local"
|
|
)
|
|
|
|
func cloudTestServerWithVars(t *testing.T) *httptest.Server {
|
|
t.Helper()
|
|
mux := http.NewServeMux()
|
|
|
|
initialPath, _ := os.Getwd()
|
|
|
|
mux.HandleFunc("/api/cloudplugin/v1/manifest", func(w http.ResponseWriter, r *http.Request) {
|
|
fileToSend, _ := os.Open(filepath.Join(initialPath, "testdata/cloud-archives/manifest.json"))
|
|
defer fileToSend.Close()
|
|
io.Copy(w, fileToSend)
|
|
})
|
|
|
|
// 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, fmt.Sprintf(`{
|
|
"service": "%s",
|
|
"product": "terraform",
|
|
"minimum": "0.1.0",
|
|
"maximum": "10.0.0"
|
|
}`, filepath.Base(r.URL.Path)))
|
|
})
|
|
|
|
// Respond to pings to get the API version header.
|
|
mux.HandleFunc("/api/v2/ping", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("TFP-API-Version", "2.5")
|
|
})
|
|
|
|
// Respond to the initial query to read the hashicorp org entitlements.
|
|
mux.HandleFunc("/api/v2/organizations/hashicorp/entitlement-set", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/vnd.api+json")
|
|
io.WriteString(w, `{
|
|
"data": {
|
|
"id": "org-GExadygjSbKP8hsY",
|
|
"type": "entitlement-sets",
|
|
"attributes": {
|
|
"operations": true,
|
|
"private-module-registry": true,
|
|
"sentinel": true,
|
|
"state-storage": true,
|
|
"teams": true,
|
|
"vcs-integrations": true
|
|
}
|
|
}
|
|
}`)
|
|
})
|
|
|
|
// Respond to our "test" workspace
|
|
mux.HandleFunc("/api/v2/organizations/hashicorp/workspaces/test", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/vnd.api+json")
|
|
io.WriteString(w, `{
|
|
"data": {
|
|
"id": "ws-GExadygjSbKP8hsY",
|
|
"type": "workspaces",
|
|
"attributes": {
|
|
"name": "test",
|
|
"terraform-version": "1.15.0",
|
|
"execution-mode": "remote"
|
|
}
|
|
}
|
|
}`)
|
|
})
|
|
|
|
// Respond to the "default" workspace request, will always be requested by Terraform
|
|
mux.HandleFunc("/api/v2/organizations/hashicorp/workspaces/default", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/vnd.api+json")
|
|
io.WriteString(w, `{
|
|
"data": {
|
|
"id": "ws-GExadygjSbKP8hsX",
|
|
"type": "workspaces",
|
|
"attributes": {
|
|
"name": "default",
|
|
"terraform-version": "1.15.0",
|
|
"execution-mode": "remote"
|
|
}
|
|
}
|
|
}`)
|
|
})
|
|
|
|
// Respond to the variables for the "test" workspace:
|
|
// module_name -> example
|
|
mux.HandleFunc("/api/v2/workspaces/ws-GExadygjSbKP8hsY/all-vars", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/vnd.api+json")
|
|
io.WriteString(w, `{
|
|
"data": [
|
|
{
|
|
"id": "var-1234",
|
|
"type": "vars",
|
|
"attributes": {
|
|
"key": "module_name",
|
|
"value": "example",
|
|
"category": "terraform",
|
|
"hcl": false,
|
|
"sensitive": false
|
|
}
|
|
}
|
|
]
|
|
}`)
|
|
})
|
|
|
|
return httptest.NewServer(mux)
|
|
}
|
|
|
|
// TestVariableBackend is a backend used in test that are fetching variable
|
|
// values for constant values.
|
|
type TestVariableBackend struct {
|
|
*backendLocal.Local
|
|
|
|
vars map[string]string
|
|
}
|
|
|
|
func TestNewVariableBackend(result map[string]string) backend.Backend {
|
|
return &TestVariableBackend{
|
|
Local: backendLocal.New(),
|
|
vars: result,
|
|
}
|
|
}
|
|
|
|
var _ backendrun.ConstVariableSupplier = (*TestVariableBackend)(nil)
|
|
var _ backendrun.Local = (*TestVariableBackend)(nil)
|
|
|
|
func (b *TestVariableBackend) FetchVariables(ctx context.Context, workspace string) (map[string]arguments.UnparsedVariableValue, tfdiags.Diagnostics) {
|
|
result := make(map[string]arguments.UnparsedVariableValue)
|
|
|
|
for name, value := range b.vars {
|
|
result[name] = testUnparsedVariableValueString{
|
|
str: value,
|
|
name: name,
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (b *TestVariableBackend) LocalRun(op *backendrun.Operation) (*backendrun.LocalRun, statemgr.Full, tfdiags.Diagnostics) {
|
|
// Sometimes a command (like graph) requires a local backend. The cloud
|
|
// backend implements LocalRun and will fetch variables from the backend.
|
|
// But our mock TestVariableBackend will fail in these tests, because it
|
|
// embends the backendrun.Local backend and never calls FetchVariables.
|
|
//
|
|
// We now set the variables manually here and defer to the regular backend
|
|
// run implementation, this helps us test the desired behavior.
|
|
if op.Variables == nil {
|
|
op.Variables = make(map[string]arguments.UnparsedVariableValue)
|
|
}
|
|
fetchedVars, _ := b.FetchVariables(context.Background(), op.Workspace)
|
|
for k, v := range fetchedVars {
|
|
if _, ok := op.Variables[k]; !ok {
|
|
op.Variables[k] = v
|
|
}
|
|
}
|
|
|
|
return b.Local.LocalRun(op)
|
|
}
|
|
|
|
type testUnparsedVariableValueString struct {
|
|
str string
|
|
name string
|
|
}
|
|
|
|
func (v testUnparsedVariableValueString) ParseVariableValue(mode configs.VariableParsingMode) (*terraform.InputValue, tfdiags.Diagnostics) {
|
|
var diags tfdiags.Diagnostics
|
|
|
|
val, hclDiags := mode.Parse(v.name, v.str)
|
|
diags = diags.Append(hclDiags)
|
|
|
|
return &terraform.InputValue{
|
|
Value: val,
|
|
SourceType: terraform.ValueFromInput,
|
|
}, diags
|
|
}
|