mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-26 00:41:27 -06:00
305 lines
6.2 KiB
Go
305 lines
6.2 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
expect "github.com/Netflix/go-expect"
|
|
tfe "github.com/hashicorp/go-tfe"
|
|
"github.com/hashicorp/go-uuid"
|
|
goversion "github.com/hashicorp/go-version"
|
|
tfversion "github.com/opentofu/opentofu/version"
|
|
)
|
|
|
|
const (
|
|
// We need to give the console enough time to hear back.
|
|
// 1 minute was too short in some cases, so this gives it ample time.
|
|
expectConsoleTimeout = 3 * time.Minute
|
|
)
|
|
|
|
type tfCommand struct {
|
|
command []string
|
|
expectedCmdOutput string
|
|
expectError bool
|
|
userInput []string
|
|
postInputOutput []string
|
|
}
|
|
|
|
type operationSets struct {
|
|
commands []tfCommand
|
|
prep func(t *testing.T, orgName, dir string)
|
|
}
|
|
|
|
type testCases map[string]struct {
|
|
operations []operationSets
|
|
validations func(t *testing.T, orgName string)
|
|
}
|
|
|
|
func defaultOpts() []expect.ConsoleOpt {
|
|
opts := []expect.ConsoleOpt{
|
|
expect.WithDefaultTimeout(expectConsoleTimeout),
|
|
}
|
|
if verboseMode {
|
|
opts = append(opts, expect.WithStdout(os.Stdout))
|
|
}
|
|
return opts
|
|
}
|
|
|
|
func createOrganization(t *testing.T) (*tfe.Organization, func()) {
|
|
ctx := context.Background()
|
|
org, err := tfeClient.Organizations.Create(ctx, tfe.OrganizationCreateOptions{
|
|
Name: tfe.String("tst-" + randomString(t)),
|
|
Email: tfe.String(fmt.Sprintf("%s@tfe.local", randomString(t))),
|
|
CostEstimationEnabled: tfe.Bool(false),
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err = tfeClient.Admin.Organizations.Update(ctx, org.Name, tfe.AdminOrganizationUpdateOptions{
|
|
AccessBetaTools: tfe.Bool(true),
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return org, func() {
|
|
if err := tfeClient.Organizations.Delete(ctx, org.Name); err != nil {
|
|
t.Errorf("Error destroying organization! WARNING: Dangling resources\n"+
|
|
"may exist! The full error is shown below.\n\n"+
|
|
"Organization: %s\nError: %s", org.Name, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func createWorkspace(t *testing.T, orgName string, wOpts tfe.WorkspaceCreateOptions) *tfe.Workspace {
|
|
ctx := context.Background()
|
|
w, err := tfeClient.Workspaces.Create(ctx, orgName, wOpts)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return w
|
|
}
|
|
|
|
func getWorkspace(workspaces []*tfe.Workspace, workspace string) (*tfe.Workspace, bool) {
|
|
for _, ws := range workspaces {
|
|
if ws.Name == workspace {
|
|
return ws, false
|
|
}
|
|
}
|
|
return nil, true
|
|
}
|
|
|
|
func randomString(t *testing.T) string {
|
|
v, err := uuid.GenerateUUID()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return v
|
|
}
|
|
|
|
func terraformConfigLocalBackend() string {
|
|
return `
|
|
terraform {
|
|
backend "local" {
|
|
}
|
|
}
|
|
|
|
output "val" {
|
|
value = "${terraform.workspace}"
|
|
}
|
|
`
|
|
}
|
|
|
|
func terraformConfigRemoteBackendName(org, name string) string {
|
|
return fmt.Sprintf(`
|
|
terraform {
|
|
backend "remote" {
|
|
hostname = "%s"
|
|
organization = "%s"
|
|
|
|
workspaces {
|
|
name = "%s"
|
|
}
|
|
}
|
|
}
|
|
|
|
output "val" {
|
|
value = "${terraform.workspace}"
|
|
}
|
|
`, tfeHostname, org, name)
|
|
}
|
|
|
|
func terraformConfigRemoteBackendPrefix(org, prefix string) string {
|
|
return fmt.Sprintf(`
|
|
terraform {
|
|
backend "remote" {
|
|
hostname = "%s"
|
|
organization = "%s"
|
|
|
|
workspaces {
|
|
prefix = "%s"
|
|
}
|
|
}
|
|
}
|
|
|
|
output "val" {
|
|
value = "${terraform.workspace}"
|
|
}
|
|
`, tfeHostname, org, prefix)
|
|
}
|
|
|
|
func terraformConfigCloudBackendTags(org, tag string) string {
|
|
return fmt.Sprintf(`
|
|
terraform {
|
|
cloud {
|
|
hostname = "%s"
|
|
organization = "%s"
|
|
|
|
workspaces {
|
|
tags = ["%s"]
|
|
}
|
|
}
|
|
}
|
|
|
|
output "tag_val" {
|
|
value = "%s"
|
|
}
|
|
`, tfeHostname, org, tag, tag)
|
|
}
|
|
|
|
func terraformConfigCloudBackendName(org, name string) string {
|
|
return fmt.Sprintf(`
|
|
terraform {
|
|
cloud {
|
|
hostname = "%s"
|
|
organization = "%s"
|
|
|
|
workspaces {
|
|
name = "%s"
|
|
}
|
|
}
|
|
}
|
|
|
|
output "val" {
|
|
value = "${terraform.workspace}"
|
|
}
|
|
`, tfeHostname, org, name)
|
|
}
|
|
|
|
func terraformConfigCloudBackendOmitOrg(workspaceName string) string {
|
|
return fmt.Sprintf(`
|
|
terraform {
|
|
cloud {
|
|
hostname = "%s"
|
|
|
|
workspaces {
|
|
name = "%s"
|
|
}
|
|
}
|
|
}
|
|
|
|
output "val" {
|
|
value = "${terraform.workspace}"
|
|
}
|
|
`, tfeHostname, workspaceName)
|
|
}
|
|
|
|
func terraformConfigCloudBackendOmitWorkspaces(orgName string) string {
|
|
return fmt.Sprintf(`
|
|
terraform {
|
|
cloud {
|
|
hostname = "%s"
|
|
organization = "%s"
|
|
}
|
|
}
|
|
|
|
output "val" {
|
|
value = "${terraform.workspace}"
|
|
}
|
|
`, tfeHostname, orgName)
|
|
}
|
|
|
|
func terraformConfigCloudBackendOmitConfig() string {
|
|
return `
|
|
terraform {
|
|
cloud {}
|
|
}
|
|
|
|
output "val" {
|
|
value = "${terraform.workspace}"
|
|
}
|
|
`
|
|
}
|
|
|
|
func writeMainTF(t *testing.T, block string, dir string) {
|
|
f, err := os.Create(fmt.Sprintf("%s/main.tf", dir))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = f.WriteString(block)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
f.Close()
|
|
}
|
|
|
|
// The e2e tests rely on the fact that the terraform version in TFC/E is able to
|
|
// run the `cloud` configuration block, which is available in 1.1 and will
|
|
// continue to be available in later versions. So this function checks that
|
|
// there is a version that is >= 1.1.
|
|
func skipWithoutRemoteTerraformVersion(t *testing.T) {
|
|
version := tfversion.Version
|
|
baseVersion, err := goversion.NewVersion(version)
|
|
if err != nil {
|
|
t.Fatalf(fmt.Sprintf("Error instantiating go-version for %s", version))
|
|
}
|
|
opts := &tfe.AdminTerraformVersionsListOptions{
|
|
ListOptions: tfe.ListOptions{
|
|
PageNumber: 1,
|
|
PageSize: 100,
|
|
},
|
|
}
|
|
hasVersion := false
|
|
|
|
findTfVersion:
|
|
for {
|
|
// TODO: update go-tfe Read() to retrieve a terraform version by name.
|
|
// Currently you can only retrieve by ID.
|
|
tfVersionList, err := tfeClient.Admin.TerraformVersions.List(context.Background(), opts)
|
|
if err != nil {
|
|
t.Fatalf("Could not retrieve list of terraform versions: %v", err)
|
|
}
|
|
for _, item := range tfVersionList.Items {
|
|
availableVersion, err := goversion.NewVersion(item.Version)
|
|
if err != nil {
|
|
t.Logf("Error instantiating go-version for %s", item.Version)
|
|
continue
|
|
}
|
|
if availableVersion.Core().GreaterThanOrEqual(baseVersion.Core()) {
|
|
hasVersion = true
|
|
break findTfVersion
|
|
}
|
|
}
|
|
|
|
// Exit the loop when we've seen all pages.
|
|
if tfVersionList.CurrentPage >= tfVersionList.TotalPages {
|
|
break
|
|
}
|
|
|
|
// Update the page number to get the next page.
|
|
opts.PageNumber = tfVersionList.NextPage
|
|
}
|
|
|
|
if !hasVersion {
|
|
t.Skipf("Skipping test because TFC/E does not have current Terraform version to test with (%s)", version)
|
|
}
|
|
}
|