Skip to content

feat: extend workspace build reasons to track connection types #18827

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion cli/parameter.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,11 @@ func parseParameterMapFile(parameterFile string) (map[string]string, error) {
return parameterMap, nil
}

// buildFlags contains options relating to troubleshooting provisioner jobs.
// buildFlags contains options relating to troubleshooting provisioner jobs
// and setting the reason for the workspace build.
type buildFlags struct {
provisionerLogDebug bool
reason string
}

func (bf *buildFlags) cliOptions() []serpent.Option {
Expand All @@ -160,5 +162,16 @@ This is useful for troubleshooting build issues.`,
Value: serpent.BoolOf(&bf.provisionerLogDebug),
Hidden: true,
},
{
Flag: "reason",
Description: `Sets the reason for the workspace build (cli, vscode_connection).`,
Value: serpent.EnumOf(
&bf.reason,
string(codersdk.BuildReasonCLI),
string(codersdk.BuildReasonVSCodeConnection),
),
Default: string(codersdk.BuildReasonCLI),
Hidden: true,
},
}
}
4 changes: 3 additions & 1 deletion cli/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -873,7 +873,9 @@ func getWorkspaceAndAgent(ctx context.Context, inv *serpent.Invocation, client *
// It's possible for a workspace build to fail due to the template requiring starting
// workspaces with the active version.
_, _ = fmt.Fprintf(inv.Stderr, "Workspace was stopped, starting workspace to allow connecting to %q...\n", workspace.Name)
_, err = startWorkspace(inv, client, workspace, workspaceParameterFlags{}, buildFlags{}, WorkspaceStart)
_, err = startWorkspace(inv, client, workspace, workspaceParameterFlags{}, buildFlags{
reason: string(codersdk.BuildReasonSSHConnection),
}, WorkspaceStart)
if cerr, ok := codersdk.AsError(err); ok {
switch cerr.StatusCode() {
case http.StatusConflict:
Expand Down
3 changes: 3 additions & 0 deletions cli/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ func buildWorkspaceStartRequest(inv *serpent.Invocation, client *codersdk.Client
if buildFlags.provisionerLogDebug {
wbr.LogLevel = codersdk.ProvisionerLogLevelDebug
}
if buildFlags.reason != "" {
wbr.Reason = codersdk.CreateWorkspaceBuildReason(buildFlags.reason)
}

return wbr, nil
}
Expand Down
36 changes: 36 additions & 0 deletions cli/start_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -477,3 +477,39 @@ func TestStart_NoWait(t *testing.T) {
pty.ExpectMatch("workspace has been started in no-wait mode")
_ = testutil.TryReceive(ctx, t, doneChan)
}

func TestStart_WithReason(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)

// Prepare user, template, workspace
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
owner := coderdtest.CreateFirstUser(t, client)
member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
version1 := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil)
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version1.ID)
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version1.ID)
workspace := coderdtest.CreateWorkspace(t, member, template.ID)
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)

// Stop the workspace
build := coderdtest.CreateWorkspaceBuild(t, member, workspace, database.WorkspaceTransitionStop)
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, build.ID)

// Start the workspace with reason
inv, root := clitest.New(t, "start", workspace.Name, "--reason", "cli")
clitest.SetupConfig(t, member, root)
doneChan := make(chan struct{})
pty := ptytest.New(t).Attach(inv)
go func() {
defer close(doneChan)
err := inv.Run()
assert.NoError(t, err)
}()

pty.ExpectMatch("workspace has been started")
_ = testutil.TryReceive(ctx, t, doneChan)

workspace = coderdtest.MustWorkspace(t, member, workspace.ID)
require.Equal(t, codersdk.BuildReasonCLI, workspace.LatestBuild.Reason)
}
46 changes: 44 additions & 2 deletions coderd/apidoc/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 49 additions & 2 deletions coderd/apidoc/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion coderd/database/dump.sql

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-- It's not possible to delete enum values.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ALTER TYPE build_reason ADD VALUE IF NOT EXISTS 'dashboard';
ALTER TYPE build_reason ADD VALUE IF NOT EXISTS 'cli';
ALTER TYPE build_reason ADD VALUE IF NOT EXISTS 'ssh_connection';
ALTER TYPE build_reason ADD VALUE IF NOT EXISTS 'vscode_connection';
ALTER TYPE build_reason ADD VALUE IF NOT EXISTS 'jetbrains_connection';
29 changes: 22 additions & 7 deletions coderd/database/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 15 additions & 1 deletion coderd/workspacebuilds.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"slices"
"sort"
"strconv"
"strings"
"time"

"github.com/go-chi/chi/v5"
Expand Down Expand Up @@ -329,20 +330,33 @@ func (api *API) workspaceBuildByBuildNumber(rw http.ResponseWriter, r *http.Requ
func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
apiKey := httpmw.APIKey(r)

workspace := httpmw.WorkspaceParam(r)
var createBuild codersdk.CreateWorkspaceBuildRequest
if !httpapi.Read(ctx, rw, r, &createBuild) {
return
}

builder := wsbuilder.New(workspace, database.WorkspaceTransition(createBuild.Transition)).
transition := database.WorkspaceTransition(createBuild.Transition)
builder := wsbuilder.New(workspace, transition).
Initiator(apiKey.UserID).
RichParameterValues(createBuild.RichParameterValues).
LogLevel(string(createBuild.LogLevel)).
DeploymentValues(api.Options.DeploymentValues).
Experiments(api.Experiments).
TemplateVersionPresetID(createBuild.TemplateVersionPresetID)

if transition == database.WorkspaceTransitionStart {
if createBuild.Reason == "" {
userAgent := r.Header.Get("User-Agent")
if strings.HasPrefix(userAgent, "Coder Toolbox") || strings.HasPrefix(userAgent, "Coder Gateway") {
builder = builder.Reason(database.BuildReasonJetbrainsConnection)
}
} else {
builder = builder.Reason(database.BuildReason(createBuild.Reason))
}
}

var (
previousWorkspaceBuild database.WorkspaceBuild
workspaceBuild *database.WorkspaceBuild
Expand Down
Loading
Loading