github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/cloud/e2e/helper_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package main
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"os"
    10  	"testing"
    11  	"time"
    12  
    13  	expect "github.com/Netflix/go-expect"
    14  	tfe "github.com/hashicorp/go-tfe"
    15  	"github.com/hashicorp/go-uuid"
    16  	goversion "github.com/hashicorp/go-version"
    17  	tfversion "github.com/terramate-io/tf/version"
    18  )
    19  
    20  const (
    21  	// We need to give the console enough time to hear back.
    22  	// 1 minute was too short in some cases, so this gives it ample time.
    23  	expectConsoleTimeout = 3 * time.Minute
    24  )
    25  
    26  type tfCommand struct {
    27  	command           []string
    28  	expectedCmdOutput string
    29  	expectError       bool
    30  	userInput         []string
    31  	postInputOutput   []string
    32  }
    33  
    34  type operationSets struct {
    35  	commands []tfCommand
    36  	prep     func(t *testing.T, orgName, dir string)
    37  }
    38  
    39  type testCases map[string]struct {
    40  	operations  []operationSets
    41  	validations func(t *testing.T, orgName string)
    42  }
    43  
    44  func defaultOpts() []expect.ConsoleOpt {
    45  	opts := []expect.ConsoleOpt{
    46  		expect.WithDefaultTimeout(expectConsoleTimeout),
    47  	}
    48  	if verboseMode {
    49  		opts = append(opts, expect.WithStdout(os.Stdout))
    50  	}
    51  	return opts
    52  }
    53  
    54  func createOrganization(t *testing.T) (*tfe.Organization, func()) {
    55  	ctx := context.Background()
    56  	org, err := tfeClient.Organizations.Create(ctx, tfe.OrganizationCreateOptions{
    57  		Name:                  tfe.String("tst-" + randomString(t)),
    58  		Email:                 tfe.String(fmt.Sprintf("%s@tfe.local", randomString(t))),
    59  		CostEstimationEnabled: tfe.Bool(false),
    60  	})
    61  	if err != nil {
    62  		t.Fatal(err)
    63  	}
    64  
    65  	_, err = tfeClient.Admin.Organizations.Update(ctx, org.Name, tfe.AdminOrganizationUpdateOptions{
    66  		AccessBetaTools: tfe.Bool(true),
    67  	})
    68  	if err != nil {
    69  		t.Fatal(err)
    70  	}
    71  
    72  	return org, func() {
    73  		if err := tfeClient.Organizations.Delete(ctx, org.Name); err != nil {
    74  			t.Errorf("Error destroying organization! WARNING: Dangling resources\n"+
    75  				"may exist! The full error is shown below.\n\n"+
    76  				"Organization: %s\nError: %s", org.Name, err)
    77  		}
    78  	}
    79  }
    80  
    81  func createWorkspace(t *testing.T, orgName string, wOpts tfe.WorkspaceCreateOptions) *tfe.Workspace {
    82  	ctx := context.Background()
    83  	w, err := tfeClient.Workspaces.Create(ctx, orgName, wOpts)
    84  	if err != nil {
    85  		t.Fatal(err)
    86  	}
    87  
    88  	return w
    89  }
    90  
    91  func getWorkspace(workspaces []*tfe.Workspace, workspace string) (*tfe.Workspace, bool) {
    92  	for _, ws := range workspaces {
    93  		if ws.Name == workspace {
    94  			return ws, false
    95  		}
    96  	}
    97  	return nil, true
    98  }
    99  
   100  func randomString(t *testing.T) string {
   101  	v, err := uuid.GenerateUUID()
   102  	if err != nil {
   103  		t.Fatal(err)
   104  	}
   105  	return v
   106  }
   107  
   108  func terraformConfigLocalBackend() string {
   109  	return `
   110  terraform {
   111    backend "local" {
   112    }
   113  }
   114  
   115  output "val" {
   116    value = "${terraform.workspace}"
   117  }
   118  `
   119  }
   120  
   121  func terraformConfigRemoteBackendName(org, name string) string {
   122  	return fmt.Sprintf(`
   123  terraform {
   124    backend "remote" {
   125      hostname = "%s"
   126      organization = "%s"
   127  
   128      workspaces {
   129        name = "%s"
   130      }
   131    }
   132  }
   133  
   134  output "val" {
   135    value = "${terraform.workspace}"
   136  }
   137  `, tfeHostname, org, name)
   138  }
   139  
   140  func terraformConfigRemoteBackendPrefix(org, prefix string) string {
   141  	return fmt.Sprintf(`
   142  terraform {
   143    backend "remote" {
   144      hostname = "%s"
   145      organization = "%s"
   146  
   147      workspaces {
   148        prefix = "%s"
   149      }
   150    }
   151  }
   152  
   153  output "val" {
   154    value = "${terraform.workspace}"
   155  }
   156  `, tfeHostname, org, prefix)
   157  }
   158  
   159  func terraformConfigCloudBackendTags(org, tag string) string {
   160  	return fmt.Sprintf(`
   161  terraform {
   162    cloud {
   163      hostname = "%s"
   164      organization = "%s"
   165  
   166      workspaces {
   167        tags = ["%s"]
   168      }
   169    }
   170  }
   171  
   172  output "tag_val" {
   173    value = "%s"
   174  }
   175  `, tfeHostname, org, tag, tag)
   176  }
   177  
   178  func terraformConfigCloudBackendName(org, name string) string {
   179  	return fmt.Sprintf(`
   180  terraform {
   181    cloud {
   182      hostname = "%s"
   183      organization = "%s"
   184  
   185      workspaces {
   186        name = "%s"
   187      }
   188    }
   189  }
   190  
   191  output "val" {
   192    value = "${terraform.workspace}"
   193  }
   194  `, tfeHostname, org, name)
   195  }
   196  
   197  func terraformConfigCloudBackendOmitOrg(workspaceName string) string {
   198  	return fmt.Sprintf(`
   199  terraform {
   200    cloud {
   201      hostname = "%s"
   202  
   203  	workspaces {
   204  	  name = "%s"
   205  	}
   206    }
   207  }
   208  
   209  output "val" {
   210    value = "${terraform.workspace}"
   211  }
   212  `, tfeHostname, workspaceName)
   213  }
   214  
   215  func terraformConfigCloudBackendOmitWorkspaces(orgName string) string {
   216  	return fmt.Sprintf(`
   217  terraform {
   218    cloud {
   219      hostname = "%s"
   220  	organization = "%s"
   221    }
   222  }
   223  
   224  output "val" {
   225    value = "${terraform.workspace}"
   226  }
   227  `, tfeHostname, orgName)
   228  }
   229  
   230  func terraformConfigCloudBackendOmitConfig() string {
   231  	return `
   232  terraform {
   233    cloud {}
   234  }
   235  
   236  output "val" {
   237    value = "${terraform.workspace}"
   238  }
   239  `
   240  }
   241  
   242  func writeMainTF(t *testing.T, block string, dir string) {
   243  	f, err := os.Create(fmt.Sprintf("%s/main.tf", dir))
   244  	if err != nil {
   245  		t.Fatal(err)
   246  	}
   247  	_, err = f.WriteString(block)
   248  	if err != nil {
   249  		t.Fatal(err)
   250  	}
   251  	f.Close()
   252  }
   253  
   254  // The e2e tests rely on the fact that the terraform version in TFC/E is able to
   255  // run the `cloud` configuration block, which is available in 1.1 and will
   256  // continue to be available in later versions. So this function checks that
   257  // there is a version that is >= 1.1.
   258  func skipWithoutRemoteTerraformVersion(t *testing.T) {
   259  	version := tfversion.Version
   260  	baseVersion, err := goversion.NewVersion(version)
   261  	if err != nil {
   262  		t.Fatalf(fmt.Sprintf("Error instantiating go-version for %s", version))
   263  	}
   264  	opts := &tfe.AdminTerraformVersionsListOptions{
   265  		ListOptions: tfe.ListOptions{
   266  			PageNumber: 1,
   267  			PageSize:   100,
   268  		},
   269  	}
   270  	hasVersion := false
   271  
   272  findTfVersion:
   273  	for {
   274  		// TODO: update go-tfe Read() to retrieve a terraform version by name.
   275  		// Currently you can only retrieve by ID.
   276  		tfVersionList, err := tfeClient.Admin.TerraformVersions.List(context.Background(), opts)
   277  		if err != nil {
   278  			t.Fatalf("Could not retrieve list of terraform versions: %v", err)
   279  		}
   280  		for _, item := range tfVersionList.Items {
   281  			availableVersion, err := goversion.NewVersion(item.Version)
   282  			if err != nil {
   283  				t.Logf("Error instantiating go-version for %s", item.Version)
   284  				continue
   285  			}
   286  			if availableVersion.Core().GreaterThanOrEqual(baseVersion.Core()) {
   287  				hasVersion = true
   288  				break findTfVersion
   289  			}
   290  		}
   291  
   292  		// Exit the loop when we've seen all pages.
   293  		if tfVersionList.CurrentPage >= tfVersionList.TotalPages {
   294  			break
   295  		}
   296  
   297  		// Update the page number to get the next page.
   298  		opts.PageNumber = tfVersionList.NextPage
   299  	}
   300  
   301  	if !hasVersion {
   302  		t.Skipf("Skipping test because TFC/E does not have current Terraform version to test with (%s)", version)
   303  	}
   304  }