blob: 7547ec8398442ff23bbac57a6efa8d33254fdc1e [file] [log] [blame] [edit]
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package command
import (
"fmt"
"io"
"log"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path"
"reflect"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/cli"
svchost "github.com/hashicorp/terraform-svchost"
"github.com/hashicorp/terraform-svchost/auth"
"github.com/hashicorp/terraform-svchost/disco"
"github.com/hashicorp/terraform/internal/backend"
backendInit "github.com/hashicorp/terraform/internal/backend/init"
backendCloud "github.com/hashicorp/terraform/internal/cloud"
"github.com/hashicorp/terraform/internal/httpclient"
"github.com/hashicorp/terraform/version"
"google.golang.org/grpc/metadata"
)
func newCloudPluginManifestHTTPTestServer(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(path.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"
}`, path.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
}
}
}`)
})
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.5.4"
}
}
}`)
})
return httptest.NewServer(mux)
}
// testDisco returns a *disco.Disco mapping app.terraform.io and
// localhost to a local test server.
func testDisco(s *httptest.Server) *disco.Disco {
host, _ := url.Parse(s.URL)
defaultHostname := "app.terraform.io"
tfeHost := svchost.Hostname(defaultHostname)
services := map[string]interface{}{
"cloudplugin.v1": fmt.Sprintf("%s/api/cloudplugin/v1/", s.URL),
"tfe.v2": fmt.Sprintf("%s/api/v2/", s.URL),
}
credsSrc := auth.StaticCredentialsSource(map[svchost.Hostname]map[string]interface{}{
tfeHost: {"token": "test-auth-token"},
})
d := disco.NewWithCredentialsSource(credsSrc)
d.SetUserAgent(httpclient.TerraformUserAgent(version.String()))
d.ForceHostServices(tfeHost, services)
d.ForceHostServices(svchost.Hostname(host.Host), services)
return d
}
func TestCloud_withBackendConfig(t *testing.T) {
t.Skip("To be converted to an e2e test")
server := newCloudPluginManifestHTTPTestServer(t)
disco := testDisco(server)
wd := tempWorkingDirFixture(t, "cloud-config")
defer testChdir(t, wd.RootModuleDir())()
// Overwrite the cloud backend with the test disco
previousBackend := backendInit.Backend("cloud")
backendInit.Set("cloud", func() backend.Backend { return backendCloud.New(disco) })
defer backendInit.Set("cloud", previousBackend)
ui := cli.NewMockUi()
view, _ := testView(t)
// Initialize the backend
ic := &InitCommand{
Meta{
Ui: ui,
View: view,
testingOverrides: metaOverridesForProvider(testProvider()),
Services: disco,
},
}
log.Print("[TRACE] TestCloud_withBackendConfig running: terraform init")
if code := ic.Run([]string{}); code != 0 {
t.Fatalf("init failed\n%s", ui.ErrorWriter)
}
// Run the cloud command
ui = cli.NewMockUi()
c := &CloudCommand{
Meta: Meta{
Ui: ui,
testingOverrides: metaOverridesForProvider(testProvider()),
Services: disco,
WorkingDir: wd,
},
}
args := []string{"version"}
if code := c.Run(args); code != 0 {
t.Fatalf("expected exit 0, got %d: \n%s", code, ui.ErrorWriter.String())
}
output := ui.OutputWriter.String()
expected := "HCP Terraform Plugin v0.1.0\n\n"
if output != expected {
t.Fatalf("the output did not equal the expected string:\n%s", cmp.Diff(expected, output))
}
}
func TestCloud_withENVConfig(t *testing.T) {
t.Skip("To be converted to an e2e test")
server := newCloudPluginManifestHTTPTestServer(t)
disco := testDisco(server)
wd := tempWorkingDir(t)
defer testChdir(t, wd.RootModuleDir())()
serverURL, _ := url.Parse(server.URL)
os.Setenv("TF_CLOUD_HOSTNAME", serverURL.Host)
defer os.Unsetenv("TF_CLOUD_HOSTNAME")
// Run the cloud command
ui := cli.NewMockUi()
c := &CloudCommand{
Meta: Meta{
Ui: ui,
testingOverrides: metaOverridesForProvider(testProvider()),
Services: disco,
WorkingDir: wd,
},
}
args := []string{"version"}
if code := c.Run(args); code != 0 {
t.Fatalf("expected exit 0, got %d: \n%s", code, ui.ErrorWriter.String())
}
output := ui.OutputWriter.String()
expected := "HCP Terraform Plugin v0.1.0\n\n"
if output != expected {
t.Fatalf("the output did not equal the expected string:\n%s", cmp.Diff(expected, output))
}
}
func TestCloudPluginConfig_ToMetadata(t *testing.T) {
expected := metadata.Pairs(
"tfc-address", "https://app.staging.terraform.io",
"tfc-base-path", "/api/v2/",
"tfc-display-hostname", "app.staging.terraform.io",
"tfc-token", "not-a-legit-token",
"tfc-organization", "example-corp",
"tfc-current-workspace", "example-space",
"tfc-workspace-name", "example-space",
// Actually combining -name and -tags is an invalid scenario from
// Terraform's point of view, but here we're just testing that every
// field makes the trip safely if sent.
"tfc-workspace-tags", "networking",
// Duplicate is on purpose.
"tfc-workspace-tags", "platform-team",
"tfc-default-project-name", "production-services",
)
inputStruct := CloudPluginConfig{
Address: "https://app.staging.terraform.io",
BasePath: "/api/v2/",
DisplayHostname: "app.staging.terraform.io",
Token: "not-a-legit-token",
Organization: "example-corp",
CurrentWorkspace: "example-space",
WorkspaceName: "example-space",
WorkspaceTags: []string{"networking", "platform-team"},
DefaultProjectName: "production-services",
}
result := inputStruct.ToMetadata()
if !reflect.DeepEqual(expected, result) {
t.Fatalf("Expected: %#v\nGot: %#v\n", expected, result)
}
}