feat: enable Terraform debug mode via deployment configuration (#8260)

This commit is contained in:
Marcin Tojek
2023-06-29 15:22:21 +02:00
committed by GitHub
parent 7ca624eceb
commit 83fee4b192
19 changed files with 214 additions and 27 deletions

3
coderd/apidoc/docs.go generated
View File

@ -7270,6 +7270,9 @@ const docTemplate = `{
"disable_session_expiry_refresh": {
"type": "boolean"
},
"enable_terraform_debug_mode": {
"type": "boolean"
},
"experiments": {
"type": "array",
"items": {

View File

@ -6493,6 +6493,9 @@
"disable_session_expiry_refresh": {
"type": "boolean"
},
"enable_terraform_debug_mode": {
"type": "boolean"
},
"experiments": {
"type": "array",
"items": {

View File

@ -316,7 +316,8 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
builder := wsbuilder.New(workspace, database.WorkspaceTransition(createBuild.Transition)).
Initiator(apiKey.UserID).
RichParameterValues(createBuild.RichParameterValues).
LogLevel(string(createBuild.LogLevel))
LogLevel(string(createBuild.LogLevel)).
DeploymentValues(api.Options.DeploymentValues)
if createBuild.TemplateVersionID != uuid.Nil {
builder = builder.VersionID(createBuild.TemplateVersionID)

View File

@ -640,11 +640,50 @@ func TestWorkspaceBuildStatus(t *testing.T) {
func TestWorkspaceBuildDebugMode(t *testing.T) {
t.Parallel()
t.Run("DebugModeDisabled", func(t *testing.T) {
t.Parallel()
// Create user
deploymentValues := coderdtest.DeploymentValues(t)
deploymentValues.EnableTerraformDebugMode = false
adminClient := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, DeploymentValues: deploymentValues})
owner := coderdtest.CreateFirstUser(t, adminClient)
// Template author: create a template
version := coderdtest.CreateTemplateVersion(t, adminClient, owner.OrganizationID, nil)
template := coderdtest.CreateTemplate(t, adminClient, owner.OrganizationID, version.ID)
coderdtest.AwaitTemplateVersionJob(t, adminClient, version.ID)
// Template author: create a workspace
workspace := coderdtest.CreateWorkspace(t, adminClient, owner.OrganizationID, template.ID)
coderdtest.AwaitWorkspaceBuildJob(t, adminClient, workspace.LatestBuild.ID)
// Template author: try to start a workspace build in debug mode
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
_, err := adminClient.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
TemplateVersionID: workspace.LatestBuild.TemplateVersionID,
Transition: codersdk.WorkspaceTransitionStart,
LogLevel: "debug",
})
// Template author: expect an error as the debug mode is disabled
require.NotNil(t, err)
var sdkError *codersdk.Error
isSdkError := xerrors.As(err, &sdkError)
require.True(t, isSdkError)
require.Contains(t, sdkError.Message, "Terraform debug mode is disabled in the deployment configuration.")
})
t.Run("AsRegularUser", func(t *testing.T) {
t.Parallel()
// Create users
templateAuthorClient := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
deploymentValues := coderdtest.DeploymentValues(t)
deploymentValues.EnableTerraformDebugMode = true
templateAuthorClient := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, DeploymentValues: deploymentValues})
templateAuthor := coderdtest.CreateFirstUser(t, templateAuthorClient)
regularUserClient, _ := coderdtest.CreateAnotherUser(t, templateAuthorClient, templateAuthor.OrganizationID)
@ -672,15 +711,54 @@ func TestWorkspaceBuildDebugMode(t *testing.T) {
var sdkError *codersdk.Error
isSdkError := xerrors.As(err, &sdkError)
require.True(t, isSdkError)
require.Contains(t, sdkError.Message, "Workspace builds with a custom log level are restricted to template authors only.")
require.Contains(t, sdkError.Message, "Workspace builds with a custom log level are restricted to administrators only.")
})
t.Run("AsTemplateAuthor", func(t *testing.T) {
t.Parallel()
// Create users
adminClient := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
deploymentValues := coderdtest.DeploymentValues(t)
deploymentValues.EnableTerraformDebugMode = true
adminClient := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, DeploymentValues: deploymentValues})
admin := coderdtest.CreateFirstUser(t, adminClient)
templateAuthorClient, _ := coderdtest.CreateAnotherUser(t, adminClient, admin.OrganizationID, rbac.RoleTemplateAdmin())
// Template author: create a template
version := coderdtest.CreateTemplateVersion(t, templateAuthorClient, admin.OrganizationID, nil)
template := coderdtest.CreateTemplate(t, templateAuthorClient, admin.OrganizationID, version.ID)
coderdtest.AwaitTemplateVersionJob(t, templateAuthorClient, version.ID)
// Template author: create a workspace
workspace := coderdtest.CreateWorkspace(t, templateAuthorClient, admin.OrganizationID, template.ID)
coderdtest.AwaitWorkspaceBuildJob(t, templateAuthorClient, workspace.LatestBuild.ID)
// Template author: try to start a workspace build in debug mode
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
_, err := templateAuthorClient.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
TemplateVersionID: workspace.LatestBuild.TemplateVersionID,
Transition: codersdk.WorkspaceTransitionStart,
LogLevel: "debug",
})
// Template author: expect an error as the debug mode is disabled
require.NotNil(t, err)
var sdkError *codersdk.Error
isSdkError := xerrors.As(err, &sdkError)
require.True(t, isSdkError)
require.Contains(t, sdkError.Message, "Workspace builds with a custom log level are restricted to administrators only.")
})
t.Run("AsAdmin", func(t *testing.T) {
t.Parallel()
// Create users
deploymentValues := coderdtest.DeploymentValues(t)
deploymentValues.EnableTerraformDebugMode = true
adminClient := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, DeploymentValues: deploymentValues})
admin := coderdtest.CreateFirstUser(t, adminClient)
templateAdminClient, _ := coderdtest.CreateAnotherUser(t, adminClient, admin.OrganizationID, rbac.RoleTemplateAdmin())
// Interact as template admin
echoResponses := &echo.Responses{
@ -713,19 +791,19 @@ func TestWorkspaceBuildDebugMode(t *testing.T) {
},
}},
}
version := coderdtest.CreateTemplateVersion(t, templateAdminClient, admin.OrganizationID, echoResponses)
template := coderdtest.CreateTemplate(t, templateAdminClient, admin.OrganizationID, version.ID)
coderdtest.AwaitTemplateVersionJob(t, templateAdminClient, version.ID)
version := coderdtest.CreateTemplateVersion(t, adminClient, admin.OrganizationID, echoResponses)
template := coderdtest.CreateTemplate(t, adminClient, admin.OrganizationID, version.ID)
coderdtest.AwaitTemplateVersionJob(t, adminClient, version.ID)
// Create workspace
workspace := coderdtest.CreateWorkspace(t, templateAdminClient, admin.OrganizationID, template.ID)
coderdtest.AwaitWorkspaceBuildJob(t, templateAdminClient, workspace.LatestBuild.ID)
workspace := coderdtest.CreateWorkspace(t, adminClient, admin.OrganizationID, template.ID)
coderdtest.AwaitWorkspaceBuildJob(t, adminClient, workspace.LatestBuild.ID)
// Create workspace build
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
build, err := templateAdminClient.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
build, err := adminClient.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
TemplateVersionID: workspace.LatestBuild.TemplateVersionID,
Transition: codersdk.WorkspaceTransitionStart,
ProvisionerState: []byte(" "),
@ -733,10 +811,10 @@ func TestWorkspaceBuildDebugMode(t *testing.T) {
})
require.Nil(t, err)
build = coderdtest.AwaitWorkspaceBuildJob(t, templateAdminClient, build.ID)
build = coderdtest.AwaitWorkspaceBuildJob(t, adminClient, build.ID)
// Watch for incoming logs
logs, closer, err := templateAdminClient.WorkspaceBuildLogsAfter(ctx, build.ID, 0)
logs, closer, err := adminClient.WorkspaceBuildLogsAfter(ctx, build.ID, 0)
require.NoError(t, err)
defer closer.Close()

View File

@ -34,11 +34,13 @@ import (
// build, job, err := b.Build(...)
type Builder struct {
// settings that control the kind of build you get
workspace database.Workspace
trans database.WorkspaceTransition
version versionTarget
state stateTarget
logLevel string
workspace database.Workspace
trans database.WorkspaceTransition
version versionTarget
state stateTarget
logLevel string
deploymentValues *codersdk.DeploymentValues
richParameterValues []codersdk.WorkspaceBuildParameter
initiator uuid.UUID
reason database.BuildReason
@ -128,6 +130,12 @@ func (b Builder) LogLevel(l string) Builder {
return b
}
func (b Builder) DeploymentValues(dv *codersdk.DeploymentValues) Builder {
// nolint: revive
b.deploymentValues = dv
return b
}
func (b Builder) Initiator(u uuid.UUID) Builder {
// nolint: revive
b.initiator = u
@ -638,11 +646,19 @@ func (b *Builder) authorize(authFunc func(action rbac.Action, object rbac.Object
}
}
if b.logLevel != "" && !authFunc(rbac.ActionUpdate, template) {
if b.logLevel != "" && !authFunc(rbac.ActionRead, rbac.ResourceDeploymentValues) {
return BuildError{
http.StatusBadRequest,
"Workspace builds with a custom log level are restricted to template authors only.",
xerrors.New("Workspace builds with a custom log level are restricted to template authors only."),
"Workspace builds with a custom log level are restricted to administrators only.",
xerrors.New("Workspace builds with a custom log level are restricted to administrators only."),
}
}
if b.logLevel != "" && b.deploymentValues != nil && !b.deploymentValues.EnableTerraformDebugMode {
return BuildError{
http.StatusBadRequest,
"Terraform debug mode is disabled in the deployment configuration.",
xerrors.New("Terraform debug mode is disabled in the deployment configuration."),
}
}
return nil