Skip to content

Commit feb7689

Browse files
committed
Merge remote-tracking branch 'origin/main' into authzquerier_layer
2 parents 0e3b9ff + 23176bf commit feb7689

File tree

5 files changed

+194
-55
lines changed

5 files changed

+194
-55
lines changed

cli/agent.go

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net/http/pprof"
88
"net/url"
99
"os"
10+
"os/signal"
1011
"path/filepath"
1112
"runtime"
1213
"time"
@@ -35,12 +36,10 @@ func workspaceAgent() *cobra.Command {
3536
Use: "agent",
3637
// This command isn't useful to manually execute.
3738
Hidden: true,
38-
RunE: func(cmd *cobra.Command, args []string) error {
39+
RunE: func(cmd *cobra.Command, _ []string) error {
3940
ctx, cancel := context.WithCancel(cmd.Context())
4041
defer cancel()
4142

42-
go dumpHandler(ctx)
43-
4443
rawURL, err := cmd.Flags().GetString(varAgentURL)
4544
if err != nil {
4645
return xerrors.Errorf("CODER_AGENT_URL must be set: %w", err)
@@ -50,18 +49,18 @@ func workspaceAgent() *cobra.Command {
5049
return xerrors.Errorf("parse %q: %w", rawURL, err)
5150
}
5251

53-
logWriter := &lumberjack.Logger{
54-
Filename: filepath.Join(os.TempDir(), "coder-agent.log"),
55-
MaxSize: 5, // MB
56-
}
57-
defer logWriter.Close()
58-
logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr()), sloghuman.Sink(logWriter)).Leveled(slog.LevelDebug)
59-
6052
isLinux := runtime.GOOS == "linux"
6153

6254
// Spawn a reaper so that we don't accumulate a ton
6355
// of zombie processes.
6456
if reaper.IsInitProcess() && !noReap && isLinux {
57+
logWriter := &lumberjack.Logger{
58+
Filename: filepath.Join(os.TempDir(), "coder-agent-init.log"),
59+
MaxSize: 5, // MB
60+
}
61+
defer logWriter.Close()
62+
logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr()), sloghuman.Sink(logWriter)).Leveled(slog.LevelDebug)
63+
6564
logger.Info(ctx, "spawning reaper process")
6665
// Do not start a reaper on the child process. It's important
6766
// to do this else we fork bomb ourselves.
@@ -76,6 +75,28 @@ func workspaceAgent() *cobra.Command {
7675
return nil
7776
}
7877

78+
// Handle interrupt signals to allow for graceful shutdown,
79+
// note that calling stopNotify disables the signal handler
80+
// and the next interrupt will terminate the program (you
81+
// probably want cancel instead).
82+
//
83+
// Note that we don't want to handle these signals in the
84+
// process that runs as PID 1, that's why we do this after
85+
// the reaper forked.
86+
ctx, stopNotify := signal.NotifyContext(ctx, InterruptSignals...)
87+
defer stopNotify()
88+
89+
// dumpHandler does signal handling, so we call it after the
90+
// reaper.
91+
go dumpHandler(ctx)
92+
93+
logWriter := &lumberjack.Logger{
94+
Filename: filepath.Join(os.TempDir(), "coder-agent.log"),
95+
MaxSize: 5, // MB
96+
}
97+
defer logWriter.Close()
98+
logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr()), sloghuman.Sink(logWriter)).Leveled(slog.LevelDebug)
99+
79100
version := buildinfo.Version()
80101
logger.Info(ctx, "starting agent",
81102
slog.F("url", coderURL),

coderd/autobuild/executor/lifecycle_executor.go

Lines changed: 53 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -271,37 +271,59 @@ func build(ctx context.Context, store database.Store, workspace database.Workspa
271271
return xerrors.Errorf("Unsupported transition: %q", trans)
272272
}
273273

274-
newProvisionerJob, err := store.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
275-
ID: provisionerJobID,
276-
CreatedAt: now,
277-
UpdatedAt: now,
278-
InitiatorID: workspace.OwnerID,
279-
OrganizationID: template.OrganizationID,
280-
Provisioner: template.Provisioner,
281-
Type: database.ProvisionerJobTypeWorkspaceBuild,
282-
StorageMethod: priorJob.StorageMethod,
283-
FileID: priorJob.FileID,
284-
Tags: priorJob.Tags,
285-
Input: input,
286-
})
287-
if err != nil {
288-
return xerrors.Errorf("insert provisioner job: %w", err)
289-
}
290-
_, err = store.InsertWorkspaceBuild(ctx, database.InsertWorkspaceBuildParams{
291-
ID: workspaceBuildID,
292-
CreatedAt: now,
293-
UpdatedAt: now,
294-
WorkspaceID: workspace.ID,
295-
TemplateVersionID: priorHistory.TemplateVersionID,
296-
BuildNumber: priorBuildNumber + 1,
297-
ProvisionerState: priorHistory.ProvisionerState,
298-
InitiatorID: workspace.OwnerID,
299-
Transition: trans,
300-
JobID: newProvisionerJob.ID,
301-
Reason: buildReason,
302-
})
274+
lastBuildParameters, err := store.GetWorkspaceBuildParameters(ctx, priorHistory.ID)
303275
if err != nil {
304-
return xerrors.Errorf("insert workspace build: %w", err)
276+
return xerrors.Errorf("fetch prior workspace build parameters: %w", err)
305277
}
306-
return nil
278+
279+
return store.InTx(func(db database.Store) error {
280+
newProvisionerJob, err := store.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
281+
ID: provisionerJobID,
282+
CreatedAt: now,
283+
UpdatedAt: now,
284+
InitiatorID: workspace.OwnerID,
285+
OrganizationID: template.OrganizationID,
286+
Provisioner: template.Provisioner,
287+
Type: database.ProvisionerJobTypeWorkspaceBuild,
288+
StorageMethod: priorJob.StorageMethod,
289+
FileID: priorJob.FileID,
290+
Tags: priorJob.Tags,
291+
Input: input,
292+
})
293+
if err != nil {
294+
return xerrors.Errorf("insert provisioner job: %w", err)
295+
}
296+
workspaceBuild, err := store.InsertWorkspaceBuild(ctx, database.InsertWorkspaceBuildParams{
297+
ID: workspaceBuildID,
298+
CreatedAt: now,
299+
UpdatedAt: now,
300+
WorkspaceID: workspace.ID,
301+
TemplateVersionID: priorHistory.TemplateVersionID,
302+
BuildNumber: priorBuildNumber + 1,
303+
ProvisionerState: priorHistory.ProvisionerState,
304+
InitiatorID: workspace.OwnerID,
305+
Transition: trans,
306+
JobID: newProvisionerJob.ID,
307+
Reason: buildReason,
308+
})
309+
if err != nil {
310+
return xerrors.Errorf("insert workspace build: %w", err)
311+
}
312+
313+
names := make([]string, 0, len(lastBuildParameters))
314+
values := make([]string, 0, len(lastBuildParameters))
315+
for _, param := range lastBuildParameters {
316+
names = append(names, param.Name)
317+
values = append(values, param.Value)
318+
}
319+
err = db.InsertWorkspaceBuildParameters(ctx, database.InsertWorkspaceBuildParametersParams{
320+
WorkspaceBuildID: workspaceBuild.ID,
321+
Name: names,
322+
Value: values,
323+
})
324+
if err != nil {
325+
return xerrors.Errorf("insert workspace build parameters: %w", err)
326+
}
327+
return nil
328+
}, nil)
307329
}

coderd/autobuild/executor/lifecycle_executor_test.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@ import (
88

99
"go.uber.org/goleak"
1010

11+
"github.com/google/uuid"
12+
1113
"github.com/coder/coder/coderd/autobuild/executor"
1214
"github.com/coder/coder/coderd/autobuild/schedule"
1315
"github.com/coder/coder/coderd/coderdtest"
1416
"github.com/coder/coder/coderd/database"
1517
"github.com/coder/coder/coderd/util/ptr"
1618
"github.com/coder/coder/codersdk"
19+
"github.com/coder/coder/provisioner/echo"
20+
"github.com/coder/coder/provisionersdk/proto"
1721

1822
"github.com/stretchr/testify/assert"
1923
"github.com/stretchr/testify/require"
@@ -540,6 +544,67 @@ func TestExecutorAutostartMultipleOK(t *testing.T) {
540544
assert.Len(t, stats2.Transitions, 0)
541545
}
542546

547+
func TestExecutorAutostartWithParameters(t *testing.T) {
548+
t.Parallel()
549+
550+
const (
551+
stringParameterName = "string_parameter"
552+
stringParameterValue = "abc"
553+
554+
numberParameterName = "number_parameter"
555+
numberParameterValue = "7"
556+
)
557+
558+
var (
559+
sched = mustSchedule(t, "CRON_TZ=UTC 0 * * * *")
560+
tickCh = make(chan time.Time)
561+
statsCh = make(chan executor.Stats)
562+
client = coderdtest.New(t, &coderdtest.Options{
563+
AutobuildTicker: tickCh,
564+
IncludeProvisionerDaemon: true,
565+
AutobuildStats: statsCh,
566+
})
567+
568+
richParameters = []*proto.RichParameter{
569+
{Name: stringParameterName, Type: "string", Mutable: true},
570+
{Name: numberParameterName, Type: "number", Mutable: true},
571+
}
572+
573+
// Given: we have a user with a workspace that has autostart enabled
574+
workspace = mustProvisionWorkspaceWithParameters(t, client, richParameters, func(cwr *codersdk.CreateWorkspaceRequest) {
575+
cwr.AutostartSchedule = ptr.Ref(sched.String())
576+
cwr.RichParameterValues = []codersdk.WorkspaceBuildParameter{
577+
{
578+
Name: stringParameterName,
579+
Value: stringParameterValue,
580+
},
581+
{
582+
Name: numberParameterName,
583+
Value: numberParameterValue,
584+
},
585+
}
586+
})
587+
)
588+
// Given: workspace is stopped
589+
workspace = coderdtest.MustTransitionWorkspace(t, client, workspace.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop)
590+
591+
// When: the autobuild executor ticks after the scheduled time
592+
go func() {
593+
tickCh <- sched.Next(workspace.LatestBuild.CreatedAt)
594+
close(tickCh)
595+
}()
596+
597+
// Then: the workspace with parameters should eventually be started
598+
stats := <-statsCh
599+
assert.NoError(t, stats.Error)
600+
assert.Len(t, stats.Transitions, 1)
601+
assert.Contains(t, stats.Transitions, workspace.ID)
602+
assert.Equal(t, database.WorkspaceTransitionStart, stats.Transitions[workspace.ID])
603+
604+
workspace = coderdtest.MustWorkspace(t, client, workspace.ID)
605+
mustWorkspaceParameters(t, client, workspace.LatestBuild.ID)
606+
}
607+
543608
func mustProvisionWorkspace(t *testing.T, client *codersdk.Client, mut ...func(*codersdk.CreateWorkspaceRequest)) codersdk.Workspace {
544609
t.Helper()
545610
user := coderdtest.CreateFirstUser(t, client)
@@ -551,13 +616,48 @@ func mustProvisionWorkspace(t *testing.T, client *codersdk.Client, mut ...func(*
551616
return coderdtest.MustWorkspace(t, client, ws.ID)
552617
}
553618

619+
func mustProvisionWorkspaceWithParameters(t *testing.T, client *codersdk.Client, richParameters []*proto.RichParameter, mut ...func(*codersdk.CreateWorkspaceRequest)) codersdk.Workspace {
620+
t.Helper()
621+
user := coderdtest.CreateFirstUser(t, client)
622+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
623+
Parse: echo.ParseComplete,
624+
ProvisionPlan: []*proto.Provision_Response{
625+
{
626+
Type: &proto.Provision_Response_Complete{
627+
Complete: &proto.Provision_Complete{
628+
Parameters: richParameters,
629+
},
630+
},
631+
}},
632+
ProvisionApply: []*proto.Provision_Response{
633+
{
634+
Type: &proto.Provision_Response_Complete{
635+
Complete: &proto.Provision_Complete{},
636+
},
637+
},
638+
},
639+
})
640+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
641+
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
642+
ws := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID, mut...)
643+
coderdtest.AwaitWorkspaceBuildJob(t, client, ws.LatestBuild.ID)
644+
return coderdtest.MustWorkspace(t, client, ws.ID)
645+
}
646+
554647
func mustSchedule(t *testing.T, s string) *schedule.Schedule {
555648
t.Helper()
556649
sched, err := schedule.Weekly(s)
557650
require.NoError(t, err)
558651
return sched
559652
}
560653

654+
func mustWorkspaceParameters(t *testing.T, client *codersdk.Client, workspaceID uuid.UUID) {
655+
ctx := context.Background()
656+
buildParameters, err := client.WorkspaceBuildParameters(ctx, workspaceID)
657+
require.NoError(t, err)
658+
require.NotEmpty(t, buildParameters)
659+
}
660+
561661
func TestMain(m *testing.M) {
562662
goleak.VerifyTestMain(m)
563663
}

codersdk/richparameters.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import (
88

99
func ValidateWorkspaceBuildParameters(richParameters []TemplateVersionParameter, buildParameters []WorkspaceBuildParameter) error {
1010
for _, buildParameter := range buildParameters {
11+
if buildParameter.Name == "" {
12+
return xerrors.Errorf(`workspace build parameter name is missing`)
13+
}
1114
richParameter, found := findTemplateVersionParameter(richParameters, buildParameter.Name)
1215
if !found {
1316
return xerrors.Errorf(`workspace build parameter is not defined in the template ("coder_parameter"): %s`, buildParameter.Name)

examples/templates/kubernetes/main.tf

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ variable "use_kubeconfig" {
2828
variable "namespace" {
2929
type = string
3030
sensitive = true
31-
description = "The namespace to create workspaces in (must exist prior to creating workspaces)"
31+
description = "The Kubernetes namespace to create workspaces in (must exist prior to creating workspaces)"
3232
}
3333

3434
variable "home_disk_size" {
@@ -57,17 +57,9 @@ resource "coder_agent" "main" {
5757
startup_script = <<-EOT
5858
set -e
5959
60-
# home folder can be empty, so copying default bash settings
61-
if [ ! -f ~/.profile ]; then
62-
cp /etc/skel/.profile $HOME
63-
fi
64-
if [ ! -f ~/.bashrc ]; then
65-
cp /etc/skel/.bashrc $HOME
66-
fi
67-
6860
# install and start code-server
69-
curl -fsSL https://code-server.dev/install.sh | sh -s -- --version 4.8.3
70-
code-server --auth none --port 13337 >/tmp/code-server.log 2>&1 &
61+
curl -fsSL https://code-server.dev/install.sh | sh -s
62+
code-server --auth none --port 13337 &
7163
EOT
7264
}
7365

@@ -144,9 +136,10 @@ resource "kubernetes_pod" "main" {
144136
fs_group = "1000"
145137
}
146138
container {
147-
name = "dev"
148-
image = "codercom/enterprise-base:ubuntu"
149-
command = ["sh", "-c", coder_agent.main.init_script]
139+
name = "dev"
140+
image = "codercom/enterprise-base:ubuntu"
141+
image_pull_policy = "Always"
142+
command = ["sh", "-c", coder_agent.main.init_script]
150143
security_context {
151144
run_as_user = "1000"
152145
}

0 commit comments

Comments
 (0)