mirror of https://github.com/coder/coder.git
fix: add requester IP to workspace build audit logs (#10242)
This commit is contained in:
parent
504cedf15a
commit
1ad998ee3a
|
@ -41,6 +41,8 @@ import (
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/collectors"
|
"github.com/prometheus/client_golang/prometheus/collectors"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
"golang.org/x/mod/semver"
|
"golang.org/x/mod/semver"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
@ -2020,6 +2022,13 @@ func ConfigureTraceProvider(
|
||||||
sqlDriver = "postgres"
|
sqlDriver = "postgres"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
otel.SetTextMapPropagator(
|
||||||
|
propagation.NewCompositeTextMapPropagator(
|
||||||
|
propagation.TraceContext{},
|
||||||
|
propagation.Baggage{},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
if cfg.Trace.Enable.Value() || cfg.Trace.DataDog.Value() || cfg.Trace.HoneycombAPIKey != "" {
|
if cfg.Trace.Enable.Value() || cfg.Trace.DataDog.Value() || cfg.Trace.HoneycombAPIKey != "" {
|
||||||
sdkTracerProvider, _closeTracing, err := tracing.TracerProvider(ctx, "coderd", tracing.TracerOpts{
|
sdkTracerProvider, _closeTracing, err := tracing.TracerProvider(ctx, "coderd", tracing.TracerOpts{
|
||||||
Default: cfg.Trace.Enable.Value(),
|
Default: cfg.Trace.Enable.Value(),
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/coder/coder/v2/cli/clibase"
|
"github.com/coder/coder/v2/cli/clibase"
|
||||||
"github.com/coder/coder/v2/cli/cliui"
|
"github.com/coder/coder/v2/cli/cliui"
|
||||||
"github.com/coder/coder/v2/codersdk"
|
"github.com/coder/coder/v2/codersdk"
|
||||||
|
"github.com/coder/pretty"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *RootCmd) templates() *clibase.Cmd {
|
func (r *RootCmd) templates() *clibase.Cmd {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/coder/coder/v2/cli/clibase"
|
"github.com/coder/coder/v2/cli/clibase"
|
||||||
"github.com/coder/coder/v2/cli/cliui"
|
"github.com/coder/coder/v2/cli/cliui"
|
||||||
"github.com/coder/coder/v2/codersdk"
|
"github.com/coder/coder/v2/codersdk"
|
||||||
|
"github.com/coder/pretty"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *RootCmd) unarchiveTemplateVersion() *clibase.Cmd {
|
func (r *RootCmd) unarchiveTemplateVersion() *clibase.Cmd {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/coder/coder/v2/cli/clibase"
|
"github.com/coder/coder/v2/cli/clibase"
|
||||||
"github.com/coder/coder/v2/cli/cliui"
|
"github.com/coder/coder/v2/cli/cliui"
|
||||||
"github.com/coder/coder/v2/codersdk"
|
"github.com/coder/coder/v2/codersdk"
|
||||||
|
"github.com/coder/pretty"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *RootCmd) templateVersions() *clibase.Cmd {
|
func (r *RootCmd) templateVersions() *clibase.Cmd {
|
||||||
|
|
|
@ -121,7 +121,6 @@ func TestUserDelete(t *testing.T) {
|
||||||
// pw, err := cryptorand.String(16)
|
// pw, err := cryptorand.String(16)
|
||||||
// require.NoError(t, err)
|
// require.NoError(t, err)
|
||||||
|
|
||||||
// fmt.Println(aUser.OrganizationID)
|
|
||||||
// toDelete, err := client.CreateUser(ctx, codersdk.CreateUserRequest{
|
// toDelete, err := client.CreateUser(ctx, codersdk.CreateUserRequest{
|
||||||
// Email: "colin5@coder.com",
|
// Email: "colin5@coder.com",
|
||||||
// Username: "coolin",
|
// Username: "coolin",
|
||||||
|
|
|
@ -11,6 +11,8 @@ import (
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/sqlc-dev/pqtype"
|
"github.com/sqlc-dev/pqtype"
|
||||||
|
"go.opentelemetry.io/otel/baggage"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
"cdr.dev/slog"
|
"cdr.dev/slog"
|
||||||
"github.com/coder/coder/v2/coderd/database"
|
"github.com/coder/coder/v2/coderd/database"
|
||||||
|
@ -54,6 +56,7 @@ type BuildAuditParams[T Auditable] struct {
|
||||||
Status int
|
Status int
|
||||||
Action database.AuditAction
|
Action database.AuditAction
|
||||||
OrganizationID uuid.UUID
|
OrganizationID uuid.UUID
|
||||||
|
IP string
|
||||||
AdditionalFields json.RawMessage
|
AdditionalFields json.RawMessage
|
||||||
|
|
||||||
New T
|
New T
|
||||||
|
@ -248,9 +251,7 @@ func InitRequest[T Auditable](w http.ResponseWriter, p *RequestParams) (*Request
|
||||||
// WorkspaceBuildAudit creates an audit log for a workspace build.
|
// WorkspaceBuildAudit creates an audit log for a workspace build.
|
||||||
// The audit log is committed upon invocation.
|
// The audit log is committed upon invocation.
|
||||||
func WorkspaceBuildAudit[T Auditable](ctx context.Context, p *BuildAuditParams[T]) {
|
func WorkspaceBuildAudit[T Auditable](ctx context.Context, p *BuildAuditParams[T]) {
|
||||||
// As the audit request has not been initiated directly by a user, we omit
|
ip := parseIP(p.IP)
|
||||||
// certain user details.
|
|
||||||
ip := parseIP("")
|
|
||||||
|
|
||||||
diff := Diff(p.Audit, p.Old, p.New)
|
diff := Diff(p.Audit, p.Old, p.New)
|
||||||
var err error
|
var err error
|
||||||
|
@ -280,16 +281,70 @@ func WorkspaceBuildAudit[T Auditable](ctx context.Context, p *BuildAuditParams[T
|
||||||
RequestID: p.JobID,
|
RequestID: p.JobID,
|
||||||
AdditionalFields: p.AdditionalFields,
|
AdditionalFields: p.AdditionalFields,
|
||||||
}
|
}
|
||||||
exportErr := p.Audit.Export(ctx, auditLog)
|
err = p.Audit.Export(ctx, auditLog)
|
||||||
if exportErr != nil {
|
if err != nil {
|
||||||
p.Log.Error(ctx, "export audit log",
|
p.Log.Error(ctx, "export audit log",
|
||||||
slog.F("audit_log", auditLog),
|
slog.F("audit_log", auditLog),
|
||||||
slog.Error(err),
|
slog.Error(err),
|
||||||
)
|
)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WorkspaceBuildBaggage struct {
|
||||||
|
IP string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b WorkspaceBuildBaggage) Props() ([]baggage.Property, error) {
|
||||||
|
ipProp, err := baggage.NewKeyValueProperty("ip", b.IP)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("create ip kv property: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []baggage.Property{ipProp}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func WorkspaceBuildBaggageFromRequest(r *http.Request) WorkspaceBuildBaggage {
|
||||||
|
return WorkspaceBuildBaggage{IP: r.RemoteAddr}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Baggage interface {
|
||||||
|
Props() ([]baggage.Property, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BaggageToContext(ctx context.Context, d Baggage) (context.Context, error) {
|
||||||
|
props, err := d.Props()
|
||||||
|
if err != nil {
|
||||||
|
return ctx, xerrors.Errorf("create baggage properties: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := baggage.NewMember("audit", "baggage", props...)
|
||||||
|
if err != nil {
|
||||||
|
return ctx, xerrors.Errorf("create new baggage member: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := baggage.New(m)
|
||||||
|
if err != nil {
|
||||||
|
return ctx, xerrors.Errorf("create new baggage carrier: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return baggage.ContextWithBaggage(ctx, b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func BaggageFromContext(ctx context.Context) WorkspaceBuildBaggage {
|
||||||
|
d := WorkspaceBuildBaggage{}
|
||||||
|
b := baggage.FromContext(ctx)
|
||||||
|
props := b.Member("audit").Properties()
|
||||||
|
for _, prop := range props {
|
||||||
|
switch prop.Key() {
|
||||||
|
case "ip":
|
||||||
|
d.IP, _ = prop.Value()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
func either[T Auditable, R any](old, new T, fn func(T) R, auditAction database.AuditAction) R {
|
func either[T Auditable, R any](old, new T, fn func(T) R, auditAction database.AuditAction) R {
|
||||||
if ResourceID(new) != uuid.Nil {
|
if ResourceID(new) != uuid.Nil {
|
||||||
return fn(new)
|
return fn(new)
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
package audit_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
|
||||||
|
"github.com/coder/coder/v2/coderd/audit"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBaggage(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
prop := propagation.NewCompositeTextMapPropagator(
|
||||||
|
propagation.TraceContext{},
|
||||||
|
propagation.Baggage{},
|
||||||
|
)
|
||||||
|
|
||||||
|
expected := audit.WorkspaceBuildBaggage{
|
||||||
|
IP: "127.0.0.1",
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, err := audit.BaggageToContext(context.Background(), expected)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
carrier := propagation.MapCarrier{}
|
||||||
|
prop.Inject(ctx, carrier)
|
||||||
|
bCtx := prop.Extract(ctx, carrier)
|
||||||
|
got := audit.BaggageFromContext(bCtx)
|
||||||
|
|
||||||
|
require.Equal(t, expected, got)
|
||||||
|
}
|
|
@ -184,7 +184,8 @@ func (e *Executor) runOnce(t time.Time) Stats {
|
||||||
builder = builder.ActiveVersion()
|
builder = builder.ActiveVersion()
|
||||||
}
|
}
|
||||||
|
|
||||||
build, job, err = builder.Build(e.ctx, tx, nil)
|
build, job, err = builder.Build(e.ctx, tx, nil, audit.WorkspaceBuildBaggage{IP: "127.0.0.1"})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(e.ctx, "unable to transition workspace",
|
log.Error(e.ctx, "unable to transition workspace",
|
||||||
slog.F("transition", nextTransition),
|
slog.F("transition", nextTransition),
|
||||||
|
|
|
@ -4589,6 +4589,7 @@ func (q *FakeQuerier) InsertProvisionerJob(_ context.Context, arg database.Inser
|
||||||
Type: arg.Type,
|
Type: arg.Type,
|
||||||
Input: arg.Input,
|
Input: arg.Input,
|
||||||
Tags: arg.Tags,
|
Tags: arg.Tags,
|
||||||
|
TraceMetadata: arg.TraceMetadata,
|
||||||
}
|
}
|
||||||
job.JobStatus = provisonerJobStatus(job)
|
job.JobStatus = provisonerJobStatus(job)
|
||||||
q.provisionerJobs = append(q.provisionerJobs, job)
|
q.provisionerJobs = append(q.provisionerJobs, job)
|
||||||
|
|
|
@ -6,9 +6,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"golang.org/x/sync/errgroup"
|
|
||||||
|
|
||||||
"github.com/sqlc-dev/pqtype"
|
"github.com/sqlc-dev/pqtype"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
"github.com/coder/coder/v2/coderd/database"
|
"github.com/coder/coder/v2/coderd/database"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||||
|
|
|
@ -912,12 +912,15 @@ func (s *server) FailJob(ctx context.Context, failJob *proto.FailedJob) (*proto.
|
||||||
s.Logger.Error(ctx, "marshal workspace resource info for failed job", slog.Error(err))
|
s.Logger.Error(ctx, "marshal workspace resource info for failed job", slog.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bag := audit.BaggageFromContext(ctx)
|
||||||
|
|
||||||
audit.WorkspaceBuildAudit(ctx, &audit.BuildAuditParams[database.WorkspaceBuild]{
|
audit.WorkspaceBuildAudit(ctx, &audit.BuildAuditParams[database.WorkspaceBuild]{
|
||||||
Audit: *auditor,
|
Audit: *auditor,
|
||||||
Log: s.Logger,
|
Log: s.Logger,
|
||||||
UserID: job.InitiatorID,
|
UserID: job.InitiatorID,
|
||||||
OrganizationID: workspace.OrganizationID,
|
OrganizationID: workspace.OrganizationID,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
|
IP: bag.IP,
|
||||||
Action: auditAction,
|
Action: auditAction,
|
||||||
Old: previousBuild,
|
Old: previousBuild,
|
||||||
New: build,
|
New: build,
|
||||||
|
@ -1259,12 +1262,15 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
|
||||||
s.Logger.Error(ctx, "marshal resource info for successful job", slog.Error(err))
|
s.Logger.Error(ctx, "marshal resource info for successful job", slog.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bag := audit.BaggageFromContext(ctx)
|
||||||
|
|
||||||
audit.WorkspaceBuildAudit(ctx, &audit.BuildAuditParams[database.WorkspaceBuild]{
|
audit.WorkspaceBuildAudit(ctx, &audit.BuildAuditParams[database.WorkspaceBuild]{
|
||||||
Audit: *auditor,
|
Audit: *auditor,
|
||||||
Log: s.Logger,
|
Log: s.Logger,
|
||||||
UserID: job.InitiatorID,
|
UserID: job.InitiatorID,
|
||||||
OrganizationID: workspace.OrganizationID,
|
OrganizationID: workspace.OrganizationID,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
|
IP: bag.IP,
|
||||||
Action: auditAction,
|
Action: auditAction,
|
||||||
Old: previousBuild,
|
Old: previousBuild,
|
||||||
New: workspaceBuild,
|
New: workspaceBuild,
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
|
|
||||||
"cdr.dev/slog"
|
"cdr.dev/slog"
|
||||||
|
|
||||||
|
"github.com/coder/coder/v2/coderd/audit"
|
||||||
"github.com/coder/coder/v2/coderd/database"
|
"github.com/coder/coder/v2/coderd/database"
|
||||||
"github.com/coder/coder/v2/coderd/database/db2sdk"
|
"github.com/coder/coder/v2/coderd/database/db2sdk"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
||||||
|
@ -372,6 +373,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
|
||||||
func(action rbac.Action, object rbac.Objecter) bool {
|
func(action rbac.Action, object rbac.Objecter) bool {
|
||||||
return api.Authorize(r, action, object)
|
return api.Authorize(r, action, object)
|
||||||
},
|
},
|
||||||
|
audit.WorkspaceBuildBaggageFromRequest(r),
|
||||||
)
|
)
|
||||||
var buildErr wsbuilder.BuildError
|
var buildErr wsbuilder.BuildError
|
||||||
if xerrors.As(err, &buildErr) {
|
if xerrors.As(err, &buildErr) {
|
||||||
|
|
|
@ -12,6 +12,8 @@ import (
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
"cdr.dev/slog"
|
"cdr.dev/slog"
|
||||||
|
@ -29,11 +31,22 @@ import (
|
||||||
|
|
||||||
func TestWorkspaceBuild(t *testing.T) {
|
func TestWorkspaceBuild(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
otel.SetTextMapPropagator(
|
||||||
|
propagation.NewCompositeTextMapPropagator(
|
||||||
|
propagation.TraceContext{},
|
||||||
|
propagation.Baggage{},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
auditor := audit.NewMock()
|
||||||
|
client := coderdtest.New(t, &coderdtest.Options{
|
||||||
|
IncludeProvisionerDaemon: true,
|
||||||
|
Auditor: auditor,
|
||||||
|
})
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
||||||
|
auditor.ResetLogs()
|
||||||
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||||
|
@ -41,6 +54,8 @@ func TestWorkspaceBuild(t *testing.T) {
|
||||||
|
|
||||||
_, err := client.WorkspaceBuild(ctx, workspace.LatestBuild.ID)
|
_, err := client.WorkspaceBuild(ctx, workspace.LatestBuild.ID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
require.Len(t, auditor.AuditLogs(), 1)
|
||||||
|
require.Equal(t, auditor.AuditLogs()[0].Ip.IPNet.IP.String(), "127.0.0.1")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWorkspaceBuildByBuildNumber(t *testing.T) {
|
func TestWorkspaceBuildByBuildNumber(t *testing.T) {
|
||||||
|
@ -854,3 +869,185 @@ func TestWorkspaceBuildDebugMode(t *testing.T) {
|
||||||
require.Equal(t, 2, logsProcessed)
|
require.Equal(t, 2, logsProcessed)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPostWorkspaceBuild(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
t.Run("NoTemplateVersion", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||||
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||||
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
||||||
|
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
_, err := client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
|
||||||
|
TemplateVersionID: uuid.New(),
|
||||||
|
Transition: codersdk.WorkspaceTransitionStart,
|
||||||
|
})
|
||||||
|
require.Error(t, err)
|
||||||
|
var apiErr *codersdk.Error
|
||||||
|
require.ErrorAs(t, err, &apiErr)
|
||||||
|
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("TemplateVersionFailedImport", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||||
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
|
ProvisionApply: []*proto.Response{{}},
|
||||||
|
})
|
||||||
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
_, err := client.CreateWorkspace(ctx, user.OrganizationID, codersdk.Me, codersdk.CreateWorkspaceRequest{
|
||||||
|
TemplateID: template.ID,
|
||||||
|
Name: "workspace",
|
||||||
|
})
|
||||||
|
var apiErr *codersdk.Error
|
||||||
|
require.ErrorAs(t, err, &apiErr)
|
||||||
|
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("AlreadyActive", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
client, closer := coderdtest.NewWithProvisionerCloser(t, nil)
|
||||||
|
defer closer.Close()
|
||||||
|
|
||||||
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||||
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
||||||
|
closer.Close()
|
||||||
|
// Close here so workspace build doesn't process!
|
||||||
|
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
_, err := client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
|
||||||
|
TemplateVersionID: template.ActiveVersionID,
|
||||||
|
Transition: codersdk.WorkspaceTransitionStart,
|
||||||
|
})
|
||||||
|
require.Error(t, err)
|
||||||
|
var apiErr *codersdk.Error
|
||||||
|
require.ErrorAs(t, err, &apiErr)
|
||||||
|
require.Equal(t, http.StatusConflict, apiErr.StatusCode())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Audit", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
otel.SetTextMapPropagator(
|
||||||
|
propagation.NewCompositeTextMapPropagator(
|
||||||
|
propagation.TraceContext{},
|
||||||
|
propagation.Baggage{},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
auditor := audit.NewMock()
|
||||||
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, Auditor: auditor})
|
||||||
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||||
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
||||||
|
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
||||||
|
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
auditor.ResetLogs()
|
||||||
|
build, err := client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
|
||||||
|
TemplateVersionID: template.ActiveVersionID,
|
||||||
|
Transition: codersdk.WorkspaceTransitionStart,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, build.ID)
|
||||||
|
|
||||||
|
require.Len(t, auditor.AuditLogs(), 1)
|
||||||
|
require.Equal(t, auditor.AuditLogs()[0].Ip.IPNet.IP.String(), "127.0.0.1")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("IncrementBuildNumber", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||||
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||||
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
||||||
|
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
||||||
|
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
build, err := client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
|
||||||
|
TemplateVersionID: template.ActiveVersionID,
|
||||||
|
Transition: codersdk.WorkspaceTransitionStart,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, workspace.LatestBuild.BuildNumber+1, build.BuildNumber)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("WithState", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
client, closeDaemon := coderdtest.NewWithProvisionerCloser(t, &coderdtest.Options{
|
||||||
|
IncludeProvisionerDaemon: true,
|
||||||
|
})
|
||||||
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||||
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
||||||
|
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
||||||
|
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
|
||||||
|
wantState := []byte("something")
|
||||||
|
_ = closeDaemon.Close()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
build, err := client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
|
||||||
|
TemplateVersionID: template.ActiveVersionID,
|
||||||
|
Transition: codersdk.WorkspaceTransitionStart,
|
||||||
|
ProvisionerState: wantState,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
gotState, err := client.WorkspaceBuildState(ctx, build.ID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, wantState, gotState)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Delete", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||||
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||||
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
||||||
|
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
||||||
|
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
build, err := client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
|
||||||
|
Transition: codersdk.WorkspaceTransitionDelete,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, workspace.LatestBuild.BuildNumber+1, build.BuildNumber)
|
||||||
|
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, build.ID)
|
||||||
|
|
||||||
|
res, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{
|
||||||
|
Owner: user.UserID.String(),
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, res.Workspaces, 0)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -512,7 +512,9 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
|
||||||
db,
|
db,
|
||||||
func(action rbac.Action, object rbac.Objecter) bool {
|
func(action rbac.Action, object rbac.Objecter) bool {
|
||||||
return api.Authorize(r, action, object)
|
return api.Authorize(r, action, object)
|
||||||
})
|
},
|
||||||
|
audit.WorkspaceBuildBaggageFromRequest(r),
|
||||||
|
)
|
||||||
return err
|
return err
|
||||||
}, nil)
|
}, nil)
|
||||||
var bldErr wsbuilder.BuildError
|
var bldErr wsbuilder.BuildError
|
||||||
|
|
|
@ -1563,155 +1563,6 @@ func TestOffsetLimit(t *testing.T) {
|
||||||
require.Len(t, ws.Workspaces, 0)
|
require.Len(t, ws.Workspaces, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPostWorkspaceBuild(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
t.Run("NoTemplateVersion", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
|
||||||
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
|
||||||
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
_, err := client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
|
|
||||||
TemplateVersionID: uuid.New(),
|
|
||||||
Transition: codersdk.WorkspaceTransitionStart,
|
|
||||||
})
|
|
||||||
require.Error(t, err)
|
|
||||||
var apiErr *codersdk.Error
|
|
||||||
require.ErrorAs(t, err, &apiErr)
|
|
||||||
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("TemplateVersionFailedImport", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
|
||||||
ProvisionApply: []*proto.Response{{}},
|
|
||||||
})
|
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
|
||||||
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
_, err := client.CreateWorkspace(ctx, user.OrganizationID, codersdk.Me, codersdk.CreateWorkspaceRequest{
|
|
||||||
TemplateID: template.ID,
|
|
||||||
Name: "workspace",
|
|
||||||
})
|
|
||||||
var apiErr *codersdk.Error
|
|
||||||
require.ErrorAs(t, err, &apiErr)
|
|
||||||
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("AlreadyActive", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
client, closer := coderdtest.NewWithProvisionerCloser(t, nil)
|
|
||||||
defer closer.Close()
|
|
||||||
|
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
|
||||||
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
|
||||||
closer.Close()
|
|
||||||
// Close here so workspace build doesn't process!
|
|
||||||
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
_, err := client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
|
|
||||||
TemplateVersionID: template.ActiveVersionID,
|
|
||||||
Transition: codersdk.WorkspaceTransitionStart,
|
|
||||||
})
|
|
||||||
require.Error(t, err)
|
|
||||||
var apiErr *codersdk.Error
|
|
||||||
require.ErrorAs(t, err, &apiErr)
|
|
||||||
require.Equal(t, http.StatusConflict, apiErr.StatusCode())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("IncrementBuildNumber", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
|
||||||
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
|
||||||
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
|
||||||
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
build, err := client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
|
|
||||||
TemplateVersionID: template.ActiveVersionID,
|
|
||||||
Transition: codersdk.WorkspaceTransitionStart,
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, workspace.LatestBuild.BuildNumber+1, build.BuildNumber)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("WithState", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
client, closeDaemon := coderdtest.NewWithProvisionerCloser(t, &coderdtest.Options{
|
|
||||||
IncludeProvisionerDaemon: true,
|
|
||||||
})
|
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
|
||||||
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
|
||||||
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
|
||||||
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
|
|
||||||
wantState := []byte("something")
|
|
||||||
_ = closeDaemon.Close()
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
build, err := client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
|
|
||||||
TemplateVersionID: template.ActiveVersionID,
|
|
||||||
Transition: codersdk.WorkspaceTransitionStart,
|
|
||||||
ProvisionerState: wantState,
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
gotState, err := client.WorkspaceBuildState(ctx, build.ID)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, wantState, gotState)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Delete", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
|
||||||
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
|
||||||
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
|
||||||
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
build, err := client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
|
|
||||||
Transition: codersdk.WorkspaceTransitionDelete,
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, workspace.LatestBuild.BuildNumber+1, build.BuildNumber)
|
|
||||||
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, build.ID)
|
|
||||||
|
|
||||||
res, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{
|
|
||||||
Owner: user.UserID.String(),
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, res.Workspaces, 0)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWorkspaceUpdateAutostart(t *testing.T) {
|
func TestWorkspaceUpdateAutostart(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
dublinLoc := mustLocation(t, "Europe/Dublin")
|
dublinLoc := mustLocation(t, "Europe/Dublin")
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/sqlc-dev/pqtype"
|
"github.com/sqlc-dev/pqtype"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/coder/coder/v2/coderd/audit"
|
||||||
"github.com/coder/coder/v2/coderd/database"
|
"github.com/coder/coder/v2/coderd/database"
|
||||||
"github.com/coder/coder/v2/coderd/database/db2sdk"
|
"github.com/coder/coder/v2/coderd/database/db2sdk"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||||
|
@ -201,16 +202,20 @@ func (b *Builder) Build(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
store database.Store,
|
store database.Store,
|
||||||
authFunc func(action rbac.Action, object rbac.Objecter) bool,
|
authFunc func(action rbac.Action, object rbac.Objecter) bool,
|
||||||
|
auditBaggage audit.WorkspaceBuildBaggage,
|
||||||
) (
|
) (
|
||||||
*database.WorkspaceBuild, *database.ProvisionerJob, error,
|
*database.WorkspaceBuild, *database.ProvisionerJob, error,
|
||||||
) {
|
) {
|
||||||
b.ctx = ctx
|
var err error
|
||||||
|
b.ctx, err = audit.BaggageToContext(ctx, auditBaggage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, xerrors.Errorf("create audit baggage: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Run the build in a transaction with RepeatableRead isolation, and retries.
|
// Run the build in a transaction with RepeatableRead isolation, and retries.
|
||||||
// RepeatableRead isolation ensures that we get a consistent view of the database while
|
// RepeatableRead isolation ensures that we get a consistent view of the database while
|
||||||
// computing the new build. This simplifies the logic so that we do not need to worry if
|
// computing the new build. This simplifies the logic so that we do not need to worry if
|
||||||
// later reads are consistent with earlier ones.
|
// later reads are consistent with earlier ones.
|
||||||
var err error
|
|
||||||
for retries := 0; retries < 5; retries++ {
|
for retries := 0; retries < 5; retries++ {
|
||||||
var workspaceBuild *database.WorkspaceBuild
|
var workspaceBuild *database.WorkspaceBuild
|
||||||
var provisionerJob *database.ProvisionerJob
|
var provisionerJob *database.ProvisionerJob
|
||||||
|
|
|
@ -12,7 +12,10 @@ import (
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
|
||||||
|
"github.com/coder/coder/v2/coderd/audit"
|
||||||
"github.com/coder/coder/v2/coderd/database"
|
"github.com/coder/coder/v2/coderd/database"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbmock"
|
"github.com/coder/coder/v2/coderd/database/dbmock"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||||
|
@ -88,7 +91,7 @@ func TestBuilder_NoOptions(t *testing.T) {
|
||||||
|
|
||||||
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
||||||
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart)
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart)
|
||||||
_, _, err := uut.Build(ctx, mDB, nil)
|
_, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
||||||
req.NoError(err)
|
req.NoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +126,48 @@ func TestBuilder_Initiator(t *testing.T) {
|
||||||
|
|
||||||
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
||||||
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).Initiator(otherUserID)
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).Initiator(otherUserID)
|
||||||
_, _, err := uut.Build(ctx, mDB, nil)
|
_, _, 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,
|
||||||
|
withRichParameters(nil),
|
||||||
|
withParameterSchemas(inactiveJobID, nil),
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
_, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{IP: "127.0.0.1"})
|
||||||
req.NoError(err)
|
req.NoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +201,7 @@ func TestBuilder_Reason(t *testing.T) {
|
||||||
|
|
||||||
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
||||||
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).Reason(database.BuildReasonAutostart)
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).Reason(database.BuildReasonAutostart)
|
||||||
_, _, err := uut.Build(ctx, mDB, nil)
|
_, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
||||||
req.NoError(err)
|
req.NoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,7 +240,7 @@ func TestBuilder_ActiveVersion(t *testing.T) {
|
||||||
|
|
||||||
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
||||||
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).ActiveVersion()
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).ActiveVersion()
|
||||||
_, _, err := uut.Build(ctx, mDB, nil)
|
_, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
||||||
req.NoError(err)
|
req.NoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,7 +318,7 @@ func TestWorkspaceBuildWithRichParameters(t *testing.T) {
|
||||||
|
|
||||||
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
||||||
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).RichParameterValues(nextBuildParameters)
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).RichParameterValues(nextBuildParameters)
|
||||||
_, _, err := uut.Build(ctx, mDB, nil)
|
_, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
||||||
req.NoError(err)
|
req.NoError(err)
|
||||||
})
|
})
|
||||||
t.Run("UsePreviousParameterValues", func(t *testing.T) {
|
t.Run("UsePreviousParameterValues", func(t *testing.T) {
|
||||||
|
@ -317,7 +361,7 @@ func TestWorkspaceBuildWithRichParameters(t *testing.T) {
|
||||||
|
|
||||||
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
||||||
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).RichParameterValues(nextBuildParameters)
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).RichParameterValues(nextBuildParameters)
|
||||||
_, _, err := uut.Build(ctx, mDB, nil)
|
_, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
||||||
req.NoError(err)
|
req.NoError(err)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -357,7 +401,7 @@ func TestWorkspaceBuildWithRichParameters(t *testing.T) {
|
||||||
|
|
||||||
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
||||||
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart)
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart)
|
||||||
_, _, err := uut.Build(ctx, mDB, nil)
|
_, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
||||||
bldErr := wsbuilder.BuildError{}
|
bldErr := wsbuilder.BuildError{}
|
||||||
req.ErrorAs(err, &bldErr)
|
req.ErrorAs(err, &bldErr)
|
||||||
asrt.Equal(http.StatusBadRequest, bldErr.Status)
|
asrt.Equal(http.StatusBadRequest, bldErr.Status)
|
||||||
|
@ -394,7 +438,7 @@ func TestWorkspaceBuildWithRichParameters(t *testing.T) {
|
||||||
|
|
||||||
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
ws := database.Workspace{ID: workspaceID, TemplateID: templateID, OwnerID: userID}
|
||||||
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).RichParameterValues(nextBuildParameters)
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).RichParameterValues(nextBuildParameters)
|
||||||
_, _, err := uut.Build(ctx, mDB, nil)
|
_, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
||||||
bldErr := wsbuilder.BuildError{}
|
bldErr := wsbuilder.BuildError{}
|
||||||
req.ErrorAs(err, &bldErr)
|
req.ErrorAs(err, &bldErr)
|
||||||
asrt.Equal(http.StatusBadRequest, bldErr.Status)
|
asrt.Equal(http.StatusBadRequest, bldErr.Status)
|
||||||
|
@ -456,7 +500,7 @@ func TestWorkspaceBuildWithRichParameters(t *testing.T) {
|
||||||
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).
|
||||||
RichParameterValues(nextBuildParameters).
|
RichParameterValues(nextBuildParameters).
|
||||||
VersionID(activeVersionID)
|
VersionID(activeVersionID)
|
||||||
_, _, err := uut.Build(ctx, mDB, nil)
|
_, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
||||||
req.NoError(err)
|
req.NoError(err)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -516,7 +560,7 @@ func TestWorkspaceBuildWithRichParameters(t *testing.T) {
|
||||||
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).
|
||||||
RichParameterValues(nextBuildParameters).
|
RichParameterValues(nextBuildParameters).
|
||||||
VersionID(activeVersionID)
|
VersionID(activeVersionID)
|
||||||
_, _, err := uut.Build(ctx, mDB, nil)
|
_, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
||||||
req.NoError(err)
|
req.NoError(err)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -574,7 +618,7 @@ func TestWorkspaceBuildWithRichParameters(t *testing.T) {
|
||||||
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).
|
uut := wsbuilder.New(ws, database.WorkspaceTransitionStart).
|
||||||
RichParameterValues(nextBuildParameters).
|
RichParameterValues(nextBuildParameters).
|
||||||
VersionID(activeVersionID)
|
VersionID(activeVersionID)
|
||||||
_, _, err := uut.Build(ctx, mDB, nil)
|
_, _, err := uut.Build(ctx, mDB, nil, audit.WorkspaceBuildBaggage{})
|
||||||
req.NoError(err)
|
req.NoError(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue