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 }