mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore: Separate the provisionerd server into it's own package (#4940)
* chore: Separate the provisionerd server into it's own package This code should be thoroughly tested now that we understand the abstraction. I separated it to make our lives a bit easier for external provisioner daemons as well! * Add tests * Add workspace builds * Add test for workspace resources
This commit is contained in:
@@ -0,0 +1,773 @@
|
||||
package provisionerdserver_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cdr.dev/slog/sloggers/slogtest"
|
||||
"github.com/coder/coder/coderd/database"
|
||||
"github.com/coder/coder/coderd/database/databasefake"
|
||||
"github.com/coder/coder/coderd/provisionerdserver"
|
||||
"github.com/coder/coder/coderd/telemetry"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/provisionerd/proto"
|
||||
sdkproto "github.com/coder/coder/provisionersdk/proto"
|
||||
)
|
||||
|
||||
func TestAcquireJob(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("NoJobs", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := setup(t)
|
||||
job, err := srv.AcquireJob(context.Background(), nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, &proto.AcquiredJob{}, job)
|
||||
})
|
||||
t.Run("InitiatorNotFound", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := setup(t)
|
||||
_, err := srv.Database.InsertProvisionerJob(context.Background(), database.InsertProvisionerJobParams{
|
||||
ID: uuid.New(),
|
||||
InitiatorID: uuid.New(),
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = srv.AcquireJob(context.Background(), nil)
|
||||
require.ErrorContains(t, err, "sql: no rows in result set")
|
||||
})
|
||||
t.Run("WorkspaceBuildJob", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := setup(t)
|
||||
ctx := context.Background()
|
||||
user, err := srv.Database.InsertUser(context.Background(), database.InsertUserParams{
|
||||
ID: uuid.New(),
|
||||
Username: "testing",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
template, err := srv.Database.InsertTemplate(ctx, database.InsertTemplateParams{
|
||||
ID: uuid.New(),
|
||||
Name: "template",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
version, err := srv.Database.InsertTemplateVersion(ctx, database.InsertTemplateVersionParams{
|
||||
ID: uuid.New(),
|
||||
TemplateID: uuid.NullUUID{
|
||||
UUID: template.ID,
|
||||
Valid: true,
|
||||
},
|
||||
JobID: uuid.New(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
workspace, err := srv.Database.InsertWorkspace(ctx, database.InsertWorkspaceParams{
|
||||
ID: uuid.New(),
|
||||
OwnerID: user.ID,
|
||||
TemplateID: template.ID,
|
||||
Name: "workspace",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
build, err := srv.Database.InsertWorkspaceBuild(ctx, database.InsertWorkspaceBuildParams{
|
||||
ID: uuid.New(),
|
||||
WorkspaceID: workspace.ID,
|
||||
BuildNumber: 1,
|
||||
JobID: uuid.New(),
|
||||
TemplateVersionID: version.ID,
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
data, err := json.Marshal(provisionerdserver.WorkspaceProvisionJob{
|
||||
WorkspaceBuildID: build.ID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
file, err := srv.Database.InsertFile(ctx, database.InsertFileParams{
|
||||
ID: uuid.New(),
|
||||
Hash: "something",
|
||||
Data: []byte{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = srv.Database.InsertProvisionerJob(context.Background(), database.InsertProvisionerJobParams{
|
||||
ID: build.JobID,
|
||||
CreatedAt: database.Now(),
|
||||
UpdatedAt: database.Now(),
|
||||
OrganizationID: uuid.New(),
|
||||
InitiatorID: user.ID,
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
FileID: file.ID,
|
||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||
Input: data,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
published := make(chan struct{})
|
||||
closeSubscribe, err := srv.Pubsub.Subscribe(codersdk.WorkspaceNotifyChannel(workspace.ID), func(_ context.Context, _ []byte) {
|
||||
close(published)
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer closeSubscribe()
|
||||
|
||||
job, err := srv.AcquireJob(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
<-published
|
||||
|
||||
got, err := json.Marshal(job.Type)
|
||||
require.NoError(t, err)
|
||||
|
||||
want, err := json.Marshal(&proto.AcquiredJob_WorkspaceBuild_{
|
||||
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
||||
WorkspaceBuildId: build.ID.String(),
|
||||
WorkspaceName: workspace.Name,
|
||||
ParameterValues: []*sdkproto.ParameterValue{},
|
||||
Metadata: &sdkproto.Provision_Metadata{
|
||||
CoderUrl: srv.AccessURL.String(),
|
||||
WorkspaceTransition: sdkproto.WorkspaceTransition_START,
|
||||
WorkspaceName: workspace.Name,
|
||||
WorkspaceOwner: user.Username,
|
||||
WorkspaceOwnerEmail: user.Email,
|
||||
WorkspaceId: workspace.ID.String(),
|
||||
WorkspaceOwnerId: user.ID.String(),
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.JSONEq(t, string(want), string(got))
|
||||
})
|
||||
t.Run("TemplateVersionDryRun", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := setup(t)
|
||||
ctx := context.Background()
|
||||
user, err := srv.Database.InsertUser(ctx, database.InsertUserParams{
|
||||
ID: uuid.New(),
|
||||
Username: "testing",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
version, err := srv.Database.InsertTemplateVersion(ctx, database.InsertTemplateVersionParams{
|
||||
ID: uuid.New(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
data, err := json.Marshal(provisionerdserver.TemplateVersionDryRunJob{
|
||||
TemplateVersionID: version.ID,
|
||||
WorkspaceName: "testing",
|
||||
ParameterValues: []database.ParameterValue{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
file, err := srv.Database.InsertFile(ctx, database.InsertFileParams{
|
||||
ID: uuid.New(),
|
||||
Hash: "something",
|
||||
Data: []byte{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = srv.Database.InsertProvisionerJob(context.Background(), database.InsertProvisionerJobParams{
|
||||
ID: uuid.New(),
|
||||
CreatedAt: database.Now(),
|
||||
UpdatedAt: database.Now(),
|
||||
OrganizationID: uuid.New(),
|
||||
InitiatorID: user.ID,
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
FileID: file.ID,
|
||||
Type: database.ProvisionerJobTypeTemplateVersionDryRun,
|
||||
Input: data,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
job, err := srv.AcquireJob(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
got, err := json.Marshal(job.Type)
|
||||
require.NoError(t, err)
|
||||
|
||||
want, err := json.Marshal(&proto.AcquiredJob_TemplateDryRun_{
|
||||
TemplateDryRun: &proto.AcquiredJob_TemplateDryRun{
|
||||
ParameterValues: []*sdkproto.ParameterValue{},
|
||||
Metadata: &sdkproto.Provision_Metadata{
|
||||
CoderUrl: srv.AccessURL.String(),
|
||||
WorkspaceName: "testing",
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, string(want), string(got))
|
||||
})
|
||||
t.Run("TemplateVersionImport", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := setup(t)
|
||||
ctx := context.Background()
|
||||
user, err := srv.Database.InsertUser(ctx, database.InsertUserParams{
|
||||
ID: uuid.New(),
|
||||
Username: "testing",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
file, err := srv.Database.InsertFile(ctx, database.InsertFileParams{
|
||||
ID: uuid.New(),
|
||||
Hash: "something",
|
||||
Data: []byte{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = srv.Database.InsertProvisionerJob(context.Background(), database.InsertProvisionerJobParams{
|
||||
ID: uuid.New(),
|
||||
CreatedAt: database.Now(),
|
||||
UpdatedAt: database.Now(),
|
||||
OrganizationID: uuid.New(),
|
||||
InitiatorID: user.ID,
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
FileID: file.ID,
|
||||
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
||||
Input: json.RawMessage{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
job, err := srv.AcquireJob(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
got, err := json.Marshal(job.Type)
|
||||
require.NoError(t, err)
|
||||
|
||||
want, err := json.Marshal(&proto.AcquiredJob_TemplateImport_{
|
||||
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
||||
Metadata: &sdkproto.Provision_Metadata{
|
||||
CoderUrl: srv.AccessURL.String(),
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, string(want), string(got))
|
||||
})
|
||||
}
|
||||
|
||||
func TestUpdateJob(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
t.Run("NotFound", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := setup(t)
|
||||
_, err := srv.UpdateJob(ctx, &proto.UpdateJobRequest{
|
||||
JobId: "hello",
|
||||
})
|
||||
require.ErrorContains(t, err, "invalid UUID")
|
||||
|
||||
_, err = srv.UpdateJob(ctx, &proto.UpdateJobRequest{
|
||||
JobId: uuid.NewString(),
|
||||
})
|
||||
require.ErrorContains(t, err, "no rows in result set")
|
||||
})
|
||||
t.Run("NotRunning", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := setup(t)
|
||||
job, err := srv.Database.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
|
||||
ID: uuid.New(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = srv.UpdateJob(ctx, &proto.UpdateJobRequest{
|
||||
JobId: job.ID.String(),
|
||||
})
|
||||
require.ErrorContains(t, err, "job isn't running yet")
|
||||
})
|
||||
// This test prevents runners from updating jobs they don't own!
|
||||
t.Run("NotOwner", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := setup(t)
|
||||
job, err := srv.Database.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
|
||||
ID: uuid.New(),
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = srv.Database.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
|
||||
WorkerID: uuid.NullUUID{
|
||||
UUID: uuid.New(),
|
||||
Valid: true,
|
||||
},
|
||||
Types: []database.ProvisionerType{database.ProvisionerTypeEcho},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = srv.UpdateJob(ctx, &proto.UpdateJobRequest{
|
||||
JobId: job.ID.String(),
|
||||
})
|
||||
require.ErrorContains(t, err, "you don't own this job")
|
||||
})
|
||||
|
||||
setupJob := func(t *testing.T, srv *provisionerdserver.Server) uuid.UUID {
|
||||
job, err := srv.Database.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
|
||||
ID: uuid.New(),
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = srv.Database.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
|
||||
WorkerID: uuid.NullUUID{
|
||||
UUID: srv.ID,
|
||||
Valid: true,
|
||||
},
|
||||
Types: []database.ProvisionerType{database.ProvisionerTypeEcho},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return job.ID
|
||||
}
|
||||
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := setup(t)
|
||||
job := setupJob(t, srv)
|
||||
_, err := srv.UpdateJob(ctx, &proto.UpdateJobRequest{
|
||||
JobId: job.String(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("Logs", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := setup(t)
|
||||
job := setupJob(t, srv)
|
||||
|
||||
published := make(chan struct{})
|
||||
|
||||
closeListener, err := srv.Pubsub.Subscribe(provisionerdserver.ProvisionerJobLogsNotifyChannel(job), func(_ context.Context, _ []byte) {
|
||||
close(published)
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer closeListener()
|
||||
|
||||
_, err = srv.UpdateJob(ctx, &proto.UpdateJobRequest{
|
||||
JobId: job.String(),
|
||||
Logs: []*proto.Log{{
|
||||
Source: proto.LogSource_PROVISIONER,
|
||||
Level: sdkproto.LogLevel_INFO,
|
||||
Output: "hi",
|
||||
}},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
<-published
|
||||
})
|
||||
t.Run("Readme", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := setup(t)
|
||||
job := setupJob(t, srv)
|
||||
version, err := srv.Database.InsertTemplateVersion(ctx, database.InsertTemplateVersionParams{
|
||||
ID: uuid.New(),
|
||||
JobID: job,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = srv.UpdateJob(ctx, &proto.UpdateJobRequest{
|
||||
JobId: job.String(),
|
||||
Readme: []byte("# hello world"),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
version, err = srv.Database.GetTemplateVersionByID(ctx, version.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "# hello world", version.Readme)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFailJob(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
t.Run("NotFound", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := setup(t)
|
||||
_, err := srv.FailJob(ctx, &proto.FailedJob{
|
||||
JobId: "hello",
|
||||
})
|
||||
require.ErrorContains(t, err, "invalid UUID")
|
||||
|
||||
_, err = srv.UpdateJob(ctx, &proto.UpdateJobRequest{
|
||||
JobId: uuid.NewString(),
|
||||
})
|
||||
require.ErrorContains(t, err, "no rows in result set")
|
||||
})
|
||||
// This test prevents runners from updating jobs they don't own!
|
||||
t.Run("NotOwner", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := setup(t)
|
||||
job, err := srv.Database.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
|
||||
ID: uuid.New(),
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = srv.Database.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
|
||||
WorkerID: uuid.NullUUID{
|
||||
UUID: uuid.New(),
|
||||
Valid: true,
|
||||
},
|
||||
Types: []database.ProvisionerType{database.ProvisionerTypeEcho},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = srv.FailJob(ctx, &proto.FailedJob{
|
||||
JobId: job.ID.String(),
|
||||
})
|
||||
require.ErrorContains(t, err, "you don't own this job")
|
||||
})
|
||||
t.Run("AlreadyCompleted", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := setup(t)
|
||||
job, err := srv.Database.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
|
||||
ID: uuid.New(),
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = srv.Database.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
|
||||
WorkerID: uuid.NullUUID{
|
||||
UUID: srv.ID,
|
||||
Valid: true,
|
||||
},
|
||||
Types: []database.ProvisionerType{database.ProvisionerTypeEcho},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
err = srv.Database.UpdateProvisionerJobWithCompleteByID(ctx, database.UpdateProvisionerJobWithCompleteByIDParams{
|
||||
ID: job.ID,
|
||||
CompletedAt: sql.NullTime{
|
||||
Time: database.Now(),
|
||||
Valid: true,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = srv.FailJob(ctx, &proto.FailedJob{
|
||||
JobId: job.ID.String(),
|
||||
})
|
||||
require.ErrorContains(t, err, "job already completed")
|
||||
})
|
||||
t.Run("WorkspaceBuild", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := setup(t)
|
||||
build, err := srv.Database.InsertWorkspaceBuild(ctx, database.InsertWorkspaceBuildParams{
|
||||
ID: uuid.New(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
input, err := json.Marshal(provisionerdserver.WorkspaceProvisionJob{
|
||||
WorkspaceBuildID: build.ID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
job, err := srv.Database.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
|
||||
ID: uuid.New(),
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
Input: input,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = srv.Database.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
|
||||
WorkerID: uuid.NullUUID{
|
||||
UUID: srv.ID,
|
||||
Valid: true,
|
||||
},
|
||||
Types: []database.ProvisionerType{database.ProvisionerTypeEcho},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
publishedWorkspace := make(chan struct{})
|
||||
closeWorkspaceSubscribe, err := srv.Pubsub.Subscribe(codersdk.WorkspaceNotifyChannel(build.WorkspaceID), func(_ context.Context, _ []byte) {
|
||||
close(publishedWorkspace)
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer closeWorkspaceSubscribe()
|
||||
publishedLogs := make(chan struct{})
|
||||
closeLogsSubscribe, err := srv.Pubsub.Subscribe(provisionerdserver.ProvisionerJobLogsNotifyChannel(job.ID), func(_ context.Context, _ []byte) {
|
||||
close(publishedLogs)
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer closeLogsSubscribe()
|
||||
|
||||
_, err = srv.FailJob(ctx, &proto.FailedJob{
|
||||
JobId: job.ID.String(),
|
||||
Type: &proto.FailedJob_WorkspaceBuild_{
|
||||
WorkspaceBuild: &proto.FailedJob_WorkspaceBuild{
|
||||
State: []byte("some state"),
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
<-publishedWorkspace
|
||||
<-publishedLogs
|
||||
build, err = srv.Database.GetWorkspaceBuildByID(ctx, build.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "some state", string(build.ProvisionerState))
|
||||
})
|
||||
}
|
||||
|
||||
func TestCompleteJob(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
t.Run("NotFound", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := setup(t)
|
||||
_, err := srv.CompleteJob(ctx, &proto.CompletedJob{
|
||||
JobId: "hello",
|
||||
})
|
||||
require.ErrorContains(t, err, "invalid UUID")
|
||||
|
||||
_, err = srv.CompleteJob(ctx, &proto.CompletedJob{
|
||||
JobId: uuid.NewString(),
|
||||
})
|
||||
require.ErrorContains(t, err, "no rows in result set")
|
||||
})
|
||||
// This test prevents runners from updating jobs they don't own!
|
||||
t.Run("NotOwner", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := setup(t)
|
||||
job, err := srv.Database.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
|
||||
ID: uuid.New(),
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = srv.Database.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
|
||||
WorkerID: uuid.NullUUID{
|
||||
UUID: uuid.New(),
|
||||
Valid: true,
|
||||
},
|
||||
Types: []database.ProvisionerType{database.ProvisionerTypeEcho},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = srv.CompleteJob(ctx, &proto.CompletedJob{
|
||||
JobId: job.ID.String(),
|
||||
})
|
||||
require.ErrorContains(t, err, "you don't own this job")
|
||||
})
|
||||
t.Run("TemplateImport", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := setup(t)
|
||||
job, err := srv.Database.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
|
||||
ID: uuid.New(),
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = srv.Database.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
|
||||
WorkerID: uuid.NullUUID{
|
||||
UUID: srv.ID,
|
||||
Valid: true,
|
||||
},
|
||||
Types: []database.ProvisionerType{database.ProvisionerTypeEcho},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = srv.CompleteJob(ctx, &proto.CompletedJob{
|
||||
JobId: job.ID.String(),
|
||||
Type: &proto.CompletedJob_TemplateImport_{
|
||||
TemplateImport: &proto.CompletedJob_TemplateImport{
|
||||
StartResources: []*sdkproto.Resource{{
|
||||
Name: "hello",
|
||||
Type: "aws_instance",
|
||||
}},
|
||||
StopResources: []*sdkproto.Resource{},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
t.Run("WorkspaceBuild", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := setup(t)
|
||||
workspace, err := srv.Database.InsertWorkspace(ctx, database.InsertWorkspaceParams{
|
||||
ID: uuid.New(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
build, err := srv.Database.InsertWorkspaceBuild(ctx, database.InsertWorkspaceBuildParams{
|
||||
ID: uuid.New(),
|
||||
WorkspaceID: workspace.ID,
|
||||
Transition: database.WorkspaceTransitionDelete,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
input, err := json.Marshal(provisionerdserver.WorkspaceProvisionJob{
|
||||
WorkspaceBuildID: build.ID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
job, err := srv.Database.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
|
||||
ID: uuid.New(),
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
Input: input,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = srv.Database.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
|
||||
WorkerID: uuid.NullUUID{
|
||||
UUID: srv.ID,
|
||||
Valid: true,
|
||||
},
|
||||
Types: []database.ProvisionerType{database.ProvisionerTypeEcho},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
publishedWorkspace := make(chan struct{})
|
||||
closeWorkspaceSubscribe, err := srv.Pubsub.Subscribe(codersdk.WorkspaceNotifyChannel(build.WorkspaceID), func(_ context.Context, _ []byte) {
|
||||
close(publishedWorkspace)
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer closeWorkspaceSubscribe()
|
||||
publishedLogs := make(chan struct{})
|
||||
closeLogsSubscribe, err := srv.Pubsub.Subscribe(provisionerdserver.ProvisionerJobLogsNotifyChannel(job.ID), func(_ context.Context, _ []byte) {
|
||||
close(publishedLogs)
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer closeLogsSubscribe()
|
||||
|
||||
_, err = srv.CompleteJob(ctx, &proto.CompletedJob{
|
||||
JobId: job.ID.String(),
|
||||
Type: &proto.CompletedJob_WorkspaceBuild_{
|
||||
WorkspaceBuild: &proto.CompletedJob_WorkspaceBuild{
|
||||
State: []byte{},
|
||||
Resources: []*sdkproto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
}},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
<-publishedWorkspace
|
||||
<-publishedLogs
|
||||
|
||||
workspace, err = srv.Database.GetWorkspaceByID(ctx, workspace.ID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, workspace.Deleted)
|
||||
})
|
||||
|
||||
t.Run("TemplateDryRun", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := setup(t)
|
||||
job, err := srv.Database.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
|
||||
ID: uuid.New(),
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = srv.Database.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
|
||||
WorkerID: uuid.NullUUID{
|
||||
UUID: srv.ID,
|
||||
Valid: true,
|
||||
},
|
||||
Types: []database.ProvisionerType{database.ProvisionerTypeEcho},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = srv.CompleteJob(ctx, &proto.CompletedJob{
|
||||
JobId: job.ID.String(),
|
||||
Type: &proto.CompletedJob_TemplateDryRun_{
|
||||
TemplateDryRun: &proto.CompletedJob_TemplateDryRun{
|
||||
Resources: []*sdkproto.Resource{{
|
||||
Name: "something",
|
||||
Type: "aws_instance",
|
||||
}},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestInsertWorkspaceResource(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
insert := func(db database.Store, jobID uuid.UUID, resource *sdkproto.Resource) error {
|
||||
return provisionerdserver.InsertWorkspaceResource(ctx, db, jobID, database.WorkspaceTransitionStart, resource, &telemetry.Snapshot{})
|
||||
}
|
||||
t.Run("NoAgents", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
db := databasefake.New()
|
||||
job := uuid.New()
|
||||
err := insert(db, job, &sdkproto.Resource{
|
||||
Name: "something",
|
||||
Type: "aws_instance",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
resources, err := db.GetWorkspaceResourcesByJobID(ctx, job)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, resources, 1)
|
||||
})
|
||||
t.Run("InvalidAgentToken", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
err := insert(databasefake.New(), uuid.New(), &sdkproto.Resource{
|
||||
Name: "something",
|
||||
Type: "aws_instance",
|
||||
Agents: []*sdkproto.Agent{{
|
||||
Auth: &sdkproto.Agent_Token{
|
||||
Token: "bananas",
|
||||
},
|
||||
}},
|
||||
})
|
||||
require.ErrorContains(t, err, "invalid UUID length")
|
||||
})
|
||||
t.Run("DuplicateApps", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
err := insert(databasefake.New(), uuid.New(), &sdkproto.Resource{
|
||||
Name: "something",
|
||||
Type: "aws_instance",
|
||||
Agents: []*sdkproto.Agent{{
|
||||
Apps: []*sdkproto.App{{
|
||||
Slug: "a",
|
||||
}, {
|
||||
Slug: "a",
|
||||
}},
|
||||
}},
|
||||
})
|
||||
require.ErrorContains(t, err, "duplicate app slug")
|
||||
})
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
db := databasefake.New()
|
||||
job := uuid.New()
|
||||
err := insert(db, job, &sdkproto.Resource{
|
||||
Name: "something",
|
||||
Type: "aws_instance",
|
||||
Agents: []*sdkproto.Agent{{
|
||||
Name: "dev",
|
||||
Env: map[string]string{
|
||||
"something": "test",
|
||||
},
|
||||
StartupScript: "value",
|
||||
OperatingSystem: "linux",
|
||||
Architecture: "amd64",
|
||||
Auth: &sdkproto.Agent_Token{
|
||||
Token: uuid.NewString(),
|
||||
},
|
||||
Apps: []*sdkproto.App{{
|
||||
Slug: "a",
|
||||
}},
|
||||
}},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
resources, err := db.GetWorkspaceResourcesByJobID(ctx, job)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, resources, 1)
|
||||
agents, err := db.GetWorkspaceAgentsByResourceIDs(ctx, []uuid.UUID{resources[0].ID})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, agents, 1)
|
||||
agent := agents[0]
|
||||
require.Equal(t, "amd64", agent.Architecture)
|
||||
require.Equal(t, "linux", agent.OperatingSystem)
|
||||
require.Equal(t, "value", agent.StartupScript.String)
|
||||
want, err := json.Marshal(map[string]string{
|
||||
"something": "test",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
got, err := agent.EnvironmentVariables.RawMessage.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, want, got)
|
||||
})
|
||||
}
|
||||
|
||||
func setup(t *testing.T) *provisionerdserver.Server {
|
||||
t.Helper()
|
||||
db := databasefake.New()
|
||||
pubsub := database.NewPubsubInMemory()
|
||||
|
||||
return &provisionerdserver.Server{
|
||||
ID: uuid.New(),
|
||||
Logger: slogtest.Make(t, nil),
|
||||
AccessURL: &url.URL{},
|
||||
Provisioners: []database.ProvisionerType{database.ProvisionerTypeEcho},
|
||||
Database: db,
|
||||
Pubsub: pubsub,
|
||||
Telemetry: telemetry.NewNoop(),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user