Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 83fee4b

Browse files
authored
feat: enable Terraform debug mode via deployment configuration (#8260)
1 parent 7ca624e commit 83fee4b

File tree

19 files changed

+214
-27
lines changed

19 files changed

+214
-27
lines changed

cli/testdata/coder_server_--help.golden

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ Use a YAML configuration file when your server launch become unwieldy.
7474
Write out the current server config as YAML to stdout.
7575

7676
Introspection / Logging Options
77+
--enable-terraform-debug-mode bool, $CODER_ENABLE_TERRAFORM_DEBUG_MODE (default: false)
78+
Allow administrators to enable Terraform debug output.
79+
7780
--log-human string, $CODER_LOGGING_HUMAN (default: /dev/stderr)
7881
Output human-readable logs to a given file.
7982

cli/testdata/server-config.yaml.golden

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,9 @@ introspection:
197197
# Output Stackdriver compatible logs to a given file.
198198
# (default: <unset>, type: string)
199199
stackdriverPath: ""
200+
# Allow administrators to enable Terraform debug output.
201+
# (default: false, type: bool)
202+
enableTerraformDebugMode: false
200203
oauth2:
201204
github:
202205
# Client ID for Login with GitHub.

coderd/apidoc/docs.go

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/workspacebuilds.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,8 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
316316
builder := wsbuilder.New(workspace, database.WorkspaceTransition(createBuild.Transition)).
317317
Initiator(apiKey.UserID).
318318
RichParameterValues(createBuild.RichParameterValues).
319-
LogLevel(string(createBuild.LogLevel))
319+
LogLevel(string(createBuild.LogLevel)).
320+
DeploymentValues(api.Options.DeploymentValues)
320321

321322
if createBuild.TemplateVersionID != uuid.Nil {
322323
builder = builder.VersionID(createBuild.TemplateVersionID)

coderd/workspacebuilds_test.go

Lines changed: 90 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -640,11 +640,50 @@ func TestWorkspaceBuildStatus(t *testing.T) {
640640
func TestWorkspaceBuildDebugMode(t *testing.T) {
641641
t.Parallel()
642642

643+
t.Run("DebugModeDisabled", func(t *testing.T) {
644+
t.Parallel()
645+
646+
// Create user
647+
deploymentValues := coderdtest.DeploymentValues(t)
648+
deploymentValues.EnableTerraformDebugMode = false
649+
650+
adminClient := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, DeploymentValues: deploymentValues})
651+
owner := coderdtest.CreateFirstUser(t, adminClient)
652+
653+
// Template author: create a template
654+
version := coderdtest.CreateTemplateVersion(t, adminClient, owner.OrganizationID, nil)
655+
template := coderdtest.CreateTemplate(t, adminClient, owner.OrganizationID, version.ID)
656+
coderdtest.AwaitTemplateVersionJob(t, adminClient, version.ID)
657+
658+
// Template author: create a workspace
659+
workspace := coderdtest.CreateWorkspace(t, adminClient, owner.OrganizationID, template.ID)
660+
coderdtest.AwaitWorkspaceBuildJob(t, adminClient, workspace.LatestBuild.ID)
661+
662+
// Template author: try to start a workspace build in debug mode
663+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
664+
defer cancel()
665+
666+
_, err := adminClient.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
667+
TemplateVersionID: workspace.LatestBuild.TemplateVersionID,
668+
Transition: codersdk.WorkspaceTransitionStart,
669+
LogLevel: "debug",
670+
})
671+
672+
// Template author: expect an error as the debug mode is disabled
673+
require.NotNil(t, err)
674+
var sdkError *codersdk.Error
675+
isSdkError := xerrors.As(err, &sdkError)
676+
require.True(t, isSdkError)
677+
require.Contains(t, sdkError.Message, "Terraform debug mode is disabled in the deployment configuration.")
678+
})
643679
t.Run("AsRegularUser", func(t *testing.T) {
644680
t.Parallel()
645681

646682
// Create users
647-
templateAuthorClient := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
683+
deploymentValues := coderdtest.DeploymentValues(t)
684+
deploymentValues.EnableTerraformDebugMode = true
685+
686+
templateAuthorClient := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, DeploymentValues: deploymentValues})
648687
templateAuthor := coderdtest.CreateFirstUser(t, templateAuthorClient)
649688
regularUserClient, _ := coderdtest.CreateAnotherUser(t, templateAuthorClient, templateAuthor.OrganizationID)
650689

@@ -672,15 +711,54 @@ func TestWorkspaceBuildDebugMode(t *testing.T) {
672711
var sdkError *codersdk.Error
673712
isSdkError := xerrors.As(err, &sdkError)
674713
require.True(t, isSdkError)
675-
require.Contains(t, sdkError.Message, "Workspace builds with a custom log level are restricted to template authors only.")
714+
require.Contains(t, sdkError.Message, "Workspace builds with a custom log level are restricted to administrators only.")
676715
})
677716
t.Run("AsTemplateAuthor", func(t *testing.T) {
678717
t.Parallel()
679718

680719
// Create users
681-
adminClient := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
720+
deploymentValues := coderdtest.DeploymentValues(t)
721+
deploymentValues.EnableTerraformDebugMode = true
722+
723+
adminClient := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, DeploymentValues: deploymentValues})
724+
admin := coderdtest.CreateFirstUser(t, adminClient)
725+
templateAuthorClient, _ := coderdtest.CreateAnotherUser(t, adminClient, admin.OrganizationID, rbac.RoleTemplateAdmin())
726+
727+
// Template author: create a template
728+
version := coderdtest.CreateTemplateVersion(t, templateAuthorClient, admin.OrganizationID, nil)
729+
template := coderdtest.CreateTemplate(t, templateAuthorClient, admin.OrganizationID, version.ID)
730+
coderdtest.AwaitTemplateVersionJob(t, templateAuthorClient, version.ID)
731+
732+
// Template author: create a workspace
733+
workspace := coderdtest.CreateWorkspace(t, templateAuthorClient, admin.OrganizationID, template.ID)
734+
coderdtest.AwaitWorkspaceBuildJob(t, templateAuthorClient, workspace.LatestBuild.ID)
735+
736+
// Template author: try to start a workspace build in debug mode
737+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
738+
defer cancel()
739+
740+
_, err := templateAuthorClient.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
741+
TemplateVersionID: workspace.LatestBuild.TemplateVersionID,
742+
Transition: codersdk.WorkspaceTransitionStart,
743+
LogLevel: "debug",
744+
})
745+
746+
// Template author: expect an error as the debug mode is disabled
747+
require.NotNil(t, err)
748+
var sdkError *codersdk.Error
749+
isSdkError := xerrors.As(err, &sdkError)
750+
require.True(t, isSdkError)
751+
require.Contains(t, sdkError.Message, "Workspace builds with a custom log level are restricted to administrators only.")
752+
})
753+
t.Run("AsAdmin", func(t *testing.T) {
754+
t.Parallel()
755+
756+
// Create users
757+
deploymentValues := coderdtest.DeploymentValues(t)
758+
deploymentValues.EnableTerraformDebugMode = true
759+
760+
adminClient := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, DeploymentValues: deploymentValues})
682761
admin := coderdtest.CreateFirstUser(t, adminClient)
683-
templateAdminClient, _ := coderdtest.CreateAnotherUser(t, adminClient, admin.OrganizationID, rbac.RoleTemplateAdmin())
684762

685763
// Interact as template admin
686764
echoResponses := &echo.Responses{
@@ -713,30 +791,30 @@ func TestWorkspaceBuildDebugMode(t *testing.T) {
713791
},
714792
}},
715793
}
716-
version := coderdtest.CreateTemplateVersion(t, templateAdminClient, admin.OrganizationID, echoResponses)
717-
template := coderdtest.CreateTemplate(t, templateAdminClient, admin.OrganizationID, version.ID)
718-
coderdtest.AwaitTemplateVersionJob(t, templateAdminClient, version.ID)
794+
version := coderdtest.CreateTemplateVersion(t, adminClient, admin.OrganizationID, echoResponses)
795+
template := coderdtest.CreateTemplate(t, adminClient, admin.OrganizationID, version.ID)
796+
coderdtest.AwaitTemplateVersionJob(t, adminClient, version.ID)
719797

720798
// Create workspace
721-
workspace := coderdtest.CreateWorkspace(t, templateAdminClient, admin.OrganizationID, template.ID)
722-
coderdtest.AwaitWorkspaceBuildJob(t, templateAdminClient, workspace.LatestBuild.ID)
799+
workspace := coderdtest.CreateWorkspace(t, adminClient, admin.OrganizationID, template.ID)
800+
coderdtest.AwaitWorkspaceBuildJob(t, adminClient, workspace.LatestBuild.ID)
723801

724802
// Create workspace build
725803
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
726804
defer cancel()
727805

728-
build, err := templateAdminClient.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
806+
build, err := adminClient.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
729807
TemplateVersionID: workspace.LatestBuild.TemplateVersionID,
730808
Transition: codersdk.WorkspaceTransitionStart,
731809
ProvisionerState: []byte(" "),
732810
LogLevel: "debug",
733811
})
734812
require.Nil(t, err)
735813

736-
build = coderdtest.AwaitWorkspaceBuildJob(t, templateAdminClient, build.ID)
814+
build = coderdtest.AwaitWorkspaceBuildJob(t, adminClient, build.ID)
737815

738816
// Watch for incoming logs
739-
logs, closer, err := templateAdminClient.WorkspaceBuildLogsAfter(ctx, build.ID, 0)
817+
logs, closer, err := adminClient.WorkspaceBuildLogsAfter(ctx, build.ID, 0)
740818
require.NoError(t, err)
741819
defer closer.Close()
742820

coderd/wsbuilder/wsbuilder.go

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,13 @@ import (
3434
// build, job, err := b.Build(...)
3535
type Builder struct {
3636
// settings that control the kind of build you get
37-
workspace database.Workspace
38-
trans database.WorkspaceTransition
39-
version versionTarget
40-
state stateTarget
41-
logLevel string
37+
workspace database.Workspace
38+
trans database.WorkspaceTransition
39+
version versionTarget
40+
state stateTarget
41+
logLevel string
42+
deploymentValues *codersdk.DeploymentValues
43+
4244
richParameterValues []codersdk.WorkspaceBuildParameter
4345
initiator uuid.UUID
4446
reason database.BuildReason
@@ -128,6 +130,12 @@ func (b Builder) LogLevel(l string) Builder {
128130
return b
129131
}
130132

133+
func (b Builder) DeploymentValues(dv *codersdk.DeploymentValues) Builder {
134+
// nolint: revive
135+
b.deploymentValues = dv
136+
return b
137+
}
138+
131139
func (b Builder) Initiator(u uuid.UUID) Builder {
132140
// nolint: revive
133141
b.initiator = u
@@ -638,11 +646,19 @@ func (b *Builder) authorize(authFunc func(action rbac.Action, object rbac.Object
638646
}
639647
}
640648

641-
if b.logLevel != "" && !authFunc(rbac.ActionUpdate, template) {
649+
if b.logLevel != "" && !authFunc(rbac.ActionRead, rbac.ResourceDeploymentValues) {
650+
return BuildError{
651+
http.StatusBadRequest,
652+
"Workspace builds with a custom log level are restricted to administrators only.",
653+
xerrors.New("Workspace builds with a custom log level are restricted to administrators only."),
654+
}
655+
}
656+
657+
if b.logLevel != "" && b.deploymentValues != nil && !b.deploymentValues.EnableTerraformDebugMode {
642658
return BuildError{
643659
http.StatusBadRequest,
644-
"Workspace builds with a custom log level are restricted to template authors only.",
645-
xerrors.New("Workspace builds with a custom log level are restricted to template authors only."),
660+
"Terraform debug mode is disabled in the deployment configuration.",
661+
xerrors.New("Terraform debug mode is disabled in the deployment configuration."),
646662
}
647663
}
648664
return nil

codersdk/deployment.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ type DeploymentValues struct {
165165
WgtunnelHost clibase.String `json:"wgtunnel_host,omitempty" typescript:",notnull"`
166166
DisableOwnerWorkspaceExec clibase.Bool `json:"disable_owner_workspace_exec,omitempty" typescript:",notnull"`
167167
ProxyHealthStatusInterval clibase.Duration `json:"proxy_health_status_interval,omitempty" typescript:",notnull"`
168+
EnableTerraformDebugMode clibase.Bool `json:"enable_terraform_debug_mode,omitempty" typescript:",notnull"`
168169

169170
Config clibase.YAMLConfigPath `json:"config,omitempty" typescript:",notnull"`
170171
WriteConfig clibase.Bool `json:"write_config,omitempty" typescript:",notnull"`
@@ -1215,10 +1216,20 @@ when required by your organization's security policy.`,
12151216
YAML: "stackdriverPath",
12161217
Annotations: clibase.Annotations{}.Mark(annotationExternalProxies, "true"),
12171218
},
1219+
{
1220+
Name: "Enable Terraform debug mode",
1221+
Description: "Allow administrators to enable Terraform debug output.",
1222+
Flag: "enable-terraform-debug-mode",
1223+
Env: "CODER_ENABLE_TERRAFORM_DEBUG_MODE",
1224+
Default: "false",
1225+
Value: &c.EnableTerraformDebugMode,
1226+
Group: &deploymentGroupIntrospectionLogging,
1227+
YAML: "enableTerraformDebugMode",
1228+
},
12181229
// ☢️ Dangerous settings
12191230
{
1220-
Name: "DANGEROUS: Allow all CORs requests",
1221-
Description: "For security reasons, CORs requests are blocked except between workspace apps owned by the same user. If external requests are required, setting this to true will set all cors headers as '*'. This should never be used in production.",
1231+
Name: "DANGEROUS: Allow all CORS requests",
1232+
Description: "For security reasons, CORS requests are blocked except between workspace apps owned by the same user. If external requests are required, setting this to true will set all cors headers as '*'. This should never be used in production.",
12221233
Flag: "dangerous-allow-cors-requests",
12231234
Env: "CODER_DANGEROUS_ALLOW_CORS_REQUESTS",
12241235
Hidden: true, // Hidden, should only be used by yarn dev server

docs/api/general.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \
196196
"disable_password_auth": true,
197197
"disable_path_apps": true,
198198
"disable_session_expiry_refresh": true,
199+
"enable_terraform_debug_mode": true,
199200
"experiments": ["string"],
200201
"git_auth": {
201202
"value": [

docs/api/schemas.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1873,6 +1873,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
18731873
"disable_password_auth": true,
18741874
"disable_path_apps": true,
18751875
"disable_session_expiry_refresh": true,
1876+
"enable_terraform_debug_mode": true,
18761877
"experiments": ["string"],
18771878
"git_auth": {
18781879
"value": [
@@ -2204,6 +2205,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
22042205
"disable_password_auth": true,
22052206
"disable_path_apps": true,
22062207
"disable_session_expiry_refresh": true,
2208+
"enable_terraform_debug_mode": true,
22072209
"experiments": ["string"],
22082210
"git_auth": {
22092211
"value": [
@@ -2398,6 +2400,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
23982400
| `disable_password_auth` | boolean | false | | |
23992401
| `disable_path_apps` | boolean | false | | |
24002402
| `disable_session_expiry_refresh` | boolean | false | | |
2403+
| `enable_terraform_debug_mode` | boolean | false | | |
24012404
| `experiments` | array of string | false | | |
24022405
| `git_auth` | [clibase.Struct-array_codersdk_GitAuthConfig](#clibasestruct-array_codersdk_gitauthconfig) | false | | |
24032406
| `http_address` | string | false | | Http address is a string because it may be set to zero to disable. |

docs/cli/server.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,17 @@ Disable workspace apps that are not served from subdomains. Path-based apps can
223223

224224
Disable automatic session expiry bumping due to activity. This forces all sessions to become invalid after the session expiry duration has been reached.
225225

226+
### --enable-terraform-debug-mode
227+
228+
| | |
229+
| ----------- | ----------------------------------------------------------- |
230+
| Type | <code>bool</code> |
231+
| Environment | <code>$CODER_ENABLE_TERRAFORM_DEBUG_MODE</code> |
232+
| YAML | <code>introspection.logging.enableTerraformDebugMode</code> |
233+
| Default | <code>false</code> |
234+
235+
Allow administrators to enable Terraform debug output.
236+
226237
### --swagger-enable
227238

228239
| | |

enterprise/cli/testdata/coder_server_--help.golden

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ Use a YAML configuration file when your server launch become unwieldy.
7474
Write out the current server config as YAML to stdout.
7575

7676
Introspection / Logging Options
77+
--enable-terraform-debug-mode bool, $CODER_ENABLE_TERRAFORM_DEBUG_MODE (default: false)
78+
Allow administrators to enable Terraform debug output.
79+
7780
--log-human string, $CODER_LOGGING_HUMAN (default: /dev/stderr)
7881
Output human-readable logs to a given file.
7982

site/src/api/typesGenerated.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ export interface DeploymentValues {
374374
readonly wgtunnel_host?: string
375375
readonly disable_owner_workspace_exec?: boolean
376376
readonly proxy_health_status_interval?: number
377+
readonly enable_terraform_debug_mode?: boolean
377378
// This is likely an enum in an external package ("github.com/coder/coder/cli/clibase.YAMLConfigPath")
378379
readonly config?: string
379380
readonly write_config?: boolean

site/src/components/Workspace/Workspace.stories.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,25 @@ export const FailedWithLogs: Story = {
156156
},
157157
}
158158

159+
export const FailedWithRetry: Story = {
160+
args: {
161+
...Running.args,
162+
workspace: {
163+
...Mocks.MockFailedWorkspace,
164+
latest_build: {
165+
...Mocks.MockFailedWorkspace.latest_build,
166+
job: {
167+
...Mocks.MockFailedWorkspace.latest_build.job,
168+
error:
169+
"recv workspace provision: plan terraform: terraform plan: exit status 1",
170+
},
171+
},
172+
},
173+
failedBuildLogs: makeFailedBuildLogs(),
174+
canRetryDebugMode: true,
175+
},
176+
}
177+
159178
export const Deleting: Story = {
160179
args: {
161180
...Running.args,

0 commit comments

Comments
 (0)