|
1 | 1 | package coderd_test
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | + "bytes" |
4 | 5 | "context"
|
5 | 6 | "database/sql"
|
| 7 | + "fmt" |
6 | 8 | "net/http"
|
7 | 9 | "sync/atomic"
|
8 | 10 | "testing"
|
@@ -1420,6 +1422,157 @@ func TestTemplateDoesNotAllowUserAutostop(t *testing.T) {
|
1420 | 1422 | })
|
1421 | 1423 | }
|
1422 | 1424 |
|
| 1425 | +// TestWorkspaceTagsTerraform tests that a workspace can be created with tags. |
| 1426 | +// This is an end-to-end-style test, meaning that we actually run the |
| 1427 | +// real Terraform provisioner and validate that the workspace is created |
| 1428 | +// successfully. The workspace itself does not specify any resources, and |
| 1429 | +// this is fine. |
| 1430 | +func TestWorkspaceTagsTerraform(t *testing.T) { |
| 1431 | + t.Parallel() |
| 1432 | + |
| 1433 | + mainTfTemplate := ` |
| 1434 | + terraform { |
| 1435 | + required_providers { |
| 1436 | + coder = { |
| 1437 | + source = "coder/coder" |
| 1438 | + } |
| 1439 | + } |
| 1440 | + } |
| 1441 | + provider "coder" {} |
| 1442 | + data "coder_workspace" "me" {} |
| 1443 | + data "coder_workspace_owner" "me" {} |
| 1444 | + %s |
| 1445 | + ` |
| 1446 | + |
| 1447 | + for _, tc := range []struct { |
| 1448 | + name string |
| 1449 | + // tags to apply to the external provisioner |
| 1450 | + provisionerTags map[string]string |
| 1451 | + // tags to apply to the create template version request |
| 1452 | + createTemplateVersionRequestTags map[string]string |
| 1453 | + // the coder_workspace_tags bit of main.tf. |
| 1454 | + // you can add more stuff here if you need |
| 1455 | + tfWorkspaceTags string |
| 1456 | + }{ |
| 1457 | + { |
| 1458 | + name: "no tags", |
| 1459 | + tfWorkspaceTags: ``, |
| 1460 | + }, |
| 1461 | + { |
| 1462 | + name: "empty tags", |
| 1463 | + tfWorkspaceTags: ` |
| 1464 | + data "coder_workspace_tags" "tags" { |
| 1465 | + tags = {} |
| 1466 | + } |
| 1467 | + `, |
| 1468 | + }, |
| 1469 | + { |
| 1470 | + name: "static tag", |
| 1471 | + provisionerTags: map[string]string{"foo": "bar"}, |
| 1472 | + tfWorkspaceTags: ` |
| 1473 | + data "coder_workspace_tags" "tags" { |
| 1474 | + tags = { |
| 1475 | + "foo" = "bar" |
| 1476 | + } |
| 1477 | + }`, |
| 1478 | + }, |
| 1479 | + { |
| 1480 | + name: "tag variable", |
| 1481 | + provisionerTags: map[string]string{"foo": "bar"}, |
| 1482 | + tfWorkspaceTags: ` |
| 1483 | + variable "foo" { |
| 1484 | + default = "bar" |
| 1485 | + } |
| 1486 | + data "coder_workspace_tags" "tags" { |
| 1487 | + tags = { |
| 1488 | + "foo" = var.foo |
| 1489 | + } |
| 1490 | + }`, |
| 1491 | + }, |
| 1492 | + { |
| 1493 | + name: "tag param", |
| 1494 | + provisionerTags: map[string]string{"foo": "bar"}, |
| 1495 | + tfWorkspaceTags: ` |
| 1496 | + data "coder_parameter" "foo" { |
| 1497 | + name = "foo" |
| 1498 | + type = "string" |
| 1499 | + default = "bar" |
| 1500 | + } |
| 1501 | + data "coder_workspace_tags" "tags" { |
| 1502 | + tags = { |
| 1503 | + "foo" = data.coder_parameter.foo.value |
| 1504 | + } |
| 1505 | + }`, |
| 1506 | + }, |
| 1507 | + { |
| 1508 | + name: "tag param with default from var", |
| 1509 | + provisionerTags: map[string]string{"foo": "bar"}, |
| 1510 | + tfWorkspaceTags: ` |
| 1511 | + variable "foo" { |
| 1512 | + type = "string" |
| 1513 | + default = "bar" |
| 1514 | + } |
| 1515 | + data "coder_parameter" "foo" { |
| 1516 | + name = "foo" |
| 1517 | + type = "string" |
| 1518 | + default = var.foo |
| 1519 | + } |
| 1520 | + data "coder_workspace_tags" "tags" { |
| 1521 | + tags = { |
| 1522 | + "foo" = data.coder_parameter.foo.value |
| 1523 | + } |
| 1524 | + }`, |
| 1525 | + }, |
| 1526 | + } { |
| 1527 | + tc := tc |
| 1528 | + t.Run(tc.name, func(t *testing.T) { |
| 1529 | + t.Parallel() |
| 1530 | + ctx := testutil.Context(t, testutil.WaitShort) |
| 1531 | + |
| 1532 | + client, owner := coderdenttest.New(t, &coderdenttest.Options{ |
| 1533 | + Options: &coderdtest.Options{ |
| 1534 | + // We intentionally do not run a built-in provisioner daemon here. |
| 1535 | + IncludeProvisionerDaemon: false, |
| 1536 | + }, |
| 1537 | + LicenseOptions: &coderdenttest.LicenseOptions{ |
| 1538 | + Features: license.Features{ |
| 1539 | + codersdk.FeatureExternalProvisionerDaemons: 1, |
| 1540 | + }, |
| 1541 | + }, |
| 1542 | + }) |
| 1543 | + templateAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleTemplateAdmin()) |
| 1544 | + member, memberUser := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) |
| 1545 | + |
| 1546 | + _ = coderdenttest.NewExternalProvisionerDaemonTerraform(t, client, owner.OrganizationID, tc.provisionerTags) |
| 1547 | + |
| 1548 | + // Creating a template as a template admin must succeed |
| 1549 | + templateFiles := map[string]string{"main.tf": fmt.Sprintf(mainTfTemplate, tc.tfWorkspaceTags)} |
| 1550 | + tarBytes := testutil.CreateTar(t, templateFiles) |
| 1551 | + fi, err := templateAdmin.Upload(ctx, "application/x-tar", bytes.NewReader(tarBytes)) |
| 1552 | + require.NoError(t, err, "failed to upload file") |
| 1553 | + tv, err := templateAdmin.CreateTemplateVersion(ctx, owner.OrganizationID, codersdk.CreateTemplateVersionRequest{ |
| 1554 | + Name: testutil.GetRandomName(t), |
| 1555 | + FileID: fi.ID, |
| 1556 | + StorageMethod: codersdk.ProvisionerStorageMethodFile, |
| 1557 | + Provisioner: codersdk.ProvisionerTypeTerraform, |
| 1558 | + ProvisionerTags: tc.createTemplateVersionRequestTags, |
| 1559 | + }) |
| 1560 | + require.NoError(t, err, "failed to create template version") |
| 1561 | + coderdtest.AwaitTemplateVersionJobCompleted(t, templateAdmin, tv.ID) |
| 1562 | + require.NoError(t, err, "failed to create template version") |
| 1563 | + tpl := coderdtest.CreateTemplate(t, templateAdmin, owner.OrganizationID, tv.ID) |
| 1564 | + |
| 1565 | + // Creating a workspace as a non-privileged user must succeed |
| 1566 | + ws, err := member.CreateUserWorkspace(ctx, memberUser.Username, codersdk.CreateWorkspaceRequest{ |
| 1567 | + TemplateID: tpl.ID, |
| 1568 | + Name: coderdtest.RandomUsername(t), |
| 1569 | + }) |
| 1570 | + require.NoError(t, err, "failed to create workspace") |
| 1571 | + coderdtest.AwaitWorkspaceBuildJobCompleted(t, member, ws.LatestBuild.ID) |
| 1572 | + }) |
| 1573 | + } |
| 1574 | +} |
| 1575 | + |
1423 | 1576 | // Blocked by autostart requirements
|
1424 | 1577 | func TestExecutorAutostartBlocked(t *testing.T) {
|
1425 | 1578 | t.Parallel()
|
|
0 commit comments