mirror of
https://github.com/coder/coder.git
synced 2026-06-04 05:28:20 +00:00
2f55e29466
Alternate fix for https://github.com/coder/coder/issues/18080 Modifies wsbuilder to complete the provisioner job and mark the workspace as deleted if it is clear that no provisioner will be able to pick up the delete build. This has a significant advantage of not deviating too much from the current semantics of `POST /api/v2/workspacebuilds`. https://github.com/coder/coder/pull/18460 ends up returning a 204 on orphan delete due to no build being created. Downside is that we have to duplicate some responsibilities of provisionerdserver in wsbuilder. There is a slight gotcha to this approach though: if you stop a provisioner and then immediately try to orphan-delete, the job will still be created because of the provisioner heartbeat interval. However you can cancel it and try again.
1344 lines
45 KiB
Go
1344 lines
45 KiB
Go
package wsbuilder_test
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"encoding/json"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/coder/coder/v2/provisionersdk"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.opentelemetry.io/otel"
|
|
"go.opentelemetry.io/otel/propagation"
|
|
"go.uber.org/mock/gomock"
|
|
|
|
"github.com/coder/coder/v2/coderd/audit"
|
|
"github.com/coder/coder/v2/coderd/database"
|
|
"github.com/coder/coder/v2/coderd/database/dbmock"
|
|
"github.com/coder/coder/v2/coderd/database/dbtime"
|
|
"github.com/coder/coder/v2/coderd/provisionerdserver"
|
|
"github.com/coder/coder/v2/coderd/wsbuilder"
|
|
"github.com/coder/coder/v2/codersdk"
|
|
)
|
|
|
|
var (
|
|
// use fixed IDs so logs are easier to read
|
|
templateID = uuid.MustParse("12341234-0000-0000-0001-000000000000")
|
|
activeVersionID = uuid.MustParse("12341234-0000-0000-0002-000000000000")
|
|
inactiveVersionID = uuid.MustParse("12341234-0000-0000-0003-000000000000")
|
|
activeJobID = uuid.MustParse("12341234-0000-0000-0004-000000000000")
|
|
inactiveJobID = uuid.MustParse("12341234-0000-0000-0005-000000000000")
|
|
orgID = uuid.MustParse("12341234-0000-0000-0006-000000000000")
|
|
workspaceID = uuid.MustParse("12341234-0000-0000-0007-000000000000")
|
|
userID = uuid.MustParse("12341234-0000-0000-0008-000000000000")
|
|
activeFileID = uuid.MustParse("12341234-0000-0000-0009-000000000000")
|
|
inactiveFileID = uuid.MustParse("12341234-0000-0000-000a-000000000000")
|
|
lastBuildID = uuid.MustParse("12341234-0000-0000-000b-000000000000")
|
|
lastBuildJobID = uuid.MustParse("12341234-0000-0000-000c-000000000000")
|
|
otherUserID = uuid.MustParse("12341234-0000-0000-000d-000000000000")
|
|
presetID = uuid.MustParse("12341234-0000-0000-000e-000000000000")
|
|
)
|
|
|
|
func TestBuilder_NoOptions(t *testing.T) {
|
|
t.Parallel()
|
|
req := require.New(t)
|
|
asrt := assert.New(t)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
var buildID uuid.UUID
|
|
|
|
mDB := expectDB(t,
|
|
// Inputs
|
|
withTemplate,
|
|
withInactiveVersion(nil),
|
|
withLastBuildFound,
|
|
withTemplateVersionVariables(inactiveVersionID, nil),
|
|
withRichParameters(nil),
|
|
withParameterSchemas(inactiveJobID, nil),
|
|
withWorkspaceTags(inactiveVersionID, nil),
|
|
withProvisionerDaemons([]database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow{}),
|
|
|
|
// Outputs
|
|
expectProvisionerJob(func(job database.InsertProvisionerJobParams) {
|
|
asrt.Equal(userID, job.InitiatorID)
|
|
asrt.Equal(inactiveFileID, job.FileID)
|
|
input := provisionerdserver.WorkspaceProvisionJob{}
|
|
err := json.Unmarshal(job.Input, &input)
|
|
req.NoError(err)
|
|
// store build ID for later
|
|
buildID = input.WorkspaceBuildID
|
|
}),
|
|
|
|
withInTx,
|
|
expectBuild(func(bld database.InsertWorkspaceBuildParams) {
|
|
asrt.Equal(inactiveVersionID, bld.TemplateVersionID)
|
|
asrt.Equal(workspaceID, bld.WorkspaceID)
|
|
asrt.Equal(int32(2), bld.BuildNumber)
|
|
asrt.Equal("last build state", string(bld.ProvisionerState))
|
|
asrt.Equal(userID, bld.InitiatorID)
|
|
asrt.Equal(database.WorkspaceTransitionStart, bld.Transition)
|
|
asrt.Equal(database.BuildReasonInitiator, bld.Reason)
|
|
asrt.Equal(buildID, bld.ID)
|
|
}),
|
|
withBuild,
|
|
expectBuildParameters(func(params database.InsertWorkspaceBuildParametersParams) {
|
|
asrt.Equal(buildID, params.WorkspaceBuildID)
|
|
asrt.Empty(params.Name)
|
|
asrt.Empty(params.Value)
|
|
}),
|
|
)
|
|
|
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart)
|
|
// nolint: dogsled
|
|
_, _, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
|
req.NoError(err)
|
|
}
|
|
|
|
func TestBuilder_Initiator(t *testing.T) {
|
|
t.Parallel()
|
|
req := require.New(t)
|
|
asrt := assert.New(t)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
mDB := expectDB(t,
|
|
// Inputs
|
|
withTemplate,
|
|
withInactiveVersion(nil),
|
|
withLastBuildFound,
|
|
withTemplateVersionVariables(inactiveVersionID, nil),
|
|
withRichParameters(nil),
|
|
withParameterSchemas(inactiveJobID, nil),
|
|
withWorkspaceTags(inactiveVersionID, nil),
|
|
withProvisionerDaemons([]database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow{}),
|
|
|
|
// Outputs
|
|
expectProvisionerJob(func(job database.InsertProvisionerJobParams) {
|
|
asrt.Equal(otherUserID, job.InitiatorID)
|
|
}),
|
|
withInTx,
|
|
expectBuild(func(bld database.InsertWorkspaceBuildParams) {
|
|
asrt.Equal(otherUserID, bld.InitiatorID)
|
|
}),
|
|
expectBuildParameters(func(params database.InsertWorkspaceBuildParametersParams) {
|
|
}),
|
|
withBuild,
|
|
)
|
|
|
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).Initiator(otherUserID)
|
|
// nolint: dogsled
|
|
_, _, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
|
req.NoError(err)
|
|
}
|
|
|
|
func TestBuilder_Baggage(t *testing.T) {
|
|
t.Parallel()
|
|
req := require.New(t)
|
|
asrt := assert.New(t)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
otel.SetTextMapPropagator(
|
|
propagation.NewCompositeTextMapPropagator(
|
|
propagation.TraceContext{},
|
|
propagation.Baggage{},
|
|
),
|
|
)
|
|
|
|
mDB := expectDB(t,
|
|
// Inputs
|
|
withTemplate,
|
|
withInactiveVersion(nil),
|
|
withLastBuildFound,
|
|
withTemplateVersionVariables(inactiveVersionID, nil),
|
|
withRichParameters(nil),
|
|
withParameterSchemas(inactiveJobID, nil),
|
|
withWorkspaceTags(inactiveVersionID, nil),
|
|
withProvisionerDaemons([]database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow{}),
|
|
|
|
// Outputs
|
|
expectProvisionerJob(func(job database.InsertProvisionerJobParams) {
|
|
asrt.Contains(string(job.TraceMetadata.RawMessage), "ip=127.0.0.1")
|
|
}),
|
|
withInTx,
|
|
expectBuild(func(bld database.InsertWorkspaceBuildParams) {
|
|
}),
|
|
expectBuildParameters(func(params database.InsertWorkspaceBuildParametersParams) {
|
|
}),
|
|
withBuild,
|
|
)
|
|
|
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).Initiator(otherUserID)
|
|
// nolint: dogsled
|
|
_, _, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{IP: "127.0.0.1"})
|
|
req.NoError(err)
|
|
}
|
|
|
|
func TestBuilder_Reason(t *testing.T) {
|
|
t.Parallel()
|
|
req := require.New(t)
|
|
asrt := assert.New(t)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
mDB := expectDB(t,
|
|
// Inputs
|
|
withTemplate,
|
|
withInactiveVersion(nil),
|
|
withLastBuildFound,
|
|
withTemplateVersionVariables(inactiveVersionID, nil),
|
|
withRichParameters(nil),
|
|
withParameterSchemas(inactiveJobID, nil),
|
|
withWorkspaceTags(inactiveVersionID, nil),
|
|
withProvisionerDaemons([]database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow{}),
|
|
|
|
// Outputs
|
|
expectProvisionerJob(func(_ database.InsertProvisionerJobParams) {
|
|
}),
|
|
withInTx,
|
|
expectBuild(func(bld database.InsertWorkspaceBuildParams) {
|
|
asrt.Equal(database.BuildReasonAutostart, bld.Reason)
|
|
}),
|
|
expectBuildParameters(func(params database.InsertWorkspaceBuildParametersParams) {
|
|
}),
|
|
withBuild,
|
|
)
|
|
|
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).Reason(database.BuildReasonAutostart)
|
|
// nolint: dogsled
|
|
_, _, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
|
req.NoError(err)
|
|
}
|
|
|
|
func TestBuilder_ActiveVersion(t *testing.T) {
|
|
t.Parallel()
|
|
req := require.New(t)
|
|
asrt := assert.New(t)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
mDB := expectDB(t,
|
|
// Inputs
|
|
withTemplate,
|
|
withActiveVersion(nil),
|
|
withLastBuildNotFound,
|
|
withTemplateVersionVariables(activeVersionID, nil),
|
|
withParameterSchemas(activeJobID, nil),
|
|
withWorkspaceTags(activeVersionID, nil),
|
|
withProvisionerDaemons([]database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow{}),
|
|
// previous rich parameters are not queried because there is no previous build.
|
|
|
|
// Outputs
|
|
expectProvisionerJob(func(job database.InsertProvisionerJobParams) {
|
|
asrt.Equal(activeFileID, job.FileID)
|
|
}),
|
|
|
|
withInTx,
|
|
expectBuild(func(bld database.InsertWorkspaceBuildParams) {
|
|
asrt.Equal(activeVersionID, bld.TemplateVersionID)
|
|
// no previous build...
|
|
asrt.Equal(int32(1), bld.BuildNumber)
|
|
asrt.Len(bld.ProvisionerState, 0)
|
|
}),
|
|
expectBuildParameters(func(params database.InsertWorkspaceBuildParametersParams) {
|
|
}),
|
|
withBuild,
|
|
)
|
|
|
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).ActiveVersion()
|
|
// nolint: dogsled
|
|
_, _, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
|
req.NoError(err)
|
|
}
|
|
|
|
func TestWorkspaceBuildWithTags(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
asrt := assert.New(t)
|
|
req := require.New(t)
|
|
|
|
workspaceTags := []database.TemplateVersionWorkspaceTag{
|
|
{
|
|
Key: "fruits_tag",
|
|
Value: "data.coder_parameter.number_of_apples.value + data.coder_parameter.number_of_oranges.value",
|
|
},
|
|
{
|
|
Key: "cluster_tag",
|
|
Value: `"best_developers"`,
|
|
},
|
|
{
|
|
Key: "project_tag",
|
|
Value: `"${data.coder_parameter.project.value}+12345"`,
|
|
},
|
|
{
|
|
Key: "team_tag",
|
|
Value: `data.coder_parameter.team.value`,
|
|
},
|
|
{
|
|
Key: "yes_or_no",
|
|
Value: `data.coder_parameter.is_debug_build.value`,
|
|
},
|
|
{
|
|
Key: "actually_no",
|
|
Value: `!data.coder_parameter.is_debug_build.value`,
|
|
},
|
|
{
|
|
Key: "is_debug_build",
|
|
Value: `data.coder_parameter.is_debug_build.value == "true" ? "in-debug-mode" : "no-debug"`,
|
|
},
|
|
{
|
|
Key: "variable_tag",
|
|
Value: `var.tag`,
|
|
},
|
|
{
|
|
Key: "another_variable_tag",
|
|
Value: `var.tag2`,
|
|
},
|
|
}
|
|
|
|
richParameters := []database.TemplateVersionParameter{
|
|
// Parameters can be mutable although it is discouraged as the workspace can be moved between provisioner nodes.
|
|
{Name: "project", Description: "This is first parameter", Mutable: true, Options: json.RawMessage("[]")},
|
|
{Name: "team", Description: "This is second parameter", Mutable: true, DefaultValue: "godzilla", Options: json.RawMessage("[]")},
|
|
{Name: "is_debug_build", Type: "bool", Description: "This is third parameter", Mutable: false, DefaultValue: "false", Options: json.RawMessage("[]")},
|
|
{Name: "number_of_apples", Type: "number", Description: "This is fourth parameter", Mutable: false, DefaultValue: "4", Options: json.RawMessage("[]")},
|
|
{Name: "number_of_oranges", Type: "number", Description: "This is fifth parameter", Mutable: false, DefaultValue: "6", Options: json.RawMessage("[]")},
|
|
}
|
|
|
|
templateVersionVariables := []database.TemplateVersionVariable{
|
|
{Name: "tag", Description: "This is a variable tag", TemplateVersionID: inactiveVersionID, Type: "string", DefaultValue: "default-value", Value: "my-value"},
|
|
{Name: "tag2", Description: "This is another variable tag", TemplateVersionID: inactiveVersionID, Type: "string", DefaultValue: "default-value-2", Value: ""},
|
|
}
|
|
|
|
buildParameters := []codersdk.WorkspaceBuildParameter{
|
|
{Name: "project", Value: "foobar-foobaz"},
|
|
{Name: "is_debug_build", Value: "true"},
|
|
// Parameters "team", "number_of_apples", "number_of_oranges" are skipped, so default value is selected
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
mDB := expectDB(t,
|
|
// Inputs
|
|
withTemplate,
|
|
withInactiveVersion(richParameters),
|
|
withLastBuildFound,
|
|
withTemplateVersionVariables(inactiveVersionID, templateVersionVariables),
|
|
withRichParameters(nil),
|
|
withParameterSchemas(inactiveJobID, nil),
|
|
withWorkspaceTags(inactiveVersionID, workspaceTags),
|
|
withProvisionerDaemons([]database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow{}),
|
|
|
|
// Outputs
|
|
expectProvisionerJob(func(job database.InsertProvisionerJobParams) {
|
|
asrt.Len(job.Tags, 12)
|
|
|
|
expected := database.StringMap{
|
|
"actually_no": "false",
|
|
"cluster_tag": "best_developers",
|
|
"fruits_tag": "10",
|
|
"is_debug_build": "in-debug-mode",
|
|
"project_tag": "foobar-foobaz+12345",
|
|
"team_tag": "godzilla",
|
|
"yes_or_no": "true",
|
|
"variable_tag": "my-value",
|
|
"another_variable_tag": "default-value-2",
|
|
|
|
"scope": "user",
|
|
"version": "inactive",
|
|
"owner": userID.String(),
|
|
}
|
|
asrt.Equal(job.Tags, expected)
|
|
}),
|
|
withInTx,
|
|
expectBuild(func(_ database.InsertWorkspaceBuildParams) {}),
|
|
expectBuildParameters(func(_ database.InsertWorkspaceBuildParametersParams) {
|
|
}),
|
|
withBuild,
|
|
)
|
|
|
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).RichParameterValues(buildParameters)
|
|
// nolint: dogsled
|
|
_, _, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
|
req.NoError(err)
|
|
}
|
|
|
|
func TestWorkspaceBuildWithRichParameters(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
const (
|
|
firstParameterName = "first_parameter"
|
|
firstParameterDescription = "This is first parameter"
|
|
firstParameterValue = "1"
|
|
|
|
secondParameterName = "second_parameter"
|
|
secondParameterDescription = "This is second parameter"
|
|
secondParameterValue = "2"
|
|
|
|
immutableParameterName = "immutable_parameter"
|
|
immutableParameterDescription = "This is immutable parameter"
|
|
immutableParameterValue = "3"
|
|
)
|
|
|
|
initialBuildParameters := []database.WorkspaceBuildParameter{
|
|
{Name: firstParameterName, Value: firstParameterValue},
|
|
{Name: secondParameterName, Value: secondParameterValue},
|
|
{Name: immutableParameterName, Value: immutableParameterValue},
|
|
}
|
|
|
|
richParameters := []database.TemplateVersionParameter{
|
|
{Name: firstParameterName, Description: firstParameterDescription, Mutable: true, Options: json.RawMessage("[]")},
|
|
{Name: secondParameterName, Description: secondParameterDescription, Mutable: true, Options: json.RawMessage("[]")},
|
|
{Name: immutableParameterName, Description: immutableParameterDescription, Mutable: false, Options: json.RawMessage("[]")},
|
|
}
|
|
|
|
t.Run("UpdateParameterValues", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
req := require.New(t)
|
|
asrt := assert.New(t)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
const updatedParameterValue = "3"
|
|
nextBuildParameters := []codersdk.WorkspaceBuildParameter{
|
|
{Name: firstParameterName, Value: firstParameterValue},
|
|
{Name: secondParameterName, Value: updatedParameterValue},
|
|
}
|
|
expectedParams := map[string]string{
|
|
firstParameterName: firstParameterValue,
|
|
secondParameterName: updatedParameterValue,
|
|
immutableParameterName: immutableParameterValue,
|
|
}
|
|
|
|
mDB := expectDB(t,
|
|
// Inputs
|
|
withTemplate,
|
|
withInactiveVersion(richParameters),
|
|
withLastBuildFound,
|
|
withTemplateVersionVariables(inactiveVersionID, nil),
|
|
withRichParameters(initialBuildParameters),
|
|
withParameterSchemas(inactiveJobID, nil),
|
|
withWorkspaceTags(inactiveVersionID, nil),
|
|
withProvisionerDaemons([]database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow{}),
|
|
|
|
// Outputs
|
|
expectProvisionerJob(func(job database.InsertProvisionerJobParams) {}),
|
|
withInTx,
|
|
expectBuild(func(bld database.InsertWorkspaceBuildParams) {}),
|
|
expectBuildParameters(func(params database.InsertWorkspaceBuildParametersParams) {
|
|
asrt.Len(params.Name, len(expectedParams))
|
|
for i := range params.Name {
|
|
value, ok := expectedParams[params.Name[i]]
|
|
asrt.True(ok, "unexpected name %s", params.Name[i])
|
|
asrt.Equal(value, params.Value[i])
|
|
}
|
|
}),
|
|
withBuild,
|
|
)
|
|
|
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).RichParameterValues(nextBuildParameters)
|
|
// nolint: dogsled
|
|
_, _, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
|
req.NoError(err)
|
|
})
|
|
t.Run("UsePreviousParameterValues", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
req := require.New(t)
|
|
asrt := assert.New(t)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
nextBuildParameters := []codersdk.WorkspaceBuildParameter{}
|
|
expectedParams := map[string]string{}
|
|
for _, p := range initialBuildParameters {
|
|
expectedParams[p.Name] = p.Value
|
|
}
|
|
|
|
mDB := expectDB(t,
|
|
// Inputs
|
|
withTemplate,
|
|
withInactiveVersion(richParameters),
|
|
withLastBuildFound,
|
|
withTemplateVersionVariables(inactiveVersionID, nil),
|
|
withRichParameters(initialBuildParameters),
|
|
withParameterSchemas(inactiveJobID, nil),
|
|
withWorkspaceTags(inactiveVersionID, nil),
|
|
withProvisionerDaemons([]database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow{}),
|
|
|
|
// Outputs
|
|
expectProvisionerJob(func(job database.InsertProvisionerJobParams) {}),
|
|
withInTx,
|
|
expectBuild(func(bld database.InsertWorkspaceBuildParams) {}),
|
|
expectBuildParameters(func(params database.InsertWorkspaceBuildParametersParams) {
|
|
asrt.Len(params.Name, len(expectedParams))
|
|
for i := range params.Name {
|
|
value, ok := expectedParams[params.Name[i]]
|
|
asrt.True(ok, "unexpected name %s", params.Name[i])
|
|
asrt.Equal(value, params.Value[i])
|
|
}
|
|
}),
|
|
withBuild,
|
|
)
|
|
|
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).RichParameterValues(nextBuildParameters)
|
|
// nolint: dogsled
|
|
_, _, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
|
req.NoError(err)
|
|
})
|
|
|
|
t.Run("StartWorkspaceWithLegacyParameterValues", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
req := require.New(t)
|
|
asrt := assert.New(t)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
schemas := []database.ParameterSchema{
|
|
{
|
|
Name: "not-replaced",
|
|
DefaultDestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable,
|
|
},
|
|
{
|
|
Name: "replaced",
|
|
DefaultDestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable,
|
|
},
|
|
}
|
|
|
|
mDB := expectDB(t,
|
|
// Inputs
|
|
withTemplate,
|
|
withInactiveVersion(richParameters),
|
|
withLastBuildFound,
|
|
withTemplateVersionVariables(inactiveVersionID, nil),
|
|
withRichParameters(nil),
|
|
withParameterSchemas(inactiveJobID, schemas),
|
|
withWorkspaceTags(inactiveVersionID, nil),
|
|
)
|
|
|
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart)
|
|
_, _, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
|
bldErr := wsbuilder.BuildError{}
|
|
req.ErrorAs(err, &bldErr)
|
|
asrt.Equal(http.StatusBadRequest, bldErr.Status)
|
|
})
|
|
|
|
t.Run("DoNotModifyImmutables", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
req := require.New(t)
|
|
asrt := assert.New(t)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
nextBuildParameters := []codersdk.WorkspaceBuildParameter{
|
|
{Name: immutableParameterName, Value: "BAD"},
|
|
}
|
|
|
|
mDB := expectDB(t,
|
|
// Inputs
|
|
withTemplate,
|
|
withInactiveVersion(richParameters),
|
|
withLastBuildFound,
|
|
withTemplateVersionVariables(inactiveVersionID, nil),
|
|
withRichParameters(initialBuildParameters),
|
|
withParameterSchemas(inactiveJobID, nil),
|
|
withWorkspaceTags(inactiveVersionID, nil),
|
|
|
|
// Outputs
|
|
// no transaction, since we failed fast while validation build parameters
|
|
)
|
|
|
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).RichParameterValues(nextBuildParameters)
|
|
// nolint: dogsled
|
|
_, _, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
|
bldErr := wsbuilder.BuildError{}
|
|
req.ErrorAs(err, &bldErr)
|
|
asrt.Equal(http.StatusBadRequest, bldErr.Status)
|
|
})
|
|
|
|
t.Run("NewImmutableRequiredParameterAdded", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
req := require.New(t)
|
|
asrt := assert.New(t)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
// new template revision
|
|
const newImmutableParameterName = "new_immutable_parameter"
|
|
const newImmutableParameterDescription = "This is also an immutable parameter"
|
|
version2params := []database.TemplateVersionParameter{
|
|
{Name: firstParameterName, Description: firstParameterDescription, Mutable: true, Options: json.RawMessage("[]")},
|
|
{Name: secondParameterName, Description: secondParameterDescription, Mutable: true, Options: json.RawMessage("[]")},
|
|
{Name: immutableParameterName, Description: immutableParameterDescription, Mutable: false, Options: json.RawMessage("[]")},
|
|
{Name: newImmutableParameterName, Description: newImmutableParameterDescription, Mutable: false, Required: true, Options: json.RawMessage("[]")},
|
|
}
|
|
|
|
nextBuildParameters := []codersdk.WorkspaceBuildParameter{
|
|
{Name: newImmutableParameterName, Value: "good"},
|
|
}
|
|
expectedParams := map[string]string{
|
|
firstParameterName: firstParameterValue,
|
|
secondParameterName: secondParameterValue,
|
|
immutableParameterName: immutableParameterValue,
|
|
newImmutableParameterName: "good",
|
|
}
|
|
|
|
mDB := expectDB(t,
|
|
// Inputs
|
|
withTemplate,
|
|
withActiveVersion(version2params),
|
|
withLastBuildFound,
|
|
withTemplateVersionVariables(activeVersionID, nil),
|
|
withRichParameters(initialBuildParameters),
|
|
withParameterSchemas(activeJobID, nil),
|
|
withWorkspaceTags(activeVersionID, nil),
|
|
withProvisionerDaemons([]database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow{}),
|
|
|
|
// Outputs
|
|
expectProvisionerJob(func(job database.InsertProvisionerJobParams) {}),
|
|
withInTx,
|
|
expectBuild(func(bld database.InsertWorkspaceBuildParams) {}),
|
|
expectBuildParameters(func(params database.InsertWorkspaceBuildParametersParams) {
|
|
asrt.Len(params.Name, len(expectedParams))
|
|
for i := range params.Name {
|
|
value, ok := expectedParams[params.Name[i]]
|
|
asrt.True(ok, "unexpected name %s", params.Name[i])
|
|
asrt.Equal(value, params.Value[i])
|
|
}
|
|
}),
|
|
withBuild,
|
|
)
|
|
|
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).
|
|
RichParameterValues(nextBuildParameters).
|
|
VersionID(activeVersionID)
|
|
_, _, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
|
req.NoError(err)
|
|
})
|
|
|
|
t.Run("NewImmutableOptionalParameterAdded", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
req := require.New(t)
|
|
asrt := assert.New(t)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
// new template revision
|
|
const newImmutableParameterName = "new_immutable_parameter"
|
|
const newImmutableParameterDescription = "This is also an immutable parameter"
|
|
version2params := []database.TemplateVersionParameter{
|
|
{Name: firstParameterName, Description: firstParameterDescription, Mutable: true, Options: json.RawMessage("[]")},
|
|
{Name: secondParameterName, Description: secondParameterDescription, Mutable: true, Options: json.RawMessage("[]")},
|
|
{Name: immutableParameterName, Description: immutableParameterDescription, Mutable: false, Options: json.RawMessage("[]")},
|
|
{Name: newImmutableParameterName, Description: newImmutableParameterDescription, Mutable: false, DefaultValue: "12345", Options: json.RawMessage("[]")},
|
|
}
|
|
|
|
nextBuildParameters := []codersdk.WorkspaceBuildParameter{
|
|
{Name: newImmutableParameterName, Value: "good"},
|
|
}
|
|
expectedParams := map[string]string{
|
|
firstParameterName: firstParameterValue,
|
|
secondParameterName: secondParameterValue,
|
|
immutableParameterName: immutableParameterValue,
|
|
newImmutableParameterName: "good",
|
|
}
|
|
|
|
mDB := expectDB(t,
|
|
// Inputs
|
|
withTemplate,
|
|
withActiveVersion(version2params),
|
|
withLastBuildFound,
|
|
withTemplateVersionVariables(activeVersionID, nil),
|
|
withRichParameters(initialBuildParameters),
|
|
withParameterSchemas(activeJobID, nil),
|
|
withWorkspaceTags(activeVersionID, nil),
|
|
withProvisionerDaemons([]database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow{}),
|
|
|
|
// Outputs
|
|
expectProvisionerJob(func(job database.InsertProvisionerJobParams) {}),
|
|
withInTx,
|
|
expectBuild(func(bld database.InsertWorkspaceBuildParams) {}),
|
|
expectBuildParameters(func(params database.InsertWorkspaceBuildParametersParams) {
|
|
asrt.Len(params.Name, len(expectedParams))
|
|
for i := range params.Name {
|
|
value, ok := expectedParams[params.Name[i]]
|
|
asrt.True(ok, "unexpected name %s", params.Name[i])
|
|
asrt.Equal(value, params.Value[i])
|
|
}
|
|
}),
|
|
withBuild,
|
|
)
|
|
|
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).
|
|
RichParameterValues(nextBuildParameters).
|
|
VersionID(activeVersionID)
|
|
_, _, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
|
req.NoError(err)
|
|
})
|
|
|
|
t.Run("NewImmutableOptionalParameterUsesDefault", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
req := require.New(t)
|
|
asrt := assert.New(t)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
// new template revision
|
|
const newImmutableParameterName = "new_immutable_parameter"
|
|
const newImmutableParameterDescription = "This is also an immutable parameter"
|
|
version2params := []database.TemplateVersionParameter{
|
|
{Name: firstParameterName, Description: firstParameterDescription, Mutable: true, Options: json.RawMessage("[]")},
|
|
{Name: secondParameterName, Description: secondParameterDescription, Mutable: true, Options: json.RawMessage("[]")},
|
|
{Name: immutableParameterName, Description: immutableParameterDescription, Mutable: false, Options: json.RawMessage("[]")},
|
|
{Name: newImmutableParameterName, Description: newImmutableParameterDescription, Mutable: false, DefaultValue: "12345", Options: json.RawMessage("[]")},
|
|
}
|
|
|
|
nextBuildParameters := []codersdk.WorkspaceBuildParameter{}
|
|
expectedParams := map[string]string{
|
|
firstParameterName: firstParameterValue,
|
|
secondParameterName: secondParameterValue,
|
|
immutableParameterName: immutableParameterValue,
|
|
newImmutableParameterName: "12345",
|
|
}
|
|
|
|
mDB := expectDB(t,
|
|
// Inputs
|
|
withTemplate,
|
|
withActiveVersion(version2params),
|
|
withLastBuildFound,
|
|
withTemplateVersionVariables(activeVersionID, nil),
|
|
withRichParameters(initialBuildParameters),
|
|
withParameterSchemas(activeJobID, nil),
|
|
withWorkspaceTags(activeVersionID, nil),
|
|
withProvisionerDaemons([]database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow{}),
|
|
|
|
// Outputs
|
|
expectProvisionerJob(func(job database.InsertProvisionerJobParams) {}),
|
|
withInTx,
|
|
expectBuild(func(bld database.InsertWorkspaceBuildParams) {}),
|
|
expectBuildParameters(func(params database.InsertWorkspaceBuildParametersParams) {
|
|
asrt.Len(params.Name, len(expectedParams))
|
|
for i := range params.Name {
|
|
value, ok := expectedParams[params.Name[i]]
|
|
asrt.True(ok, "unexpected name %s", params.Name[i])
|
|
asrt.Equal(value, params.Value[i])
|
|
}
|
|
}),
|
|
withBuild,
|
|
)
|
|
|
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).
|
|
RichParameterValues(nextBuildParameters).
|
|
VersionID(activeVersionID)
|
|
// nolint: dogsled
|
|
_, _, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
|
req.NoError(err)
|
|
})
|
|
}
|
|
|
|
func TestWorkspaceBuildWithPreset(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
req := require.New(t)
|
|
asrt := assert.New(t)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
var buildID uuid.UUID
|
|
|
|
mDB := expectDB(t,
|
|
// Inputs
|
|
withTemplate,
|
|
withActiveVersion(nil),
|
|
// building workspaces using presets with different combinations of parameters
|
|
// is tested at the API layer, in TestWorkspace. Here, it is sufficient to
|
|
// test that the preset is used when provided.
|
|
withTemplateVersionPresetParameters(presetID, nil),
|
|
withLastBuildNotFound,
|
|
withTemplateVersionVariables(activeVersionID, nil),
|
|
withParameterSchemas(activeJobID, nil),
|
|
withWorkspaceTags(activeVersionID, nil),
|
|
withProvisionerDaemons([]database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow{}),
|
|
|
|
// Outputs
|
|
expectProvisionerJob(func(job database.InsertProvisionerJobParams) {
|
|
asrt.Equal(userID, job.InitiatorID)
|
|
asrt.Equal(activeFileID, job.FileID)
|
|
input := provisionerdserver.WorkspaceProvisionJob{}
|
|
err := json.Unmarshal(job.Input, &input)
|
|
req.NoError(err)
|
|
// store build ID for later
|
|
buildID = input.WorkspaceBuildID
|
|
}),
|
|
|
|
withInTx,
|
|
expectBuild(func(bld database.InsertWorkspaceBuildParams) {
|
|
asrt.Equal(activeVersionID, bld.TemplateVersionID)
|
|
asrt.Equal(workspaceID, bld.WorkspaceID)
|
|
asrt.Equal(int32(1), bld.BuildNumber)
|
|
asrt.Equal(userID, bld.InitiatorID)
|
|
asrt.Equal(database.WorkspaceTransitionStart, bld.Transition)
|
|
asrt.Equal(database.BuildReasonInitiator, bld.Reason)
|
|
asrt.Equal(buildID, bld.ID)
|
|
asrt.True(bld.TemplateVersionPresetID.Valid)
|
|
asrt.Equal(presetID, bld.TemplateVersionPresetID.UUID)
|
|
}),
|
|
withBuild,
|
|
expectBuildParameters(func(params database.InsertWorkspaceBuildParametersParams) {
|
|
asrt.Equal(buildID, params.WorkspaceBuildID)
|
|
asrt.Empty(params.Name)
|
|
asrt.Empty(params.Value)
|
|
}),
|
|
)
|
|
|
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).
|
|
ActiveVersion().
|
|
TemplateVersionPresetID(presetID)
|
|
// nolint: dogsled
|
|
_, _, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
|
req.NoError(err)
|
|
}
|
|
|
|
func TestWorkspaceBuildDeleteOrphan(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("WithActiveProvisioners", func(t *testing.T) {
|
|
t.Parallel()
|
|
req := require.New(t)
|
|
asrt := assert.New(t)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
var buildID uuid.UUID
|
|
|
|
mDB := expectDB(t,
|
|
// Inputs
|
|
withTemplate,
|
|
withInactiveVersion(nil),
|
|
withLastBuildFound,
|
|
withTemplateVersionVariables(inactiveVersionID, nil),
|
|
withRichParameters(nil),
|
|
withWorkspaceTags(inactiveVersionID, nil),
|
|
withProvisionerDaemons([]database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow{{
|
|
JobID: inactiveJobID,
|
|
ProvisionerDaemon: database.ProvisionerDaemon{
|
|
LastSeenAt: sql.NullTime{Valid: true, Time: dbtime.Now()},
|
|
},
|
|
}}),
|
|
|
|
// Outputs
|
|
expectProvisionerJob(func(job database.InsertProvisionerJobParams) {
|
|
asrt.Equal(userID, job.InitiatorID)
|
|
asrt.Equal(inactiveFileID, job.FileID)
|
|
input := provisionerdserver.WorkspaceProvisionJob{}
|
|
err := json.Unmarshal(job.Input, &input)
|
|
req.NoError(err)
|
|
// store build ID for later
|
|
buildID = input.WorkspaceBuildID
|
|
}),
|
|
|
|
withInTx,
|
|
expectBuild(func(bld database.InsertWorkspaceBuildParams) {
|
|
asrt.Equal(inactiveVersionID, bld.TemplateVersionID)
|
|
asrt.Equal(workspaceID, bld.WorkspaceID)
|
|
asrt.Equal(int32(2), bld.BuildNumber)
|
|
asrt.Empty(string(bld.ProvisionerState))
|
|
asrt.Equal(userID, bld.InitiatorID)
|
|
asrt.Equal(database.WorkspaceTransitionDelete, bld.Transition)
|
|
asrt.Equal(database.BuildReasonInitiator, bld.Reason)
|
|
asrt.Equal(buildID, bld.ID)
|
|
}),
|
|
withBuild,
|
|
expectBuildParameters(func(params database.InsertWorkspaceBuildParametersParams) {
|
|
asrt.Equal(buildID, params.WorkspaceBuildID)
|
|
asrt.Empty(params.Name)
|
|
asrt.Empty(params.Value)
|
|
}),
|
|
)
|
|
|
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionDelete).Orphan()
|
|
// nolint: dogsled
|
|
_, _, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
|
req.NoError(err)
|
|
})
|
|
|
|
t.Run("NoActiveProvisioners", func(t *testing.T) {
|
|
t.Parallel()
|
|
req := require.New(t)
|
|
asrt := assert.New(t)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
var buildID uuid.UUID
|
|
var jobID uuid.UUID
|
|
|
|
mDB := expectDB(t,
|
|
// Inputs
|
|
withTemplate,
|
|
withInactiveVersion(nil),
|
|
withLastBuildFound,
|
|
withTemplateVersionVariables(inactiveVersionID, nil),
|
|
withRichParameters(nil),
|
|
withWorkspaceTags(inactiveVersionID, nil),
|
|
withProvisionerDaemons([]database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow{}),
|
|
|
|
// Outputs
|
|
expectProvisionerJob(func(job database.InsertProvisionerJobParams) {
|
|
asrt.Equal(userID, job.InitiatorID)
|
|
asrt.Equal(inactiveFileID, job.FileID)
|
|
input := provisionerdserver.WorkspaceProvisionJob{}
|
|
err := json.Unmarshal(job.Input, &input)
|
|
req.NoError(err)
|
|
// store build ID for later
|
|
buildID = input.WorkspaceBuildID
|
|
// store job ID for later
|
|
jobID = job.ID
|
|
}),
|
|
|
|
withInTx,
|
|
expectBuild(func(bld database.InsertWorkspaceBuildParams) {
|
|
asrt.Equal(inactiveVersionID, bld.TemplateVersionID)
|
|
asrt.Equal(workspaceID, bld.WorkspaceID)
|
|
asrt.Equal(int32(2), bld.BuildNumber)
|
|
asrt.Empty(string(bld.ProvisionerState))
|
|
asrt.Equal(userID, bld.InitiatorID)
|
|
asrt.Equal(database.WorkspaceTransitionDelete, bld.Transition)
|
|
asrt.Equal(database.BuildReasonInitiator, bld.Reason)
|
|
asrt.Equal(buildID, bld.ID)
|
|
}),
|
|
withBuild,
|
|
expectBuildParameters(func(params database.InsertWorkspaceBuildParametersParams) {
|
|
asrt.Equal(buildID, params.WorkspaceBuildID)
|
|
asrt.Empty(params.Name)
|
|
asrt.Empty(params.Value)
|
|
}),
|
|
|
|
// Because no provisioners were available and the request was to delete --orphan
|
|
expectUpdateProvisionerJobWithCompleteWithStartedAtByID(func(params database.UpdateProvisionerJobWithCompleteWithStartedAtByIDParams) {
|
|
asrt.Equal(jobID, params.ID)
|
|
asrt.False(params.Error.Valid)
|
|
asrt.True(params.CompletedAt.Valid)
|
|
asrt.True(params.StartedAt.Valid)
|
|
}),
|
|
expectUpdateWorkspaceDeletedByID(func(params database.UpdateWorkspaceDeletedByIDParams) {
|
|
asrt.Equal(workspaceID, params.ID)
|
|
asrt.True(params.Deleted)
|
|
}),
|
|
expectGetProvisionerJobByID(func(job database.ProvisionerJob) {
|
|
asrt.Equal(jobID, job.ID)
|
|
}),
|
|
)
|
|
|
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionDelete).Orphan()
|
|
// nolint: dogsled
|
|
_, _, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
|
req.NoError(err)
|
|
})
|
|
}
|
|
|
|
func TestProvisionerVersionSupportsDynamicParameters(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
for v, dyn := range map[string]bool{
|
|
"": false,
|
|
"na": false,
|
|
"0.0": false,
|
|
"0.10": false,
|
|
"1.4": false,
|
|
"1.5": false,
|
|
"1.6": true,
|
|
"1.7": true,
|
|
"1.8": true,
|
|
"2.0": true,
|
|
"2.17": true,
|
|
"4.0": true,
|
|
} {
|
|
t.Run(v, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
does := wsbuilder.ProvisionerVersionSupportsDynamicParameters(v)
|
|
require.Equal(t, dyn, does)
|
|
})
|
|
}
|
|
}
|
|
|
|
type txExpect func(mTx *dbmock.MockStore)
|
|
|
|
func expectDB(t *testing.T, opts ...txExpect) *dbmock.MockStore {
|
|
t.Helper()
|
|
ctrl := gomock.NewController(t)
|
|
mDB := dbmock.NewMockStore(ctrl)
|
|
mTx := dbmock.NewMockStore(ctrl)
|
|
|
|
// we expect to be run in a transaction; we use mTx to record the
|
|
// "in transaction" calls.
|
|
mDB.EXPECT().InTx(
|
|
gomock.Any(), gomock.Eq(&database.TxOptions{Isolation: sql.LevelRepeatableRead}),
|
|
).
|
|
DoAndReturn(func(f func(database.Store) error, _ *database.TxOptions) error {
|
|
err := f(mTx)
|
|
return err
|
|
})
|
|
|
|
// txExpect args set up the expectations for what happens in the transaction.
|
|
for _, o := range opts {
|
|
o(mTx)
|
|
}
|
|
return mDB
|
|
}
|
|
|
|
func withTemplate(mTx *dbmock.MockStore) {
|
|
mTx.EXPECT().GetTemplateByID(gomock.Any(), templateID).
|
|
Times(1).
|
|
Return(database.Template{
|
|
ID: templateID,
|
|
OrganizationID: orgID,
|
|
Provisioner: database.ProvisionerTypeTerraform,
|
|
ActiveVersionID: activeVersionID,
|
|
UseClassicParameterFlow: true,
|
|
}, nil)
|
|
}
|
|
|
|
// withInTx runs the given functions on the same db mock.
|
|
func withInTx(mTx *dbmock.MockStore) {
|
|
mTx.EXPECT().InTx(gomock.Any(), gomock.Any()).Times(1).DoAndReturn(
|
|
func(f func(store database.Store) error, _ *database.TxOptions) error {
|
|
return f(mTx)
|
|
},
|
|
)
|
|
}
|
|
|
|
func withActiveVersion(params []database.TemplateVersionParameter) func(mTx *dbmock.MockStore) {
|
|
return func(mTx *dbmock.MockStore) {
|
|
mTx.EXPECT().GetTemplateVersionByID(gomock.Any(), activeVersionID).
|
|
Times(1).
|
|
Return(database.TemplateVersion{
|
|
ID: activeVersionID,
|
|
TemplateID: uuid.NullUUID{UUID: templateID, Valid: true},
|
|
OrganizationID: orgID,
|
|
Name: "active",
|
|
JobID: activeJobID,
|
|
}, nil)
|
|
|
|
mTx.EXPECT().GetProvisionerJobByID(gomock.Any(), activeJobID).
|
|
Times(1).Return(database.ProvisionerJob{
|
|
ID: activeJobID,
|
|
OrganizationID: orgID,
|
|
InitiatorID: userID,
|
|
Provisioner: database.ProvisionerTypeTerraform,
|
|
StorageMethod: database.ProvisionerStorageMethodFile,
|
|
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
|
Input: nil,
|
|
Tags: database.StringMap{
|
|
"version": "active",
|
|
provisionersdk.TagScope: provisionersdk.ScopeUser,
|
|
},
|
|
FileID: activeFileID,
|
|
StartedAt: sql.NullTime{Time: dbtime.Now(), Valid: true},
|
|
UpdatedAt: time.Now(),
|
|
CompletedAt: sql.NullTime{Time: dbtime.Now(), Valid: true},
|
|
}, nil)
|
|
paramsCall := mTx.EXPECT().GetTemplateVersionParameters(gomock.Any(), activeVersionID).
|
|
Times(1)
|
|
if len(params) > 0 {
|
|
paramsCall.Return(params, nil)
|
|
} else {
|
|
paramsCall.Return(nil, sql.ErrNoRows)
|
|
}
|
|
}
|
|
}
|
|
|
|
func withInactiveVersion(params []database.TemplateVersionParameter) func(mTx *dbmock.MockStore) {
|
|
return func(mTx *dbmock.MockStore) {
|
|
mTx.EXPECT().GetTemplateVersionByID(gomock.Any(), inactiveVersionID).
|
|
Times(1).
|
|
Return(database.TemplateVersion{
|
|
ID: inactiveVersionID,
|
|
TemplateID: uuid.NullUUID{UUID: templateID, Valid: true},
|
|
OrganizationID: orgID,
|
|
Name: "inactive",
|
|
JobID: inactiveJobID,
|
|
}, nil)
|
|
|
|
mTx.EXPECT().GetProvisionerJobByID(gomock.Any(), inactiveJobID).
|
|
Times(1).Return(database.ProvisionerJob{
|
|
ID: inactiveJobID,
|
|
OrganizationID: orgID,
|
|
InitiatorID: userID,
|
|
Provisioner: database.ProvisionerTypeTerraform,
|
|
StorageMethod: database.ProvisionerStorageMethodFile,
|
|
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
|
Input: nil,
|
|
Tags: database.StringMap{
|
|
"version": "inactive",
|
|
provisionersdk.TagScope: provisionersdk.ScopeUser,
|
|
},
|
|
FileID: inactiveFileID,
|
|
StartedAt: sql.NullTime{Time: dbtime.Now(), Valid: true},
|
|
UpdatedAt: time.Now(),
|
|
CompletedAt: sql.NullTime{Time: dbtime.Now(), Valid: true},
|
|
}, nil)
|
|
paramsCall := mTx.EXPECT().GetTemplateVersionParameters(gomock.Any(), inactiveVersionID).
|
|
Times(1)
|
|
if len(params) > 0 {
|
|
paramsCall.Return(params, nil)
|
|
} else {
|
|
paramsCall.Return(nil, sql.ErrNoRows)
|
|
}
|
|
}
|
|
}
|
|
|
|
func withTemplateVersionPresetParameters(presetID uuid.UUID, params []database.TemplateVersionPresetParameter) func(mTx *dbmock.MockStore) {
|
|
return func(mTx *dbmock.MockStore) {
|
|
mTx.EXPECT().GetPresetParametersByPresetID(gomock.Any(), presetID).Return(params, nil)
|
|
}
|
|
}
|
|
|
|
func withLastBuildFound(mTx *dbmock.MockStore) {
|
|
mTx.EXPECT().GetLatestWorkspaceBuildByWorkspaceID(gomock.Any(), workspaceID).
|
|
Times(1).
|
|
Return(database.WorkspaceBuild{
|
|
ID: lastBuildID,
|
|
WorkspaceID: workspaceID,
|
|
TemplateVersionID: inactiveVersionID,
|
|
BuildNumber: 1,
|
|
Transition: database.WorkspaceTransitionStart,
|
|
InitiatorID: userID,
|
|
JobID: lastBuildJobID,
|
|
ProvisionerState: []byte("last build state"),
|
|
Reason: database.BuildReasonInitiator,
|
|
}, nil)
|
|
|
|
mTx.EXPECT().GetProvisionerJobByID(gomock.Any(), lastBuildJobID).
|
|
Times(1).
|
|
Return(database.ProvisionerJob{
|
|
ID: lastBuildJobID,
|
|
OrganizationID: orgID,
|
|
InitiatorID: userID,
|
|
Provisioner: database.ProvisionerTypeTerraform,
|
|
StorageMethod: database.ProvisionerStorageMethodFile,
|
|
FileID: inactiveFileID,
|
|
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
|
StartedAt: sql.NullTime{Time: dbtime.Now(), Valid: true},
|
|
UpdatedAt: time.Now(),
|
|
CompletedAt: sql.NullTime{Time: dbtime.Now(), Valid: true},
|
|
}, nil)
|
|
}
|
|
|
|
func withLastBuildNotFound(mTx *dbmock.MockStore) {
|
|
mTx.EXPECT().GetLatestWorkspaceBuildByWorkspaceID(gomock.Any(), workspaceID).
|
|
Times(1).
|
|
Return(database.WorkspaceBuild{}, sql.ErrNoRows)
|
|
}
|
|
|
|
func withParameterSchemas(jobID uuid.UUID, schemas []database.ParameterSchema) func(mTx *dbmock.MockStore) {
|
|
return func(mTx *dbmock.MockStore) {
|
|
c := mTx.EXPECT().GetParameterSchemasByJobID(
|
|
gomock.Any(),
|
|
jobID).
|
|
Times(1)
|
|
if len(schemas) > 0 {
|
|
c.Return(schemas, nil)
|
|
} else {
|
|
c.Return(nil, sql.ErrNoRows)
|
|
}
|
|
}
|
|
}
|
|
|
|
func withTemplateVersionVariables(versionID uuid.UUID, params []database.TemplateVersionVariable) func(mTx *dbmock.MockStore) {
|
|
return func(mTx *dbmock.MockStore) {
|
|
c := mTx.EXPECT().GetTemplateVersionVariables(gomock.Any(), versionID).
|
|
Times(1)
|
|
if len(params) > 0 {
|
|
c.Return(params, nil)
|
|
} else {
|
|
c.Return(nil, sql.ErrNoRows)
|
|
}
|
|
}
|
|
}
|
|
|
|
func withRichParameters(params []database.WorkspaceBuildParameter) func(mTx *dbmock.MockStore) {
|
|
return func(mTx *dbmock.MockStore) {
|
|
c := mTx.EXPECT().GetWorkspaceBuildParameters(gomock.Any(), lastBuildID).
|
|
Times(1)
|
|
if len(params) > 0 {
|
|
c.Return(params, nil)
|
|
} else {
|
|
c.Return(nil, sql.ErrNoRows)
|
|
}
|
|
}
|
|
}
|
|
|
|
func withWorkspaceTags(versionID uuid.UUID, tags []database.TemplateVersionWorkspaceTag) func(mTx *dbmock.MockStore) {
|
|
return func(mTx *dbmock.MockStore) {
|
|
c := mTx.EXPECT().GetTemplateVersionWorkspaceTags(gomock.Any(), versionID).
|
|
Times(1)
|
|
if len(tags) > 0 {
|
|
c.Return(tags, nil)
|
|
} else {
|
|
c.Return(nil, sql.ErrNoRows)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Since there is expected to be only one each of job, build, and build-parameters inserted, instead
|
|
// of building matchers, we match any call and then assert its parameters. This will feel
|
|
// more familiar to the way we write other tests.
|
|
|
|
// expectProvisionerJob captures a call to InsertProvisionerJob and runs the provided assertions
|
|
// against it.
|
|
func expectProvisionerJob(
|
|
assertions func(job database.InsertProvisionerJobParams),
|
|
) func(mTx *dbmock.MockStore) {
|
|
return func(mTx *dbmock.MockStore) {
|
|
mTx.EXPECT().InsertProvisionerJob(gomock.Any(), gomock.Any()).
|
|
Times(1).
|
|
DoAndReturn(
|
|
func(ctx context.Context, params database.InsertProvisionerJobParams) (database.ProvisionerJob, error) {
|
|
assertions(params)
|
|
// there is no point copying anything other than the ID, since this object is just
|
|
// returned to our test code, and we've already asserted what we care about.
|
|
return database.ProvisionerJob{ID: params.ID}, nil
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
// expectUpdateProvisionerJobWithCompleteWithStartedAtByID asserts a call to
|
|
// expectUpdateProvisionerJobWithCompleteWithStartedAtByID and runs the provided
|
|
// assertions against it.
|
|
func expectUpdateProvisionerJobWithCompleteWithStartedAtByID(assertions func(params database.UpdateProvisionerJobWithCompleteWithStartedAtByIDParams)) func(mTx *dbmock.MockStore) {
|
|
return func(mTx *dbmock.MockStore) {
|
|
mTx.EXPECT().UpdateProvisionerJobWithCompleteWithStartedAtByID(gomock.Any(), gomock.Any()).
|
|
Times(1).
|
|
DoAndReturn(
|
|
func(ctx context.Context, params database.UpdateProvisionerJobWithCompleteWithStartedAtByIDParams) error {
|
|
assertions(params)
|
|
return nil
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
// expectUpdateWorkspaceDeletedByID asserts a call to UpdateWorkspaceDeletedByID
|
|
// and runs the provided assertions against it.
|
|
func expectUpdateWorkspaceDeletedByID(assertions func(params database.UpdateWorkspaceDeletedByIDParams)) func(mTx *dbmock.MockStore) {
|
|
return func(mTx *dbmock.MockStore) {
|
|
mTx.EXPECT().UpdateWorkspaceDeletedByID(gomock.Any(), gomock.Any()).
|
|
Times(1).
|
|
DoAndReturn(
|
|
func(ctx context.Context, params database.UpdateWorkspaceDeletedByIDParams) error {
|
|
assertions(params)
|
|
return nil
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
// expectGetProvisionerJobByID asserts a call to GetProvisionerJobByID
|
|
// and runs the provided assertions against it.
|
|
func expectGetProvisionerJobByID(assertions func(job database.ProvisionerJob)) func(mTx *dbmock.MockStore) {
|
|
return func(mTx *dbmock.MockStore) {
|
|
mTx.EXPECT().GetProvisionerJobByID(gomock.Any(), gomock.Any()).
|
|
Times(1).
|
|
DoAndReturn(
|
|
func(ctx context.Context, id uuid.UUID) (database.ProvisionerJob, error) {
|
|
job := database.ProvisionerJob{ID: id}
|
|
assertions(job)
|
|
return job, nil
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
func withBuild(mTx *dbmock.MockStore) {
|
|
mTx.EXPECT().GetWorkspaceBuildByID(gomock.Any(), gomock.Any()).Times(1).
|
|
DoAndReturn(func(ctx context.Context, id uuid.UUID) (database.WorkspaceBuild, error) {
|
|
return database.WorkspaceBuild{ID: id}, nil
|
|
})
|
|
}
|
|
|
|
// expectBuild captures a call to InsertWorkspaceBuild and runs the provided assertions
|
|
// against it.
|
|
func expectBuild(
|
|
assertions func(job database.InsertWorkspaceBuildParams),
|
|
) func(mTx *dbmock.MockStore) {
|
|
return func(mTx *dbmock.MockStore) {
|
|
mTx.EXPECT().InsertWorkspaceBuild(gomock.Any(), gomock.Any()).
|
|
Times(1).
|
|
DoAndReturn(
|
|
func(ctx context.Context, params database.InsertWorkspaceBuildParams) error {
|
|
assertions(params)
|
|
return nil
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
// expectBuildParameters captures a call to InsertWorkspaceBuildParameters and runs the provided assertions
|
|
// against it.
|
|
func expectBuildParameters(
|
|
assertions func(database.InsertWorkspaceBuildParametersParams),
|
|
) func(mTx *dbmock.MockStore) {
|
|
return func(mTx *dbmock.MockStore) {
|
|
mTx.EXPECT().InsertWorkspaceBuildParameters(gomock.Any(), gomock.Any()).
|
|
Times(1).
|
|
DoAndReturn(
|
|
func(ctx context.Context, params database.InsertWorkspaceBuildParametersParams) error {
|
|
assertions(params)
|
|
return nil
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
func withProvisionerDaemons(provisionerDaemons []database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow) func(mTx *dbmock.MockStore) {
|
|
return func(mTx *dbmock.MockStore) {
|
|
mTx.EXPECT().GetEligibleProvisionerDaemonsByProvisionerJobIDs(gomock.Any(), gomock.Any()).Return(provisionerDaemons, nil)
|
|
}
|
|
}
|