mirror of https://github.com/coder/coder.git
feat: integrate Acquirer for provisioner jobs (#9717)
* chore: add Acquirer to provisionerdserver pkg Signed-off-by: Spike Curtis <spike@coder.com> * code review improvements & fixes Signed-off-by: Spike Curtis <spike@coder.com> * feat: integrate Acquirer for provisioner jobs Signed-off-by: Spike Curtis <spike@coder.com> * Fix imports, whitespace Signed-off-by: Spike Curtis <spike@coder.com> * provisionerdserver always closes; remove poll interval from playwright Signed-off-by: Spike Curtis <spike@coder.com> * post jobs outside transactions Signed-off-by: Spike Curtis <spike@coder.com> * graceful shutdown in test Signed-off-by: Spike Curtis <spike@coder.com> * Mark AcquireJob deprecated Signed-off-by: Spike Curtis <spike@coder.com> * Graceful shutdown on all provisionerd tests Signed-off-by: Spike Curtis <spike@coder.com> * Deprecate, not remove CLI flags Signed-off-by: Spike Curtis <spike@coder.com> --------- Signed-off-by: Spike Curtis <spike@coder.com>
This commit is contained in:
parent
6cf531bfef
commit
375c70d141
|
@ -1012,7 +1012,8 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
|
||||||
|
|
||||||
autobuildTicker := time.NewTicker(vals.AutobuildPollInterval.Value())
|
autobuildTicker := time.NewTicker(vals.AutobuildPollInterval.Value())
|
||||||
defer autobuildTicker.Stop()
|
defer autobuildTicker.Stop()
|
||||||
autobuildExecutor := autobuild.NewExecutor(ctx, options.Database, coderAPI.TemplateScheduleStore, logger, autobuildTicker.C)
|
autobuildExecutor := autobuild.NewExecutor(
|
||||||
|
ctx, options.Database, options.Pubsub, coderAPI.TemplateScheduleStore, logger, autobuildTicker.C)
|
||||||
autobuildExecutor.Run()
|
autobuildExecutor.Run()
|
||||||
|
|
||||||
hangDetectorTicker := time.NewTicker(vals.JobHangDetectorInterval.Value())
|
hangDetectorTicker := time.NewTicker(vals.JobHangDetectorInterval.Value())
|
||||||
|
@ -1378,16 +1379,12 @@ func newProvisionerDaemon(
|
||||||
connector[string(database.ProvisionerTypeTerraform)] = sdkproto.NewDRPCProvisionerClient(terraformClient)
|
connector[string(database.ProvisionerTypeTerraform)] = sdkproto.NewDRPCProvisionerClient(terraformClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
debounce := time.Second
|
|
||||||
return provisionerd.New(func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
return provisionerd.New(func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
||||||
// This debounces calls to listen every second. Read the comment
|
// This debounces calls to listen every second. Read the comment
|
||||||
// in provisionerdserver.go to learn more!
|
// in provisionerdserver.go to learn more!
|
||||||
return coderAPI.CreateInMemoryProvisionerDaemon(ctx, debounce)
|
return coderAPI.CreateInMemoryProvisionerDaemon(ctx)
|
||||||
}, &provisionerd.Options{
|
}, &provisionerd.Options{
|
||||||
Logger: logger.Named("provisionerd"),
|
Logger: logger.Named("provisionerd"),
|
||||||
JobPollInterval: cfg.Provisioner.DaemonPollInterval.Value(),
|
|
||||||
JobPollJitter: cfg.Provisioner.DaemonPollJitter.Value(),
|
|
||||||
JobPollDebounce: debounce,
|
|
||||||
UpdateInterval: time.Second,
|
UpdateInterval: time.Second,
|
||||||
ForceCancelInterval: cfg.Provisioner.ForceCancelInterval.Value(),
|
ForceCancelInterval: cfg.Provisioner.ForceCancelInterval.Value(),
|
||||||
Connector: connector,
|
Connector: connector,
|
||||||
|
|
|
@ -393,10 +393,10 @@ updating, and deleting workspace resources.
|
||||||
Time to force cancel provisioning tasks that are stuck.
|
Time to force cancel provisioning tasks that are stuck.
|
||||||
|
|
||||||
--provisioner-daemon-poll-interval duration, $CODER_PROVISIONER_DAEMON_POLL_INTERVAL (default: 1s)
|
--provisioner-daemon-poll-interval duration, $CODER_PROVISIONER_DAEMON_POLL_INTERVAL (default: 1s)
|
||||||
Time to wait before polling for a new job.
|
Deprecated and ignored.
|
||||||
|
|
||||||
--provisioner-daemon-poll-jitter duration, $CODER_PROVISIONER_DAEMON_POLL_JITTER (default: 100ms)
|
--provisioner-daemon-poll-jitter duration, $CODER_PROVISIONER_DAEMON_POLL_JITTER (default: 100ms)
|
||||||
Random jitter added to the poll interval.
|
Deprecated and ignored.
|
||||||
|
|
||||||
--provisioner-daemon-psk string, $CODER_PROVISIONER_DAEMON_PSK
|
--provisioner-daemon-psk string, $CODER_PROVISIONER_DAEMON_PSK
|
||||||
Pre-shared key to authenticate external provisioner daemons to Coder
|
Pre-shared key to authenticate external provisioner daemons to Coder
|
||||||
|
|
|
@ -348,10 +348,10 @@ provisioning:
|
||||||
# tests.
|
# tests.
|
||||||
# (default: false, type: bool)
|
# (default: false, type: bool)
|
||||||
daemonsEcho: false
|
daemonsEcho: false
|
||||||
# Time to wait before polling for a new job.
|
# Deprecated and ignored.
|
||||||
# (default: 1s, type: duration)
|
# (default: 1s, type: duration)
|
||||||
daemonPollInterval: 1s
|
daemonPollInterval: 1s
|
||||||
# Random jitter added to the poll interval.
|
# Deprecated and ignored.
|
||||||
# (default: 100ms, type: duration)
|
# (default: 100ms, type: duration)
|
||||||
daemonPollJitter: 100ms
|
daemonPollJitter: 100ms
|
||||||
# Time to force cancel provisioning tasks that are stuck.
|
# Time to force cancel provisioning tasks that are stuck.
|
||||||
|
|
|
@ -134,7 +134,7 @@ func Test_ActivityBumpWorkspace(t *testing.T) {
|
||||||
TemplateID: template.ID,
|
TemplateID: template.ID,
|
||||||
Ttl: sql.NullInt64{Valid: true, Int64: int64(tt.workspaceTTL)},
|
Ttl: sql.NullInt64{Valid: true, Int64: int64(tt.workspaceTTL)},
|
||||||
})
|
})
|
||||||
job = dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
job = dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
|
||||||
OrganizationID: org.ID,
|
OrganizationID: org.ID,
|
||||||
CompletedAt: tt.jobCompletedAt,
|
CompletedAt: tt.jobCompletedAt,
|
||||||
})
|
})
|
||||||
|
@ -225,7 +225,7 @@ func Test_ActivityBumpWorkspace(t *testing.T) {
|
||||||
func insertPrevWorkspaceBuild(t *testing.T, db database.Store, orgID, tvID, workspaceID uuid.UUID, transition database.WorkspaceTransition, buildNumber int32) {
|
func insertPrevWorkspaceBuild(t *testing.T, db database.Store, orgID, tvID, workspaceID uuid.UUID, transition database.WorkspaceTransition, buildNumber int32) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
job := dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
job := dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
|
||||||
OrganizationID: orgID,
|
OrganizationID: orgID,
|
||||||
})
|
})
|
||||||
_ = dbgen.WorkspaceResource(t, db, database.WorkspaceResource{
|
_ = dbgen.WorkspaceResource(t, db, database.WorkspaceResource{
|
||||||
|
|
|
@ -16,6 +16,8 @@ import (
|
||||||
"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"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||||
|
"github.com/coder/coder/v2/coderd/database/provisionerjobs"
|
||||||
|
"github.com/coder/coder/v2/coderd/database/pubsub"
|
||||||
"github.com/coder/coder/v2/coderd/schedule"
|
"github.com/coder/coder/v2/coderd/schedule"
|
||||||
"github.com/coder/coder/v2/coderd/schedule/cron"
|
"github.com/coder/coder/v2/coderd/schedule/cron"
|
||||||
"github.com/coder/coder/v2/coderd/wsbuilder"
|
"github.com/coder/coder/v2/coderd/wsbuilder"
|
||||||
|
@ -26,6 +28,7 @@ import (
|
||||||
type Executor struct {
|
type Executor struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
db database.Store
|
db database.Store
|
||||||
|
ps pubsub.Pubsub
|
||||||
templateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore]
|
templateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore]
|
||||||
log slog.Logger
|
log slog.Logger
|
||||||
tick <-chan time.Time
|
tick <-chan time.Time
|
||||||
|
@ -40,11 +43,12 @@ type Stats struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new wsactions executor.
|
// New returns a new wsactions executor.
|
||||||
func NewExecutor(ctx context.Context, db database.Store, tss *atomic.Pointer[schedule.TemplateScheduleStore], log slog.Logger, tick <-chan time.Time) *Executor {
|
func NewExecutor(ctx context.Context, db database.Store, ps pubsub.Pubsub, tss *atomic.Pointer[schedule.TemplateScheduleStore], log slog.Logger, tick <-chan time.Time) *Executor {
|
||||||
le := &Executor{
|
le := &Executor{
|
||||||
//nolint:gocritic // Autostart has a limited set of permissions.
|
//nolint:gocritic // Autostart has a limited set of permissions.
|
||||||
ctx: dbauthz.AsAutostart(ctx),
|
ctx: dbauthz.AsAutostart(ctx),
|
||||||
db: db,
|
db: db,
|
||||||
|
ps: ps,
|
||||||
templateScheduleStore: tss,
|
templateScheduleStore: tss,
|
||||||
tick: tick,
|
tick: tick,
|
||||||
log: log.Named("autobuild"),
|
log: log.Named("autobuild"),
|
||||||
|
@ -129,6 +133,7 @@ func (e *Executor) runOnce(t time.Time) Stats {
|
||||||
log := e.log.With(slog.F("workspace_id", wsID))
|
log := e.log.With(slog.F("workspace_id", wsID))
|
||||||
|
|
||||||
eg.Go(func() error {
|
eg.Go(func() error {
|
||||||
|
var job *database.ProvisionerJob
|
||||||
err := e.db.InTx(func(tx database.Store) error {
|
err := e.db.InTx(func(tx database.Store) error {
|
||||||
// Re-check eligibility since the first check was outside the
|
// Re-check eligibility since the first check was outside the
|
||||||
// transaction and the workspace settings may have changed.
|
// transaction and the workspace settings may have changed.
|
||||||
|
@ -168,7 +173,8 @@ func (e *Executor) runOnce(t time.Time) Stats {
|
||||||
SetLastWorkspaceBuildJobInTx(&latestJob).
|
SetLastWorkspaceBuildJobInTx(&latestJob).
|
||||||
Reason(reason)
|
Reason(reason)
|
||||||
|
|
||||||
if _, _, err := builder.Build(e.ctx, tx, nil); err != nil {
|
_, job, err = builder.Build(e.ctx, tx, 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),
|
||||||
slog.Error(err),
|
slog.Error(err),
|
||||||
|
@ -230,6 +236,17 @@ func (e *Executor) runOnce(t time.Time) Stats {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(e.ctx, "workspace scheduling failed", slog.Error(err))
|
log.Error(e.ctx, "workspace scheduling failed", slog.Error(err))
|
||||||
}
|
}
|
||||||
|
if job != nil && err == nil {
|
||||||
|
// Note that we can't refactor such that posting the job happens inside wsbuilder because it's called
|
||||||
|
// with an outer transaction like this, and we need to make sure the outer transaction commits before
|
||||||
|
// posting the job. If we post before the transaction commits, provisionerd might try to acquire the
|
||||||
|
// job, fail, and then sit idle instead of picking up the job.
|
||||||
|
err = provisionerjobs.PostJob(e.ps, *job)
|
||||||
|
if err != nil {
|
||||||
|
// Client probably doesn't care about this error, so just log it.
|
||||||
|
log.Error(e.ctx, "failed to post provisioner job to pubsub", slog.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/coder/coder/v2/coderd/database/dbgen"
|
"github.com/coder/coder/v2/coderd/database/dbgen"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbtestutil"
|
"github.com/coder/coder/v2/coderd/database/dbtestutil"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||||
|
"github.com/coder/coder/v2/coderd/database/pubsub"
|
||||||
"github.com/coder/coder/v2/coderd/rbac"
|
"github.com/coder/coder/v2/coderd/rbac"
|
||||||
"github.com/coder/coder/v2/codersdk/agentsdk"
|
"github.com/coder/coder/v2/codersdk/agentsdk"
|
||||||
"github.com/coder/coder/v2/cryptorand"
|
"github.com/coder/coder/v2/cryptorand"
|
||||||
|
@ -26,11 +27,11 @@ func TestBatchStats(t *testing.T) {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
t.Cleanup(cancel)
|
t.Cleanup(cancel)
|
||||||
log := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug)
|
log := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug)
|
||||||
store, _ := dbtestutil.NewDB(t)
|
store, ps := dbtestutil.NewDB(t)
|
||||||
|
|
||||||
// Set up some test dependencies.
|
// Set up some test dependencies.
|
||||||
deps1 := setupDeps(t, store)
|
deps1 := setupDeps(t, store, ps)
|
||||||
deps2 := setupDeps(t, store)
|
deps2 := setupDeps(t, store, ps)
|
||||||
tick := make(chan time.Time)
|
tick := make(chan time.Time)
|
||||||
flushed := make(chan int, 1)
|
flushed := make(chan int, 1)
|
||||||
|
|
||||||
|
@ -168,7 +169,7 @@ type deps struct {
|
||||||
// It creates an organization, user, template, workspace, and agent
|
// It creates an organization, user, template, workspace, and agent
|
||||||
// along with all the other miscellaneous plumbing required to link
|
// along with all the other miscellaneous plumbing required to link
|
||||||
// them together.
|
// them together.
|
||||||
func setupDeps(t *testing.T, store database.Store) deps {
|
func setupDeps(t *testing.T, store database.Store, ps pubsub.Pubsub) deps {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
org := dbgen.Organization(t, store, database.Organization{})
|
org := dbgen.Organization(t, store, database.Organization{})
|
||||||
|
@ -194,7 +195,7 @@ func setupDeps(t *testing.T, store database.Store) deps {
|
||||||
OrganizationID: org.ID,
|
OrganizationID: org.ID,
|
||||||
LastUsedAt: time.Now().Add(-time.Hour),
|
LastUsedAt: time.Now().Add(-time.Hour),
|
||||||
})
|
})
|
||||||
pj := dbgen.ProvisionerJob(t, store, database.ProvisionerJob{
|
pj := dbgen.ProvisionerJob(t, store, ps, database.ProvisionerJob{
|
||||||
InitiatorID: user.ID,
|
InitiatorID: user.ID,
|
||||||
OrganizationID: org.ID,
|
OrganizationID: org.ID,
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/json"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -366,6 +365,11 @@ func New(options *Options) *API {
|
||||||
UserQuietHoursScheduleStore: options.UserQuietHoursScheduleStore,
|
UserQuietHoursScheduleStore: options.UserQuietHoursScheduleStore,
|
||||||
Experiments: experiments,
|
Experiments: experiments,
|
||||||
healthCheckGroup: &singleflight.Group[string, *healthcheck.Report]{},
|
healthCheckGroup: &singleflight.Group[string, *healthcheck.Report]{},
|
||||||
|
Acquirer: provisionerdserver.NewAcquirer(
|
||||||
|
ctx,
|
||||||
|
options.Logger.Named("acquirer"),
|
||||||
|
options.Database,
|
||||||
|
options.Pubsub),
|
||||||
}
|
}
|
||||||
if options.UpdateCheckOptions != nil {
|
if options.UpdateCheckOptions != nil {
|
||||||
api.updateChecker = updatecheck.New(
|
api.updateChecker = updatecheck.New(
|
||||||
|
@ -1016,6 +1020,8 @@ type API struct {
|
||||||
healthCheckCache atomic.Pointer[healthcheck.Report]
|
healthCheckCache atomic.Pointer[healthcheck.Report]
|
||||||
|
|
||||||
statsBatcher *batchstats.Batcher
|
statsBatcher *batchstats.Batcher
|
||||||
|
|
||||||
|
Acquirer *provisionerdserver.Acquirer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close waits for all WebSocket connections to drain before returning.
|
// Close waits for all WebSocket connections to drain before returning.
|
||||||
|
@ -1067,7 +1073,7 @@ func compressHandler(h http.Handler) http.Handler {
|
||||||
|
|
||||||
// CreateInMemoryProvisionerDaemon is an in-memory connection to a provisionerd.
|
// CreateInMemoryProvisionerDaemon is an in-memory connection to a provisionerd.
|
||||||
// Useful when starting coderd and provisionerd in the same process.
|
// Useful when starting coderd and provisionerd in the same process.
|
||||||
func (api *API) CreateInMemoryProvisionerDaemon(ctx context.Context, debounce time.Duration) (client proto.DRPCProvisionerDaemonClient, err error) {
|
func (api *API) CreateInMemoryProvisionerDaemon(ctx context.Context) (client proto.DRPCProvisionerDaemonClient, err error) {
|
||||||
tracer := api.TracerProvider.Tracer(tracing.TracerName)
|
tracer := api.TracerProvider.Tracer(tracing.TracerName)
|
||||||
clientSession, serverSession := provisionersdk.MemTransportPipe()
|
clientSession, serverSession := provisionersdk.MemTransportPipe()
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -1077,11 +1083,8 @@ func (api *API) CreateInMemoryProvisionerDaemon(ctx context.Context, debounce ti
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
tags, err := json.Marshal(database.StringMap{
|
tags := provisionerdserver.Tags{
|
||||||
provisionerdserver.TagScope: provisionerdserver.ScopeOrganization,
|
provisionerdserver.TagScope: provisionerdserver.ScopeOrganization,
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Errorf("marshal tags: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mux := drpcmux.New()
|
mux := drpcmux.New()
|
||||||
|
@ -1098,6 +1101,7 @@ func (api *API) CreateInMemoryProvisionerDaemon(ctx context.Context, debounce ti
|
||||||
tags,
|
tags,
|
||||||
api.Database,
|
api.Database,
|
||||||
api.Pubsub,
|
api.Pubsub,
|
||||||
|
api.Acquirer,
|
||||||
api.Telemetry,
|
api.Telemetry,
|
||||||
tracer,
|
tracer,
|
||||||
&api.QuotaCommitter,
|
&api.QuotaCommitter,
|
||||||
|
@ -1105,7 +1109,6 @@ func (api *API) CreateInMemoryProvisionerDaemon(ctx context.Context, debounce ti
|
||||||
api.TemplateScheduleStore,
|
api.TemplateScheduleStore,
|
||||||
api.UserQuietHoursScheduleStore,
|
api.UserQuietHoursScheduleStore,
|
||||||
api.DeploymentValues,
|
api.DeploymentValues,
|
||||||
debounce,
|
|
||||||
provisionerdserver.Options{
|
provisionerdserver.Options{
|
||||||
OIDCConfig: api.OIDCConfig,
|
OIDCConfig: api.OIDCConfig,
|
||||||
GitAuthConfigs: api.GitAuthConfigs,
|
GitAuthConfigs: api.GitAuthConfigs,
|
||||||
|
|
|
@ -266,6 +266,7 @@ func NewOptions(t testing.TB, options *Options) (func(http.Handler), context.Can
|
||||||
lifecycleExecutor := autobuild.NewExecutor(
|
lifecycleExecutor := autobuild.NewExecutor(
|
||||||
ctx,
|
ctx,
|
||||||
options.Database,
|
options.Database,
|
||||||
|
options.Pubsub,
|
||||||
&templateScheduleStore,
|
&templateScheduleStore,
|
||||||
slogtest.Make(t, nil).Named("autobuild.executor").Leveled(slog.LevelDebug),
|
slogtest.Make(t, nil).Named("autobuild.executor").Leveled(slog.LevelDebug),
|
||||||
options.AutobuildTicker,
|
options.AutobuildTicker,
|
||||||
|
@ -453,6 +454,30 @@ func NewWithAPI(t testing.TB, options *Options) (*codersdk.Client, io.Closer, *c
|
||||||
return client, provisionerCloser, coderAPI
|
return client, provisionerCloser, coderAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// provisionerdCloser wraps a provisioner daemon as an io.Closer that can be called multiple times
|
||||||
|
type provisionerdCloser struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
closed bool
|
||||||
|
d *provisionerd.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *provisionerdCloser) Close() error {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
if c.closed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
c.closed = true
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||||
|
defer cancel()
|
||||||
|
shutdownErr := c.d.Shutdown(ctx)
|
||||||
|
closeErr := c.d.Close()
|
||||||
|
if shutdownErr != nil {
|
||||||
|
return shutdownErr
|
||||||
|
}
|
||||||
|
return closeErr
|
||||||
|
}
|
||||||
|
|
||||||
// NewProvisionerDaemon launches a provisionerd instance configured to work
|
// NewProvisionerDaemon launches a provisionerd instance configured to work
|
||||||
// well with coderd testing. It registers the "echo" provisioner for
|
// well with coderd testing. It registers the "echo" provisioner for
|
||||||
// quick testing.
|
// quick testing.
|
||||||
|
@ -482,17 +507,17 @@ func NewProvisionerDaemon(t testing.TB, coderAPI *coderd.API) io.Closer {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
closer := provisionerd.New(func(ctx context.Context) (provisionerdproto.DRPCProvisionerDaemonClient, error) {
|
daemon := provisionerd.New(func(ctx context.Context) (provisionerdproto.DRPCProvisionerDaemonClient, error) {
|
||||||
return coderAPI.CreateInMemoryProvisionerDaemon(ctx, 0)
|
return coderAPI.CreateInMemoryProvisionerDaemon(ctx)
|
||||||
}, &provisionerd.Options{
|
}, &provisionerd.Options{
|
||||||
Logger: coderAPI.Logger.Named("provisionerd").Leveled(slog.LevelDebug),
|
Logger: coderAPI.Logger.Named("provisionerd").Leveled(slog.LevelDebug),
|
||||||
JobPollInterval: 50 * time.Millisecond,
|
|
||||||
UpdateInterval: 250 * time.Millisecond,
|
UpdateInterval: 250 * time.Millisecond,
|
||||||
ForceCancelInterval: time.Second,
|
ForceCancelInterval: time.Second,
|
||||||
Connector: provisionerd.LocalProvisioners{
|
Connector: provisionerd.LocalProvisioners{
|
||||||
string(database.ProvisionerTypeEcho): sdkproto.NewDRPCProvisionerClient(echoClient),
|
string(database.ProvisionerTypeEcho): sdkproto.NewDRPCProvisionerClient(echoClient),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
closer := &provisionerdCloser{d: daemon}
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
_ = closer.Close()
|
_ = closer.Close()
|
||||||
})
|
})
|
||||||
|
@ -518,7 +543,7 @@ func NewExternalProvisionerDaemon(t *testing.T, client *codersdk.Client, org uui
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
closer := provisionerd.New(func(ctx context.Context) (provisionerdproto.DRPCProvisionerDaemonClient, error) {
|
daemon := provisionerd.New(func(ctx context.Context) (provisionerdproto.DRPCProvisionerDaemonClient, error) {
|
||||||
return client.ServeProvisionerDaemon(ctx, codersdk.ServeProvisionerDaemonRequest{
|
return client.ServeProvisionerDaemon(ctx, codersdk.ServeProvisionerDaemonRequest{
|
||||||
Organization: org,
|
Organization: org,
|
||||||
Provisioners: []codersdk.ProvisionerType{codersdk.ProvisionerTypeEcho},
|
Provisioners: []codersdk.ProvisionerType{codersdk.ProvisionerTypeEcho},
|
||||||
|
@ -526,13 +551,13 @@ func NewExternalProvisionerDaemon(t *testing.T, client *codersdk.Client, org uui
|
||||||
})
|
})
|
||||||
}, &provisionerd.Options{
|
}, &provisionerd.Options{
|
||||||
Logger: slogtest.Make(t, nil).Named("provisionerd").Leveled(slog.LevelDebug),
|
Logger: slogtest.Make(t, nil).Named("provisionerd").Leveled(slog.LevelDebug),
|
||||||
JobPollInterval: 50 * time.Millisecond,
|
|
||||||
UpdateInterval: 250 * time.Millisecond,
|
UpdateInterval: 250 * time.Millisecond,
|
||||||
ForceCancelInterval: time.Second,
|
ForceCancelInterval: time.Second,
|
||||||
Connector: provisionerd.LocalProvisioners{
|
Connector: provisionerd.LocalProvisioners{
|
||||||
string(database.ProvisionerTypeEcho): sdkproto.NewDRPCProvisionerClient(echoClient),
|
string(database.ProvisionerTypeEcho): sdkproto.NewDRPCProvisionerClient(echoClient),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
closer := &provisionerdCloser{d: daemon}
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
_ = closer.Close()
|
_ = closer.Close()
|
||||||
})
|
})
|
||||||
|
|
|
@ -344,14 +344,14 @@ func (s *MethodTestSuite) TestGroup() {
|
||||||
func (s *MethodTestSuite) TestProvsionerJob() {
|
func (s *MethodTestSuite) TestProvsionerJob() {
|
||||||
s.Run("Build/GetProvisionerJobByID", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("Build/GetProvisionerJobByID", s.Subtest(func(db database.Store, check *expects) {
|
||||||
w := dbgen.Workspace(s.T(), db, database.Workspace{})
|
w := dbgen.Workspace(s.T(), db, database.Workspace{})
|
||||||
j := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{
|
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{
|
||||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||||
})
|
})
|
||||||
_ = dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{JobID: j.ID, WorkspaceID: w.ID})
|
_ = dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{JobID: j.ID, WorkspaceID: w.ID})
|
||||||
check.Args(j.ID).Asserts(w, rbac.ActionRead).Returns(j)
|
check.Args(j.ID).Asserts(w, rbac.ActionRead).Returns(j)
|
||||||
}))
|
}))
|
||||||
s.Run("TemplateVersion/GetProvisionerJobByID", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("TemplateVersion/GetProvisionerJobByID", s.Subtest(func(db database.Store, check *expects) {
|
||||||
j := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{
|
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{
|
||||||
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
||||||
})
|
})
|
||||||
tpl := dbgen.Template(s.T(), db, database.Template{})
|
tpl := dbgen.Template(s.T(), db, database.Template{})
|
||||||
|
@ -366,7 +366,7 @@ func (s *MethodTestSuite) TestProvsionerJob() {
|
||||||
v := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{
|
v := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{
|
||||||
TemplateID: uuid.NullUUID{UUID: tpl.ID, Valid: true},
|
TemplateID: uuid.NullUUID{UUID: tpl.ID, Valid: true},
|
||||||
})
|
})
|
||||||
j := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{
|
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{
|
||||||
Type: database.ProvisionerJobTypeTemplateVersionDryRun,
|
Type: database.ProvisionerJobTypeTemplateVersionDryRun,
|
||||||
Input: must(json.Marshal(struct {
|
Input: must(json.Marshal(struct {
|
||||||
TemplateVersionID uuid.UUID `json:"template_version_id"`
|
TemplateVersionID uuid.UUID `json:"template_version_id"`
|
||||||
|
@ -377,7 +377,7 @@ func (s *MethodTestSuite) TestProvsionerJob() {
|
||||||
s.Run("Build/UpdateProvisionerJobWithCancelByID", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("Build/UpdateProvisionerJobWithCancelByID", s.Subtest(func(db database.Store, check *expects) {
|
||||||
tpl := dbgen.Template(s.T(), db, database.Template{AllowUserCancelWorkspaceJobs: true})
|
tpl := dbgen.Template(s.T(), db, database.Template{AllowUserCancelWorkspaceJobs: true})
|
||||||
w := dbgen.Workspace(s.T(), db, database.Workspace{TemplateID: tpl.ID})
|
w := dbgen.Workspace(s.T(), db, database.Workspace{TemplateID: tpl.ID})
|
||||||
j := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{
|
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{
|
||||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||||
})
|
})
|
||||||
_ = dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{JobID: j.ID, WorkspaceID: w.ID})
|
_ = dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{JobID: j.ID, WorkspaceID: w.ID})
|
||||||
|
@ -386,14 +386,14 @@ func (s *MethodTestSuite) TestProvsionerJob() {
|
||||||
s.Run("BuildFalseCancel/UpdateProvisionerJobWithCancelByID", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("BuildFalseCancel/UpdateProvisionerJobWithCancelByID", s.Subtest(func(db database.Store, check *expects) {
|
||||||
tpl := dbgen.Template(s.T(), db, database.Template{AllowUserCancelWorkspaceJobs: false})
|
tpl := dbgen.Template(s.T(), db, database.Template{AllowUserCancelWorkspaceJobs: false})
|
||||||
w := dbgen.Workspace(s.T(), db, database.Workspace{TemplateID: tpl.ID})
|
w := dbgen.Workspace(s.T(), db, database.Workspace{TemplateID: tpl.ID})
|
||||||
j := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{
|
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{
|
||||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||||
})
|
})
|
||||||
_ = dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{JobID: j.ID, WorkspaceID: w.ID})
|
_ = dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{JobID: j.ID, WorkspaceID: w.ID})
|
||||||
check.Args(database.UpdateProvisionerJobWithCancelByIDParams{ID: j.ID}).Asserts(w, rbac.ActionUpdate).Returns()
|
check.Args(database.UpdateProvisionerJobWithCancelByIDParams{ID: j.ID}).Asserts(w, rbac.ActionUpdate).Returns()
|
||||||
}))
|
}))
|
||||||
s.Run("TemplateVersion/UpdateProvisionerJobWithCancelByID", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("TemplateVersion/UpdateProvisionerJobWithCancelByID", s.Subtest(func(db database.Store, check *expects) {
|
||||||
j := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{
|
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{
|
||||||
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
||||||
})
|
})
|
||||||
tpl := dbgen.Template(s.T(), db, database.Template{})
|
tpl := dbgen.Template(s.T(), db, database.Template{})
|
||||||
|
@ -405,7 +405,7 @@ func (s *MethodTestSuite) TestProvsionerJob() {
|
||||||
Asserts(v.RBACObject(tpl), []rbac.Action{rbac.ActionRead, rbac.ActionUpdate}).Returns()
|
Asserts(v.RBACObject(tpl), []rbac.Action{rbac.ActionRead, rbac.ActionUpdate}).Returns()
|
||||||
}))
|
}))
|
||||||
s.Run("TemplateVersionNoTemplate/UpdateProvisionerJobWithCancelByID", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("TemplateVersionNoTemplate/UpdateProvisionerJobWithCancelByID", s.Subtest(func(db database.Store, check *expects) {
|
||||||
j := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{
|
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{
|
||||||
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
||||||
})
|
})
|
||||||
v := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{
|
v := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{
|
||||||
|
@ -420,7 +420,7 @@ func (s *MethodTestSuite) TestProvsionerJob() {
|
||||||
v := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{
|
v := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{
|
||||||
TemplateID: uuid.NullUUID{UUID: tpl.ID, Valid: true},
|
TemplateID: uuid.NullUUID{UUID: tpl.ID, Valid: true},
|
||||||
})
|
})
|
||||||
j := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{
|
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{
|
||||||
Type: database.ProvisionerJobTypeTemplateVersionDryRun,
|
Type: database.ProvisionerJobTypeTemplateVersionDryRun,
|
||||||
Input: must(json.Marshal(struct {
|
Input: must(json.Marshal(struct {
|
||||||
TemplateVersionID uuid.UUID `json:"template_version_id"`
|
TemplateVersionID uuid.UUID `json:"template_version_id"`
|
||||||
|
@ -430,13 +430,13 @@ func (s *MethodTestSuite) TestProvsionerJob() {
|
||||||
Asserts(v.RBACObject(tpl), []rbac.Action{rbac.ActionRead, rbac.ActionUpdate}).Returns()
|
Asserts(v.RBACObject(tpl), []rbac.Action{rbac.ActionRead, rbac.ActionUpdate}).Returns()
|
||||||
}))
|
}))
|
||||||
s.Run("GetProvisionerJobsByIDs", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("GetProvisionerJobsByIDs", s.Subtest(func(db database.Store, check *expects) {
|
||||||
a := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{})
|
a := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{})
|
||||||
b := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{})
|
b := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{})
|
||||||
check.Args([]uuid.UUID{a.ID, b.ID}).Asserts().Returns(slice.New(a, b))
|
check.Args([]uuid.UUID{a.ID, b.ID}).Asserts().Returns(slice.New(a, b))
|
||||||
}))
|
}))
|
||||||
s.Run("GetProvisionerLogsAfterID", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("GetProvisionerLogsAfterID", s.Subtest(func(db database.Store, check *expects) {
|
||||||
w := dbgen.Workspace(s.T(), db, database.Workspace{})
|
w := dbgen.Workspace(s.T(), db, database.Workspace{})
|
||||||
j := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{
|
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{
|
||||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||||
})
|
})
|
||||||
_ = dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{JobID: j.ID, WorkspaceID: w.ID})
|
_ = dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{JobID: j.ID, WorkspaceID: w.ID})
|
||||||
|
@ -1151,20 +1151,20 @@ func (s *MethodTestSuite) TestWorkspace() {
|
||||||
s.Run("GetWorkspaceResourceByID", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("GetWorkspaceResourceByID", s.Subtest(func(db database.Store, check *expects) {
|
||||||
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
|
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
|
||||||
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
|
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
|
||||||
_ = dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{ID: build.JobID, Type: database.ProvisionerJobTypeWorkspaceBuild})
|
_ = dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{ID: build.JobID, Type: database.ProvisionerJobTypeWorkspaceBuild})
|
||||||
res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID})
|
res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID})
|
||||||
check.Args(res.ID).Asserts(ws, rbac.ActionRead).Returns(res)
|
check.Args(res.ID).Asserts(ws, rbac.ActionRead).Returns(res)
|
||||||
}))
|
}))
|
||||||
s.Run("Build/GetWorkspaceResourcesByJobID", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("Build/GetWorkspaceResourcesByJobID", s.Subtest(func(db database.Store, check *expects) {
|
||||||
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
|
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
|
||||||
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
|
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
|
||||||
job := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{ID: build.JobID, Type: database.ProvisionerJobTypeWorkspaceBuild})
|
job := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{ID: build.JobID, Type: database.ProvisionerJobTypeWorkspaceBuild})
|
||||||
check.Args(job.ID).Asserts(ws, rbac.ActionRead).Returns([]database.WorkspaceResource{})
|
check.Args(job.ID).Asserts(ws, rbac.ActionRead).Returns([]database.WorkspaceResource{})
|
||||||
}))
|
}))
|
||||||
s.Run("Template/GetWorkspaceResourcesByJobID", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("Template/GetWorkspaceResourcesByJobID", s.Subtest(func(db database.Store, check *expects) {
|
||||||
tpl := dbgen.Template(s.T(), db, database.Template{})
|
tpl := dbgen.Template(s.T(), db, database.Template{})
|
||||||
v := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{TemplateID: uuid.NullUUID{UUID: tpl.ID, Valid: true}, JobID: uuid.New()})
|
v := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{TemplateID: uuid.NullUUID{UUID: tpl.ID, Valid: true}, JobID: uuid.New()})
|
||||||
job := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{ID: v.JobID, Type: database.ProvisionerJobTypeTemplateVersionImport})
|
job := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{ID: v.JobID, Type: database.ProvisionerJobTypeTemplateVersionImport})
|
||||||
check.Args(job.ID).Asserts(v.RBACObject(tpl), []rbac.Action{rbac.ActionRead, rbac.ActionRead}).Returns([]database.WorkspaceResource{})
|
check.Args(job.ID).Asserts(v.RBACObject(tpl), []rbac.Action{rbac.ActionRead, rbac.ActionRead}).Returns([]database.WorkspaceResource{})
|
||||||
}))
|
}))
|
||||||
s.Run("InsertWorkspace", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("InsertWorkspace", s.Subtest(func(db database.Store, check *expects) {
|
||||||
|
@ -1411,7 +1411,7 @@ func (s *MethodTestSuite) TestSystemFunctions() {
|
||||||
}))
|
}))
|
||||||
s.Run("GetProvisionerJobsCreatedAfter", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("GetProvisionerJobsCreatedAfter", s.Subtest(func(db database.Store, check *expects) {
|
||||||
// TODO: add provisioner job resource type
|
// TODO: add provisioner job resource type
|
||||||
_ = dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{CreatedAt: time.Now().Add(-time.Hour)})
|
_ = dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{CreatedAt: time.Now().Add(-time.Hour)})
|
||||||
check.Args(time.Now()).Asserts( /*rbac.ResourceSystem, rbac.ActionRead*/ )
|
check.Args(time.Now()).Asserts( /*rbac.ResourceSystem, rbac.ActionRead*/ )
|
||||||
}))
|
}))
|
||||||
s.Run("GetTemplateVersionsByIDs", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("GetTemplateVersionsByIDs", s.Subtest(func(db database.Store, check *expects) {
|
||||||
|
@ -1450,11 +1450,11 @@ func (s *MethodTestSuite) TestSystemFunctions() {
|
||||||
s.Run("GetWorkspaceResourcesByJobIDs", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("GetWorkspaceResourcesByJobIDs", s.Subtest(func(db database.Store, check *expects) {
|
||||||
tpl := dbgen.Template(s.T(), db, database.Template{})
|
tpl := dbgen.Template(s.T(), db, database.Template{})
|
||||||
v := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{TemplateID: uuid.NullUUID{UUID: tpl.ID, Valid: true}, JobID: uuid.New()})
|
v := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{TemplateID: uuid.NullUUID{UUID: tpl.ID, Valid: true}, JobID: uuid.New()})
|
||||||
tJob := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{ID: v.JobID, Type: database.ProvisionerJobTypeTemplateVersionImport})
|
tJob := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{ID: v.JobID, Type: database.ProvisionerJobTypeTemplateVersionImport})
|
||||||
|
|
||||||
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
|
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
|
||||||
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
|
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
|
||||||
wJob := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{ID: build.JobID, Type: database.ProvisionerJobTypeWorkspaceBuild})
|
wJob := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{ID: build.JobID, Type: database.ProvisionerJobTypeWorkspaceBuild})
|
||||||
check.Args([]uuid.UUID{tJob.ID, wJob.ID}).
|
check.Args([]uuid.UUID{tJob.ID, wJob.ID}).
|
||||||
Asserts(rbac.ResourceSystem, rbac.ActionRead).
|
Asserts(rbac.ResourceSystem, rbac.ActionRead).
|
||||||
Returns([]database.WorkspaceResource{})
|
Returns([]database.WorkspaceResource{})
|
||||||
|
@ -1462,7 +1462,7 @@ func (s *MethodTestSuite) TestSystemFunctions() {
|
||||||
s.Run("GetWorkspaceResourceMetadataByResourceIDs", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("GetWorkspaceResourceMetadataByResourceIDs", s.Subtest(func(db database.Store, check *expects) {
|
||||||
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
|
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
|
||||||
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
|
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
|
||||||
_ = dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{ID: build.JobID, Type: database.ProvisionerJobTypeWorkspaceBuild})
|
_ = dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{ID: build.JobID, Type: database.ProvisionerJobTypeWorkspaceBuild})
|
||||||
a := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID})
|
a := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID})
|
||||||
b := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID})
|
b := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID})
|
||||||
check.Args([]uuid.UUID{a.ID, b.ID}).
|
check.Args([]uuid.UUID{a.ID, b.ID}).
|
||||||
|
@ -1479,8 +1479,8 @@ func (s *MethodTestSuite) TestSystemFunctions() {
|
||||||
}))
|
}))
|
||||||
s.Run("GetProvisionerJobsByIDs", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("GetProvisionerJobsByIDs", s.Subtest(func(db database.Store, check *expects) {
|
||||||
// TODO: add a ProvisionerJob resource type
|
// TODO: add a ProvisionerJob resource type
|
||||||
a := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{})
|
a := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{})
|
||||||
b := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{})
|
b := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{})
|
||||||
check.Args([]uuid.UUID{a.ID, b.ID}).
|
check.Args([]uuid.UUID{a.ID, b.ID}).
|
||||||
Asserts( /*rbac.ResourceSystem, rbac.ActionRead*/ ).
|
Asserts( /*rbac.ResourceSystem, rbac.ActionRead*/ ).
|
||||||
Returns(slice.New(a, b))
|
Returns(slice.New(a, b))
|
||||||
|
@ -1514,7 +1514,7 @@ func (s *MethodTestSuite) TestSystemFunctions() {
|
||||||
}))
|
}))
|
||||||
s.Run("AcquireProvisionerJob", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("AcquireProvisionerJob", s.Subtest(func(db database.Store, check *expects) {
|
||||||
// TODO: we need to create a ProvisionerJob resource
|
// TODO: we need to create a ProvisionerJob resource
|
||||||
j := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{
|
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{
|
||||||
StartedAt: sql.NullTime{Valid: false},
|
StartedAt: sql.NullTime{Valid: false},
|
||||||
})
|
})
|
||||||
check.Args(database.AcquireProvisionerJobParams{Types: []database.ProvisionerType{j.Provisioner}, Tags: must(json.Marshal(j.Tags))}).
|
check.Args(database.AcquireProvisionerJobParams{Types: []database.ProvisionerType{j.Provisioner}, Tags: must(json.Marshal(j.Tags))}).
|
||||||
|
@ -1522,14 +1522,14 @@ func (s *MethodTestSuite) TestSystemFunctions() {
|
||||||
}))
|
}))
|
||||||
s.Run("UpdateProvisionerJobWithCompleteByID", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("UpdateProvisionerJobWithCompleteByID", s.Subtest(func(db database.Store, check *expects) {
|
||||||
// TODO: we need to create a ProvisionerJob resource
|
// TODO: we need to create a ProvisionerJob resource
|
||||||
j := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{})
|
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{})
|
||||||
check.Args(database.UpdateProvisionerJobWithCompleteByIDParams{
|
check.Args(database.UpdateProvisionerJobWithCompleteByIDParams{
|
||||||
ID: j.ID,
|
ID: j.ID,
|
||||||
}).Asserts( /*rbac.ResourceSystem, rbac.ActionUpdate*/ )
|
}).Asserts( /*rbac.ResourceSystem, rbac.ActionUpdate*/ )
|
||||||
}))
|
}))
|
||||||
s.Run("UpdateProvisionerJobByID", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("UpdateProvisionerJobByID", s.Subtest(func(db database.Store, check *expects) {
|
||||||
// TODO: we need to create a ProvisionerJob resource
|
// TODO: we need to create a ProvisionerJob resource
|
||||||
j := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{})
|
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{})
|
||||||
check.Args(database.UpdateProvisionerJobByIDParams{
|
check.Args(database.UpdateProvisionerJobByIDParams{
|
||||||
ID: j.ID,
|
ID: j.ID,
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: time.Now(),
|
||||||
|
@ -1546,7 +1546,7 @@ func (s *MethodTestSuite) TestSystemFunctions() {
|
||||||
}))
|
}))
|
||||||
s.Run("InsertProvisionerJobLogs", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("InsertProvisionerJobLogs", s.Subtest(func(db database.Store, check *expects) {
|
||||||
// TODO: we need to create a ProvisionerJob resource
|
// TODO: we need to create a ProvisionerJob resource
|
||||||
j := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{})
|
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{})
|
||||||
check.Args(database.InsertProvisionerJobLogsParams{
|
check.Args(database.InsertProvisionerJobLogsParams{
|
||||||
JobID: j.ID,
|
JobID: j.ID,
|
||||||
}).Asserts( /*rbac.ResourceSystem, rbac.ActionCreate*/ )
|
}).Asserts( /*rbac.ResourceSystem, rbac.ActionCreate*/ )
|
||||||
|
|
|
@ -19,6 +19,8 @@ import (
|
||||||
"github.com/coder/coder/v2/coderd/database"
|
"github.com/coder/coder/v2/coderd/database"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||||
|
"github.com/coder/coder/v2/coderd/database/provisionerjobs"
|
||||||
|
"github.com/coder/coder/v2/coderd/database/pubsub"
|
||||||
"github.com/coder/coder/v2/coderd/rbac"
|
"github.com/coder/coder/v2/coderd/rbac"
|
||||||
"github.com/coder/coder/v2/cryptorand"
|
"github.com/coder/coder/v2/cryptorand"
|
||||||
)
|
)
|
||||||
|
@ -315,8 +317,9 @@ func GroupMember(t testing.TB, db database.Store, orig database.GroupMember) dat
|
||||||
return member
|
return member
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProvisionerJob is a bit more involved to get the values such as "completedAt", "startedAt", "cancelledAt" set.
|
// ProvisionerJob is a bit more involved to get the values such as "completedAt", "startedAt", "cancelledAt" set. ps
|
||||||
func ProvisionerJob(t testing.TB, db database.Store, orig database.ProvisionerJob) database.ProvisionerJob {
|
// can be set to nil if you are SURE that you don't require a provisionerdaemon to acquire the job in your test.
|
||||||
|
func ProvisionerJob(t testing.TB, db database.Store, ps pubsub.Pubsub, orig database.ProvisionerJob) database.ProvisionerJob {
|
||||||
id := takeFirst(orig.ID, uuid.New())
|
id := takeFirst(orig.ID, uuid.New())
|
||||||
// Always set some tags to prevent Acquire from grabbing jobs it should not.
|
// Always set some tags to prevent Acquire from grabbing jobs it should not.
|
||||||
if !orig.StartedAt.Time.IsZero() {
|
if !orig.StartedAt.Time.IsZero() {
|
||||||
|
@ -341,7 +344,10 @@ func ProvisionerJob(t testing.TB, db database.Store, orig database.ProvisionerJo
|
||||||
Tags: orig.Tags,
|
Tags: orig.Tags,
|
||||||
})
|
})
|
||||||
require.NoError(t, err, "insert job")
|
require.NoError(t, err, "insert job")
|
||||||
|
if ps != nil {
|
||||||
|
err = provisionerjobs.PostJob(ps, job)
|
||||||
|
require.NoError(t, err, "post job to pubsub")
|
||||||
|
}
|
||||||
if !orig.StartedAt.Time.IsZero() {
|
if !orig.StartedAt.Time.IsZero() {
|
||||||
job, err = db.AcquireProvisionerJob(genCtx, database.AcquireProvisionerJobParams{
|
job, err = db.AcquireProvisionerJob(genCtx, database.AcquireProvisionerJobParams{
|
||||||
StartedAt: orig.StartedAt,
|
StartedAt: orig.StartedAt,
|
||||||
|
|
|
@ -86,7 +86,7 @@ func TestGenerator(t *testing.T) {
|
||||||
t.Run("Job", func(t *testing.T) {
|
t.Run("Job", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
db := dbfake.New()
|
db := dbfake.New()
|
||||||
exp := dbgen.ProvisionerJob(t, db, database.ProvisionerJob{})
|
exp := dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{})
|
||||||
require.Equal(t, exp, must(db.GetProvisionerJobByID(context.Background(), exp.ID)))
|
require.Equal(t, exp, must(db.GetProvisionerJobByID(context.Background(), exp.ID)))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package provisionerjobs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/coder/coder/v2/coderd/database"
|
||||||
|
"github.com/coder/coder/v2/coderd/database/pubsub"
|
||||||
|
)
|
||||||
|
|
||||||
|
const EventJobPosted = "provisioner_job_posted"
|
||||||
|
|
||||||
|
type JobPosting struct {
|
||||||
|
ProvisionerType database.ProvisionerType `json:"type"`
|
||||||
|
Tags map[string]string `json:"tags"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func PostJob(ps pubsub.Pubsub, job database.ProvisionerJob) error {
|
||||||
|
msg, err := json.Marshal(JobPosting{
|
||||||
|
ProvisionerType: job.Provisioner,
|
||||||
|
Tags: job.Tags,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("marshal job posting: %w", err)
|
||||||
|
}
|
||||||
|
err = ps.Publish(EventJobPosted, msg)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -103,7 +103,7 @@ func TestInsertWorkspaceAgentLogs(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
db := database.New(sqlDB)
|
db := database.New(sqlDB)
|
||||||
org := dbgen.Organization(t, db, database.Organization{})
|
org := dbgen.Organization(t, db, database.Organization{})
|
||||||
job := dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
job := dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
|
||||||
OrganizationID: org.ID,
|
OrganizationID: org.ID,
|
||||||
})
|
})
|
||||||
resource := dbgen.WorkspaceResource(t, db, database.WorkspaceResource{
|
resource := dbgen.WorkspaceResource(t, db, database.WorkspaceResource{
|
||||||
|
@ -335,7 +335,7 @@ func TestQueuePosition(t *testing.T) {
|
||||||
jobs := []database.ProvisionerJob{}
|
jobs := []database.ProvisionerJob{}
|
||||||
jobIDs := []uuid.UUID{}
|
jobIDs := []uuid.UUID{}
|
||||||
for i := 0; i < jobCount; i++ {
|
for i := 0; i < jobCount; i++ {
|
||||||
job := dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
job := dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
|
||||||
OrganizationID: org.ID,
|
OrganizationID: org.ID,
|
||||||
Tags: database.StringMap{},
|
Tags: database.StringMap{},
|
||||||
})
|
})
|
||||||
|
|
|
@ -83,7 +83,7 @@ func setup(t testing.TB, db database.Store, authToken uuid.UUID, mw func(http.Ha
|
||||||
OrganizationID: org.ID,
|
OrganizationID: org.ID,
|
||||||
TemplateID: template.ID,
|
TemplateID: template.ID,
|
||||||
})
|
})
|
||||||
job := dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
job := dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
|
||||||
OrganizationID: org.ID,
|
OrganizationID: org.ID,
|
||||||
})
|
})
|
||||||
resource := dbgen.WorkspaceResource(t, db, database.WorkspaceResource{
|
resource := dbgen.WorkspaceResource(t, db, database.WorkspaceResource{
|
||||||
|
|
|
@ -34,7 +34,7 @@ func TestWorkspaceAgentParam(t *testing.T) {
|
||||||
Transition: database.WorkspaceTransitionStart,
|
Transition: database.WorkspaceTransitionStart,
|
||||||
Reason: database.BuildReasonInitiator,
|
Reason: database.BuildReasonInitiator,
|
||||||
})
|
})
|
||||||
job = dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
job = dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
|
||||||
ID: build.JobID,
|
ID: build.JobID,
|
||||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||||
Provisioner: database.ProvisionerTypeEcho,
|
Provisioner: database.ProvisionerTypeEcho,
|
||||||
|
|
|
@ -363,7 +363,7 @@ func setupWorkspaceWithAgents(t testing.TB, cfg setupConfig) (database.Store, *h
|
||||||
Transition: database.WorkspaceTransitionStart,
|
Transition: database.WorkspaceTransitionStart,
|
||||||
Reason: database.BuildReasonInitiator,
|
Reason: database.BuildReasonInitiator,
|
||||||
})
|
})
|
||||||
job = dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
job = dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
|
||||||
ID: build.JobID,
|
ID: build.JobID,
|
||||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||||
Provisioner: database.ProvisionerTypeEcho,
|
Provisioner: database.ProvisionerTypeEcho,
|
||||||
|
|
|
@ -21,7 +21,7 @@ func TestWorkspaceResourceParam(t *testing.T) {
|
||||||
|
|
||||||
setup := func(t *testing.T, db database.Store, jobType database.ProvisionerJobType) (*http.Request, database.WorkspaceResource) {
|
setup := func(t *testing.T, db database.Store, jobType database.ProvisionerJobType) (*http.Request, database.WorkspaceResource) {
|
||||||
r := httptest.NewRequest("GET", "/", nil)
|
r := httptest.NewRequest("GET", "/", nil)
|
||||||
job := dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
job := dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
|
||||||
Type: jobType,
|
Type: jobType,
|
||||||
Provisioner: database.ProvisionerTypeEcho,
|
Provisioner: database.ProvisionerTypeEcho,
|
||||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||||
|
|
|
@ -15,14 +15,13 @@ import (
|
||||||
|
|
||||||
"cdr.dev/slog"
|
"cdr.dev/slog"
|
||||||
"github.com/coder/coder/v2/coderd/database"
|
"github.com/coder/coder/v2/coderd/database"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
|
||||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||||
|
"github.com/coder/coder/v2/coderd/database/provisionerjobs"
|
||||||
"github.com/coder/coder/v2/coderd/database/pubsub"
|
"github.com/coder/coder/v2/coderd/database/pubsub"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
EventJobPosted = "provisioner_job_posted"
|
dbMaxBackoff = 10 * time.Second
|
||||||
dbMaxBackoff = 10 * time.Second
|
|
||||||
// backPollDuration is the period for the backup polling described in Acquirer comment
|
// backPollDuration is the period for the backup polling described in Acquirer comment
|
||||||
backupPollDuration = 30 * time.Second
|
backupPollDuration = 30 * time.Second
|
||||||
)
|
)
|
||||||
|
@ -106,8 +105,6 @@ func (a *Acquirer) AcquireJob(
|
||||||
}
|
}
|
||||||
// buffer of 1 so that cancel doesn't deadlock while writing to the channel
|
// buffer of 1 so that cancel doesn't deadlock while writing to the channel
|
||||||
clearance := make(chan struct{}, 1)
|
clearance := make(chan struct{}, 1)
|
||||||
//nolint:gocritic // Provisionerd has specific authz rules.
|
|
||||||
principal := dbauthz.AsProvisionerd(ctx)
|
|
||||||
for {
|
for {
|
||||||
a.want(pt, tags, clearance)
|
a.want(pt, tags, clearance)
|
||||||
select {
|
select {
|
||||||
|
@ -122,7 +119,7 @@ func (a *Acquirer) AcquireJob(
|
||||||
return database.ProvisionerJob{}, err
|
return database.ProvisionerJob{}, err
|
||||||
case <-clearance:
|
case <-clearance:
|
||||||
logger.Debug(ctx, "got clearance to call database")
|
logger.Debug(ctx, "got clearance to call database")
|
||||||
job, err := a.store.AcquireProvisionerJob(principal, database.AcquireProvisionerJobParams{
|
job, err := a.store.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
|
||||||
StartedAt: sql.NullTime{
|
StartedAt: sql.NullTime{
|
||||||
Time: dbtime.Now(),
|
Time: dbtime.Now(),
|
||||||
Valid: true,
|
Valid: true,
|
||||||
|
@ -298,7 +295,7 @@ func (a *Acquirer) subscribe() {
|
||||||
bkoff := backoff.WithContext(eb, a.ctx)
|
bkoff := backoff.WithContext(eb, a.ctx)
|
||||||
var cancel context.CancelFunc
|
var cancel context.CancelFunc
|
||||||
err := backoff.Retry(func() error {
|
err := backoff.Retry(func() error {
|
||||||
cancelFn, err := a.ps.SubscribeWithErr(EventJobPosted, a.jobPosted)
|
cancelFn, err := a.ps.SubscribeWithErr(provisionerjobs.EventJobPosted, a.jobPosted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.logger.Warn(a.ctx, "failed to subscribe to job postings", slog.Error(err))
|
a.logger.Warn(a.ctx, "failed to subscribe to job postings", slog.Error(err))
|
||||||
return err
|
return err
|
||||||
|
@ -335,7 +332,7 @@ func (a *Acquirer) jobPosted(ctx context.Context, message []byte, err error) {
|
||||||
a.logger.Warn(a.ctx, "unhandled pubsub error", slog.Error(err))
|
a.logger.Warn(a.ctx, "unhandled pubsub error", slog.Error(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
posting := JobPosting{}
|
posting := provisionerjobs.JobPosting{}
|
||||||
err = json.Unmarshal(message, &posting)
|
err = json.Unmarshal(message, &posting)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.logger.Error(a.ctx, "unable to parse job posting",
|
a.logger.Error(a.ctx, "unable to parse job posting",
|
||||||
|
@ -457,7 +454,7 @@ type domain struct {
|
||||||
acquirees map[chan<- struct{}]*acquiree
|
acquirees map[chan<- struct{}]*acquiree
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d domain) contains(p JobPosting) bool {
|
func (d domain) contains(p provisionerjobs.JobPosting) bool {
|
||||||
if !slices.Contains(d.pt, p.ProvisionerType) {
|
if !slices.Contains(d.pt, p.ProvisionerType) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -485,8 +482,3 @@ func (d domain) poll(dur time.Duration) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type JobPosting struct {
|
|
||||||
ProvisionerType database.ProvisionerType `json:"type"`
|
|
||||||
Tags map[string]string `json:"tags"`
|
|
||||||
}
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"cdr.dev/slog/sloggers/slogtest"
|
"cdr.dev/slog/sloggers/slogtest"
|
||||||
"github.com/coder/coder/v2/coderd/database"
|
"github.com/coder/coder/v2/coderd/database"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbfake"
|
"github.com/coder/coder/v2/coderd/database/dbfake"
|
||||||
|
"github.com/coder/coder/v2/coderd/database/provisionerjobs"
|
||||||
"github.com/coder/coder/v2/coderd/database/pubsub"
|
"github.com/coder/coder/v2/coderd/database/pubsub"
|
||||||
"github.com/coder/coder/v2/coderd/provisionerdserver"
|
"github.com/coder/coder/v2/coderd/provisionerdserver"
|
||||||
"github.com/coder/coder/v2/testutil"
|
"github.com/coder/coder/v2/testutil"
|
||||||
|
@ -316,12 +317,12 @@ func TestAcquirer_UnblockOnCancel(t *testing.T) {
|
||||||
|
|
||||||
func postJob(t *testing.T, ps pubsub.Pubsub, pt database.ProvisionerType, tags provisionerdserver.Tags) {
|
func postJob(t *testing.T, ps pubsub.Pubsub, pt database.ProvisionerType, tags provisionerdserver.Tags) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
msg, err := json.Marshal(provisionerdserver.JobPosting{
|
msg, err := json.Marshal(provisionerjobs.JobPosting{
|
||||||
ProvisionerType: pt,
|
ProvisionerType: pt,
|
||||||
Tags: tags,
|
Tags: tags,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
err = ps.Publish(provisionerdserver.EventJobPosted, msg)
|
err = ps.Publish(provisionerjobs.EventJobPosted, msg)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -44,16 +43,18 @@ import (
|
||||||
sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
|
sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// DefaultAcquireJobLongPollDur is the time the (deprecated) AcquireJob rpc waits to try to obtain a job before
|
||||||
lastAcquire time.Time
|
// canceling and returning an empty job.
|
||||||
lastAcquireMutex sync.RWMutex
|
const DefaultAcquireJobLongPollDur = time.Second * 5
|
||||||
)
|
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
OIDCConfig httpmw.OAuth2Config
|
OIDCConfig httpmw.OAuth2Config
|
||||||
GitAuthConfigs []*gitauth.Config
|
GitAuthConfigs []*gitauth.Config
|
||||||
// TimeNowFn is only used in tests
|
// TimeNowFn is only used in tests
|
||||||
TimeNowFn func() time.Time
|
TimeNowFn func() time.Time
|
||||||
|
|
||||||
|
// AcquireJobLongPollDur is used in tests
|
||||||
|
AcquireJobLongPollDur time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
type server struct {
|
type server struct {
|
||||||
|
@ -62,9 +63,10 @@ type server struct {
|
||||||
Logger slog.Logger
|
Logger slog.Logger
|
||||||
Provisioners []database.ProvisionerType
|
Provisioners []database.ProvisionerType
|
||||||
GitAuthConfigs []*gitauth.Config
|
GitAuthConfigs []*gitauth.Config
|
||||||
Tags json.RawMessage
|
Tags Tags
|
||||||
Database database.Store
|
Database database.Store
|
||||||
Pubsub pubsub.Pubsub
|
Pubsub pubsub.Pubsub
|
||||||
|
Acquirer *Acquirer
|
||||||
Telemetry telemetry.Reporter
|
Telemetry telemetry.Reporter
|
||||||
Tracer trace.Tracer
|
Tracer trace.Tracer
|
||||||
QuotaCommitter *atomic.Pointer[proto.QuotaCommitter]
|
QuotaCommitter *atomic.Pointer[proto.QuotaCommitter]
|
||||||
|
@ -73,10 +75,11 @@ type server struct {
|
||||||
UserQuietHoursScheduleStore *atomic.Pointer[schedule.UserQuietHoursScheduleStore]
|
UserQuietHoursScheduleStore *atomic.Pointer[schedule.UserQuietHoursScheduleStore]
|
||||||
DeploymentValues *codersdk.DeploymentValues
|
DeploymentValues *codersdk.DeploymentValues
|
||||||
|
|
||||||
AcquireJobDebounce time.Duration
|
OIDCConfig httpmw.OAuth2Config
|
||||||
OIDCConfig httpmw.OAuth2Config
|
|
||||||
|
|
||||||
TimeNowFn func() time.Time
|
TimeNowFn func() time.Time
|
||||||
|
|
||||||
|
acquireJobLongPollDur time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use the null byte (0x00) in generating a canonical map key for tags, so
|
// We use the null byte (0x00) in generating a canonical map key for tags, so
|
||||||
|
@ -108,9 +111,10 @@ func NewServer(
|
||||||
id uuid.UUID,
|
id uuid.UUID,
|
||||||
logger slog.Logger,
|
logger slog.Logger,
|
||||||
provisioners []database.ProvisionerType,
|
provisioners []database.ProvisionerType,
|
||||||
tags json.RawMessage,
|
tags Tags,
|
||||||
db database.Store,
|
db database.Store,
|
||||||
ps pubsub.Pubsub,
|
ps pubsub.Pubsub,
|
||||||
|
acquirer *Acquirer,
|
||||||
tel telemetry.Reporter,
|
tel telemetry.Reporter,
|
||||||
tracer trace.Tracer,
|
tracer trace.Tracer,
|
||||||
quotaCommitter *atomic.Pointer[proto.QuotaCommitter],
|
quotaCommitter *atomic.Pointer[proto.QuotaCommitter],
|
||||||
|
@ -118,7 +122,6 @@ func NewServer(
|
||||||
templateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore],
|
templateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore],
|
||||||
userQuietHoursScheduleStore *atomic.Pointer[schedule.UserQuietHoursScheduleStore],
|
userQuietHoursScheduleStore *atomic.Pointer[schedule.UserQuietHoursScheduleStore],
|
||||||
deploymentValues *codersdk.DeploymentValues,
|
deploymentValues *codersdk.DeploymentValues,
|
||||||
acquireJobDebounce time.Duration,
|
|
||||||
options Options,
|
options Options,
|
||||||
) (proto.DRPCProvisionerDaemonServer, error) {
|
) (proto.DRPCProvisionerDaemonServer, error) {
|
||||||
// Panic early if pointers are nil
|
// Panic early if pointers are nil
|
||||||
|
@ -137,6 +140,18 @@ func NewServer(
|
||||||
if deploymentValues == nil {
|
if deploymentValues == nil {
|
||||||
return nil, xerrors.New("deploymentValues is nil")
|
return nil, xerrors.New("deploymentValues is nil")
|
||||||
}
|
}
|
||||||
|
if acquirer == nil {
|
||||||
|
return nil, xerrors.New("acquirer is nil")
|
||||||
|
}
|
||||||
|
if tags == nil {
|
||||||
|
return nil, xerrors.Errorf("tags is nil")
|
||||||
|
}
|
||||||
|
if err := tags.Valid(); err != nil {
|
||||||
|
return nil, xerrors.Errorf("invalid tags: %w", err)
|
||||||
|
}
|
||||||
|
if options.AcquireJobLongPollDur == 0 {
|
||||||
|
options.AcquireJobLongPollDur = DefaultAcquireJobLongPollDur
|
||||||
|
}
|
||||||
return &server{
|
return &server{
|
||||||
AccessURL: accessURL,
|
AccessURL: accessURL,
|
||||||
ID: id,
|
ID: id,
|
||||||
|
@ -146,6 +161,7 @@ func NewServer(
|
||||||
Tags: tags,
|
Tags: tags,
|
||||||
Database: db,
|
Database: db,
|
||||||
Pubsub: ps,
|
Pubsub: ps,
|
||||||
|
Acquirer: acquirer,
|
||||||
Telemetry: tel,
|
Telemetry: tel,
|
||||||
Tracer: tracer,
|
Tracer: tracer,
|
||||||
QuotaCommitter: quotaCommitter,
|
QuotaCommitter: quotaCommitter,
|
||||||
|
@ -153,9 +169,9 @@ func NewServer(
|
||||||
TemplateScheduleStore: templateScheduleStore,
|
TemplateScheduleStore: templateScheduleStore,
|
||||||
UserQuietHoursScheduleStore: userQuietHoursScheduleStore,
|
UserQuietHoursScheduleStore: userQuietHoursScheduleStore,
|
||||||
DeploymentValues: deploymentValues,
|
DeploymentValues: deploymentValues,
|
||||||
AcquireJobDebounce: acquireJobDebounce,
|
|
||||||
OIDCConfig: options.OIDCConfig,
|
OIDCConfig: options.OIDCConfig,
|
||||||
TimeNowFn: options.TimeNowFn,
|
TimeNowFn: options.TimeNowFn,
|
||||||
|
acquireJobLongPollDur: options.AcquireJobLongPollDur,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,50 +185,119 @@ func (s *server) timeNow() time.Time {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AcquireJob queries the database to lock a job.
|
// AcquireJob queries the database to lock a job.
|
||||||
|
//
|
||||||
|
// Deprecated: This method is only available for back-level provisioner daemons.
|
||||||
func (s *server) AcquireJob(ctx context.Context, _ *proto.Empty) (*proto.AcquiredJob, error) {
|
func (s *server) AcquireJob(ctx context.Context, _ *proto.Empty) (*proto.AcquiredJob, error) {
|
||||||
//nolint:gocritic // Provisionerd has specific authz rules.
|
//nolint:gocritic // Provisionerd has specific authz rules.
|
||||||
ctx = dbauthz.AsProvisionerd(ctx)
|
ctx = dbauthz.AsProvisionerd(ctx)
|
||||||
// This prevents loads of provisioner daemons from consistently
|
// Since AcquireJob blocks until a job is available, we set a long (5s by default) timeout. This allows back-level
|
||||||
// querying the database when no jobs are available.
|
// provisioner daemons to gracefully shut down within a few seconds, but keeps them from rapidly polling the
|
||||||
//
|
// database.
|
||||||
// The debounce only occurs when no job is returned, so if loads of
|
acqCtx, acqCancel := context.WithTimeout(ctx, s.acquireJobLongPollDur)
|
||||||
// jobs are added at once, they will start after at most this duration.
|
defer acqCancel()
|
||||||
lastAcquireMutex.RLock()
|
job, err := s.Acquirer.AcquireJob(acqCtx, s.ID, s.Provisioners, s.Tags)
|
||||||
if !lastAcquire.IsZero() && time.Since(lastAcquire) < s.AcquireJobDebounce {
|
if xerrors.Is(err, context.DeadlineExceeded) {
|
||||||
s.Logger.Debug(ctx, "debounce acquire job", slog.F("debounce", s.AcquireJobDebounce), slog.F("last_acquire", lastAcquire))
|
s.Logger.Debug(ctx, "successful cancel")
|
||||||
lastAcquireMutex.RUnlock()
|
|
||||||
return &proto.AcquiredJob{}, nil
|
|
||||||
}
|
|
||||||
lastAcquireMutex.RUnlock()
|
|
||||||
// This marks the job as locked in the database.
|
|
||||||
job, err := s.Database.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
|
|
||||||
StartedAt: sql.NullTime{
|
|
||||||
Time: dbtime.Now(),
|
|
||||||
Valid: true,
|
|
||||||
},
|
|
||||||
WorkerID: uuid.NullUUID{
|
|
||||||
UUID: s.ID,
|
|
||||||
Valid: true,
|
|
||||||
},
|
|
||||||
Types: s.Provisioners,
|
|
||||||
Tags: s.Tags,
|
|
||||||
})
|
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
|
||||||
// The provisioner daemon assumes no jobs are available if
|
|
||||||
// an empty struct is returned.
|
|
||||||
lastAcquireMutex.Lock()
|
|
||||||
lastAcquire = dbtime.Now()
|
|
||||||
lastAcquireMutex.Unlock()
|
|
||||||
return &proto.AcquiredJob{}, nil
|
return &proto.AcquiredJob{}, nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("acquire job: %w", err)
|
return nil, xerrors.Errorf("acquire job: %w", err)
|
||||||
}
|
}
|
||||||
s.Logger.Debug(ctx, "locked job from database", slog.F("job_id", job.ID))
|
s.Logger.Debug(ctx, "locked job from database", slog.F("job_id", job.ID))
|
||||||
|
return s.acquireProtoJob(ctx, job)
|
||||||
|
}
|
||||||
|
|
||||||
|
type jobAndErr struct {
|
||||||
|
job database.ProvisionerJob
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// AcquireJobWithCancel queries the database to lock a job.
|
||||||
|
func (s *server) AcquireJobWithCancel(stream proto.DRPCProvisionerDaemon_AcquireJobWithCancelStream) (retErr error) {
|
||||||
|
//nolint:gocritic // Provisionerd has specific authz rules.
|
||||||
|
streamCtx := dbauthz.AsProvisionerd(stream.Context())
|
||||||
|
defer func() {
|
||||||
|
closeErr := stream.Close()
|
||||||
|
s.Logger.Debug(streamCtx, "closed stream", slog.Error(closeErr))
|
||||||
|
if retErr == nil {
|
||||||
|
retErr = closeErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
acqCtx, acqCancel := context.WithCancel(streamCtx)
|
||||||
|
defer acqCancel()
|
||||||
|
recvCh := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
_, err := stream.Recv() // cancel is the only message
|
||||||
|
recvCh <- err
|
||||||
|
}()
|
||||||
|
jec := make(chan jobAndErr, 1)
|
||||||
|
go func() {
|
||||||
|
job, err := s.Acquirer.AcquireJob(acqCtx, s.ID, s.Provisioners, s.Tags)
|
||||||
|
jec <- jobAndErr{job: job, err: err}
|
||||||
|
}()
|
||||||
|
var recvErr error
|
||||||
|
var je jobAndErr
|
||||||
|
select {
|
||||||
|
case recvErr = <-recvCh:
|
||||||
|
acqCancel()
|
||||||
|
je = <-jec
|
||||||
|
case je = <-jec:
|
||||||
|
}
|
||||||
|
if xerrors.Is(je.err, context.Canceled) {
|
||||||
|
s.Logger.Debug(streamCtx, "successful cancel")
|
||||||
|
err := stream.Send(&proto.AcquiredJob{})
|
||||||
|
if err != nil {
|
||||||
|
// often this is just because the other side hangs up and doesn't wait for the cancel, so log at INFO
|
||||||
|
s.Logger.Info(streamCtx, "failed to send empty job", slog.Error(err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if je.err != nil {
|
||||||
|
return xerrors.Errorf("acquire job: %w", je.err)
|
||||||
|
}
|
||||||
|
logger := s.Logger.With(slog.F("job_id", je.job.ID))
|
||||||
|
logger.Debug(streamCtx, "locked job from database")
|
||||||
|
|
||||||
|
if recvErr != nil {
|
||||||
|
logger.Error(streamCtx, "recv error and failed to cancel acquire job", slog.Error(recvErr))
|
||||||
|
// Well, this is awkward. We hit an error receiving from the stream, but didn't cancel before we locked a job
|
||||||
|
// in the database. We need to mark this job as failed so the end user can retry if they want to.
|
||||||
|
err := s.Database.UpdateProvisionerJobWithCompleteByID(
|
||||||
|
context.Background(),
|
||||||
|
database.UpdateProvisionerJobWithCompleteByIDParams{
|
||||||
|
ID: je.job.ID,
|
||||||
|
CompletedAt: sql.NullTime{
|
||||||
|
Time: dbtime.Now(),
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
Error: sql.NullString{
|
||||||
|
String: "connection to provisioner daemon broken",
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(streamCtx, "error updating failed job", slog.Error(err))
|
||||||
|
}
|
||||||
|
return recvErr
|
||||||
|
}
|
||||||
|
|
||||||
|
pj, err := s.acquireProtoJob(streamCtx, je.job)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = stream.Send(pj)
|
||||||
|
if err != nil {
|
||||||
|
s.Logger.Error(streamCtx, "failed to send job", slog.Error(err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) acquireProtoJob(ctx context.Context, job database.ProvisionerJob) (*proto.AcquiredJob, error) {
|
||||||
// Marks the acquired job as failed with the error message provided.
|
// Marks the acquired job as failed with the error message provided.
|
||||||
failJob := func(errorMessage string) error {
|
failJob := func(errorMessage string) error {
|
||||||
err = s.Database.UpdateProvisionerJobWithCompleteByID(ctx, database.UpdateProvisionerJobWithCompleteByIDParams{
|
err := s.Database.UpdateProvisionerJobWithCompleteByID(ctx, database.UpdateProvisionerJobWithCompleteByIDParams{
|
||||||
ID: job.ID,
|
ID: job.ID,
|
||||||
CompletedAt: sql.NullTime{
|
CompletedAt: sql.NullTime{
|
||||||
Time: dbtime.Now(),
|
Time: dbtime.Now(),
|
||||||
|
|
|
@ -4,12 +4,19 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"io"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
"storj.io/drpc"
|
||||||
|
|
||||||
|
"cdr.dev/slog"
|
||||||
|
|
||||||
"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"
|
||||||
|
@ -57,400 +64,411 @@ func testUserQuietHoursScheduleStore() *atomic.Pointer[schedule.UserQuietHoursSc
|
||||||
return ptr
|
return ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAcquireJob_LongPoll(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
srv, _, _ := setup(t, false, &overrides{acquireJobLongPollDuration: time.Microsecond})
|
||||||
|
job, err := srv.AcquireJob(context.Background(), nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, &proto.AcquiredJob{}, job)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAcquireJobWithCancel_Cancel(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
srv, _, _ := setup(t, false, nil)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||||
|
defer cancel()
|
||||||
|
fs := newFakeStream(ctx)
|
||||||
|
errCh := make(chan error)
|
||||||
|
go func() {
|
||||||
|
errCh <- srv.AcquireJobWithCancel(fs)
|
||||||
|
}()
|
||||||
|
fs.cancel()
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
t.Fatal("timed out waiting for AcquireJobWithCancel")
|
||||||
|
case err := <-errCh:
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
job, err := fs.waitForJob()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, job)
|
||||||
|
require.Equal(t, "", job.JobId)
|
||||||
|
}
|
||||||
|
|
||||||
func TestAcquireJob(t *testing.T) {
|
func TestAcquireJob(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
t.Run("Debounce", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
db := dbfake.New()
|
|
||||||
ps := pubsub.NewInMemory()
|
|
||||||
srv, err := provisionerdserver.NewServer(
|
|
||||||
&url.URL{},
|
|
||||||
uuid.New(),
|
|
||||||
slogtest.Make(t, nil),
|
|
||||||
[]database.ProvisionerType{database.ProvisionerTypeEcho},
|
|
||||||
nil,
|
|
||||||
db,
|
|
||||||
ps,
|
|
||||||
telemetry.NewNoop(),
|
|
||||||
trace.NewNoopTracerProvider().Tracer("noop"),
|
|
||||||
&atomic.Pointer[proto.QuotaCommitter]{},
|
|
||||||
mockAuditor(),
|
|
||||||
testTemplateScheduleStore(),
|
|
||||||
testUserQuietHoursScheduleStore(),
|
|
||||||
&codersdk.DeploymentValues{},
|
|
||||||
time.Hour,
|
|
||||||
provisionerdserver.Options{},
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
|
||||||
job, err := srv.AcquireJob(context.Background(), nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, &proto.AcquiredJob{}, job)
|
|
||||||
_, err = db.InsertProvisionerJob(context.Background(), database.InsertProvisionerJobParams{
|
|
||||||
ID: uuid.New(),
|
|
||||||
InitiatorID: uuid.New(),
|
|
||||||
Provisioner: database.ProvisionerTypeEcho,
|
|
||||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
|
||||||
Type: database.ProvisionerJobTypeTemplateVersionDryRun,
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
job, err = srv.AcquireJob(context.Background(), nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, &proto.AcquiredJob{}, job)
|
|
||||||
})
|
|
||||||
t.Run("NoJobs", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
srv, _, _ := setup(t, false, nil)
|
|
||||||
job, err := srv.AcquireJob(context.Background(), nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, &proto.AcquiredJob{}, job)
|
|
||||||
})
|
|
||||||
t.Run("InitiatorNotFound", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
srv, db, _ := setup(t, false, nil)
|
|
||||||
_, err := db.InsertProvisionerJob(context.Background(), database.InsertProvisionerJobParams{
|
|
||||||
ID: uuid.New(),
|
|
||||||
InitiatorID: uuid.New(),
|
|
||||||
Provisioner: database.ProvisionerTypeEcho,
|
|
||||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
|
||||||
Type: database.ProvisionerJobTypeTemplateVersionDryRun,
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
_, err = srv.AcquireJob(context.Background(), nil)
|
|
||||||
require.ErrorContains(t, err, "sql: no rows in result set")
|
|
||||||
})
|
|
||||||
t.Run("WorkspaceBuildJob", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
// Set the max session token lifetime so we can assert we
|
|
||||||
// create an API key with an expiration within the bounds of the
|
|
||||||
// deployment config.
|
|
||||||
dv := &codersdk.DeploymentValues{MaxTokenLifetime: clibase.Duration(time.Hour)}
|
|
||||||
gitAuthProvider := "github"
|
|
||||||
srv, db, ps := setup(t, false, &overrides{
|
|
||||||
deploymentValues: dv,
|
|
||||||
gitAuthConfigs: []*gitauth.Config{{
|
|
||||||
ID: gitAuthProvider,
|
|
||||||
OAuth2Config: &testutil.OAuth2Config{},
|
|
||||||
}},
|
|
||||||
})
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
user := dbgen.User(t, db, database.User{})
|
// These test acquiring a single job without canceling, and tests both AcquireJob (deprecated) and
|
||||||
link := dbgen.UserLink(t, db, database.UserLink{
|
// AcquireJobWithCancel as the way to get the job.
|
||||||
LoginType: database.LoginTypeOIDC,
|
cases := []struct {
|
||||||
UserID: user.ID,
|
name string
|
||||||
OAuthExpiry: dbtime.Now().Add(time.Hour),
|
acquire func(context.Context, proto.DRPCProvisionerDaemonServer) (*proto.AcquiredJob, error)
|
||||||
OAuthAccessToken: "access-token",
|
}{
|
||||||
})
|
{name: "Deprecated", acquire: func(ctx context.Context, srv proto.DRPCProvisionerDaemonServer) (*proto.AcquiredJob, error) {
|
||||||
dbgen.GitAuthLink(t, db, database.GitAuthLink{
|
return srv.AcquireJob(ctx, nil)
|
||||||
ProviderID: gitAuthProvider,
|
}},
|
||||||
UserID: user.ID,
|
{name: "WithCancel", acquire: func(ctx context.Context, srv proto.DRPCProvisionerDaemonServer) (*proto.AcquiredJob, error) {
|
||||||
})
|
fs := newFakeStream(ctx)
|
||||||
template := dbgen.Template(t, db, database.Template{
|
err := srv.AcquireJobWithCancel(fs)
|
||||||
Name: "template",
|
if err != nil {
|
||||||
Provisioner: database.ProvisionerTypeEcho,
|
return nil, err
|
||||||
})
|
|
||||||
file := dbgen.File(t, db, database.File{CreatedBy: user.ID})
|
|
||||||
versionFile := dbgen.File(t, db, database.File{CreatedBy: user.ID})
|
|
||||||
version := dbgen.TemplateVersion(t, db, database.TemplateVersion{
|
|
||||||
TemplateID: uuid.NullUUID{
|
|
||||||
UUID: template.ID,
|
|
||||||
Valid: true,
|
|
||||||
},
|
|
||||||
JobID: uuid.New(),
|
|
||||||
})
|
|
||||||
err := db.UpdateTemplateVersionGitAuthProvidersByJobID(ctx, database.UpdateTemplateVersionGitAuthProvidersByJobIDParams{
|
|
||||||
JobID: version.JobID,
|
|
||||||
GitAuthProviders: []string{gitAuthProvider},
|
|
||||||
UpdatedAt: dbtime.Now(),
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
// Import version job
|
|
||||||
_ = dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
|
||||||
ID: version.JobID,
|
|
||||||
InitiatorID: user.ID,
|
|
||||||
FileID: versionFile.ID,
|
|
||||||
Provisioner: database.ProvisionerTypeEcho,
|
|
||||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
|
||||||
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
|
||||||
Input: must(json.Marshal(provisionerdserver.TemplateVersionImportJob{
|
|
||||||
TemplateVersionID: version.ID,
|
|
||||||
UserVariableValues: []codersdk.VariableValue{
|
|
||||||
{Name: "second", Value: "bah"},
|
|
||||||
},
|
|
||||||
})),
|
|
||||||
})
|
|
||||||
_ = dbgen.TemplateVersionVariable(t, db, database.TemplateVersionVariable{
|
|
||||||
TemplateVersionID: version.ID,
|
|
||||||
Name: "first",
|
|
||||||
Value: "first_value",
|
|
||||||
DefaultValue: "default_value",
|
|
||||||
Sensitive: true,
|
|
||||||
})
|
|
||||||
_ = dbgen.TemplateVersionVariable(t, db, database.TemplateVersionVariable{
|
|
||||||
TemplateVersionID: version.ID,
|
|
||||||
Name: "second",
|
|
||||||
Value: "second_value",
|
|
||||||
DefaultValue: "default_value",
|
|
||||||
Required: true,
|
|
||||||
Sensitive: false,
|
|
||||||
})
|
|
||||||
workspace := dbgen.Workspace(t, db, database.Workspace{
|
|
||||||
TemplateID: template.ID,
|
|
||||||
OwnerID: user.ID,
|
|
||||||
})
|
|
||||||
build := dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
|
|
||||||
WorkspaceID: workspace.ID,
|
|
||||||
BuildNumber: 1,
|
|
||||||
JobID: uuid.New(),
|
|
||||||
TemplateVersionID: version.ID,
|
|
||||||
Transition: database.WorkspaceTransitionStart,
|
|
||||||
Reason: database.BuildReasonInitiator,
|
|
||||||
})
|
|
||||||
_ = dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
|
||||||
ID: build.ID,
|
|
||||||
InitiatorID: user.ID,
|
|
||||||
Provisioner: database.ProvisionerTypeEcho,
|
|
||||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
|
||||||
FileID: file.ID,
|
|
||||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
|
||||||
Input: must(json.Marshal(provisionerdserver.WorkspaceProvisionJob{
|
|
||||||
WorkspaceBuildID: build.ID,
|
|
||||||
})),
|
|
||||||
})
|
|
||||||
|
|
||||||
startPublished := make(chan struct{})
|
|
||||||
var closed bool
|
|
||||||
closeStartSubscribe, err := ps.Subscribe(codersdk.WorkspaceNotifyChannel(workspace.ID), func(_ context.Context, _ []byte) {
|
|
||||||
if !closed {
|
|
||||||
close(startPublished)
|
|
||||||
closed = true
|
|
||||||
}
|
}
|
||||||
|
return fs.waitForJob()
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
for _, tc := range cases {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.name+"_InitiatorNotFound", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
srv, db, _ := setup(t, false, nil)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||||
|
defer cancel()
|
||||||
|
_, err := db.InsertProvisionerJob(context.Background(), database.InsertProvisionerJobParams{
|
||||||
|
ID: uuid.New(),
|
||||||
|
InitiatorID: uuid.New(),
|
||||||
|
Provisioner: database.ProvisionerTypeEcho,
|
||||||
|
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||||
|
Type: database.ProvisionerJobTypeTemplateVersionDryRun,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, err = tc.acquire(ctx, srv)
|
||||||
|
require.ErrorContains(t, err, "sql: no rows in result set")
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
t.Run(tc.name+"_WorkspaceBuildJob", func(t *testing.T) {
|
||||||
defer closeStartSubscribe()
|
t.Parallel()
|
||||||
|
// Set the max session token lifetime so we can assert we
|
||||||
|
// create an API key with an expiration within the bounds of the
|
||||||
|
// deployment config.
|
||||||
|
dv := &codersdk.DeploymentValues{MaxTokenLifetime: clibase.Duration(time.Hour)}
|
||||||
|
gitAuthProvider := "github"
|
||||||
|
srv, db, ps := setup(t, false, &overrides{
|
||||||
|
deploymentValues: dv,
|
||||||
|
gitAuthConfigs: []*gitauth.Config{{
|
||||||
|
ID: gitAuthProvider,
|
||||||
|
OAuth2Config: &testutil.OAuth2Config{},
|
||||||
|
}},
|
||||||
|
})
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
var job *proto.AcquiredJob
|
user := dbgen.User(t, db, database.User{})
|
||||||
|
link := dbgen.UserLink(t, db, database.UserLink{
|
||||||
|
LoginType: database.LoginTypeOIDC,
|
||||||
|
UserID: user.ID,
|
||||||
|
OAuthExpiry: dbtime.Now().Add(time.Hour),
|
||||||
|
OAuthAccessToken: "access-token",
|
||||||
|
})
|
||||||
|
dbgen.GitAuthLink(t, db, database.GitAuthLink{
|
||||||
|
ProviderID: gitAuthProvider,
|
||||||
|
UserID: user.ID,
|
||||||
|
})
|
||||||
|
template := dbgen.Template(t, db, database.Template{
|
||||||
|
Name: "template",
|
||||||
|
Provisioner: database.ProvisionerTypeEcho,
|
||||||
|
})
|
||||||
|
file := dbgen.File(t, db, database.File{CreatedBy: user.ID})
|
||||||
|
versionFile := dbgen.File(t, db, database.File{CreatedBy: user.ID})
|
||||||
|
version := dbgen.TemplateVersion(t, db, database.TemplateVersion{
|
||||||
|
TemplateID: uuid.NullUUID{
|
||||||
|
UUID: template.ID,
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
JobID: uuid.New(),
|
||||||
|
})
|
||||||
|
err := db.UpdateTemplateVersionGitAuthProvidersByJobID(ctx, database.UpdateTemplateVersionGitAuthProvidersByJobIDParams{
|
||||||
|
JobID: version.JobID,
|
||||||
|
GitAuthProviders: []string{gitAuthProvider},
|
||||||
|
UpdatedAt: dbtime.Now(),
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
// Import version job
|
||||||
|
_ = dbgen.ProvisionerJob(t, db, ps, database.ProvisionerJob{
|
||||||
|
ID: version.JobID,
|
||||||
|
InitiatorID: user.ID,
|
||||||
|
FileID: versionFile.ID,
|
||||||
|
Provisioner: database.ProvisionerTypeEcho,
|
||||||
|
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||||
|
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
||||||
|
Input: must(json.Marshal(provisionerdserver.TemplateVersionImportJob{
|
||||||
|
TemplateVersionID: version.ID,
|
||||||
|
UserVariableValues: []codersdk.VariableValue{
|
||||||
|
{Name: "second", Value: "bah"},
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
})
|
||||||
|
_ = dbgen.TemplateVersionVariable(t, db, database.TemplateVersionVariable{
|
||||||
|
TemplateVersionID: version.ID,
|
||||||
|
Name: "first",
|
||||||
|
Value: "first_value",
|
||||||
|
DefaultValue: "default_value",
|
||||||
|
Sensitive: true,
|
||||||
|
})
|
||||||
|
_ = dbgen.TemplateVersionVariable(t, db, database.TemplateVersionVariable{
|
||||||
|
TemplateVersionID: version.ID,
|
||||||
|
Name: "second",
|
||||||
|
Value: "second_value",
|
||||||
|
DefaultValue: "default_value",
|
||||||
|
Required: true,
|
||||||
|
Sensitive: false,
|
||||||
|
})
|
||||||
|
workspace := dbgen.Workspace(t, db, database.Workspace{
|
||||||
|
TemplateID: template.ID,
|
||||||
|
OwnerID: user.ID,
|
||||||
|
})
|
||||||
|
build := dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
|
||||||
|
WorkspaceID: workspace.ID,
|
||||||
|
BuildNumber: 1,
|
||||||
|
JobID: uuid.New(),
|
||||||
|
TemplateVersionID: version.ID,
|
||||||
|
Transition: database.WorkspaceTransitionStart,
|
||||||
|
Reason: database.BuildReasonInitiator,
|
||||||
|
})
|
||||||
|
_ = dbgen.ProvisionerJob(t, db, ps, database.ProvisionerJob{
|
||||||
|
ID: build.ID,
|
||||||
|
InitiatorID: user.ID,
|
||||||
|
Provisioner: database.ProvisionerTypeEcho,
|
||||||
|
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||||
|
FileID: file.ID,
|
||||||
|
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||||
|
Input: must(json.Marshal(provisionerdserver.WorkspaceProvisionJob{
|
||||||
|
WorkspaceBuildID: build.ID,
|
||||||
|
})),
|
||||||
|
})
|
||||||
|
|
||||||
|
startPublished := make(chan struct{})
|
||||||
|
var closed bool
|
||||||
|
closeStartSubscribe, err := ps.Subscribe(codersdk.WorkspaceNotifyChannel(workspace.ID), func(_ context.Context, _ []byte) {
|
||||||
|
if !closed {
|
||||||
|
close(startPublished)
|
||||||
|
closed = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer closeStartSubscribe()
|
||||||
|
|
||||||
|
var job *proto.AcquiredJob
|
||||||
|
|
||||||
|
for {
|
||||||
|
// Grab jobs until we find the workspace build job. There is also
|
||||||
|
// an import version job that we need to ignore.
|
||||||
|
job, err = tc.acquire(ctx, srv)
|
||||||
|
require.NoError(t, err)
|
||||||
|
if _, ok := job.Type.(*proto.AcquiredJob_WorkspaceBuild_); ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<-startPublished
|
||||||
|
|
||||||
|
got, err := json.Marshal(job.Type)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Validate that a session token is generated during the job.
|
||||||
|
sessionToken := job.Type.(*proto.AcquiredJob_WorkspaceBuild_).WorkspaceBuild.Metadata.WorkspaceOwnerSessionToken
|
||||||
|
require.NotEmpty(t, sessionToken)
|
||||||
|
toks := strings.Split(sessionToken, "-")
|
||||||
|
require.Len(t, toks, 2, "invalid api key")
|
||||||
|
key, err := db.GetAPIKeyByID(ctx, toks[0])
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, int64(dv.MaxTokenLifetime.Value().Seconds()), key.LifetimeSeconds)
|
||||||
|
require.WithinDuration(t, time.Now().Add(dv.MaxTokenLifetime.Value()), key.ExpiresAt, time.Minute)
|
||||||
|
|
||||||
|
want, err := json.Marshal(&proto.AcquiredJob_WorkspaceBuild_{
|
||||||
|
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
||||||
|
WorkspaceBuildId: build.ID.String(),
|
||||||
|
WorkspaceName: workspace.Name,
|
||||||
|
VariableValues: []*sdkproto.VariableValue{
|
||||||
|
{
|
||||||
|
Name: "first",
|
||||||
|
Value: "first_value",
|
||||||
|
Sensitive: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "second",
|
||||||
|
Value: "second_value",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
GitAuthProviders: []*sdkproto.GitAuthProvider{{
|
||||||
|
Id: gitAuthProvider,
|
||||||
|
AccessToken: "access_token",
|
||||||
|
}},
|
||||||
|
Metadata: &sdkproto.Metadata{
|
||||||
|
CoderUrl: (&url.URL{}).String(),
|
||||||
|
WorkspaceTransition: sdkproto.WorkspaceTransition_START,
|
||||||
|
WorkspaceName: workspace.Name,
|
||||||
|
WorkspaceOwner: user.Username,
|
||||||
|
WorkspaceOwnerEmail: user.Email,
|
||||||
|
WorkspaceOwnerOidcAccessToken: link.OAuthAccessToken,
|
||||||
|
WorkspaceId: workspace.ID.String(),
|
||||||
|
WorkspaceOwnerId: user.ID.String(),
|
||||||
|
TemplateId: template.ID.String(),
|
||||||
|
TemplateName: template.Name,
|
||||||
|
TemplateVersion: version.Name,
|
||||||
|
WorkspaceOwnerSessionToken: sessionToken,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.JSONEq(t, string(want), string(got))
|
||||||
|
|
||||||
|
// Assert that we delete the session token whenever
|
||||||
|
// a stop is issued.
|
||||||
|
stopbuild := dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
|
||||||
|
WorkspaceID: workspace.ID,
|
||||||
|
BuildNumber: 2,
|
||||||
|
JobID: uuid.New(),
|
||||||
|
TemplateVersionID: version.ID,
|
||||||
|
Transition: database.WorkspaceTransitionStop,
|
||||||
|
Reason: database.BuildReasonInitiator,
|
||||||
|
})
|
||||||
|
_ = dbgen.ProvisionerJob(t, db, ps, database.ProvisionerJob{
|
||||||
|
ID: stopbuild.ID,
|
||||||
|
InitiatorID: user.ID,
|
||||||
|
Provisioner: database.ProvisionerTypeEcho,
|
||||||
|
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||||
|
FileID: file.ID,
|
||||||
|
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||||
|
Input: must(json.Marshal(provisionerdserver.WorkspaceProvisionJob{
|
||||||
|
WorkspaceBuildID: stopbuild.ID,
|
||||||
|
})),
|
||||||
|
})
|
||||||
|
|
||||||
|
stopPublished := make(chan struct{})
|
||||||
|
closeStopSubscribe, err := ps.Subscribe(codersdk.WorkspaceNotifyChannel(workspace.ID), func(_ context.Context, _ []byte) {
|
||||||
|
close(stopPublished)
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer closeStopSubscribe()
|
||||||
|
|
||||||
for {
|
|
||||||
// Grab jobs until we find the workspace build job. There is also
|
// Grab jobs until we find the workspace build job. There is also
|
||||||
// an import version job that we need to ignore.
|
// an import version job that we need to ignore.
|
||||||
job, err = srv.AcquireJob(ctx, nil)
|
job, err = tc.acquire(ctx, srv)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
if _, ok := job.Type.(*proto.AcquiredJob_WorkspaceBuild_); ok {
|
_, ok := job.Type.(*proto.AcquiredJob_WorkspaceBuild_)
|
||||||
break
|
require.True(t, ok, "acquired job not a workspace build?")
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<-startPublished
|
<-stopPublished
|
||||||
|
|
||||||
got, err := json.Marshal(job.Type)
|
// Validate that a session token is deleted during a stop job.
|
||||||
require.NoError(t, err)
|
sessionToken = job.Type.(*proto.AcquiredJob_WorkspaceBuild_).WorkspaceBuild.Metadata.WorkspaceOwnerSessionToken
|
||||||
|
require.Empty(t, sessionToken)
|
||||||
|
_, err = db.GetAPIKeyByID(ctx, key.ID)
|
||||||
|
require.ErrorIs(t, err, sql.ErrNoRows)
|
||||||
|
})
|
||||||
|
|
||||||
// Validate that a session token is generated during the job.
|
t.Run(tc.name+"_TemplateVersionDryRun", func(t *testing.T) {
|
||||||
sessionToken := job.Type.(*proto.AcquiredJob_WorkspaceBuild_).WorkspaceBuild.Metadata.WorkspaceOwnerSessionToken
|
t.Parallel()
|
||||||
require.NotEmpty(t, sessionToken)
|
srv, db, ps := setup(t, false, nil)
|
||||||
toks := strings.Split(sessionToken, "-")
|
ctx := context.Background()
|
||||||
require.Len(t, toks, 2, "invalid api key")
|
|
||||||
key, err := db.GetAPIKeyByID(ctx, toks[0])
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, int64(dv.MaxTokenLifetime.Value().Seconds()), key.LifetimeSeconds)
|
|
||||||
require.WithinDuration(t, time.Now().Add(dv.MaxTokenLifetime.Value()), key.ExpiresAt, time.Minute)
|
|
||||||
|
|
||||||
want, err := json.Marshal(&proto.AcquiredJob_WorkspaceBuild_{
|
user := dbgen.User(t, db, database.User{})
|
||||||
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
version := dbgen.TemplateVersion(t, db, database.TemplateVersion{})
|
||||||
WorkspaceBuildId: build.ID.String(),
|
file := dbgen.File(t, db, database.File{CreatedBy: user.ID})
|
||||||
WorkspaceName: workspace.Name,
|
_ = dbgen.ProvisionerJob(t, db, ps, database.ProvisionerJob{
|
||||||
VariableValues: []*sdkproto.VariableValue{
|
InitiatorID: user.ID,
|
||||||
{
|
Provisioner: database.ProvisionerTypeEcho,
|
||||||
Name: "first",
|
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||||
Value: "first_value",
|
FileID: file.ID,
|
||||||
Sensitive: true,
|
Type: database.ProvisionerJobTypeTemplateVersionDryRun,
|
||||||
},
|
Input: must(json.Marshal(provisionerdserver.TemplateVersionDryRunJob{
|
||||||
{
|
TemplateVersionID: version.ID,
|
||||||
Name: "second",
|
WorkspaceName: "testing",
|
||||||
Value: "second_value",
|
})),
|
||||||
|
})
|
||||||
|
|
||||||
|
job, err := tc.acquire(ctx, srv)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
got, err := json.Marshal(job.Type)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
want, err := json.Marshal(&proto.AcquiredJob_TemplateDryRun_{
|
||||||
|
TemplateDryRun: &proto.AcquiredJob_TemplateDryRun{
|
||||||
|
Metadata: &sdkproto.Metadata{
|
||||||
|
CoderUrl: (&url.URL{}).String(),
|
||||||
|
WorkspaceName: "testing",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
GitAuthProviders: []*sdkproto.GitAuthProvider{{
|
})
|
||||||
Id: gitAuthProvider,
|
require.NoError(t, err)
|
||||||
AccessToken: "access_token",
|
require.JSONEq(t, string(want), string(got))
|
||||||
}},
|
})
|
||||||
Metadata: &sdkproto.Metadata{
|
t.Run(tc.name+"_TemplateVersionImport", func(t *testing.T) {
|
||||||
CoderUrl: (&url.URL{}).String(),
|
t.Parallel()
|
||||||
WorkspaceTransition: sdkproto.WorkspaceTransition_START,
|
srv, db, ps := setup(t, false, nil)
|
||||||
WorkspaceName: workspace.Name,
|
ctx := context.Background()
|
||||||
WorkspaceOwner: user.Username,
|
|
||||||
WorkspaceOwnerEmail: user.Email,
|
user := dbgen.User(t, db, database.User{})
|
||||||
WorkspaceOwnerOidcAccessToken: link.OAuthAccessToken,
|
file := dbgen.File(t, db, database.File{CreatedBy: user.ID})
|
||||||
WorkspaceId: workspace.ID.String(),
|
_ = dbgen.ProvisionerJob(t, db, ps, database.ProvisionerJob{
|
||||||
WorkspaceOwnerId: user.ID.String(),
|
FileID: file.ID,
|
||||||
TemplateId: template.ID.String(),
|
InitiatorID: user.ID,
|
||||||
TemplateName: template.Name,
|
Provisioner: database.ProvisionerTypeEcho,
|
||||||
TemplateVersion: version.Name,
|
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||||
WorkspaceOwnerSessionToken: sessionToken,
|
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
||||||
|
})
|
||||||
|
|
||||||
|
job, err := tc.acquire(ctx, srv)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
got, err := json.Marshal(job.Type)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
want, err := json.Marshal(&proto.AcquiredJob_TemplateImport_{
|
||||||
|
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
||||||
|
Metadata: &sdkproto.Metadata{
|
||||||
|
CoderUrl: (&url.URL{}).String(),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, string(want), string(got))
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
t.Run(tc.name+"_TemplateVersionImportWithUserVariable", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
srv, db, ps := setup(t, false, nil)
|
||||||
|
|
||||||
require.JSONEq(t, string(want), string(got))
|
user := dbgen.User(t, db, database.User{})
|
||||||
|
version := dbgen.TemplateVersion(t, db, database.TemplateVersion{})
|
||||||
|
file := dbgen.File(t, db, database.File{CreatedBy: user.ID})
|
||||||
|
_ = dbgen.ProvisionerJob(t, db, ps, database.ProvisionerJob{
|
||||||
|
FileID: file.ID,
|
||||||
|
InitiatorID: user.ID,
|
||||||
|
Provisioner: database.ProvisionerTypeEcho,
|
||||||
|
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||||
|
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
||||||
|
Input: must(json.Marshal(provisionerdserver.TemplateVersionImportJob{
|
||||||
|
TemplateVersionID: version.ID,
|
||||||
|
UserVariableValues: []codersdk.VariableValue{
|
||||||
|
{Name: "first", Value: "first_value"},
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
})
|
||||||
|
|
||||||
// Assert that we delete the session token whenever
|
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||||
// a stop is issued.
|
defer cancel()
|
||||||
stopbuild := dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
|
|
||||||
WorkspaceID: workspace.ID,
|
|
||||||
BuildNumber: 2,
|
|
||||||
JobID: uuid.New(),
|
|
||||||
TemplateVersionID: version.ID,
|
|
||||||
Transition: database.WorkspaceTransitionStop,
|
|
||||||
Reason: database.BuildReasonInitiator,
|
|
||||||
})
|
|
||||||
_ = dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
|
||||||
ID: stopbuild.ID,
|
|
||||||
InitiatorID: user.ID,
|
|
||||||
Provisioner: database.ProvisionerTypeEcho,
|
|
||||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
|
||||||
FileID: file.ID,
|
|
||||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
|
||||||
Input: must(json.Marshal(provisionerdserver.WorkspaceProvisionJob{
|
|
||||||
WorkspaceBuildID: stopbuild.ID,
|
|
||||||
})),
|
|
||||||
})
|
|
||||||
|
|
||||||
stopPublished := make(chan struct{})
|
job, err := tc.acquire(ctx, srv)
|
||||||
closeStopSubscribe, err := ps.Subscribe(codersdk.WorkspaceNotifyChannel(workspace.ID), func(_ context.Context, _ []byte) {
|
require.NoError(t, err)
|
||||||
close(stopPublished)
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer closeStopSubscribe()
|
|
||||||
|
|
||||||
// Grab jobs until we find the workspace build job. There is also
|
got, err := json.Marshal(job.Type)
|
||||||
// an import version job that we need to ignore.
|
require.NoError(t, err)
|
||||||
job, err = srv.AcquireJob(ctx, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
_, ok := job.Type.(*proto.AcquiredJob_WorkspaceBuild_)
|
|
||||||
require.True(t, ok, "acquired job not a workspace build?")
|
|
||||||
|
|
||||||
<-stopPublished
|
want, err := json.Marshal(&proto.AcquiredJob_TemplateImport_{
|
||||||
|
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
||||||
// Validate that a session token is deleted during a stop job.
|
UserVariableValues: []*sdkproto.VariableValue{
|
||||||
sessionToken = job.Type.(*proto.AcquiredJob_WorkspaceBuild_).WorkspaceBuild.Metadata.WorkspaceOwnerSessionToken
|
{Name: "first", Sensitive: true, Value: "first_value"},
|
||||||
require.Empty(t, sessionToken)
|
},
|
||||||
_, err = db.GetAPIKeyByID(ctx, key.ID)
|
Metadata: &sdkproto.Metadata{
|
||||||
require.ErrorIs(t, err, sql.ErrNoRows)
|
CoderUrl: (&url.URL{}).String(),
|
||||||
})
|
},
|
||||||
|
|
||||||
t.Run("TemplateVersionDryRun", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
srv, db, _ := setup(t, false, nil)
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
user := dbgen.User(t, db, database.User{})
|
|
||||||
version := dbgen.TemplateVersion(t, db, database.TemplateVersion{})
|
|
||||||
file := dbgen.File(t, db, database.File{CreatedBy: user.ID})
|
|
||||||
_ = dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
|
||||||
InitiatorID: user.ID,
|
|
||||||
Provisioner: database.ProvisionerTypeEcho,
|
|
||||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
|
||||||
FileID: file.ID,
|
|
||||||
Type: database.ProvisionerJobTypeTemplateVersionDryRun,
|
|
||||||
Input: must(json.Marshal(provisionerdserver.TemplateVersionDryRunJob{
|
|
||||||
TemplateVersionID: version.ID,
|
|
||||||
WorkspaceName: "testing",
|
|
||||||
})),
|
|
||||||
})
|
|
||||||
|
|
||||||
job, err := srv.AcquireJob(ctx, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
got, err := json.Marshal(job.Type)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
want, err := json.Marshal(&proto.AcquiredJob_TemplateDryRun_{
|
|
||||||
TemplateDryRun: &proto.AcquiredJob_TemplateDryRun{
|
|
||||||
Metadata: &sdkproto.Metadata{
|
|
||||||
CoderUrl: (&url.URL{}).String(),
|
|
||||||
WorkspaceName: "testing",
|
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, string(want), string(got))
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
}
|
||||||
require.JSONEq(t, string(want), string(got))
|
|
||||||
})
|
|
||||||
t.Run("TemplateVersionImport", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
srv, db, _ := setup(t, false, nil)
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
user := dbgen.User(t, db, database.User{})
|
|
||||||
file := dbgen.File(t, db, database.File{CreatedBy: user.ID})
|
|
||||||
_ = dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
|
||||||
FileID: file.ID,
|
|
||||||
InitiatorID: user.ID,
|
|
||||||
Provisioner: database.ProvisionerTypeEcho,
|
|
||||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
|
||||||
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
|
||||||
})
|
|
||||||
|
|
||||||
job, err := srv.AcquireJob(ctx, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
got, err := json.Marshal(job.Type)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
want, err := json.Marshal(&proto.AcquiredJob_TemplateImport_{
|
|
||||||
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
|
||||||
Metadata: &sdkproto.Metadata{
|
|
||||||
CoderUrl: (&url.URL{}).String(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.JSONEq(t, string(want), string(got))
|
|
||||||
})
|
|
||||||
t.Run("TemplateVersionImportWithUserVariable", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
srv, db, _ := setup(t, false, nil)
|
|
||||||
|
|
||||||
user := dbgen.User(t, db, database.User{})
|
|
||||||
version := dbgen.TemplateVersion(t, db, database.TemplateVersion{})
|
|
||||||
file := dbgen.File(t, db, database.File{CreatedBy: user.ID})
|
|
||||||
_ = dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
|
||||||
FileID: file.ID,
|
|
||||||
InitiatorID: user.ID,
|
|
||||||
Provisioner: database.ProvisionerTypeEcho,
|
|
||||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
|
||||||
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
|
||||||
Input: must(json.Marshal(provisionerdserver.TemplateVersionImportJob{
|
|
||||||
TemplateVersionID: version.ID,
|
|
||||||
UserVariableValues: []codersdk.VariableValue{
|
|
||||||
{Name: "first", Value: "first_value"},
|
|
||||||
},
|
|
||||||
})),
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
job, err := srv.AcquireJob(ctx, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
got, err := json.Marshal(job.Type)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
want, err := json.Marshal(&proto.AcquiredJob_TemplateImport_{
|
|
||||||
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
|
||||||
UserVariableValues: []*sdkproto.VariableValue{
|
|
||||||
{Name: "first", Sensitive: true, Value: "first_value"},
|
|
||||||
},
|
|
||||||
Metadata: &sdkproto.Metadata{
|
|
||||||
CoderUrl: (&url.URL{}).String(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.JSONEq(t, string(want), string(got))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateJob(t *testing.T) {
|
func TestUpdateJob(t *testing.T) {
|
||||||
|
@ -1142,7 +1160,7 @@ func TestCompleteJob(t *testing.T) {
|
||||||
Transition: c.transition,
|
Transition: c.transition,
|
||||||
Reason: database.BuildReasonInitiator,
|
Reason: database.BuildReasonInitiator,
|
||||||
})
|
})
|
||||||
job := dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
job := dbgen.ProvisionerJob(t, db, ps, database.ProvisionerJob{
|
||||||
FileID: file.ID,
|
FileID: file.ID,
|
||||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||||
Input: must(json.Marshal(provisionerdserver.WorkspaceProvisionJob{
|
Input: must(json.Marshal(provisionerdserver.WorkspaceProvisionJob{
|
||||||
|
@ -1390,7 +1408,7 @@ func TestCompleteJob(t *testing.T) {
|
||||||
Transition: c.transition,
|
Transition: c.transition,
|
||||||
Reason: database.BuildReasonInitiator,
|
Reason: database.BuildReasonInitiator,
|
||||||
})
|
})
|
||||||
job := dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
job := dbgen.ProvisionerJob(t, db, ps, database.ProvisionerJob{
|
||||||
FileID: file.ID,
|
FileID: file.ID,
|
||||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||||
Input: must(json.Marshal(provisionerdserver.WorkspaceProvisionJob{
|
Input: must(json.Marshal(provisionerdserver.WorkspaceProvisionJob{
|
||||||
|
@ -1662,10 +1680,14 @@ type overrides struct {
|
||||||
templateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore]
|
templateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore]
|
||||||
userQuietHoursScheduleStore *atomic.Pointer[schedule.UserQuietHoursScheduleStore]
|
userQuietHoursScheduleStore *atomic.Pointer[schedule.UserQuietHoursScheduleStore]
|
||||||
timeNowFn func() time.Time
|
timeNowFn func() time.Time
|
||||||
|
acquireJobLongPollDuration time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisionerDaemonServer, database.Store, pubsub.Pubsub) {
|
func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisionerDaemonServer, database.Store, pubsub.Pubsub) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
t.Cleanup(cancel)
|
||||||
|
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||||
db := dbfake.New()
|
db := dbfake.New()
|
||||||
ps := pubsub.NewInMemory()
|
ps := pubsub.NewInMemory()
|
||||||
deploymentValues := &codersdk.DeploymentValues{}
|
deploymentValues := &codersdk.DeploymentValues{}
|
||||||
|
@ -1674,6 +1696,7 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
|
||||||
tss := testTemplateScheduleStore()
|
tss := testTemplateScheduleStore()
|
||||||
uqhss := testUserQuietHoursScheduleStore()
|
uqhss := testUserQuietHoursScheduleStore()
|
||||||
var timeNowFn func() time.Time
|
var timeNowFn func() time.Time
|
||||||
|
pollDur := time.Duration(0)
|
||||||
if ov != nil {
|
if ov != nil {
|
||||||
if ov.deploymentValues != nil {
|
if ov.deploymentValues != nil {
|
||||||
deploymentValues = ov.deploymentValues
|
deploymentValues = ov.deploymentValues
|
||||||
|
@ -1705,6 +1728,7 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
|
||||||
if ov.timeNowFn != nil {
|
if ov.timeNowFn != nil {
|
||||||
timeNowFn = ov.timeNowFn
|
timeNowFn = ov.timeNowFn
|
||||||
}
|
}
|
||||||
|
pollDur = ov.acquireJobLongPollDuration
|
||||||
}
|
}
|
||||||
|
|
||||||
srv, err := provisionerdserver.NewServer(
|
srv, err := provisionerdserver.NewServer(
|
||||||
|
@ -1712,9 +1736,10 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
|
||||||
srvID,
|
srvID,
|
||||||
slogtest.Make(t, &slogtest.Options{IgnoreErrors: ignoreLogErrors}),
|
slogtest.Make(t, &slogtest.Options{IgnoreErrors: ignoreLogErrors}),
|
||||||
[]database.ProvisionerType{database.ProvisionerTypeEcho},
|
[]database.ProvisionerType{database.ProvisionerTypeEcho},
|
||||||
nil,
|
provisionerdserver.Tags{},
|
||||||
db,
|
db,
|
||||||
ps,
|
ps,
|
||||||
|
provisionerdserver.NewAcquirer(ctx, logger.Named("acquirer"), db, ps),
|
||||||
telemetry.NewNoop(),
|
telemetry.NewNoop(),
|
||||||
trace.NewNoopTracerProvider().Tracer("noop"),
|
trace.NewNoopTracerProvider().Tracer("noop"),
|
||||||
&atomic.Pointer[proto.QuotaCommitter]{},
|
&atomic.Pointer[proto.QuotaCommitter]{},
|
||||||
|
@ -1722,12 +1747,11 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
|
||||||
tss,
|
tss,
|
||||||
uqhss,
|
uqhss,
|
||||||
deploymentValues,
|
deploymentValues,
|
||||||
// Negative values cause the debounce to never kick in.
|
|
||||||
-time.Minute,
|
|
||||||
provisionerdserver.Options{
|
provisionerdserver.Options{
|
||||||
GitAuthConfigs: gitAuthConfigs,
|
GitAuthConfigs: gitAuthConfigs,
|
||||||
TimeNowFn: timeNowFn,
|
TimeNowFn: timeNowFn,
|
||||||
OIDCConfig: &oauth2.Config{},
|
OIDCConfig: &oauth2.Config{},
|
||||||
|
AcquireJobLongPollDur: pollDur,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -1740,3 +1764,95 @@ func must[T any](value T, err error) T {
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
errUnimplemented = xerrors.New("unimplemented")
|
||||||
|
errClosed = xerrors.New("closed")
|
||||||
|
)
|
||||||
|
|
||||||
|
type fakeStream struct {
|
||||||
|
ctx context.Context
|
||||||
|
c *sync.Cond
|
||||||
|
closed bool
|
||||||
|
canceled bool
|
||||||
|
sendCalled bool
|
||||||
|
job *proto.AcquiredJob
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFakeStream(ctx context.Context) *fakeStream {
|
||||||
|
return &fakeStream{
|
||||||
|
ctx: ctx,
|
||||||
|
c: sync.NewCond(&sync.Mutex{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fakeStream) Send(j *proto.AcquiredJob) error {
|
||||||
|
s.c.L.Lock()
|
||||||
|
defer s.c.L.Unlock()
|
||||||
|
s.sendCalled = true
|
||||||
|
s.job = j
|
||||||
|
s.c.Broadcast()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fakeStream) Recv() (*proto.CancelAcquire, error) {
|
||||||
|
s.c.L.Lock()
|
||||||
|
defer s.c.L.Unlock()
|
||||||
|
for !(s.canceled || s.closed) {
|
||||||
|
s.c.Wait()
|
||||||
|
}
|
||||||
|
if s.canceled {
|
||||||
|
return &proto.CancelAcquire{}, nil
|
||||||
|
}
|
||||||
|
return nil, io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context returns the context associated with the stream. It is canceled
|
||||||
|
// when the Stream is closed and no more messages will ever be sent or
|
||||||
|
// received on it.
|
||||||
|
func (s *fakeStream) Context() context.Context {
|
||||||
|
return s.ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsgSend sends the Message to the remote.
|
||||||
|
func (*fakeStream) MsgSend(drpc.Message, drpc.Encoding) error {
|
||||||
|
return errUnimplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsgRecv receives a Message from the remote.
|
||||||
|
func (*fakeStream) MsgRecv(drpc.Message, drpc.Encoding) error {
|
||||||
|
return errUnimplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseSend signals to the remote that we will no longer send any messages.
|
||||||
|
func (*fakeStream) CloseSend() error {
|
||||||
|
return errUnimplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the stream.
|
||||||
|
func (s *fakeStream) Close() error {
|
||||||
|
s.c.L.Lock()
|
||||||
|
defer s.c.L.Unlock()
|
||||||
|
s.closed = true
|
||||||
|
s.c.Broadcast()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fakeStream) waitForJob() (*proto.AcquiredJob, error) {
|
||||||
|
s.c.L.Lock()
|
||||||
|
defer s.c.L.Unlock()
|
||||||
|
for !(s.sendCalled || s.closed) {
|
||||||
|
s.c.Wait()
|
||||||
|
}
|
||||||
|
if s.sendCalled {
|
||||||
|
return s.job, nil
|
||||||
|
}
|
||||||
|
return nil, errClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fakeStream) cancel() {
|
||||||
|
s.c.L.Lock()
|
||||||
|
defer s.c.L.Unlock()
|
||||||
|
s.canceled = true
|
||||||
|
s.c.Broadcast()
|
||||||
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ func TestTelemetry(t *testing.T) {
|
||||||
|
|
||||||
ctx := testutil.Context(t, testutil.WaitMedium)
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||||
_, _ = dbgen.APIKey(t, db, database.APIKey{})
|
_, _ = dbgen.APIKey(t, db, database.APIKey{})
|
||||||
_ = dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
_ = dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
|
||||||
Provisioner: database.ProvisionerTypeTerraform,
|
Provisioner: database.ProvisionerTypeTerraform,
|
||||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||||
Type: database.ProvisionerJobTypeTemplateVersionDryRun,
|
Type: database.ProvisionerJobTypeTemplateVersionDryRun,
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/coder/coder/v2/coderd/audit"
|
"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/dbtime"
|
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||||
|
"github.com/coder/coder/v2/coderd/database/provisionerjobs"
|
||||||
"github.com/coder/coder/v2/coderd/gitauth"
|
"github.com/coder/coder/v2/coderd/gitauth"
|
||||||
"github.com/coder/coder/v2/coderd/httpapi"
|
"github.com/coder/coder/v2/coderd/httpapi"
|
||||||
"github.com/coder/coder/v2/coderd/httpmw"
|
"github.com/coder/coder/v2/coderd/httpmw"
|
||||||
|
@ -502,6 +503,11 @@ func (api *API) postTemplateVersionDryRun(rw http.ResponseWriter, r *http.Reques
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
err = provisionerjobs.PostJob(api.Pubsub, provisionerJob)
|
||||||
|
if err != nil {
|
||||||
|
// Client probably doesn't care about this error, so just log it.
|
||||||
|
api.Logger.Error(ctx, "failed to post provisioner job to pubsub", slog.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
httpapi.Write(ctx, rw, http.StatusCreated, convertProvisionerJob(database.GetProvisionerJobsByIDsWithQueuePositionRow{
|
httpapi.Write(ctx, rw, http.StatusCreated, convertProvisionerJob(database.GetProvisionerJobsByIDsWithQueuePositionRow{
|
||||||
ProvisionerJob: provisionerJob,
|
ProvisionerJob: provisionerJob,
|
||||||
|
@ -1289,6 +1295,11 @@ func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
aReq.New = templateVersion
|
aReq.New = templateVersion
|
||||||
|
err = provisionerjobs.PostJob(api.Pubsub, provisionerJob)
|
||||||
|
if err != nil {
|
||||||
|
// Client probably doesn't care about this error, so just log it.
|
||||||
|
api.Logger.Error(ctx, "failed to post provisioner job to pubsub", slog.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
httpapi.Write(ctx, rw, http.StatusCreated, convertTemplateVersion(templateVersion, convertProvisionerJob(database.GetProvisionerJobsByIDsWithQueuePositionRow{
|
httpapi.Write(ctx, rw, http.StatusCreated, convertTemplateVersion(templateVersion, convertProvisionerJob(database.GetProvisionerJobsByIDsWithQueuePositionRow{
|
||||||
ProvisionerJob: provisionerJob,
|
ProvisionerJob: provisionerJob,
|
||||||
|
|
|
@ -67,7 +67,7 @@ func TestDetectorNoHungJobs(t *testing.T) {
|
||||||
user := dbgen.User(t, db, database.User{})
|
user := dbgen.User(t, db, database.User{})
|
||||||
file := dbgen.File(t, db, database.File{})
|
file := dbgen.File(t, db, database.File{})
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
dbgen.ProvisionerJob(t, db, pubsub, database.ProvisionerJob{
|
||||||
CreatedAt: now.Add(-time.Minute * 5),
|
CreatedAt: now.Add(-time.Minute * 5),
|
||||||
UpdatedAt: now.Add(-time.Minute * time.Duration(i)),
|
UpdatedAt: now.Add(-time.Minute * time.Duration(i)),
|
||||||
StartedAt: sql.NullTime{
|
StartedAt: sql.NullTime{
|
||||||
|
@ -135,7 +135,7 @@ func TestDetectorHungWorkspaceBuild(t *testing.T) {
|
||||||
|
|
||||||
// Previous build.
|
// Previous build.
|
||||||
expectedWorkspaceBuildState = []byte(`{"dean":"cool","colin":"also cool"}`)
|
expectedWorkspaceBuildState = []byte(`{"dean":"cool","colin":"also cool"}`)
|
||||||
previousWorkspaceBuildJob = dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
previousWorkspaceBuildJob = dbgen.ProvisionerJob(t, db, pubsub, database.ProvisionerJob{
|
||||||
CreatedAt: twentyMinAgo,
|
CreatedAt: twentyMinAgo,
|
||||||
UpdatedAt: twentyMinAgo,
|
UpdatedAt: twentyMinAgo,
|
||||||
StartedAt: sql.NullTime{
|
StartedAt: sql.NullTime{
|
||||||
|
@ -163,7 +163,7 @@ func TestDetectorHungWorkspaceBuild(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Current build.
|
// Current build.
|
||||||
currentWorkspaceBuildJob = dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
currentWorkspaceBuildJob = dbgen.ProvisionerJob(t, db, pubsub, database.ProvisionerJob{
|
||||||
CreatedAt: tenMinAgo,
|
CreatedAt: tenMinAgo,
|
||||||
UpdatedAt: sixMinAgo,
|
UpdatedAt: sixMinAgo,
|
||||||
StartedAt: sql.NullTime{
|
StartedAt: sql.NullTime{
|
||||||
|
@ -256,7 +256,7 @@ func TestDetectorHungWorkspaceBuildNoOverrideState(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Previous build.
|
// Previous build.
|
||||||
previousWorkspaceBuildJob = dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
previousWorkspaceBuildJob = dbgen.ProvisionerJob(t, db, pubsub, database.ProvisionerJob{
|
||||||
CreatedAt: twentyMinAgo,
|
CreatedAt: twentyMinAgo,
|
||||||
UpdatedAt: twentyMinAgo,
|
UpdatedAt: twentyMinAgo,
|
||||||
StartedAt: sql.NullTime{
|
StartedAt: sql.NullTime{
|
||||||
|
@ -285,7 +285,7 @@ func TestDetectorHungWorkspaceBuildNoOverrideState(t *testing.T) {
|
||||||
|
|
||||||
// Current build.
|
// Current build.
|
||||||
expectedWorkspaceBuildState = []byte(`{"dean":"cool","colin":"also cool"}`)
|
expectedWorkspaceBuildState = []byte(`{"dean":"cool","colin":"also cool"}`)
|
||||||
currentWorkspaceBuildJob = dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
currentWorkspaceBuildJob = dbgen.ProvisionerJob(t, db, pubsub, database.ProvisionerJob{
|
||||||
CreatedAt: tenMinAgo,
|
CreatedAt: tenMinAgo,
|
||||||
UpdatedAt: sixMinAgo,
|
UpdatedAt: sixMinAgo,
|
||||||
StartedAt: sql.NullTime{
|
StartedAt: sql.NullTime{
|
||||||
|
@ -379,7 +379,7 @@ func TestDetectorHungWorkspaceBuildNoOverrideStateIfNoExistingBuild(t *testing.T
|
||||||
|
|
||||||
// First build.
|
// First build.
|
||||||
expectedWorkspaceBuildState = []byte(`{"dean":"cool","colin":"also cool"}`)
|
expectedWorkspaceBuildState = []byte(`{"dean":"cool","colin":"also cool"}`)
|
||||||
currentWorkspaceBuildJob = dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
currentWorkspaceBuildJob = dbgen.ProvisionerJob(t, db, pubsub, database.ProvisionerJob{
|
||||||
CreatedAt: tenMinAgo,
|
CreatedAt: tenMinAgo,
|
||||||
UpdatedAt: sixMinAgo,
|
UpdatedAt: sixMinAgo,
|
||||||
StartedAt: sql.NullTime{
|
StartedAt: sql.NullTime{
|
||||||
|
@ -454,7 +454,7 @@ func TestDetectorHungOtherJobTypes(t *testing.T) {
|
||||||
file = dbgen.File(t, db, database.File{})
|
file = dbgen.File(t, db, database.File{})
|
||||||
|
|
||||||
// Template import job.
|
// Template import job.
|
||||||
templateImportJob = dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
templateImportJob = dbgen.ProvisionerJob(t, db, pubsub, database.ProvisionerJob{
|
||||||
CreatedAt: tenMinAgo,
|
CreatedAt: tenMinAgo,
|
||||||
UpdatedAt: sixMinAgo,
|
UpdatedAt: sixMinAgo,
|
||||||
StartedAt: sql.NullTime{
|
StartedAt: sql.NullTime{
|
||||||
|
@ -471,7 +471,7 @@ func TestDetectorHungOtherJobTypes(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Template dry-run job.
|
// Template dry-run job.
|
||||||
templateDryRunJob = dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
templateDryRunJob = dbgen.ProvisionerJob(t, db, pubsub, database.ProvisionerJob{
|
||||||
CreatedAt: tenMinAgo,
|
CreatedAt: tenMinAgo,
|
||||||
UpdatedAt: sixMinAgo,
|
UpdatedAt: sixMinAgo,
|
||||||
StartedAt: sql.NullTime{
|
StartedAt: sql.NullTime{
|
||||||
|
@ -545,7 +545,7 @@ func TestDetectorHungCanceledJob(t *testing.T) {
|
||||||
file = dbgen.File(t, db, database.File{})
|
file = dbgen.File(t, db, database.File{})
|
||||||
|
|
||||||
// Template import job.
|
// Template import job.
|
||||||
templateImportJob = dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
templateImportJob = dbgen.ProvisionerJob(t, db, pubsub, database.ProvisionerJob{
|
||||||
CreatedAt: tenMinAgo,
|
CreatedAt: tenMinAgo,
|
||||||
CanceledAt: sql.NullTime{
|
CanceledAt: sql.NullTime{
|
||||||
Time: tenMinAgo,
|
Time: tenMinAgo,
|
||||||
|
@ -642,7 +642,7 @@ func TestDetectorPushesLogs(t *testing.T) {
|
||||||
file = dbgen.File(t, db, database.File{})
|
file = dbgen.File(t, db, database.File{})
|
||||||
|
|
||||||
// Template import job.
|
// Template import job.
|
||||||
templateImportJob = dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
templateImportJob = dbgen.ProvisionerJob(t, db, pubsub, database.ProvisionerJob{
|
||||||
CreatedAt: tenMinAgo,
|
CreatedAt: tenMinAgo,
|
||||||
UpdatedAt: sixMinAgo,
|
UpdatedAt: sixMinAgo,
|
||||||
StartedAt: sql.NullTime{
|
StartedAt: sql.NullTime{
|
||||||
|
@ -752,7 +752,7 @@ func TestDetectorMaxJobsPerRun(t *testing.T) {
|
||||||
// Create unhanger.MaxJobsPerRun + 1 hung jobs.
|
// Create unhanger.MaxJobsPerRun + 1 hung jobs.
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
for i := 0; i < unhanger.MaxJobsPerRun+1; i++ {
|
for i := 0; i < unhanger.MaxJobsPerRun+1; i++ {
|
||||||
dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
dbgen.ProvisionerJob(t, db, pubsub, database.ProvisionerJob{
|
||||||
CreatedAt: now.Add(-time.Hour),
|
CreatedAt: now.Add(-time.Hour),
|
||||||
UpdatedAt: now.Add(-time.Hour),
|
UpdatedAt: now.Add(-time.Hour),
|
||||||
StartedAt: sql.NullTime{
|
StartedAt: sql.NullTime{
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"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"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||||
|
"github.com/coder/coder/v2/coderd/database/provisionerjobs"
|
||||||
"github.com/coder/coder/v2/coderd/httpapi"
|
"github.com/coder/coder/v2/coderd/httpapi"
|
||||||
"github.com/coder/coder/v2/coderd/httpmw"
|
"github.com/coder/coder/v2/coderd/httpmw"
|
||||||
"github.com/coder/coder/v2/coderd/rbac"
|
"github.com/coder/coder/v2/coderd/rbac"
|
||||||
|
@ -373,6 +374,11 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
err = provisionerjobs.PostJob(api.Pubsub, *provisionerJob)
|
||||||
|
if err != nil {
|
||||||
|
// Client probably doesn't care about this error, so just log it.
|
||||||
|
api.Logger.Error(ctx, "failed to post provisioner job to pubsub", slog.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
users, err := api.Database.GetUsersByIDs(ctx, []uuid.UUID{
|
users, err := api.Database.GetUsersByIDs(ctx, []uuid.UUID{
|
||||||
workspace.OwnerID,
|
workspace.OwnerID,
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"github.com/coder/coder/v2/coderd/database"
|
"github.com/coder/coder/v2/coderd/database"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||||
|
"github.com/coder/coder/v2/coderd/database/provisionerjobs"
|
||||||
"github.com/coder/coder/v2/coderd/httpapi"
|
"github.com/coder/coder/v2/coderd/httpapi"
|
||||||
"github.com/coder/coder/v2/coderd/httpmw"
|
"github.com/coder/coder/v2/coderd/httpmw"
|
||||||
"github.com/coder/coder/v2/coderd/rbac"
|
"github.com/coder/coder/v2/coderd/rbac"
|
||||||
|
@ -485,7 +486,9 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
|
||||||
}
|
}
|
||||||
|
|
||||||
workspaceBuild, provisionerJob, err = builder.Build(
|
workspaceBuild, provisionerJob, err = builder.Build(
|
||||||
ctx, db, func(action rbac.Action, object rbac.Objecter) bool {
|
ctx,
|
||||||
|
db,
|
||||||
|
func(action rbac.Action, object rbac.Objecter) bool {
|
||||||
return api.Authorize(r, action, object)
|
return api.Authorize(r, action, object)
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
|
@ -505,6 +508,11 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
err = provisionerjobs.PostJob(api.Pubsub, *provisionerJob)
|
||||||
|
if err != nil {
|
||||||
|
// Client probably doesn't care about this error, so just log it.
|
||||||
|
api.Logger.Error(ctx, "failed to post provisioner job to pubsub", slog.Error(err))
|
||||||
|
}
|
||||||
aReq.New = workspace
|
aReq.New = workspace
|
||||||
|
|
||||||
initiator, err := api.Database.GetUserByID(ctx, workspaceBuild.InitiatorID)
|
initiator, err := api.Database.GetUserByID(ctx, workspaceBuild.InitiatorID)
|
||||||
|
|
|
@ -789,7 +789,7 @@ func TestWorkspaceFilterAllStatus(t *testing.T) {
|
||||||
file := dbgen.File(t, db, database.File{
|
file := dbgen.File(t, db, database.File{
|
||||||
CreatedBy: owner.UserID,
|
CreatedBy: owner.UserID,
|
||||||
})
|
})
|
||||||
versionJob := dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
versionJob := dbgen.ProvisionerJob(t, db, pubsub, database.ProvisionerJob{
|
||||||
OrganizationID: owner.OrganizationID,
|
OrganizationID: owner.OrganizationID,
|
||||||
InitiatorID: owner.UserID,
|
InitiatorID: owner.UserID,
|
||||||
WorkerID: uuid.NullUUID{},
|
WorkerID: uuid.NullUUID{},
|
||||||
|
@ -825,7 +825,7 @@ func TestWorkspaceFilterAllStatus(t *testing.T) {
|
||||||
job.Tags = database.StringMap{
|
job.Tags = database.StringMap{
|
||||||
jobID.String(): "true",
|
jobID.String(): "true",
|
||||||
}
|
}
|
||||||
job = dbgen.ProvisionerJob(t, db, job)
|
job = dbgen.ProvisionerJob(t, db, pubsub, job)
|
||||||
|
|
||||||
build := dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
|
build := dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
|
||||||
WorkspaceID: workspace.ID,
|
WorkspaceID: workspace.ID,
|
||||||
|
|
|
@ -1272,7 +1272,7 @@ when required by your organization's security policy.`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Poll Interval",
|
Name: "Poll Interval",
|
||||||
Description: "Time to wait before polling for a new job.",
|
Description: "Deprecated and ignored.",
|
||||||
Flag: "provisioner-daemon-poll-interval",
|
Flag: "provisioner-daemon-poll-interval",
|
||||||
Env: "CODER_PROVISIONER_DAEMON_POLL_INTERVAL",
|
Env: "CODER_PROVISIONER_DAEMON_POLL_INTERVAL",
|
||||||
Default: time.Second.String(),
|
Default: time.Second.String(),
|
||||||
|
@ -1282,7 +1282,7 @@ when required by your organization's security policy.`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Poll Jitter",
|
Name: "Poll Jitter",
|
||||||
Description: "Random jitter added to the poll interval.",
|
Description: "Deprecated and ignored.",
|
||||||
Flag: "provisioner-daemon-poll-jitter",
|
Flag: "provisioner-daemon-poll-jitter",
|
||||||
Env: "CODER_PROVISIONER_DAEMON_POLL_JITTER",
|
Env: "CODER_PROVISIONER_DAEMON_POLL_JITTER",
|
||||||
Default: (100 * time.Millisecond).String(),
|
Default: (100 * time.Millisecond).String(),
|
||||||
|
|
|
@ -30,7 +30,7 @@ Directory to store cached data.
|
||||||
| Environment | <code>$CODER_PROVISIONERD_POLL_INTERVAL</code> |
|
| Environment | <code>$CODER_PROVISIONERD_POLL_INTERVAL</code> |
|
||||||
| Default | <code>1s</code> |
|
| Default | <code>1s</code> |
|
||||||
|
|
||||||
How often to poll for provisioner jobs.
|
Deprecated and ignored.
|
||||||
|
|
||||||
### --poll-jitter
|
### --poll-jitter
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ How often to poll for provisioner jobs.
|
||||||
| Environment | <code>$CODER_PROVISIONERD_POLL_JITTER</code> |
|
| Environment | <code>$CODER_PROVISIONERD_POLL_JITTER</code> |
|
||||||
| Default | <code>100ms</code> |
|
| Default | <code>100ms</code> |
|
||||||
|
|
||||||
How much to jitter the poll interval by.
|
Deprecated and ignored.
|
||||||
|
|
||||||
### --psk
|
### --psk
|
||||||
|
|
||||||
|
|
|
@ -644,7 +644,7 @@ URL pointing to the icon to use on the OpenID Connect login button.
|
||||||
| YAML | <code>provisioning.daemonPollInterval</code> |
|
| YAML | <code>provisioning.daemonPollInterval</code> |
|
||||||
| Default | <code>1s</code> |
|
| Default | <code>1s</code> |
|
||||||
|
|
||||||
Time to wait before polling for a new job.
|
Deprecated and ignored.
|
||||||
|
|
||||||
### --provisioner-daemon-poll-jitter
|
### --provisioner-daemon-poll-jitter
|
||||||
|
|
||||||
|
@ -655,7 +655,7 @@ Time to wait before polling for a new job.
|
||||||
| YAML | <code>provisioning.daemonPollJitter</code> |
|
| YAML | <code>provisioning.daemonPollJitter</code> |
|
||||||
| Default | <code>100ms</code> |
|
| Default | <code>100ms</code> |
|
||||||
|
|
||||||
Random jitter added to the poll interval.
|
Deprecated and ignored.
|
||||||
|
|
||||||
### --postgres-url
|
### --postgres-url
|
||||||
|
|
||||||
|
|
|
@ -136,11 +136,9 @@ func (r *RootCmd) provisionerDaemonStart() *clibase.Cmd {
|
||||||
PreSharedKey: preSharedKey,
|
PreSharedKey: preSharedKey,
|
||||||
})
|
})
|
||||||
}, &provisionerd.Options{
|
}, &provisionerd.Options{
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
JobPollInterval: pollInterval,
|
UpdateInterval: 500 * time.Millisecond,
|
||||||
JobPollJitter: pollJitter,
|
Connector: connector,
|
||||||
UpdateInterval: 500 * time.Millisecond,
|
|
||||||
Connector: connector,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
var exitErr error
|
var exitErr error
|
||||||
|
@ -189,13 +187,13 @@ func (r *RootCmd) provisionerDaemonStart() *clibase.Cmd {
|
||||||
Flag: "poll-interval",
|
Flag: "poll-interval",
|
||||||
Env: "CODER_PROVISIONERD_POLL_INTERVAL",
|
Env: "CODER_PROVISIONERD_POLL_INTERVAL",
|
||||||
Default: time.Second.String(),
|
Default: time.Second.String(),
|
||||||
Description: "How often to poll for provisioner jobs.",
|
Description: "Deprecated and ignored.",
|
||||||
Value: clibase.DurationOf(&pollInterval),
|
Value: clibase.DurationOf(&pollInterval),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Flag: "poll-jitter",
|
Flag: "poll-jitter",
|
||||||
Env: "CODER_PROVISIONERD_POLL_JITTER",
|
Env: "CODER_PROVISIONERD_POLL_JITTER",
|
||||||
Description: "How much to jitter the poll interval by.",
|
Description: "Deprecated and ignored.",
|
||||||
Default: (100 * time.Millisecond).String(),
|
Default: (100 * time.Millisecond).String(),
|
||||||
Value: clibase.DurationOf(&pollJitter),
|
Value: clibase.DurationOf(&pollJitter),
|
||||||
},
|
},
|
||||||
|
|
|
@ -10,10 +10,10 @@ OPTIONS:
|
||||||
Directory to store cached data.
|
Directory to store cached data.
|
||||||
|
|
||||||
--poll-interval duration, $CODER_PROVISIONERD_POLL_INTERVAL (default: 1s)
|
--poll-interval duration, $CODER_PROVISIONERD_POLL_INTERVAL (default: 1s)
|
||||||
How often to poll for provisioner jobs.
|
Deprecated and ignored.
|
||||||
|
|
||||||
--poll-jitter duration, $CODER_PROVISIONERD_POLL_JITTER (default: 100ms)
|
--poll-jitter duration, $CODER_PROVISIONERD_POLL_JITTER (default: 100ms)
|
||||||
How much to jitter the poll interval by.
|
Deprecated and ignored.
|
||||||
|
|
||||||
--psk string, $CODER_PROVISIONER_DAEMON_PSK
|
--psk string, $CODER_PROVISIONER_DAEMON_PSK
|
||||||
Pre-shared key to authenticate with Coder server.
|
Pre-shared key to authenticate with Coder server.
|
||||||
|
|
|
@ -394,10 +394,10 @@ updating, and deleting workspace resources.
|
||||||
Time to force cancel provisioning tasks that are stuck.
|
Time to force cancel provisioning tasks that are stuck.
|
||||||
|
|
||||||
--provisioner-daemon-poll-interval duration, $CODER_PROVISIONER_DAEMON_POLL_INTERVAL (default: 1s)
|
--provisioner-daemon-poll-interval duration, $CODER_PROVISIONER_DAEMON_POLL_INTERVAL (default: 1s)
|
||||||
Time to wait before polling for a new job.
|
Deprecated and ignored.
|
||||||
|
|
||||||
--provisioner-daemon-poll-jitter duration, $CODER_PROVISIONER_DAEMON_POLL_JITTER (default: 100ms)
|
--provisioner-daemon-poll-jitter duration, $CODER_PROVISIONER_DAEMON_POLL_JITTER (default: 100ms)
|
||||||
Random jitter added to the poll interval.
|
Deprecated and ignored.
|
||||||
|
|
||||||
--provisioner-daemon-psk string, $CODER_PROVISIONER_DAEMON_PSK
|
--provisioner-daemon-psk string, $CODER_PROVISIONER_DAEMON_PSK
|
||||||
Pre-shared key to authenticate external provisioner daemons to Coder
|
Pre-shared key to authenticate external provisioner daemons to Coder
|
||||||
|
|
|
@ -4,14 +4,12 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/hashicorp/yamux"
|
"github.com/hashicorp/yamux"
|
||||||
|
@ -180,6 +178,15 @@ func (api *API) provisionerDaemonServe(rw http.ResponseWriter, r *http.Request)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
api.Logger.Debug(ctx, "provisioner authorized", slog.F("tags", tags))
|
api.Logger.Debug(ctx, "provisioner authorized", slog.F("tags", tags))
|
||||||
|
if err := provisionerdserver.Tags(tags).Valid(); err != nil {
|
||||||
|
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||||
|
Message: "Given tags are not acceptable to the service",
|
||||||
|
Validations: []codersdk.ValidationError{
|
||||||
|
{Field: "tags", Detail: err.Error()},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
provisioners := make([]database.ProvisionerType, 0)
|
provisioners := make([]database.ProvisionerType, 0)
|
||||||
for p := range provisionersMap {
|
for p := range provisionersMap {
|
||||||
|
@ -197,17 +204,6 @@ func (api *API) provisionerDaemonServe(rw http.ResponseWriter, r *http.Request)
|
||||||
slog.F("provisioners", provisioners),
|
slog.F("provisioners", provisioners),
|
||||||
slog.F("tags", tags),
|
slog.F("tags", tags),
|
||||||
)
|
)
|
||||||
rawTags, err := json.Marshal(tags)
|
|
||||||
if err != nil {
|
|
||||||
if !xerrors.Is(err, context.Canceled) {
|
|
||||||
log.Error(ctx, "marshal provisioner tags", slog.Error(err))
|
|
||||||
}
|
|
||||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
|
||||||
Message: "Internal error marshaling daemon tags.",
|
|
||||||
Detail: err.Error(),
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
api.AGPL.WebsocketWaitMutex.Lock()
|
api.AGPL.WebsocketWaitMutex.Lock()
|
||||||
api.AGPL.WebsocketWaitGroup.Add(1)
|
api.AGPL.WebsocketWaitGroup.Add(1)
|
||||||
|
@ -251,9 +247,10 @@ func (api *API) provisionerDaemonServe(rw http.ResponseWriter, r *http.Request)
|
||||||
uuid.New(),
|
uuid.New(),
|
||||||
logger,
|
logger,
|
||||||
provisioners,
|
provisioners,
|
||||||
rawTags,
|
tags,
|
||||||
api.Database,
|
api.Database,
|
||||||
api.Pubsub,
|
api.Pubsub,
|
||||||
|
api.AGPL.Acquirer,
|
||||||
api.Telemetry,
|
api.Telemetry,
|
||||||
trace.NewNoopTracerProvider().Tracer("noop"),
|
trace.NewNoopTracerProvider().Tracer("noop"),
|
||||||
&api.AGPL.QuotaCommitter,
|
&api.AGPL.QuotaCommitter,
|
||||||
|
@ -261,8 +258,6 @@ func (api *API) provisionerDaemonServe(rw http.ResponseWriter, r *http.Request)
|
||||||
api.AGPL.TemplateScheduleStore,
|
api.AGPL.TemplateScheduleStore,
|
||||||
api.AGPL.UserQuietHoursScheduleStore,
|
api.AGPL.UserQuietHoursScheduleStore,
|
||||||
api.DeploymentValues,
|
api.DeploymentValues,
|
||||||
// TODO(spikecurtis) - fix debounce to not cause flaky tests.
|
|
||||||
time.Duration(0),
|
|
||||||
provisionerdserver.Options{
|
provisionerdserver.Options{
|
||||||
GitAuthConfigs: api.GitAuthConfigs,
|
GitAuthConfigs: api.GitAuthConfigs,
|
||||||
OIDCConfig: api.OIDCConfig,
|
OIDCConfig: api.OIDCConfig,
|
||||||
|
|
|
@ -31,7 +31,7 @@ func TestTemplateUpdateBuildDeadlines(t *testing.T) {
|
||||||
file = dbgen.File(t, db, database.File{
|
file = dbgen.File(t, db, database.File{
|
||||||
CreatedBy: user.ID,
|
CreatedBy: user.ID,
|
||||||
})
|
})
|
||||||
templateJob = dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
templateJob = dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
|
||||||
OrganizationID: org.ID,
|
OrganizationID: org.ID,
|
||||||
FileID: file.ID,
|
FileID: file.ID,
|
||||||
InitiatorID: user.ID,
|
InitiatorID: user.ID,
|
||||||
|
@ -149,7 +149,7 @@ func TestTemplateUpdateBuildDeadlines(t *testing.T) {
|
||||||
OwnerID: user.ID,
|
OwnerID: user.ID,
|
||||||
TemplateID: template.ID,
|
TemplateID: template.ID,
|
||||||
})
|
})
|
||||||
job = dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
job = dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
|
||||||
OrganizationID: org.ID,
|
OrganizationID: org.ID,
|
||||||
FileID: file.ID,
|
FileID: file.ID,
|
||||||
InitiatorID: user.ID,
|
InitiatorID: user.ID,
|
||||||
|
@ -255,7 +255,7 @@ func TestTemplateUpdateBuildDeadlinesSkip(t *testing.T) {
|
||||||
file = dbgen.File(t, db, database.File{
|
file = dbgen.File(t, db, database.File{
|
||||||
CreatedBy: user.ID,
|
CreatedBy: user.ID,
|
||||||
})
|
})
|
||||||
templateJob = dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
templateJob = dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
|
||||||
OrganizationID: org.ID,
|
OrganizationID: org.ID,
|
||||||
FileID: file.ID,
|
FileID: file.ID,
|
||||||
InitiatorID: user.ID,
|
InitiatorID: user.ID,
|
||||||
|
@ -405,7 +405,7 @@ func TestTemplateUpdateBuildDeadlinesSkip(t *testing.T) {
|
||||||
})
|
})
|
||||||
wsID = ws.ID
|
wsID = ws.ID
|
||||||
}
|
}
|
||||||
job := dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
|
job := dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
|
||||||
OrganizationID: org.ID,
|
OrganizationID: org.ID,
|
||||||
FileID: file.ID,
|
FileID: file.ID,
|
||||||
InitiatorID: user.ID,
|
InitiatorID: user.ID,
|
||||||
|
|
|
@ -809,6 +809,44 @@ func (x *CommitQuotaResponse) GetBudget() int32 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CancelAcquire struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CancelAcquire) Reset() {
|
||||||
|
*x = CancelAcquire{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[9]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CancelAcquire) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*CancelAcquire) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *CancelAcquire) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[9]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use CancelAcquire.ProtoReflect.Descriptor instead.
|
||||||
|
func (*CancelAcquire) Descriptor() ([]byte, []int) {
|
||||||
|
return file_provisionerd_proto_provisionerd_proto_rawDescGZIP(), []int{9}
|
||||||
|
}
|
||||||
|
|
||||||
type AcquiredJob_WorkspaceBuild struct {
|
type AcquiredJob_WorkspaceBuild struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
@ -827,7 +865,7 @@ type AcquiredJob_WorkspaceBuild struct {
|
||||||
func (x *AcquiredJob_WorkspaceBuild) Reset() {
|
func (x *AcquiredJob_WorkspaceBuild) Reset() {
|
||||||
*x = AcquiredJob_WorkspaceBuild{}
|
*x = AcquiredJob_WorkspaceBuild{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[9]
|
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[10]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
@ -840,7 +878,7 @@ func (x *AcquiredJob_WorkspaceBuild) String() string {
|
||||||
func (*AcquiredJob_WorkspaceBuild) ProtoMessage() {}
|
func (*AcquiredJob_WorkspaceBuild) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *AcquiredJob_WorkspaceBuild) ProtoReflect() protoreflect.Message {
|
func (x *AcquiredJob_WorkspaceBuild) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[9]
|
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[10]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
@ -924,7 +962,7 @@ type AcquiredJob_TemplateImport struct {
|
||||||
func (x *AcquiredJob_TemplateImport) Reset() {
|
func (x *AcquiredJob_TemplateImport) Reset() {
|
||||||
*x = AcquiredJob_TemplateImport{}
|
*x = AcquiredJob_TemplateImport{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[10]
|
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[11]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
@ -937,7 +975,7 @@ func (x *AcquiredJob_TemplateImport) String() string {
|
||||||
func (*AcquiredJob_TemplateImport) ProtoMessage() {}
|
func (*AcquiredJob_TemplateImport) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *AcquiredJob_TemplateImport) ProtoReflect() protoreflect.Message {
|
func (x *AcquiredJob_TemplateImport) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[10]
|
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[11]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
@ -980,7 +1018,7 @@ type AcquiredJob_TemplateDryRun struct {
|
||||||
func (x *AcquiredJob_TemplateDryRun) Reset() {
|
func (x *AcquiredJob_TemplateDryRun) Reset() {
|
||||||
*x = AcquiredJob_TemplateDryRun{}
|
*x = AcquiredJob_TemplateDryRun{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[11]
|
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[12]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
@ -993,7 +1031,7 @@ func (x *AcquiredJob_TemplateDryRun) String() string {
|
||||||
func (*AcquiredJob_TemplateDryRun) ProtoMessage() {}
|
func (*AcquiredJob_TemplateDryRun) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *AcquiredJob_TemplateDryRun) ProtoReflect() protoreflect.Message {
|
func (x *AcquiredJob_TemplateDryRun) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[11]
|
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[12]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
@ -1041,7 +1079,7 @@ type FailedJob_WorkspaceBuild struct {
|
||||||
func (x *FailedJob_WorkspaceBuild) Reset() {
|
func (x *FailedJob_WorkspaceBuild) Reset() {
|
||||||
*x = FailedJob_WorkspaceBuild{}
|
*x = FailedJob_WorkspaceBuild{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[13]
|
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[14]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
@ -1054,7 +1092,7 @@ func (x *FailedJob_WorkspaceBuild) String() string {
|
||||||
func (*FailedJob_WorkspaceBuild) ProtoMessage() {}
|
func (*FailedJob_WorkspaceBuild) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *FailedJob_WorkspaceBuild) ProtoReflect() protoreflect.Message {
|
func (x *FailedJob_WorkspaceBuild) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[13]
|
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[14]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
@ -1086,7 +1124,7 @@ type FailedJob_TemplateImport struct {
|
||||||
func (x *FailedJob_TemplateImport) Reset() {
|
func (x *FailedJob_TemplateImport) Reset() {
|
||||||
*x = FailedJob_TemplateImport{}
|
*x = FailedJob_TemplateImport{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[14]
|
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[15]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
@ -1099,7 +1137,7 @@ func (x *FailedJob_TemplateImport) String() string {
|
||||||
func (*FailedJob_TemplateImport) ProtoMessage() {}
|
func (*FailedJob_TemplateImport) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *FailedJob_TemplateImport) ProtoReflect() protoreflect.Message {
|
func (x *FailedJob_TemplateImport) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[14]
|
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[15]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
@ -1124,7 +1162,7 @@ type FailedJob_TemplateDryRun struct {
|
||||||
func (x *FailedJob_TemplateDryRun) Reset() {
|
func (x *FailedJob_TemplateDryRun) Reset() {
|
||||||
*x = FailedJob_TemplateDryRun{}
|
*x = FailedJob_TemplateDryRun{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[15]
|
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[16]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
@ -1137,7 +1175,7 @@ func (x *FailedJob_TemplateDryRun) String() string {
|
||||||
func (*FailedJob_TemplateDryRun) ProtoMessage() {}
|
func (*FailedJob_TemplateDryRun) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *FailedJob_TemplateDryRun) ProtoReflect() protoreflect.Message {
|
func (x *FailedJob_TemplateDryRun) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[15]
|
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[16]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
@ -1165,7 +1203,7 @@ type CompletedJob_WorkspaceBuild struct {
|
||||||
func (x *CompletedJob_WorkspaceBuild) Reset() {
|
func (x *CompletedJob_WorkspaceBuild) Reset() {
|
||||||
*x = CompletedJob_WorkspaceBuild{}
|
*x = CompletedJob_WorkspaceBuild{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[16]
|
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[17]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
@ -1178,7 +1216,7 @@ func (x *CompletedJob_WorkspaceBuild) String() string {
|
||||||
func (*CompletedJob_WorkspaceBuild) ProtoMessage() {}
|
func (*CompletedJob_WorkspaceBuild) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *CompletedJob_WorkspaceBuild) ProtoReflect() protoreflect.Message {
|
func (x *CompletedJob_WorkspaceBuild) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[16]
|
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[17]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
@ -1222,7 +1260,7 @@ type CompletedJob_TemplateImport struct {
|
||||||
func (x *CompletedJob_TemplateImport) Reset() {
|
func (x *CompletedJob_TemplateImport) Reset() {
|
||||||
*x = CompletedJob_TemplateImport{}
|
*x = CompletedJob_TemplateImport{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[17]
|
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[18]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
@ -1235,7 +1273,7 @@ func (x *CompletedJob_TemplateImport) String() string {
|
||||||
func (*CompletedJob_TemplateImport) ProtoMessage() {}
|
func (*CompletedJob_TemplateImport) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *CompletedJob_TemplateImport) ProtoReflect() protoreflect.Message {
|
func (x *CompletedJob_TemplateImport) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[17]
|
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[18]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
@ -1290,7 +1328,7 @@ type CompletedJob_TemplateDryRun struct {
|
||||||
func (x *CompletedJob_TemplateDryRun) Reset() {
|
func (x *CompletedJob_TemplateDryRun) Reset() {
|
||||||
*x = CompletedJob_TemplateDryRun{}
|
*x = CompletedJob_TemplateDryRun{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[18]
|
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[19]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
@ -1303,7 +1341,7 @@ func (x *CompletedJob_TemplateDryRun) String() string {
|
||||||
func (*CompletedJob_TemplateDryRun) ProtoMessage() {}
|
func (*CompletedJob_TemplateDryRun) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *CompletedJob_TemplateDryRun) ProtoReflect() protoreflect.Message {
|
func (x *CompletedJob_TemplateDryRun) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[18]
|
mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[19]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
@ -1543,37 +1581,44 @@ var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{
|
||||||
0x69, 0x74, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01,
|
0x69, 0x74, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01,
|
||||||
0x28, 0x05, 0x52, 0x0f, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x73, 0x75,
|
0x28, 0x05, 0x52, 0x0f, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x73, 0x75,
|
||||||
0x6d, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x18, 0x03, 0x20,
|
0x6d, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x18, 0x03, 0x20,
|
||||||
0x01, 0x28, 0x05, 0x52, 0x06, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x2a, 0x34, 0x0a, 0x09, 0x4c,
|
0x01, 0x28, 0x05, 0x52, 0x06, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x22, 0x0f, 0x0a, 0x0d, 0x43,
|
||||||
0x6f, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x50, 0x52, 0x4f, 0x56,
|
0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x2a, 0x34, 0x0a, 0x09,
|
||||||
0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x45, 0x4d, 0x4f, 0x4e, 0x10, 0x00,
|
0x4c, 0x6f, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x50, 0x52, 0x4f,
|
||||||
0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x52, 0x10,
|
0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x45, 0x4d, 0x4f, 0x4e, 0x10,
|
||||||
0x01, 0x32, 0xec, 0x02, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
|
0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x52,
|
||||||
0x72, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x3c, 0x0a, 0x0a, 0x41, 0x63, 0x71, 0x75, 0x69,
|
0x10, 0x01, 0x32, 0xc5, 0x03, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
|
||||||
0x72, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
|
0x65, 0x72, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x41, 0x0a, 0x0a, 0x41, 0x63, 0x71, 0x75,
|
||||||
0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f,
|
0x69, 0x72, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
|
||||||
0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72,
|
0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x70, 0x72,
|
||||||
0x65, 0x64, 0x4a, 0x6f, 0x62, 0x12, 0x52, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51,
|
0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x41, 0x63, 0x71, 0x75, 0x69,
|
||||||
0x75, 0x6f, 0x74, 0x61, 0x12, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
|
0x72, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x52, 0x0a, 0x14, 0x41,
|
||||||
0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52,
|
0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x4a, 0x6f, 0x62, 0x57, 0x69, 0x74, 0x68, 0x43, 0x61, 0x6e,
|
||||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
|
0x63, 0x65, 0x6c, 0x12, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
|
||||||
0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74,
|
0x72, 0x64, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65,
|
||||||
0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x09, 0x55, 0x70, 0x64,
|
0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e,
|
||||||
0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
|
0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x28, 0x01, 0x30, 0x01, 0x12,
|
||||||
0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52,
|
0x52, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x12, 0x20,
|
||||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
|
0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f,
|
||||||
0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52,
|
0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x46, 0x61, 0x69, 0x6c, 0x4a,
|
0x1a, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e,
|
||||||
0x6f, 0x62, 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
|
0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||||
0x64, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72,
|
0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x09, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62,
|
||||||
0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e,
|
||||||
0x12, 0x3e, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12,
|
0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||||
0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43,
|
0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e,
|
||||||
0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72,
|
0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||||
0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
0x65, 0x12, 0x37, 0x0a, 0x07, 0x46, 0x61, 0x69, 0x6c, 0x4a, 0x6f, 0x62, 0x12, 0x17, 0x2e, 0x70,
|
||||||
0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63,
|
0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x46, 0x61, 0x69, 0x6c,
|
||||||
0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x72,
|
0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
|
||||||
0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3e, 0x0a, 0x0b, 0x43, 0x6f,
|
||||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76,
|
||||||
|
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74,
|
||||||
|
0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
|
||||||
|
0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69,
|
||||||
|
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63,
|
||||||
|
0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
|
||||||
|
0x6e, 0x65, 0x72, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -1589,7 +1634,7 @@ func file_provisionerd_proto_provisionerd_proto_rawDescGZIP() []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_provisionerd_proto_provisionerd_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
var file_provisionerd_proto_provisionerd_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||||
var file_provisionerd_proto_provisionerd_proto_msgTypes = make([]protoimpl.MessageInfo, 19)
|
var file_provisionerd_proto_provisionerd_proto_msgTypes = make([]protoimpl.MessageInfo, 20)
|
||||||
var file_provisionerd_proto_provisionerd_proto_goTypes = []interface{}{
|
var file_provisionerd_proto_provisionerd_proto_goTypes = []interface{}{
|
||||||
(LogSource)(0), // 0: provisionerd.LogSource
|
(LogSource)(0), // 0: provisionerd.LogSource
|
||||||
(*Empty)(nil), // 1: provisionerd.Empty
|
(*Empty)(nil), // 1: provisionerd.Empty
|
||||||
|
@ -1601,68 +1646,71 @@ var file_provisionerd_proto_provisionerd_proto_goTypes = []interface{}{
|
||||||
(*UpdateJobResponse)(nil), // 7: provisionerd.UpdateJobResponse
|
(*UpdateJobResponse)(nil), // 7: provisionerd.UpdateJobResponse
|
||||||
(*CommitQuotaRequest)(nil), // 8: provisionerd.CommitQuotaRequest
|
(*CommitQuotaRequest)(nil), // 8: provisionerd.CommitQuotaRequest
|
||||||
(*CommitQuotaResponse)(nil), // 9: provisionerd.CommitQuotaResponse
|
(*CommitQuotaResponse)(nil), // 9: provisionerd.CommitQuotaResponse
|
||||||
(*AcquiredJob_WorkspaceBuild)(nil), // 10: provisionerd.AcquiredJob.WorkspaceBuild
|
(*CancelAcquire)(nil), // 10: provisionerd.CancelAcquire
|
||||||
(*AcquiredJob_TemplateImport)(nil), // 11: provisionerd.AcquiredJob.TemplateImport
|
(*AcquiredJob_WorkspaceBuild)(nil), // 11: provisionerd.AcquiredJob.WorkspaceBuild
|
||||||
(*AcquiredJob_TemplateDryRun)(nil), // 12: provisionerd.AcquiredJob.TemplateDryRun
|
(*AcquiredJob_TemplateImport)(nil), // 12: provisionerd.AcquiredJob.TemplateImport
|
||||||
nil, // 13: provisionerd.AcquiredJob.TraceMetadataEntry
|
(*AcquiredJob_TemplateDryRun)(nil), // 13: provisionerd.AcquiredJob.TemplateDryRun
|
||||||
(*FailedJob_WorkspaceBuild)(nil), // 14: provisionerd.FailedJob.WorkspaceBuild
|
nil, // 14: provisionerd.AcquiredJob.TraceMetadataEntry
|
||||||
(*FailedJob_TemplateImport)(nil), // 15: provisionerd.FailedJob.TemplateImport
|
(*FailedJob_WorkspaceBuild)(nil), // 15: provisionerd.FailedJob.WorkspaceBuild
|
||||||
(*FailedJob_TemplateDryRun)(nil), // 16: provisionerd.FailedJob.TemplateDryRun
|
(*FailedJob_TemplateImport)(nil), // 16: provisionerd.FailedJob.TemplateImport
|
||||||
(*CompletedJob_WorkspaceBuild)(nil), // 17: provisionerd.CompletedJob.WorkspaceBuild
|
(*FailedJob_TemplateDryRun)(nil), // 17: provisionerd.FailedJob.TemplateDryRun
|
||||||
(*CompletedJob_TemplateImport)(nil), // 18: provisionerd.CompletedJob.TemplateImport
|
(*CompletedJob_WorkspaceBuild)(nil), // 18: provisionerd.CompletedJob.WorkspaceBuild
|
||||||
(*CompletedJob_TemplateDryRun)(nil), // 19: provisionerd.CompletedJob.TemplateDryRun
|
(*CompletedJob_TemplateImport)(nil), // 19: provisionerd.CompletedJob.TemplateImport
|
||||||
(proto.LogLevel)(0), // 20: provisioner.LogLevel
|
(*CompletedJob_TemplateDryRun)(nil), // 20: provisionerd.CompletedJob.TemplateDryRun
|
||||||
(*proto.TemplateVariable)(nil), // 21: provisioner.TemplateVariable
|
(proto.LogLevel)(0), // 21: provisioner.LogLevel
|
||||||
(*proto.VariableValue)(nil), // 22: provisioner.VariableValue
|
(*proto.TemplateVariable)(nil), // 22: provisioner.TemplateVariable
|
||||||
(*proto.RichParameterValue)(nil), // 23: provisioner.RichParameterValue
|
(*proto.VariableValue)(nil), // 23: provisioner.VariableValue
|
||||||
(*proto.GitAuthProvider)(nil), // 24: provisioner.GitAuthProvider
|
(*proto.RichParameterValue)(nil), // 24: provisioner.RichParameterValue
|
||||||
(*proto.Metadata)(nil), // 25: provisioner.Metadata
|
(*proto.GitAuthProvider)(nil), // 25: provisioner.GitAuthProvider
|
||||||
(*proto.Resource)(nil), // 26: provisioner.Resource
|
(*proto.Metadata)(nil), // 26: provisioner.Metadata
|
||||||
(*proto.RichParameter)(nil), // 27: provisioner.RichParameter
|
(*proto.Resource)(nil), // 27: provisioner.Resource
|
||||||
|
(*proto.RichParameter)(nil), // 28: provisioner.RichParameter
|
||||||
}
|
}
|
||||||
var file_provisionerd_proto_provisionerd_proto_depIdxs = []int32{
|
var file_provisionerd_proto_provisionerd_proto_depIdxs = []int32{
|
||||||
10, // 0: provisionerd.AcquiredJob.workspace_build:type_name -> provisionerd.AcquiredJob.WorkspaceBuild
|
11, // 0: provisionerd.AcquiredJob.workspace_build:type_name -> provisionerd.AcquiredJob.WorkspaceBuild
|
||||||
11, // 1: provisionerd.AcquiredJob.template_import:type_name -> provisionerd.AcquiredJob.TemplateImport
|
12, // 1: provisionerd.AcquiredJob.template_import:type_name -> provisionerd.AcquiredJob.TemplateImport
|
||||||
12, // 2: provisionerd.AcquiredJob.template_dry_run:type_name -> provisionerd.AcquiredJob.TemplateDryRun
|
13, // 2: provisionerd.AcquiredJob.template_dry_run:type_name -> provisionerd.AcquiredJob.TemplateDryRun
|
||||||
13, // 3: provisionerd.AcquiredJob.trace_metadata:type_name -> provisionerd.AcquiredJob.TraceMetadataEntry
|
14, // 3: provisionerd.AcquiredJob.trace_metadata:type_name -> provisionerd.AcquiredJob.TraceMetadataEntry
|
||||||
14, // 4: provisionerd.FailedJob.workspace_build:type_name -> provisionerd.FailedJob.WorkspaceBuild
|
15, // 4: provisionerd.FailedJob.workspace_build:type_name -> provisionerd.FailedJob.WorkspaceBuild
|
||||||
15, // 5: provisionerd.FailedJob.template_import:type_name -> provisionerd.FailedJob.TemplateImport
|
16, // 5: provisionerd.FailedJob.template_import:type_name -> provisionerd.FailedJob.TemplateImport
|
||||||
16, // 6: provisionerd.FailedJob.template_dry_run:type_name -> provisionerd.FailedJob.TemplateDryRun
|
17, // 6: provisionerd.FailedJob.template_dry_run:type_name -> provisionerd.FailedJob.TemplateDryRun
|
||||||
17, // 7: provisionerd.CompletedJob.workspace_build:type_name -> provisionerd.CompletedJob.WorkspaceBuild
|
18, // 7: provisionerd.CompletedJob.workspace_build:type_name -> provisionerd.CompletedJob.WorkspaceBuild
|
||||||
18, // 8: provisionerd.CompletedJob.template_import:type_name -> provisionerd.CompletedJob.TemplateImport
|
19, // 8: provisionerd.CompletedJob.template_import:type_name -> provisionerd.CompletedJob.TemplateImport
|
||||||
19, // 9: provisionerd.CompletedJob.template_dry_run:type_name -> provisionerd.CompletedJob.TemplateDryRun
|
20, // 9: provisionerd.CompletedJob.template_dry_run:type_name -> provisionerd.CompletedJob.TemplateDryRun
|
||||||
0, // 10: provisionerd.Log.source:type_name -> provisionerd.LogSource
|
0, // 10: provisionerd.Log.source:type_name -> provisionerd.LogSource
|
||||||
20, // 11: provisionerd.Log.level:type_name -> provisioner.LogLevel
|
21, // 11: provisionerd.Log.level:type_name -> provisioner.LogLevel
|
||||||
5, // 12: provisionerd.UpdateJobRequest.logs:type_name -> provisionerd.Log
|
5, // 12: provisionerd.UpdateJobRequest.logs:type_name -> provisionerd.Log
|
||||||
21, // 13: provisionerd.UpdateJobRequest.template_variables:type_name -> provisioner.TemplateVariable
|
22, // 13: provisionerd.UpdateJobRequest.template_variables:type_name -> provisioner.TemplateVariable
|
||||||
22, // 14: provisionerd.UpdateJobRequest.user_variable_values:type_name -> provisioner.VariableValue
|
23, // 14: provisionerd.UpdateJobRequest.user_variable_values:type_name -> provisioner.VariableValue
|
||||||
22, // 15: provisionerd.UpdateJobResponse.variable_values:type_name -> provisioner.VariableValue
|
23, // 15: provisionerd.UpdateJobResponse.variable_values:type_name -> provisioner.VariableValue
|
||||||
23, // 16: provisionerd.AcquiredJob.WorkspaceBuild.rich_parameter_values:type_name -> provisioner.RichParameterValue
|
24, // 16: provisionerd.AcquiredJob.WorkspaceBuild.rich_parameter_values:type_name -> provisioner.RichParameterValue
|
||||||
22, // 17: provisionerd.AcquiredJob.WorkspaceBuild.variable_values:type_name -> provisioner.VariableValue
|
23, // 17: provisionerd.AcquiredJob.WorkspaceBuild.variable_values:type_name -> provisioner.VariableValue
|
||||||
24, // 18: provisionerd.AcquiredJob.WorkspaceBuild.git_auth_providers:type_name -> provisioner.GitAuthProvider
|
25, // 18: provisionerd.AcquiredJob.WorkspaceBuild.git_auth_providers:type_name -> provisioner.GitAuthProvider
|
||||||
25, // 19: provisionerd.AcquiredJob.WorkspaceBuild.metadata:type_name -> provisioner.Metadata
|
26, // 19: provisionerd.AcquiredJob.WorkspaceBuild.metadata:type_name -> provisioner.Metadata
|
||||||
25, // 20: provisionerd.AcquiredJob.TemplateImport.metadata:type_name -> provisioner.Metadata
|
26, // 20: provisionerd.AcquiredJob.TemplateImport.metadata:type_name -> provisioner.Metadata
|
||||||
22, // 21: provisionerd.AcquiredJob.TemplateImport.user_variable_values:type_name -> provisioner.VariableValue
|
23, // 21: provisionerd.AcquiredJob.TemplateImport.user_variable_values:type_name -> provisioner.VariableValue
|
||||||
23, // 22: provisionerd.AcquiredJob.TemplateDryRun.rich_parameter_values:type_name -> provisioner.RichParameterValue
|
24, // 22: provisionerd.AcquiredJob.TemplateDryRun.rich_parameter_values:type_name -> provisioner.RichParameterValue
|
||||||
22, // 23: provisionerd.AcquiredJob.TemplateDryRun.variable_values:type_name -> provisioner.VariableValue
|
23, // 23: provisionerd.AcquiredJob.TemplateDryRun.variable_values:type_name -> provisioner.VariableValue
|
||||||
25, // 24: provisionerd.AcquiredJob.TemplateDryRun.metadata:type_name -> provisioner.Metadata
|
26, // 24: provisionerd.AcquiredJob.TemplateDryRun.metadata:type_name -> provisioner.Metadata
|
||||||
26, // 25: provisionerd.CompletedJob.WorkspaceBuild.resources:type_name -> provisioner.Resource
|
27, // 25: provisionerd.CompletedJob.WorkspaceBuild.resources:type_name -> provisioner.Resource
|
||||||
26, // 26: provisionerd.CompletedJob.TemplateImport.start_resources:type_name -> provisioner.Resource
|
27, // 26: provisionerd.CompletedJob.TemplateImport.start_resources:type_name -> provisioner.Resource
|
||||||
26, // 27: provisionerd.CompletedJob.TemplateImport.stop_resources:type_name -> provisioner.Resource
|
27, // 27: provisionerd.CompletedJob.TemplateImport.stop_resources:type_name -> provisioner.Resource
|
||||||
27, // 28: provisionerd.CompletedJob.TemplateImport.rich_parameters:type_name -> provisioner.RichParameter
|
28, // 28: provisionerd.CompletedJob.TemplateImport.rich_parameters:type_name -> provisioner.RichParameter
|
||||||
26, // 29: provisionerd.CompletedJob.TemplateDryRun.resources:type_name -> provisioner.Resource
|
27, // 29: provisionerd.CompletedJob.TemplateDryRun.resources:type_name -> provisioner.Resource
|
||||||
1, // 30: provisionerd.ProvisionerDaemon.AcquireJob:input_type -> provisionerd.Empty
|
1, // 30: provisionerd.ProvisionerDaemon.AcquireJob:input_type -> provisionerd.Empty
|
||||||
8, // 31: provisionerd.ProvisionerDaemon.CommitQuota:input_type -> provisionerd.CommitQuotaRequest
|
10, // 31: provisionerd.ProvisionerDaemon.AcquireJobWithCancel:input_type -> provisionerd.CancelAcquire
|
||||||
6, // 32: provisionerd.ProvisionerDaemon.UpdateJob:input_type -> provisionerd.UpdateJobRequest
|
8, // 32: provisionerd.ProvisionerDaemon.CommitQuota:input_type -> provisionerd.CommitQuotaRequest
|
||||||
3, // 33: provisionerd.ProvisionerDaemon.FailJob:input_type -> provisionerd.FailedJob
|
6, // 33: provisionerd.ProvisionerDaemon.UpdateJob:input_type -> provisionerd.UpdateJobRequest
|
||||||
4, // 34: provisionerd.ProvisionerDaemon.CompleteJob:input_type -> provisionerd.CompletedJob
|
3, // 34: provisionerd.ProvisionerDaemon.FailJob:input_type -> provisionerd.FailedJob
|
||||||
2, // 35: provisionerd.ProvisionerDaemon.AcquireJob:output_type -> provisionerd.AcquiredJob
|
4, // 35: provisionerd.ProvisionerDaemon.CompleteJob:input_type -> provisionerd.CompletedJob
|
||||||
9, // 36: provisionerd.ProvisionerDaemon.CommitQuota:output_type -> provisionerd.CommitQuotaResponse
|
2, // 36: provisionerd.ProvisionerDaemon.AcquireJob:output_type -> provisionerd.AcquiredJob
|
||||||
7, // 37: provisionerd.ProvisionerDaemon.UpdateJob:output_type -> provisionerd.UpdateJobResponse
|
2, // 37: provisionerd.ProvisionerDaemon.AcquireJobWithCancel:output_type -> provisionerd.AcquiredJob
|
||||||
1, // 38: provisionerd.ProvisionerDaemon.FailJob:output_type -> provisionerd.Empty
|
9, // 38: provisionerd.ProvisionerDaemon.CommitQuota:output_type -> provisionerd.CommitQuotaResponse
|
||||||
1, // 39: provisionerd.ProvisionerDaemon.CompleteJob:output_type -> provisionerd.Empty
|
7, // 39: provisionerd.ProvisionerDaemon.UpdateJob:output_type -> provisionerd.UpdateJobResponse
|
||||||
35, // [35:40] is the sub-list for method output_type
|
1, // 40: provisionerd.ProvisionerDaemon.FailJob:output_type -> provisionerd.Empty
|
||||||
30, // [30:35] is the sub-list for method input_type
|
1, // 41: provisionerd.ProvisionerDaemon.CompleteJob:output_type -> provisionerd.Empty
|
||||||
|
36, // [36:42] is the sub-list for method output_type
|
||||||
|
30, // [30:36] is the sub-list for method input_type
|
||||||
30, // [30:30] is the sub-list for extension type_name
|
30, // [30:30] is the sub-list for extension type_name
|
||||||
30, // [30:30] is the sub-list for extension extendee
|
30, // [30:30] is the sub-list for extension extendee
|
||||||
0, // [0:30] is the sub-list for field type_name
|
0, // [0:30] is the sub-list for field type_name
|
||||||
|
@ -1783,7 +1831,7 @@ func file_provisionerd_proto_provisionerd_proto_init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_provisionerd_proto_provisionerd_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
|
file_provisionerd_proto_provisionerd_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*AcquiredJob_WorkspaceBuild); i {
|
switch v := v.(*CancelAcquire); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -1795,7 +1843,7 @@ func file_provisionerd_proto_provisionerd_proto_init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_provisionerd_proto_provisionerd_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
|
file_provisionerd_proto_provisionerd_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*AcquiredJob_TemplateImport); i {
|
switch v := v.(*AcquiredJob_WorkspaceBuild); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -1807,6 +1855,18 @@ func file_provisionerd_proto_provisionerd_proto_init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_provisionerd_proto_provisionerd_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
|
file_provisionerd_proto_provisionerd_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*AcquiredJob_TemplateImport); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_provisionerd_proto_provisionerd_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*AcquiredJob_TemplateDryRun); i {
|
switch v := v.(*AcquiredJob_TemplateDryRun); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1818,7 +1878,7 @@ func file_provisionerd_proto_provisionerd_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_provisionerd_proto_provisionerd_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
|
file_provisionerd_proto_provisionerd_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*FailedJob_WorkspaceBuild); i {
|
switch v := v.(*FailedJob_WorkspaceBuild); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1830,7 +1890,7 @@ func file_provisionerd_proto_provisionerd_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_provisionerd_proto_provisionerd_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
|
file_provisionerd_proto_provisionerd_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*FailedJob_TemplateImport); i {
|
switch v := v.(*FailedJob_TemplateImport); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1842,7 +1902,7 @@ func file_provisionerd_proto_provisionerd_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_provisionerd_proto_provisionerd_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
|
file_provisionerd_proto_provisionerd_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*FailedJob_TemplateDryRun); i {
|
switch v := v.(*FailedJob_TemplateDryRun); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1854,7 +1914,7 @@ func file_provisionerd_proto_provisionerd_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_provisionerd_proto_provisionerd_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
|
file_provisionerd_proto_provisionerd_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*CompletedJob_WorkspaceBuild); i {
|
switch v := v.(*CompletedJob_WorkspaceBuild); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1866,7 +1926,7 @@ func file_provisionerd_proto_provisionerd_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_provisionerd_proto_provisionerd_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
|
file_provisionerd_proto_provisionerd_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*CompletedJob_TemplateImport); i {
|
switch v := v.(*CompletedJob_TemplateImport); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1878,7 +1938,7 @@ func file_provisionerd_proto_provisionerd_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_provisionerd_proto_provisionerd_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} {
|
file_provisionerd_proto_provisionerd_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*CompletedJob_TemplateDryRun); i {
|
switch v := v.(*CompletedJob_TemplateDryRun); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1912,7 +1972,7 @@ func file_provisionerd_proto_provisionerd_proto_init() {
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_provisionerd_proto_provisionerd_proto_rawDesc,
|
RawDescriptor: file_provisionerd_proto_provisionerd_proto_rawDesc,
|
||||||
NumEnums: 1,
|
NumEnums: 1,
|
||||||
NumMessages: 19,
|
NumMessages: 20,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 1,
|
NumServices: 1,
|
||||||
},
|
},
|
||||||
|
|
|
@ -135,11 +135,22 @@ message CommitQuotaResponse {
|
||||||
int32 budget = 3;
|
int32 budget = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message CancelAcquire {}
|
||||||
|
|
||||||
service ProvisionerDaemon {
|
service ProvisionerDaemon {
|
||||||
// AcquireJob requests a job. Implementations should
|
// AcquireJob requests a job. Implementations should
|
||||||
// hold a lock on the job until CompleteJob() is
|
// hold a lock on the job until CompleteJob() is
|
||||||
// called with the matching ID.
|
// called with the matching ID.
|
||||||
rpc AcquireJob(Empty) returns (AcquiredJob);
|
rpc AcquireJob(Empty) returns (AcquiredJob) {
|
||||||
|
option deprecated = true;
|
||||||
|
};
|
||||||
|
// AcquireJobWithCancel requests a job, blocking until
|
||||||
|
// a job is available or the client sends CancelAcquire.
|
||||||
|
// Server will send exactly one AcquiredJob, which is
|
||||||
|
// empty if a cancel was successful. This RPC is a bidirectional
|
||||||
|
// stream since both messages are asynchronous with no implied
|
||||||
|
// ordering.
|
||||||
|
rpc AcquireJobWithCancel(stream CancelAcquire) returns (stream AcquiredJob);
|
||||||
|
|
||||||
rpc CommitQuota(CommitQuotaRequest) returns (CommitQuotaResponse);
|
rpc CommitQuota(CommitQuotaRequest) returns (CommitQuotaResponse);
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ type DRPCProvisionerDaemonClient interface {
|
||||||
DRPCConn() drpc.Conn
|
DRPCConn() drpc.Conn
|
||||||
|
|
||||||
AcquireJob(ctx context.Context, in *Empty) (*AcquiredJob, error)
|
AcquireJob(ctx context.Context, in *Empty) (*AcquiredJob, error)
|
||||||
|
AcquireJobWithCancel(ctx context.Context) (DRPCProvisionerDaemon_AcquireJobWithCancelClient, error)
|
||||||
CommitQuota(ctx context.Context, in *CommitQuotaRequest) (*CommitQuotaResponse, error)
|
CommitQuota(ctx context.Context, in *CommitQuotaRequest) (*CommitQuotaResponse, error)
|
||||||
UpdateJob(ctx context.Context, in *UpdateJobRequest) (*UpdateJobResponse, error)
|
UpdateJob(ctx context.Context, in *UpdateJobRequest) (*UpdateJobResponse, error)
|
||||||
FailJob(ctx context.Context, in *FailedJob) (*Empty, error)
|
FailJob(ctx context.Context, in *FailedJob) (*Empty, error)
|
||||||
|
@ -64,6 +65,45 @@ func (c *drpcProvisionerDaemonClient) AcquireJob(ctx context.Context, in *Empty)
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *drpcProvisionerDaemonClient) AcquireJobWithCancel(ctx context.Context) (DRPCProvisionerDaemon_AcquireJobWithCancelClient, error) {
|
||||||
|
stream, err := c.cc.NewStream(ctx, "/provisionerd.ProvisionerDaemon/AcquireJobWithCancel", drpcEncoding_File_provisionerd_proto_provisionerd_proto{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &drpcProvisionerDaemon_AcquireJobWithCancelClient{stream}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DRPCProvisionerDaemon_AcquireJobWithCancelClient interface {
|
||||||
|
drpc.Stream
|
||||||
|
Send(*CancelAcquire) error
|
||||||
|
Recv() (*AcquiredJob, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type drpcProvisionerDaemon_AcquireJobWithCancelClient struct {
|
||||||
|
drpc.Stream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *drpcProvisionerDaemon_AcquireJobWithCancelClient) GetStream() drpc.Stream {
|
||||||
|
return x.Stream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *drpcProvisionerDaemon_AcquireJobWithCancelClient) Send(m *CancelAcquire) error {
|
||||||
|
return x.MsgSend(m, drpcEncoding_File_provisionerd_proto_provisionerd_proto{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *drpcProvisionerDaemon_AcquireJobWithCancelClient) Recv() (*AcquiredJob, error) {
|
||||||
|
m := new(AcquiredJob)
|
||||||
|
if err := x.MsgRecv(m, drpcEncoding_File_provisionerd_proto_provisionerd_proto{}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *drpcProvisionerDaemon_AcquireJobWithCancelClient) RecvMsg(m *AcquiredJob) error {
|
||||||
|
return x.MsgRecv(m, drpcEncoding_File_provisionerd_proto_provisionerd_proto{})
|
||||||
|
}
|
||||||
|
|
||||||
func (c *drpcProvisionerDaemonClient) CommitQuota(ctx context.Context, in *CommitQuotaRequest) (*CommitQuotaResponse, error) {
|
func (c *drpcProvisionerDaemonClient) CommitQuota(ctx context.Context, in *CommitQuotaRequest) (*CommitQuotaResponse, error) {
|
||||||
out := new(CommitQuotaResponse)
|
out := new(CommitQuotaResponse)
|
||||||
err := c.cc.Invoke(ctx, "/provisionerd.ProvisionerDaemon/CommitQuota", drpcEncoding_File_provisionerd_proto_provisionerd_proto{}, in, out)
|
err := c.cc.Invoke(ctx, "/provisionerd.ProvisionerDaemon/CommitQuota", drpcEncoding_File_provisionerd_proto_provisionerd_proto{}, in, out)
|
||||||
|
@ -102,6 +142,7 @@ func (c *drpcProvisionerDaemonClient) CompleteJob(ctx context.Context, in *Compl
|
||||||
|
|
||||||
type DRPCProvisionerDaemonServer interface {
|
type DRPCProvisionerDaemonServer interface {
|
||||||
AcquireJob(context.Context, *Empty) (*AcquiredJob, error)
|
AcquireJob(context.Context, *Empty) (*AcquiredJob, error)
|
||||||
|
AcquireJobWithCancel(DRPCProvisionerDaemon_AcquireJobWithCancelStream) error
|
||||||
CommitQuota(context.Context, *CommitQuotaRequest) (*CommitQuotaResponse, error)
|
CommitQuota(context.Context, *CommitQuotaRequest) (*CommitQuotaResponse, error)
|
||||||
UpdateJob(context.Context, *UpdateJobRequest) (*UpdateJobResponse, error)
|
UpdateJob(context.Context, *UpdateJobRequest) (*UpdateJobResponse, error)
|
||||||
FailJob(context.Context, *FailedJob) (*Empty, error)
|
FailJob(context.Context, *FailedJob) (*Empty, error)
|
||||||
|
@ -114,6 +155,10 @@ func (s *DRPCProvisionerDaemonUnimplementedServer) AcquireJob(context.Context, *
|
||||||
return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
|
return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DRPCProvisionerDaemonUnimplementedServer) AcquireJobWithCancel(DRPCProvisionerDaemon_AcquireJobWithCancelStream) error {
|
||||||
|
return drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *DRPCProvisionerDaemonUnimplementedServer) CommitQuota(context.Context, *CommitQuotaRequest) (*CommitQuotaResponse, error) {
|
func (s *DRPCProvisionerDaemonUnimplementedServer) CommitQuota(context.Context, *CommitQuotaRequest) (*CommitQuotaResponse, error) {
|
||||||
return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
|
return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
|
||||||
}
|
}
|
||||||
|
@ -132,7 +177,7 @@ func (s *DRPCProvisionerDaemonUnimplementedServer) CompleteJob(context.Context,
|
||||||
|
|
||||||
type DRPCProvisionerDaemonDescription struct{}
|
type DRPCProvisionerDaemonDescription struct{}
|
||||||
|
|
||||||
func (DRPCProvisionerDaemonDescription) NumMethods() int { return 5 }
|
func (DRPCProvisionerDaemonDescription) NumMethods() int { return 6 }
|
||||||
|
|
||||||
func (DRPCProvisionerDaemonDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver, interface{}, bool) {
|
func (DRPCProvisionerDaemonDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver, interface{}, bool) {
|
||||||
switch n {
|
switch n {
|
||||||
|
@ -146,6 +191,14 @@ func (DRPCProvisionerDaemonDescription) Method(n int) (string, drpc.Encoding, dr
|
||||||
)
|
)
|
||||||
}, DRPCProvisionerDaemonServer.AcquireJob, true
|
}, DRPCProvisionerDaemonServer.AcquireJob, true
|
||||||
case 1:
|
case 1:
|
||||||
|
return "/provisionerd.ProvisionerDaemon/AcquireJobWithCancel", drpcEncoding_File_provisionerd_proto_provisionerd_proto{},
|
||||||
|
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
||||||
|
return nil, srv.(DRPCProvisionerDaemonServer).
|
||||||
|
AcquireJobWithCancel(
|
||||||
|
&drpcProvisionerDaemon_AcquireJobWithCancelStream{in1.(drpc.Stream)},
|
||||||
|
)
|
||||||
|
}, DRPCProvisionerDaemonServer.AcquireJobWithCancel, true
|
||||||
|
case 2:
|
||||||
return "/provisionerd.ProvisionerDaemon/CommitQuota", drpcEncoding_File_provisionerd_proto_provisionerd_proto{},
|
return "/provisionerd.ProvisionerDaemon/CommitQuota", drpcEncoding_File_provisionerd_proto_provisionerd_proto{},
|
||||||
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
||||||
return srv.(DRPCProvisionerDaemonServer).
|
return srv.(DRPCProvisionerDaemonServer).
|
||||||
|
@ -154,7 +207,7 @@ func (DRPCProvisionerDaemonDescription) Method(n int) (string, drpc.Encoding, dr
|
||||||
in1.(*CommitQuotaRequest),
|
in1.(*CommitQuotaRequest),
|
||||||
)
|
)
|
||||||
}, DRPCProvisionerDaemonServer.CommitQuota, true
|
}, DRPCProvisionerDaemonServer.CommitQuota, true
|
||||||
case 2:
|
case 3:
|
||||||
return "/provisionerd.ProvisionerDaemon/UpdateJob", drpcEncoding_File_provisionerd_proto_provisionerd_proto{},
|
return "/provisionerd.ProvisionerDaemon/UpdateJob", drpcEncoding_File_provisionerd_proto_provisionerd_proto{},
|
||||||
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
||||||
return srv.(DRPCProvisionerDaemonServer).
|
return srv.(DRPCProvisionerDaemonServer).
|
||||||
|
@ -163,7 +216,7 @@ func (DRPCProvisionerDaemonDescription) Method(n int) (string, drpc.Encoding, dr
|
||||||
in1.(*UpdateJobRequest),
|
in1.(*UpdateJobRequest),
|
||||||
)
|
)
|
||||||
}, DRPCProvisionerDaemonServer.UpdateJob, true
|
}, DRPCProvisionerDaemonServer.UpdateJob, true
|
||||||
case 3:
|
case 4:
|
||||||
return "/provisionerd.ProvisionerDaemon/FailJob", drpcEncoding_File_provisionerd_proto_provisionerd_proto{},
|
return "/provisionerd.ProvisionerDaemon/FailJob", drpcEncoding_File_provisionerd_proto_provisionerd_proto{},
|
||||||
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
||||||
return srv.(DRPCProvisionerDaemonServer).
|
return srv.(DRPCProvisionerDaemonServer).
|
||||||
|
@ -172,7 +225,7 @@ func (DRPCProvisionerDaemonDescription) Method(n int) (string, drpc.Encoding, dr
|
||||||
in1.(*FailedJob),
|
in1.(*FailedJob),
|
||||||
)
|
)
|
||||||
}, DRPCProvisionerDaemonServer.FailJob, true
|
}, DRPCProvisionerDaemonServer.FailJob, true
|
||||||
case 4:
|
case 5:
|
||||||
return "/provisionerd.ProvisionerDaemon/CompleteJob", drpcEncoding_File_provisionerd_proto_provisionerd_proto{},
|
return "/provisionerd.ProvisionerDaemon/CompleteJob", drpcEncoding_File_provisionerd_proto_provisionerd_proto{},
|
||||||
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
||||||
return srv.(DRPCProvisionerDaemonServer).
|
return srv.(DRPCProvisionerDaemonServer).
|
||||||
|
@ -206,6 +259,32 @@ func (x *drpcProvisionerDaemon_AcquireJobStream) SendAndClose(m *AcquiredJob) er
|
||||||
return x.CloseSend()
|
return x.CloseSend()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DRPCProvisionerDaemon_AcquireJobWithCancelStream interface {
|
||||||
|
drpc.Stream
|
||||||
|
Send(*AcquiredJob) error
|
||||||
|
Recv() (*CancelAcquire, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type drpcProvisionerDaemon_AcquireJobWithCancelStream struct {
|
||||||
|
drpc.Stream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *drpcProvisionerDaemon_AcquireJobWithCancelStream) Send(m *AcquiredJob) error {
|
||||||
|
return x.MsgSend(m, drpcEncoding_File_provisionerd_proto_provisionerd_proto{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *drpcProvisionerDaemon_AcquireJobWithCancelStream) Recv() (*CancelAcquire, error) {
|
||||||
|
m := new(CancelAcquire)
|
||||||
|
if err := x.MsgRecv(m, drpcEncoding_File_provisionerd_proto_provisionerd_proto{}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *drpcProvisionerDaemon_AcquireJobWithCancelStream) RecvMsg(m *CancelAcquire) error {
|
||||||
|
return x.MsgRecv(m, drpcEncoding_File_provisionerd_proto_provisionerd_proto{})
|
||||||
|
}
|
||||||
|
|
||||||
type DRPCProvisionerDaemon_CommitQuotaStream interface {
|
type DRPCProvisionerDaemon_CommitQuotaStream interface {
|
||||||
drpc.Stream
|
drpc.Stream
|
||||||
SendAndClose(*CommitQuotaResponse) error
|
SendAndClose(*CommitQuotaResponse) error
|
||||||
|
|
|
@ -16,13 +16,10 @@ import (
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
semconv "go.opentelemetry.io/otel/semconv/v1.14.0"
|
semconv "go.opentelemetry.io/otel/semconv/v1.14.0"
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
"go.uber.org/atomic"
|
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
"cdr.dev/slog"
|
"cdr.dev/slog"
|
||||||
"github.com/coder/coder/v2/coderd/tracing"
|
"github.com/coder/coder/v2/coderd/tracing"
|
||||||
"github.com/coder/coder/v2/coderd/util/ptr"
|
|
||||||
"github.com/coder/coder/v2/cryptorand"
|
|
||||||
"github.com/coder/coder/v2/provisionerd/proto"
|
"github.com/coder/coder/v2/provisionerd/proto"
|
||||||
"github.com/coder/coder/v2/provisionerd/runner"
|
"github.com/coder/coder/v2/provisionerd/runner"
|
||||||
sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
|
sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
|
||||||
|
@ -60,9 +57,6 @@ type Options struct {
|
||||||
ForceCancelInterval time.Duration
|
ForceCancelInterval time.Duration
|
||||||
UpdateInterval time.Duration
|
UpdateInterval time.Duration
|
||||||
LogBufferInterval time.Duration
|
LogBufferInterval time.Duration
|
||||||
JobPollInterval time.Duration
|
|
||||||
JobPollJitter time.Duration
|
|
||||||
JobPollDebounce time.Duration
|
|
||||||
Connector Connector
|
Connector Connector
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,12 +65,6 @@ func New(clientDialer Dialer, opts *Options) *Server {
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = &Options{}
|
opts = &Options{}
|
||||||
}
|
}
|
||||||
if opts.JobPollInterval == 0 {
|
|
||||||
opts.JobPollInterval = 5 * time.Second
|
|
||||||
}
|
|
||||||
if opts.JobPollJitter == 0 {
|
|
||||||
opts.JobPollJitter = time.Second
|
|
||||||
}
|
|
||||||
if opts.UpdateInterval == 0 {
|
if opts.UpdateInterval == 0 {
|
||||||
opts.UpdateInterval = 5 * time.Second
|
opts.UpdateInterval = 5 * time.Second
|
||||||
}
|
}
|
||||||
|
@ -101,14 +89,18 @@ func New(clientDialer Dialer, opts *Options) *Server {
|
||||||
tracer: opts.TracerProvider.Tracer(tracing.TracerName),
|
tracer: opts.TracerProvider.Tracer(tracing.TracerName),
|
||||||
|
|
||||||
clientDialer: clientDialer,
|
clientDialer: clientDialer,
|
||||||
|
clientCh: make(chan proto.DRPCProvisionerDaemonClient),
|
||||||
|
|
||||||
closeContext: ctx,
|
closeContext: ctx,
|
||||||
closeCancel: ctxCancel,
|
closeCancel: ctxCancel,
|
||||||
|
closedCh: make(chan struct{}),
|
||||||
shutdown: make(chan struct{}),
|
shuttingDownCh: make(chan struct{}),
|
||||||
|
acquireDoneCh: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
go daemon.connect(ctx)
|
daemon.wg.Add(2)
|
||||||
|
go daemon.connect()
|
||||||
|
go daemon.acquireLoop()
|
||||||
return daemon
|
return daemon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,15 +109,28 @@ type Server struct {
|
||||||
tracer trace.Tracer
|
tracer trace.Tracer
|
||||||
|
|
||||||
clientDialer Dialer
|
clientDialer Dialer
|
||||||
clientValue atomic.Pointer[proto.DRPCProvisionerDaemonClient]
|
clientCh chan proto.DRPCProvisionerDaemonClient
|
||||||
|
|
||||||
// Locked when closing the daemon, shutting down, or starting a new job.
|
wg sync.WaitGroup
|
||||||
mutex sync.Mutex
|
|
||||||
|
// mutex protects all subsequent fields
|
||||||
|
mutex sync.Mutex
|
||||||
|
// closeContext is canceled when we start closing.
|
||||||
closeContext context.Context
|
closeContext context.Context
|
||||||
closeCancel context.CancelFunc
|
closeCancel context.CancelFunc
|
||||||
closeError error
|
// closeError stores the error when closing to return to subsequent callers
|
||||||
shutdown chan struct{}
|
closeError error
|
||||||
activeJob *runner.Runner
|
// closingB is set to true when we start closing
|
||||||
|
closingB bool
|
||||||
|
// closedCh will receive when we complete closing
|
||||||
|
closedCh chan struct{}
|
||||||
|
// shuttingDownB is set to true when we start graceful shutdown
|
||||||
|
shuttingDownB bool
|
||||||
|
// shuttingDownCh will receive when we start graceful shutdown
|
||||||
|
shuttingDownCh chan struct{}
|
||||||
|
// acquireDoneCh will receive when the acquireLoop exits
|
||||||
|
acquireDoneCh chan struct{}
|
||||||
|
activeJob *runner.Runner
|
||||||
}
|
}
|
||||||
|
|
||||||
type Metrics struct {
|
type Metrics struct {
|
||||||
|
@ -176,16 +181,20 @@ func NewMetrics(reg prometheus.Registerer) Metrics {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect establishes a connection to coderd.
|
// Connect establishes a connection to coderd.
|
||||||
func (p *Server) connect(ctx context.Context) {
|
func (p *Server) connect() {
|
||||||
|
defer p.opts.Logger.Debug(p.closeContext, "connect loop exited")
|
||||||
|
defer p.wg.Done()
|
||||||
// An exponential back-off occurs when the connection is failing to dial.
|
// An exponential back-off occurs when the connection is failing to dial.
|
||||||
// This is to prevent server spam in case of a coderd outage.
|
// This is to prevent server spam in case of a coderd outage.
|
||||||
for retrier := retry.New(50*time.Millisecond, 10*time.Second); retrier.Wait(ctx); {
|
connectLoop:
|
||||||
|
for retrier := retry.New(50*time.Millisecond, 10*time.Second); retrier.Wait(p.closeContext); {
|
||||||
// It's possible for the provisioner daemon to be shut down
|
// It's possible for the provisioner daemon to be shut down
|
||||||
// before the wait is complete!
|
// before the wait is complete!
|
||||||
if p.isClosed() {
|
if p.isClosed() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
client, err := p.clientDialer(ctx)
|
p.opts.Logger.Debug(p.closeContext, "dialing coderd")
|
||||||
|
client, err := p.clientDialer(p.closeContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, context.Canceled) {
|
if errors.Is(err, context.Canceled) {
|
||||||
return
|
return
|
||||||
|
@ -193,144 +202,75 @@ func (p *Server) connect(ctx context.Context) {
|
||||||
if p.isClosed() {
|
if p.isClosed() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.opts.Logger.Warn(context.Background(), "coderd client failed to dial", slog.Error(err))
|
p.opts.Logger.Warn(p.closeContext, "coderd client failed to dial", slog.Error(err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Ensure connection is not left hanging during a race between
|
p.opts.Logger.Info(p.closeContext, "successfully connected to coderd")
|
||||||
// close and dial succeeding.
|
retrier.Reset()
|
||||||
p.mutex.Lock()
|
|
||||||
if p.isClosed() {
|
// serve the client until we are closed or it disconnects
|
||||||
client.DRPCConn().Close()
|
for {
|
||||||
p.mutex.Unlock()
|
select {
|
||||||
break
|
case <-p.closeContext.Done():
|
||||||
|
client.DRPCConn().Close()
|
||||||
|
return
|
||||||
|
case <-client.DRPCConn().Closed():
|
||||||
|
p.opts.Logger.Info(p.closeContext, "connection to coderd closed")
|
||||||
|
continue connectLoop
|
||||||
|
case p.clientCh <- client:
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
p.clientValue.Store(ptr.Ref(client))
|
|
||||||
p.mutex.Unlock()
|
|
||||||
|
|
||||||
p.opts.Logger.Debug(ctx, "successfully connected to coderd")
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Server) client() (proto.DRPCProvisionerDaemonClient, bool) {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-p.closeContext.Done():
|
||||||
return
|
return nil, false
|
||||||
default:
|
case client := <-p.clientCh:
|
||||||
|
return client, true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
go func() {
|
func (p *Server) acquireLoop() {
|
||||||
if p.isClosed() {
|
defer p.opts.Logger.Debug(p.closeContext, "acquire loop exited")
|
||||||
|
defer p.wg.Done()
|
||||||
|
defer func() { close(p.acquireDoneCh) }()
|
||||||
|
ctx := p.closeContext
|
||||||
|
for {
|
||||||
|
if p.acquireExit() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
client, ok := p.client()
|
client, ok := p.client()
|
||||||
if !ok {
|
if !ok {
|
||||||
|
p.opts.Logger.Debug(ctx, "shut down before client (re) connected")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
select {
|
p.acquireAndRunOne(client)
|
||||||
case <-p.closeContext.Done():
|
|
||||||
return
|
|
||||||
case <-client.DRPCConn().Closed():
|
|
||||||
// We use the update stream to detect when the connection
|
|
||||||
// has been interrupted. This works well, because logs need
|
|
||||||
// to buffer if a job is running in the background.
|
|
||||||
p.opts.Logger.Debug(context.Background(), "client stream ended")
|
|
||||||
p.connect(ctx)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
if p.isClosed() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
timer := time.NewTimer(p.opts.JobPollInterval)
|
|
||||||
defer timer.Stop()
|
|
||||||
for {
|
|
||||||
client, ok := p.client()
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-p.closeContext.Done():
|
|
||||||
return
|
|
||||||
case <-client.DRPCConn().Closed():
|
|
||||||
return
|
|
||||||
case <-timer.C:
|
|
||||||
p.acquireJob(ctx)
|
|
||||||
timer.Reset(p.nextInterval())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Server) nextInterval() time.Duration {
|
|
||||||
r, err := cryptorand.Float64()
|
|
||||||
if err != nil {
|
|
||||||
panic("get random float:" + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return p.opts.JobPollInterval + time.Duration(float64(p.opts.JobPollJitter)*r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Server) client() (proto.DRPCProvisionerDaemonClient, bool) {
|
|
||||||
client := p.clientValue.Load()
|
|
||||||
if client == nil {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
return *client, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// isRunningJob returns true if a job is running. Caller must hold the mutex.
|
|
||||||
func (p *Server) isRunningJob() bool {
|
|
||||||
if p.activeJob == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-p.activeJob.Done():
|
|
||||||
return false
|
|
||||||
default:
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
// acquireExit returns true if the acquire loop should exit
|
||||||
lastAcquire time.Time
|
func (p *Server) acquireExit() bool {
|
||||||
lastAcquireMutex sync.RWMutex
|
|
||||||
)
|
|
||||||
|
|
||||||
// Locks a job in the database, and runs it!
|
|
||||||
func (p *Server) acquireJob(ctx context.Context) {
|
|
||||||
p.mutex.Lock()
|
p.mutex.Lock()
|
||||||
defer p.mutex.Unlock()
|
defer p.mutex.Unlock()
|
||||||
if p.isClosed() {
|
if p.closingB {
|
||||||
return
|
p.opts.Logger.Debug(p.closeContext, "exiting acquire; provisionerd is closing")
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
if p.isRunningJob() {
|
if p.shuttingDownB {
|
||||||
return
|
p.opts.Logger.Debug(p.closeContext, "exiting acquire; provisionerd is shutting down")
|
||||||
}
|
return true
|
||||||
if p.isShutdown() {
|
|
||||||
p.opts.Logger.Debug(context.Background(), "skipping acquire; provisionerd is shutting down")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// This prevents loads of provisioner daemons from consistently sending
|
func (p *Server) acquireAndRunOne(client proto.DRPCProvisionerDaemonClient) {
|
||||||
// requests when no jobs are available.
|
ctx := p.closeContext
|
||||||
//
|
p.opts.Logger.Debug(ctx, "start of acquireAndRunOne")
|
||||||
// The debounce only occurs when no job is returned, so if loads of jobs are
|
job, err := p.acquireGraceful(client)
|
||||||
// added at once, they will start after at most this duration.
|
p.opts.Logger.Debug(ctx, "graceful acquire done", slog.F("job_id", job.GetJobId()), slog.Error(err))
|
||||||
lastAcquireMutex.RLock()
|
|
||||||
if !lastAcquire.IsZero() && time.Since(lastAcquire) < p.opts.JobPollDebounce {
|
|
||||||
lastAcquireMutex.RUnlock()
|
|
||||||
p.opts.Logger.Debug(ctx, "debounce acquire job")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
lastAcquireMutex.RUnlock()
|
|
||||||
|
|
||||||
var err error
|
|
||||||
client, ok := p.client()
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
job, err := client.AcquireJob(ctx, &proto.Empty{})
|
|
||||||
p.opts.Logger.Debug(ctx, "called AcquireJob on client", slog.F("job_id", job.GetJobId()), slog.Error(err))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, context.Canceled) ||
|
if errors.Is(err, context.Canceled) ||
|
||||||
errors.Is(err, yamux.ErrSessionShutdown) ||
|
errors.Is(err, yamux.ErrSessionShutdown) ||
|
||||||
|
@ -342,9 +282,7 @@ func (p *Server) acquireJob(ctx context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if job.JobId == "" {
|
if job.JobId == "" {
|
||||||
lastAcquireMutex.Lock()
|
p.opts.Logger.Debug(ctx, "acquire job successfully canceled")
|
||||||
lastAcquire = time.Now()
|
|
||||||
lastAcquireMutex.Unlock()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,6 +343,7 @@ func (p *Server) acquireJob(ctx context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p.mutex.Lock()
|
||||||
p.activeJob = runner.New(
|
p.activeJob = runner.New(
|
||||||
ctx,
|
ctx,
|
||||||
job,
|
job,
|
||||||
|
@ -420,8 +359,39 @@ func (p *Server) acquireJob(ctx context.Context) {
|
||||||
Metrics: p.opts.Metrics.Runner,
|
Metrics: p.opts.Metrics.Runner,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
p.mutex.Unlock()
|
||||||
|
p.activeJob.Run()
|
||||||
|
p.mutex.Lock()
|
||||||
|
p.activeJob = nil
|
||||||
|
p.mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
go p.activeJob.Run()
|
// acquireGraceful attempts to acquire a job from the server, handling canceling the acquisition if we gracefully shut
|
||||||
|
// down.
|
||||||
|
func (p *Server) acquireGraceful(client proto.DRPCProvisionerDaemonClient) (*proto.AcquiredJob, error) {
|
||||||
|
stream, err := client.AcquireJobWithCancel(p.closeContext)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
acquireDone := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-p.closeContext.Done():
|
||||||
|
return
|
||||||
|
case <-p.shuttingDownCh:
|
||||||
|
p.opts.Logger.Debug(p.closeContext, "sending acquire job cancel")
|
||||||
|
err := stream.Send(&proto.CancelAcquire{})
|
||||||
|
if err != nil {
|
||||||
|
p.opts.Logger.Warn(p.closeContext, "failed to gracefully cancel acquire job")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case <-acquireDone:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
job, err := stream.Recv()
|
||||||
|
close(acquireDone)
|
||||||
|
return job, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func retryable(err error) bool {
|
func retryable(err error) bool {
|
||||||
|
@ -496,36 +466,23 @@ func (p *Server) isClosed() bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// isShutdown returns whether the API is shutdown or not.
|
|
||||||
func (p *Server) isShutdown() bool {
|
|
||||||
select {
|
|
||||||
case <-p.shutdown:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shutdown triggers a graceful exit of each registered provisioner.
|
// Shutdown triggers a graceful exit of each registered provisioner.
|
||||||
// It exits when an active job stops.
|
|
||||||
func (p *Server) Shutdown(ctx context.Context) error {
|
func (p *Server) Shutdown(ctx context.Context) error {
|
||||||
p.mutex.Lock()
|
p.mutex.Lock()
|
||||||
defer p.mutex.Unlock()
|
|
||||||
if !p.isRunningJob() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
p.opts.Logger.Info(ctx, "attempting graceful shutdown")
|
p.opts.Logger.Info(ctx, "attempting graceful shutdown")
|
||||||
close(p.shutdown)
|
if !p.shuttingDownB {
|
||||||
if p.activeJob == nil {
|
close(p.shuttingDownCh)
|
||||||
return nil
|
p.shuttingDownB = true
|
||||||
}
|
}
|
||||||
// wait for active job
|
if p.activeJob != nil {
|
||||||
p.activeJob.Cancel()
|
p.activeJob.Cancel()
|
||||||
|
}
|
||||||
|
p.mutex.Unlock()
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
p.opts.Logger.Warn(ctx, "graceful shutdown failed", slog.Error(ctx.Err()))
|
p.opts.Logger.Warn(ctx, "graceful shutdown failed", slog.Error(ctx.Err()))
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
case <-p.activeJob.Done():
|
case <-p.acquireDoneCh:
|
||||||
p.opts.Logger.Info(ctx, "gracefully shutdown")
|
p.opts.Logger.Info(ctx, "gracefully shutdown")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -533,41 +490,51 @@ func (p *Server) Shutdown(ctx context.Context) error {
|
||||||
|
|
||||||
// Close ends the provisioner. It will mark any running jobs as failed.
|
// Close ends the provisioner. It will mark any running jobs as failed.
|
||||||
func (p *Server) Close() error {
|
func (p *Server) Close() error {
|
||||||
|
p.opts.Logger.Info(p.closeContext, "closing provisionerd")
|
||||||
return p.closeWithError(nil)
|
return p.closeWithError(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// closeWithError closes the provisioner; subsequent reads/writes will return the error err.
|
// closeWithError closes the provisioner; subsequent reads/writes will return the error err.
|
||||||
func (p *Server) closeWithError(err error) error {
|
func (p *Server) closeWithError(err error) error {
|
||||||
p.mutex.Lock()
|
p.mutex.Lock()
|
||||||
defer p.mutex.Unlock()
|
var activeJob *runner.Runner
|
||||||
if p.isClosed() {
|
first := false
|
||||||
return p.closeError
|
if !p.closingB {
|
||||||
|
first = true
|
||||||
|
p.closingB = true
|
||||||
|
// only the first caller to close should attempt to fail the active job
|
||||||
|
activeJob = p.activeJob
|
||||||
}
|
}
|
||||||
p.closeError = err
|
// don't hold the mutex while doing I/O.
|
||||||
|
p.mutex.Unlock()
|
||||||
errMsg := "provisioner daemon was shutdown gracefully"
|
if activeJob != nil {
|
||||||
if err != nil {
|
errMsg := "provisioner daemon was shutdown gracefully"
|
||||||
errMsg = err.Error()
|
if err != nil {
|
||||||
}
|
errMsg = err.Error()
|
||||||
if p.activeJob != nil {
|
}
|
||||||
|
p.opts.Logger.Debug(p.closeContext, "failing active job because of close")
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
failErr := p.activeJob.Fail(ctx, &proto.FailedJob{Error: errMsg})
|
failErr := activeJob.Fail(ctx, &proto.FailedJob{Error: errMsg})
|
||||||
if failErr != nil {
|
if failErr != nil {
|
||||||
p.activeJob.ForceStop()
|
activeJob.ForceStop()
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = failErr
|
err = failErr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p.closeCancel()
|
if first {
|
||||||
|
p.closeCancel()
|
||||||
p.opts.Logger.Debug(context.Background(), "closing server with error", slog.Error(err))
|
p.opts.Logger.Debug(context.Background(), "waiting for goroutines to exit")
|
||||||
|
p.wg.Wait()
|
||||||
if c, ok := p.client(); ok {
|
p.opts.Logger.Debug(context.Background(), "closing server with error", slog.Error(err))
|
||||||
_ = c.DRPCConn().Close()
|
p.closeError = err
|
||||||
|
close(p.closedCh)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
p.opts.Logger.Debug(p.closeContext, "waiting for first closer to complete")
|
||||||
return err
|
<-p.closedCh
|
||||||
|
p.opts.Logger.Debug(p.closeContext, "first closer completed")
|
||||||
|
return p.closeError
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ func TestProvisionerd(t *testing.T) {
|
||||||
close(done)
|
close(done)
|
||||||
})
|
})
|
||||||
closer := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
closer := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
||||||
return createProvisionerDaemonClient(ctx, t, done, provisionerDaemonTestServer{}), nil
|
return createProvisionerDaemonClient(t, done, provisionerDaemonTestServer{}), nil
|
||||||
}, provisionerd.LocalProvisioners{})
|
}, provisionerd.LocalProvisioners{})
|
||||||
require.NoError(t, closer.Close())
|
require.NoError(t, closer.Close())
|
||||||
})
|
})
|
||||||
|
@ -79,33 +79,6 @@ func TestProvisionerd(t *testing.T) {
|
||||||
require.NoError(t, closer.Close())
|
require.NoError(t, closer.Close())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("AcquireEmptyJob", func(t *testing.T) {
|
|
||||||
// The provisioner daemon is supposed to skip the job acquire if
|
|
||||||
// the job provided is empty. This is to show it successfully
|
|
||||||
// tried to get a job, but none were available.
|
|
||||||
t.Parallel()
|
|
||||||
done := make(chan struct{})
|
|
||||||
t.Cleanup(func() {
|
|
||||||
close(done)
|
|
||||||
})
|
|
||||||
completeChan := make(chan struct{})
|
|
||||||
closer := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
|
||||||
acquireJobAttempt := 0
|
|
||||||
return createProvisionerDaemonClient(ctx, t, done, provisionerDaemonTestServer{
|
|
||||||
acquireJob: func(ctx context.Context, _ *proto.Empty) (*proto.AcquiredJob, error) {
|
|
||||||
if acquireJobAttempt == 1 {
|
|
||||||
close(completeChan)
|
|
||||||
}
|
|
||||||
acquireJobAttempt++
|
|
||||||
return &proto.AcquiredJob{}, nil
|
|
||||||
},
|
|
||||||
updateJob: noopUpdateJob,
|
|
||||||
}), nil
|
|
||||||
}, provisionerd.LocalProvisioners{})
|
|
||||||
require.Condition(t, closedWithin(completeChan, testutil.WaitShort))
|
|
||||||
require.NoError(t, closer.Close())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("CloseCancelsJob", func(t *testing.T) {
|
t.Run("CloseCancelsJob", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
|
@ -118,9 +91,9 @@ func TestProvisionerd(t *testing.T) {
|
||||||
var closerMutex sync.Mutex
|
var closerMutex sync.Mutex
|
||||||
closerMutex.Lock()
|
closerMutex.Lock()
|
||||||
closer = createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
closer = createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
||||||
return createProvisionerDaemonClient(ctx, t, done, provisionerDaemonTestServer{
|
return createProvisionerDaemonClient(t, done, provisionerDaemonTestServer{
|
||||||
acquireJob: func(ctx context.Context, _ *proto.Empty) (*proto.AcquiredJob, error) {
|
acquireJobWithCancel: func(stream proto.DRPCProvisionerDaemon_AcquireJobWithCancelStream) error {
|
||||||
return &proto.AcquiredJob{
|
err := stream.Send(&proto.AcquiredJob{
|
||||||
JobId: "test",
|
JobId: "test",
|
||||||
Provisioner: "someprovisioner",
|
Provisioner: "someprovisioner",
|
||||||
TemplateSourceArchive: createTar(t, map[string]string{
|
TemplateSourceArchive: createTar(t, map[string]string{
|
||||||
|
@ -131,7 +104,9 @@ func TestProvisionerd(t *testing.T) {
|
||||||
Metadata: &sdkproto.Metadata{},
|
Metadata: &sdkproto.Metadata{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
updateJob: noopUpdateJob,
|
updateJob: noopUpdateJob,
|
||||||
failJob: func(ctx context.Context, job *proto.FailedJob) (*proto.Empty, error) {
|
failJob: func(ctx context.Context, job *proto.FailedJob) (*proto.Empty, error) {
|
||||||
|
@ -174,9 +149,9 @@ func TestProvisionerd(t *testing.T) {
|
||||||
)
|
)
|
||||||
|
|
||||||
closer := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
closer := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
||||||
return createProvisionerDaemonClient(ctx, t, done, provisionerDaemonTestServer{
|
return createProvisionerDaemonClient(t, done, provisionerDaemonTestServer{
|
||||||
acquireJob: func(ctx context.Context, _ *proto.Empty) (*proto.AcquiredJob, error) {
|
acquireJobWithCancel: func(stream proto.DRPCProvisionerDaemon_AcquireJobWithCancelStream) error {
|
||||||
return &proto.AcquiredJob{
|
err := stream.Send(&proto.AcquiredJob{
|
||||||
JobId: "test",
|
JobId: "test",
|
||||||
Provisioner: "someprovisioner",
|
Provisioner: "someprovisioner",
|
||||||
TemplateSourceArchive: createTar(t, map[string]string{
|
TemplateSourceArchive: createTar(t, map[string]string{
|
||||||
|
@ -187,7 +162,9 @@ func TestProvisionerd(t *testing.T) {
|
||||||
Metadata: &sdkproto.Metadata{},
|
Metadata: &sdkproto.Metadata{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
updateJob: noopUpdateJob,
|
updateJob: noopUpdateJob,
|
||||||
failJob: func(ctx context.Context, job *proto.FailedJob) (*proto.Empty, error) {
|
failJob: func(ctx context.Context, job *proto.FailedJob) (*proto.Empty, error) {
|
||||||
|
@ -214,9 +191,9 @@ func TestProvisionerd(t *testing.T) {
|
||||||
)
|
)
|
||||||
|
|
||||||
closer := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
closer := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
||||||
return createProvisionerDaemonClient(ctx, t, done, provisionerDaemonTestServer{
|
return createProvisionerDaemonClient(t, done, provisionerDaemonTestServer{
|
||||||
acquireJob: func(ctx context.Context, _ *proto.Empty) (*proto.AcquiredJob, error) {
|
acquireJobWithCancel: func(stream proto.DRPCProvisionerDaemon_AcquireJobWithCancelStream) error {
|
||||||
return &proto.AcquiredJob{
|
err := stream.Send(&proto.AcquiredJob{
|
||||||
JobId: "test",
|
JobId: "test",
|
||||||
Provisioner: "someprovisioner",
|
Provisioner: "someprovisioner",
|
||||||
TemplateSourceArchive: createTar(t, map[string]string{
|
TemplateSourceArchive: createTar(t, map[string]string{
|
||||||
|
@ -227,7 +204,9 @@ func TestProvisionerd(t *testing.T) {
|
||||||
Metadata: &sdkproto.Metadata{},
|
Metadata: &sdkproto.Metadata{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
updateJob: func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) {
|
updateJob: func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) {
|
||||||
completeOnce.Do(func() { close(completeChan) })
|
completeOnce.Do(func() { close(completeChan) })
|
||||||
|
@ -260,36 +239,27 @@ func TestProvisionerd(t *testing.T) {
|
||||||
close(done)
|
close(done)
|
||||||
})
|
})
|
||||||
var (
|
var (
|
||||||
didComplete atomic.Bool
|
didComplete atomic.Bool
|
||||||
didLog atomic.Bool
|
didLog atomic.Bool
|
||||||
didAcquireJob atomic.Bool
|
didReadme atomic.Bool
|
||||||
didReadme atomic.Bool
|
acq = newAcquireOne(t, &proto.AcquiredJob{
|
||||||
completeChan = make(chan struct{})
|
JobId: "test",
|
||||||
completeOnce sync.Once
|
Provisioner: "someprovisioner",
|
||||||
|
TemplateSourceArchive: createTar(t, map[string]string{
|
||||||
|
"test.txt": "content",
|
||||||
|
provisionersdk.ReadmeFile: "# A cool template 😎\n",
|
||||||
|
}),
|
||||||
|
Type: &proto.AcquiredJob_TemplateImport_{
|
||||||
|
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
||||||
|
Metadata: &sdkproto.Metadata{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
closer := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
closer := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
||||||
return createProvisionerDaemonClient(ctx, t, done, provisionerDaemonTestServer{
|
return createProvisionerDaemonClient(t, done, provisionerDaemonTestServer{
|
||||||
acquireJob: func(ctx context.Context, _ *proto.Empty) (*proto.AcquiredJob, error) {
|
acquireJobWithCancel: acq.acquireWithCancel,
|
||||||
if !didAcquireJob.CAS(false, true) {
|
|
||||||
completeOnce.Do(func() { close(completeChan) })
|
|
||||||
return &proto.AcquiredJob{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &proto.AcquiredJob{
|
|
||||||
JobId: "test",
|
|
||||||
Provisioner: "someprovisioner",
|
|
||||||
TemplateSourceArchive: createTar(t, map[string]string{
|
|
||||||
"test.txt": "content",
|
|
||||||
provisionersdk.ReadmeFile: "# A cool template 😎\n",
|
|
||||||
}),
|
|
||||||
Type: &proto.AcquiredJob_TemplateImport_{
|
|
||||||
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
|
||||||
Metadata: &sdkproto.Metadata{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
updateJob: func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) {
|
updateJob: func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) {
|
||||||
if len(update.Logs) > 0 {
|
if len(update.Logs) > 0 {
|
||||||
didLog.Store(true)
|
didLog.Store(true)
|
||||||
|
@ -338,7 +308,7 @@ func TestProvisionerd(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
require.Condition(t, closedWithin(completeChan, testutil.WaitShort))
|
require.Condition(t, closedWithin(acq.complete, testutil.WaitShort))
|
||||||
require.NoError(t, closer.Close())
|
require.NoError(t, closer.Close())
|
||||||
assert.True(t, didLog.Load(), "should log some updates")
|
assert.True(t, didLog.Load(), "should log some updates")
|
||||||
assert.True(t, didComplete.Load(), "should complete the job")
|
assert.True(t, didComplete.Load(), "should complete the job")
|
||||||
|
@ -351,36 +321,26 @@ func TestProvisionerd(t *testing.T) {
|
||||||
close(done)
|
close(done)
|
||||||
})
|
})
|
||||||
var (
|
var (
|
||||||
didComplete atomic.Bool
|
didComplete atomic.Bool
|
||||||
didLog atomic.Bool
|
didLog atomic.Bool
|
||||||
didAcquireJob atomic.Bool
|
metadata = &sdkproto.Metadata{}
|
||||||
completeChan = make(chan struct{})
|
acq = newAcquireOne(t, &proto.AcquiredJob{
|
||||||
completeOnce sync.Once
|
JobId: "test",
|
||||||
|
Provisioner: "someprovisioner",
|
||||||
metadata = &sdkproto.Metadata{}
|
TemplateSourceArchive: createTar(t, map[string]string{
|
||||||
|
"test.txt": "content",
|
||||||
|
}),
|
||||||
|
Type: &proto.AcquiredJob_TemplateDryRun_{
|
||||||
|
TemplateDryRun: &proto.AcquiredJob_TemplateDryRun{
|
||||||
|
Metadata: metadata,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
closer := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
closer := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
||||||
return createProvisionerDaemonClient(ctx, t, done, provisionerDaemonTestServer{
|
return createProvisionerDaemonClient(t, done, provisionerDaemonTestServer{
|
||||||
acquireJob: func(ctx context.Context, _ *proto.Empty) (*proto.AcquiredJob, error) {
|
acquireJobWithCancel: acq.acquireWithCancel,
|
||||||
if !didAcquireJob.CAS(false, true) {
|
|
||||||
completeOnce.Do(func() { close(completeChan) })
|
|
||||||
return &proto.AcquiredJob{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &proto.AcquiredJob{
|
|
||||||
JobId: "test",
|
|
||||||
Provisioner: "someprovisioner",
|
|
||||||
TemplateSourceArchive: createTar(t, map[string]string{
|
|
||||||
"test.txt": "content",
|
|
||||||
}),
|
|
||||||
Type: &proto.AcquiredJob_TemplateDryRun_{
|
|
||||||
TemplateDryRun: &proto.AcquiredJob_TemplateDryRun{
|
|
||||||
Metadata: metadata,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
updateJob: func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) {
|
updateJob: func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) {
|
||||||
if len(update.Logs) == 0 {
|
if len(update.Logs) == 0 {
|
||||||
t.Log("provisionerDaemonTestServer: no log messages")
|
t.Log("provisionerDaemonTestServer: no log messages")
|
||||||
|
@ -420,7 +380,7 @@ func TestProvisionerd(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
require.Condition(t, closedWithin(completeChan, testutil.WaitShort))
|
require.Condition(t, closedWithin(acq.complete, testutil.WaitShort))
|
||||||
require.NoError(t, closer.Close())
|
require.NoError(t, closer.Close())
|
||||||
assert.True(t, didLog.Load(), "should log some updates")
|
assert.True(t, didLog.Load(), "should log some updates")
|
||||||
assert.True(t, didComplete.Load(), "should complete the job")
|
assert.True(t, didComplete.Load(), "should complete the job")
|
||||||
|
@ -433,34 +393,25 @@ func TestProvisionerd(t *testing.T) {
|
||||||
close(done)
|
close(done)
|
||||||
})
|
})
|
||||||
var (
|
var (
|
||||||
didComplete atomic.Bool
|
didComplete atomic.Bool
|
||||||
didLog atomic.Bool
|
didLog atomic.Bool
|
||||||
didAcquireJob atomic.Bool
|
acq = newAcquireOne(t, &proto.AcquiredJob{
|
||||||
completeChan = make(chan struct{})
|
JobId: "test",
|
||||||
completeOnce sync.Once
|
Provisioner: "someprovisioner",
|
||||||
|
TemplateSourceArchive: createTar(t, map[string]string{
|
||||||
|
"test.txt": "content",
|
||||||
|
}),
|
||||||
|
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
||||||
|
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
||||||
|
Metadata: &sdkproto.Metadata{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
closer := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
closer := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
||||||
return createProvisionerDaemonClient(ctx, t, done, provisionerDaemonTestServer{
|
return createProvisionerDaemonClient(t, done, provisionerDaemonTestServer{
|
||||||
acquireJob: func(ctx context.Context, _ *proto.Empty) (*proto.AcquiredJob, error) {
|
acquireJobWithCancel: acq.acquireWithCancel,
|
||||||
if !didAcquireJob.CAS(false, true) {
|
|
||||||
completeOnce.Do(func() { close(completeChan) })
|
|
||||||
return &proto.AcquiredJob{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &proto.AcquiredJob{
|
|
||||||
JobId: "test",
|
|
||||||
Provisioner: "someprovisioner",
|
|
||||||
TemplateSourceArchive: createTar(t, map[string]string{
|
|
||||||
"test.txt": "content",
|
|
||||||
}),
|
|
||||||
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
|
||||||
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
|
||||||
Metadata: &sdkproto.Metadata{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
updateJob: func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) {
|
updateJob: func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) {
|
||||||
if len(update.Logs) != 0 {
|
if len(update.Logs) != 0 {
|
||||||
didLog.Store(true)
|
didLog.Store(true)
|
||||||
|
@ -491,7 +442,7 @@ func TestProvisionerd(t *testing.T) {
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
require.Condition(t, closedWithin(completeChan, testutil.WaitShort))
|
require.Condition(t, closedWithin(acq.complete, testutil.WaitShort))
|
||||||
require.NoError(t, closer.Close())
|
require.NoError(t, closer.Close())
|
||||||
assert.True(t, didLog.Load(), "should log some updates")
|
assert.True(t, didLog.Load(), "should log some updates")
|
||||||
assert.True(t, didComplete.Load(), "should complete the job")
|
assert.True(t, didComplete.Load(), "should complete the job")
|
||||||
|
@ -504,35 +455,26 @@ func TestProvisionerd(t *testing.T) {
|
||||||
close(done)
|
close(done)
|
||||||
})
|
})
|
||||||
var (
|
var (
|
||||||
didComplete atomic.Bool
|
didComplete atomic.Bool
|
||||||
didLog atomic.Bool
|
didLog atomic.Bool
|
||||||
didAcquireJob atomic.Bool
|
didFail atomic.Bool
|
||||||
didFail atomic.Bool
|
acq = newAcquireOne(t, &proto.AcquiredJob{
|
||||||
completeChan = make(chan struct{})
|
JobId: "test",
|
||||||
completeOnce sync.Once
|
Provisioner: "someprovisioner",
|
||||||
|
TemplateSourceArchive: createTar(t, map[string]string{
|
||||||
|
"test.txt": "content",
|
||||||
|
}),
|
||||||
|
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
||||||
|
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
||||||
|
Metadata: &sdkproto.Metadata{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
closer := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
closer := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
||||||
return createProvisionerDaemonClient(ctx, t, done, provisionerDaemonTestServer{
|
return createProvisionerDaemonClient(t, done, provisionerDaemonTestServer{
|
||||||
acquireJob: func(ctx context.Context, _ *proto.Empty) (*proto.AcquiredJob, error) {
|
acquireJobWithCancel: acq.acquireWithCancel,
|
||||||
if !didAcquireJob.CAS(false, true) {
|
|
||||||
completeOnce.Do(func() { close(completeChan) })
|
|
||||||
return &proto.AcquiredJob{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &proto.AcquiredJob{
|
|
||||||
JobId: "test",
|
|
||||||
Provisioner: "someprovisioner",
|
|
||||||
TemplateSourceArchive: createTar(t, map[string]string{
|
|
||||||
"test.txt": "content",
|
|
||||||
}),
|
|
||||||
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
|
||||||
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
|
||||||
Metadata: &sdkproto.Metadata{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
updateJob: func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) {
|
updateJob: func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) {
|
||||||
if len(update.Logs) != 0 {
|
if len(update.Logs) != 0 {
|
||||||
didLog.Store(true)
|
didLog.Store(true)
|
||||||
|
@ -591,7 +533,7 @@ func TestProvisionerd(t *testing.T) {
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
require.Condition(t, closedWithin(completeChan, testutil.WaitShort))
|
require.Condition(t, closedWithin(acq.complete, testutil.WaitShort))
|
||||||
require.NoError(t, closer.Close())
|
require.NoError(t, closer.Close())
|
||||||
assert.True(t, didLog.Load(), "should log some updates")
|
assert.True(t, didLog.Load(), "should log some updates")
|
||||||
assert.False(t, didComplete.Load(), "should not complete the job")
|
assert.False(t, didComplete.Load(), "should not complete the job")
|
||||||
|
@ -605,34 +547,25 @@ func TestProvisionerd(t *testing.T) {
|
||||||
close(done)
|
close(done)
|
||||||
})
|
})
|
||||||
var (
|
var (
|
||||||
didFail atomic.Bool
|
didFail atomic.Bool
|
||||||
didAcquireJob atomic.Bool
|
acq = newAcquireOne(t, &proto.AcquiredJob{
|
||||||
completeChan = make(chan struct{})
|
JobId: "test",
|
||||||
completeOnce sync.Once
|
Provisioner: "someprovisioner",
|
||||||
|
TemplateSourceArchive: createTar(t, map[string]string{
|
||||||
|
"test.txt": "content",
|
||||||
|
}),
|
||||||
|
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
||||||
|
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
||||||
|
Metadata: &sdkproto.Metadata{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
closer := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
closer := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
||||||
return createProvisionerDaemonClient(ctx, t, done, provisionerDaemonTestServer{
|
return createProvisionerDaemonClient(t, done, provisionerDaemonTestServer{
|
||||||
acquireJob: func(ctx context.Context, _ *proto.Empty) (*proto.AcquiredJob, error) {
|
acquireJobWithCancel: acq.acquireWithCancel,
|
||||||
if !didAcquireJob.CAS(false, true) {
|
updateJob: noopUpdateJob,
|
||||||
completeOnce.Do(func() { close(completeChan) })
|
|
||||||
return &proto.AcquiredJob{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &proto.AcquiredJob{
|
|
||||||
JobId: "test",
|
|
||||||
Provisioner: "someprovisioner",
|
|
||||||
TemplateSourceArchive: createTar(t, map[string]string{
|
|
||||||
"test.txt": "content",
|
|
||||||
}),
|
|
||||||
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
|
||||||
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
|
||||||
Metadata: &sdkproto.Metadata{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
updateJob: noopUpdateJob,
|
|
||||||
failJob: func(ctx context.Context, job *proto.FailedJob) (*proto.Empty, error) {
|
failJob: func(ctx context.Context, job *proto.FailedJob) (*proto.Empty, error) {
|
||||||
didFail.Store(true)
|
didFail.Store(true)
|
||||||
return &proto.Empty{}, nil
|
return &proto.Empty{}, nil
|
||||||
|
@ -661,7 +594,7 @@ func TestProvisionerd(t *testing.T) {
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
require.Condition(t, closedWithin(completeChan, testutil.WaitShort))
|
require.Condition(t, closedWithin(acq.complete, testutil.WaitShort))
|
||||||
require.NoError(t, closer.Close())
|
require.NoError(t, closer.Close())
|
||||||
assert.True(t, didFail.Load(), "should fail the job")
|
assert.True(t, didFail.Load(), "should fail the job")
|
||||||
})
|
})
|
||||||
|
@ -677,9 +610,9 @@ func TestProvisionerd(t *testing.T) {
|
||||||
updateChan := make(chan struct{})
|
updateChan := make(chan struct{})
|
||||||
completeChan := make(chan struct{})
|
completeChan := make(chan struct{})
|
||||||
server := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
server := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
||||||
return createProvisionerDaemonClient(ctx, t, done, provisionerDaemonTestServer{
|
return createProvisionerDaemonClient(t, done, provisionerDaemonTestServer{
|
||||||
acquireJob: func(ctx context.Context, _ *proto.Empty) (*proto.AcquiredJob, error) {
|
acquireJobWithCancel: func(stream proto.DRPCProvisionerDaemon_AcquireJobWithCancelStream) error {
|
||||||
return &proto.AcquiredJob{
|
err := stream.Send(&proto.AcquiredJob{
|
||||||
JobId: "test",
|
JobId: "test",
|
||||||
Provisioner: "someprovisioner",
|
Provisioner: "someprovisioner",
|
||||||
TemplateSourceArchive: createTar(t, map[string]string{
|
TemplateSourceArchive: createTar(t, map[string]string{
|
||||||
|
@ -690,7 +623,9 @@ func TestProvisionerd(t *testing.T) {
|
||||||
Metadata: &sdkproto.Metadata{},
|
Metadata: &sdkproto.Metadata{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
updateJob: func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) {
|
updateJob: func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) {
|
||||||
if len(update.Logs) > 0 {
|
if len(update.Logs) > 0 {
|
||||||
|
@ -755,9 +690,9 @@ func TestProvisionerd(t *testing.T) {
|
||||||
updateChan := make(chan struct{})
|
updateChan := make(chan struct{})
|
||||||
completeChan := make(chan struct{})
|
completeChan := make(chan struct{})
|
||||||
server := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
server := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
||||||
return createProvisionerDaemonClient(ctx, t, done, provisionerDaemonTestServer{
|
return createProvisionerDaemonClient(t, done, provisionerDaemonTestServer{
|
||||||
acquireJob: func(ctx context.Context, _ *proto.Empty) (*proto.AcquiredJob, error) {
|
acquireJobWithCancel: func(stream proto.DRPCProvisionerDaemon_AcquireJobWithCancelStream) error {
|
||||||
return &proto.AcquiredJob{
|
err := stream.Send(&proto.AcquiredJob{
|
||||||
JobId: "test",
|
JobId: "test",
|
||||||
Provisioner: "someprovisioner",
|
Provisioner: "someprovisioner",
|
||||||
TemplateSourceArchive: createTar(t, map[string]string{
|
TemplateSourceArchive: createTar(t, map[string]string{
|
||||||
|
@ -768,7 +703,9 @@ func TestProvisionerd(t *testing.T) {
|
||||||
Metadata: &sdkproto.Metadata{},
|
Metadata: &sdkproto.Metadata{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
updateJob: func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) {
|
updateJob: func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) {
|
||||||
resp := &proto.UpdateJobResponse{}
|
resp := &proto.UpdateJobResponse{}
|
||||||
|
@ -825,6 +762,9 @@ func TestProvisionerd(t *testing.T) {
|
||||||
})
|
})
|
||||||
require.Condition(t, closedWithin(updateChan, testutil.WaitShort))
|
require.Condition(t, closedWithin(updateChan, testutil.WaitShort))
|
||||||
require.Condition(t, closedWithin(completeChan, testutil.WaitShort))
|
require.Condition(t, closedWithin(completeChan, testutil.WaitShort))
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||||
|
defer cancel()
|
||||||
|
require.NoError(t, server.Shutdown(ctx))
|
||||||
require.NoError(t, server.Close())
|
require.NoError(t, server.Close())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -844,12 +784,9 @@ func TestProvisionerd(t *testing.T) {
|
||||||
completeOnce sync.Once
|
completeOnce sync.Once
|
||||||
)
|
)
|
||||||
server := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
server := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
||||||
client := createProvisionerDaemonClient(ctx, t, done, provisionerDaemonTestServer{
|
client := createProvisionerDaemonClient(t, done, provisionerDaemonTestServer{
|
||||||
acquireJob: func(ctx context.Context, _ *proto.Empty) (*proto.AcquiredJob, error) {
|
acquireJobWithCancel: func(stream proto.DRPCProvisionerDaemon_AcquireJobWithCancelStream) error {
|
||||||
if second.Load() {
|
job := &proto.AcquiredJob{
|
||||||
return &proto.AcquiredJob{}, nil
|
|
||||||
}
|
|
||||||
return &proto.AcquiredJob{
|
|
||||||
JobId: "test",
|
JobId: "test",
|
||||||
Provisioner: "someprovisioner",
|
Provisioner: "someprovisioner",
|
||||||
TemplateSourceArchive: createTar(t, map[string]string{
|
TemplateSourceArchive: createTar(t, map[string]string{
|
||||||
|
@ -860,7 +797,15 @@ func TestProvisionerd(t *testing.T) {
|
||||||
Metadata: &sdkproto.Metadata{},
|
Metadata: &sdkproto.Metadata{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
}
|
||||||
|
if second.Load() {
|
||||||
|
job = &proto.AcquiredJob{}
|
||||||
|
_, err := stream.Recv()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
err := stream.Send(job)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
updateJob: func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) {
|
updateJob: func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) {
|
||||||
return &proto.UpdateJobResponse{}, nil
|
return &proto.UpdateJobResponse{}, nil
|
||||||
|
@ -908,6 +853,9 @@ func TestProvisionerd(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
require.Condition(t, closedWithin(completeChan, testutil.WaitShort))
|
require.Condition(t, closedWithin(completeChan, testutil.WaitShort))
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||||
|
defer cancel()
|
||||||
|
require.NoError(t, server.Shutdown(ctx))
|
||||||
require.NoError(t, server.Close())
|
require.NoError(t, server.Close())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -927,13 +875,15 @@ func TestProvisionerd(t *testing.T) {
|
||||||
completeOnce sync.Once
|
completeOnce sync.Once
|
||||||
)
|
)
|
||||||
server := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
server := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
||||||
client := createProvisionerDaemonClient(ctx, t, done, provisionerDaemonTestServer{
|
client := createProvisionerDaemonClient(t, done, provisionerDaemonTestServer{
|
||||||
acquireJob: func(ctx context.Context, _ *proto.Empty) (*proto.AcquiredJob, error) {
|
acquireJobWithCancel: func(stream proto.DRPCProvisionerDaemon_AcquireJobWithCancelStream) error {
|
||||||
if second.Load() {
|
if second.Load() {
|
||||||
completeOnce.Do(func() { close(completeChan) })
|
completeOnce.Do(func() { close(completeChan) })
|
||||||
return &proto.AcquiredJob{}, nil
|
_, err := stream.Recv()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return &proto.AcquiredJob{
|
job := &proto.AcquiredJob{
|
||||||
JobId: "test",
|
JobId: "test",
|
||||||
Provisioner: "someprovisioner",
|
Provisioner: "someprovisioner",
|
||||||
TemplateSourceArchive: createTar(t, map[string]string{
|
TemplateSourceArchive: createTar(t, map[string]string{
|
||||||
|
@ -944,7 +894,10 @@ func TestProvisionerd(t *testing.T) {
|
||||||
Metadata: &sdkproto.Metadata{},
|
Metadata: &sdkproto.Metadata{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
}
|
||||||
|
err := stream.Send(job)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
failJob: func(ctx context.Context, job *proto.FailedJob) (*proto.Empty, error) {
|
failJob: func(ctx context.Context, job *proto.FailedJob) (*proto.Empty, error) {
|
||||||
return nil, yamux.ErrSessionShutdown
|
return nil, yamux.ErrSessionShutdown
|
||||||
|
@ -990,6 +943,10 @@ func TestProvisionerd(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
require.Condition(t, closedWithin(completeChan, testutil.WaitShort))
|
require.Condition(t, closedWithin(completeChan, testutil.WaitShort))
|
||||||
|
t.Log("completeChan closed")
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||||
|
defer cancel()
|
||||||
|
require.NoError(t, server.Shutdown(ctx))
|
||||||
require.NoError(t, server.Close())
|
require.NoError(t, server.Close())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1006,17 +963,21 @@ func TestProvisionerd(t *testing.T) {
|
||||||
completeOnce := sync.Once{}
|
completeOnce := sync.Once{}
|
||||||
|
|
||||||
server := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
server := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
||||||
return createProvisionerDaemonClient(ctx, t, done, provisionerDaemonTestServer{
|
return createProvisionerDaemonClient(t, done, provisionerDaemonTestServer{
|
||||||
acquireJob: func(ctx context.Context, _ *proto.Empty) (*proto.AcquiredJob, error) {
|
acquireJobWithCancel: func(stream proto.DRPCProvisionerDaemon_AcquireJobWithCancelStream) error {
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
logger.Info(ctx, "provisioner stage: AcquiredJob")
|
logger.Info(ctx, "provisioner stage: AcquiredJob")
|
||||||
if len(ops) > 0 {
|
if len(ops) > 0 {
|
||||||
return &proto.AcquiredJob{}, nil
|
_, err := stream.Recv()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = stream.Send(&proto.AcquiredJob{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
ops = append(ops, "AcquireJob")
|
ops = append(ops, "AcquireJob")
|
||||||
|
|
||||||
return &proto.AcquiredJob{
|
err := stream.Send(&proto.AcquiredJob{
|
||||||
JobId: "test",
|
JobId: "test",
|
||||||
Provisioner: "someprovisioner",
|
Provisioner: "someprovisioner",
|
||||||
TemplateSourceArchive: createTar(t, map[string]string{
|
TemplateSourceArchive: createTar(t, map[string]string{
|
||||||
|
@ -1027,7 +988,9 @@ func TestProvisionerd(t *testing.T) {
|
||||||
Metadata: &sdkproto.Metadata{},
|
Metadata: &sdkproto.Metadata{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
updateJob: func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) {
|
updateJob: func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) {
|
||||||
m.Lock()
|
m.Lock()
|
||||||
|
@ -1076,6 +1039,9 @@ func TestProvisionerd(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
require.Condition(t, closedWithin(completeChan, testutil.WaitShort))
|
require.Condition(t, closedWithin(completeChan, testutil.WaitShort))
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||||
|
defer cancel()
|
||||||
|
require.NoError(t, server.Shutdown(ctx))
|
||||||
require.NoError(t, server.Close())
|
require.NoError(t, server.Close())
|
||||||
assert.Equal(t, ops[len(ops)-1], "CompleteJob")
|
assert.Equal(t, ops[len(ops)-1], "CompleteJob")
|
||||||
assert.Contains(t, ops[0:len(ops)-1], "Log: Cleaning Up | ")
|
assert.Contains(t, ops[0:len(ops)-1], "Log: Cleaning Up | ")
|
||||||
|
@ -1105,12 +1071,14 @@ func createTar(t *testing.T, files map[string]string) []byte {
|
||||||
// Creates a provisionerd implementation with the provided dialer and provisioners.
|
// Creates a provisionerd implementation with the provided dialer and provisioners.
|
||||||
func createProvisionerd(t *testing.T, dialer provisionerd.Dialer, connector provisionerd.LocalProvisioners) *provisionerd.Server {
|
func createProvisionerd(t *testing.T, dialer provisionerd.Dialer, connector provisionerd.LocalProvisioners) *provisionerd.Server {
|
||||||
server := provisionerd.New(dialer, &provisionerd.Options{
|
server := provisionerd.New(dialer, &provisionerd.Options{
|
||||||
Logger: slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Named("provisionerd").Leveled(slog.LevelDebug),
|
Logger: slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Named("provisionerd").Leveled(slog.LevelDebug),
|
||||||
JobPollInterval: 50 * time.Millisecond,
|
UpdateInterval: 50 * time.Millisecond,
|
||||||
UpdateInterval: 50 * time.Millisecond,
|
Connector: connector,
|
||||||
Connector: connector,
|
|
||||||
})
|
})
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||||
|
defer cancel()
|
||||||
|
_ = server.Shutdown(ctx)
|
||||||
_ = server.Close()
|
_ = server.Close()
|
||||||
})
|
})
|
||||||
return server
|
return server
|
||||||
|
@ -1118,7 +1086,7 @@ func createProvisionerd(t *testing.T, dialer provisionerd.Dialer, connector prov
|
||||||
|
|
||||||
// Creates a provisionerd protobuf client that's connected
|
// Creates a provisionerd protobuf client that's connected
|
||||||
// to the server implementation provided.
|
// to the server implementation provided.
|
||||||
func createProvisionerDaemonClient(ctx context.Context, t *testing.T, done <-chan struct{}, server provisionerDaemonTestServer) proto.DRPCProvisionerDaemonClient {
|
func createProvisionerDaemonClient(t *testing.T, done <-chan struct{}, server provisionerDaemonTestServer) proto.DRPCProvisionerDaemonClient {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if server.failJob == nil {
|
if server.failJob == nil {
|
||||||
// Default to asserting the error from the failure, otherwise
|
// Default to asserting the error from the failure, otherwise
|
||||||
|
@ -1137,7 +1105,7 @@ func createProvisionerDaemonClient(ctx context.Context, t *testing.T, done <-cha
|
||||||
err := proto.DRPCRegisterProvisionerDaemon(mux, &server)
|
err := proto.DRPCRegisterProvisionerDaemon(mux, &server)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
srv := drpcserver.New(mux)
|
srv := drpcserver.New(mux)
|
||||||
ctx, cancelFunc := context.WithCancel(ctx)
|
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||||
closed := make(chan struct{})
|
closed := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
defer close(closed)
|
defer close(closed)
|
||||||
|
@ -1148,31 +1116,13 @@ func createProvisionerDaemonClient(ctx context.Context, t *testing.T, done <-cha
|
||||||
<-closed
|
<-closed
|
||||||
select {
|
select {
|
||||||
case <-done:
|
case <-done:
|
||||||
// It's possible to get unlucky since the dialer is run in a retry in a goroutine.
|
t.Error("createProvisionerDaemonClient cleanup after test was done!")
|
||||||
// The following can occur:
|
|
||||||
// 1. The provisionerd.connect goroutine checks if we're closed prior to attempting to establish a connection
|
|
||||||
// with coderd, sees that we're not.
|
|
||||||
// 2. A test closes the server.
|
|
||||||
// 3. The provisionerd.connect goroutine calls the dialer to establish a connection. This
|
|
||||||
// function detects that someone has tried to create a client after the test finishes.
|
|
||||||
if ctx.Err() == nil {
|
|
||||||
t.Error("createProvisionerDaemonClient cleanup after test was done!")
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
select {
|
select {
|
||||||
case <-done:
|
case <-done:
|
||||||
// It's possible to get unlucky since the dialer is run in a retry in a goroutine.
|
t.Error("called createProvisionerDaemonClient after test was done!")
|
||||||
// The following can occur:
|
|
||||||
// 1. The provisionerd.connect goroutine checks if we're closed prior to attempting to establish a connection
|
|
||||||
// with coderd, sees that we're not.
|
|
||||||
// 2. A test closes the server.
|
|
||||||
// 3. The provisionerd.connect goroutine calls the dialer to establish a connection. This
|
|
||||||
// function detects that someone has tried to create a client after the test finishes.
|
|
||||||
if ctx.Err() == nil {
|
|
||||||
t.Error("createProvisionerDaemonClient cleanup after test was done!")
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
return proto.NewDRPCProvisionerDaemonClient(clientPipe)
|
return proto.NewDRPCProvisionerDaemonClient(clientPipe)
|
||||||
|
@ -1235,15 +1185,25 @@ func (p *provisionerTestServer) Apply(s *provisionersdk.Session, r *sdkproto.App
|
||||||
// Fulfills the protobuf interface for a ProvisionerDaemon with
|
// Fulfills the protobuf interface for a ProvisionerDaemon with
|
||||||
// passable functions for dynamic functionality.
|
// passable functions for dynamic functionality.
|
||||||
type provisionerDaemonTestServer struct {
|
type provisionerDaemonTestServer struct {
|
||||||
acquireJob func(ctx context.Context, _ *proto.Empty) (*proto.AcquiredJob, error)
|
acquireJobWithCancel func(stream proto.DRPCProvisionerDaemon_AcquireJobWithCancelStream) error
|
||||||
commitQuota func(ctx context.Context, com *proto.CommitQuotaRequest) (*proto.CommitQuotaResponse, error)
|
commitQuota func(ctx context.Context, com *proto.CommitQuotaRequest) (*proto.CommitQuotaResponse, error)
|
||||||
updateJob func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error)
|
updateJob func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error)
|
||||||
failJob func(ctx context.Context, job *proto.FailedJob) (*proto.Empty, error)
|
failJob func(ctx context.Context, job *proto.FailedJob) (*proto.Empty, error)
|
||||||
completeJob func(ctx context.Context, job *proto.CompletedJob) (*proto.Empty, error)
|
completeJob func(ctx context.Context, job *proto.CompletedJob) (*proto.Empty, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *provisionerDaemonTestServer) AcquireJob(ctx context.Context, empty *proto.Empty) (*proto.AcquiredJob, error) {
|
func (*provisionerDaemonTestServer) AcquireJob(context.Context, *proto.Empty) (*proto.AcquiredJob, error) {
|
||||||
return p.acquireJob(ctx, empty)
|
return nil, xerrors.New("deprecated!")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *provisionerDaemonTestServer) AcquireJobWithCancel(stream proto.DRPCProvisionerDaemon_AcquireJobWithCancelStream) error {
|
||||||
|
if p.acquireJobWithCancel != nil {
|
||||||
|
return p.acquireJobWithCancel(stream)
|
||||||
|
}
|
||||||
|
// default behavior is to wait for cancel
|
||||||
|
_, _ = stream.Recv()
|
||||||
|
_ = stream.Send(&proto.AcquiredJob{})
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *provisionerDaemonTestServer) CommitQuota(ctx context.Context, com *proto.CommitQuotaRequest) (*proto.CommitQuotaResponse, error) {
|
func (p *provisionerDaemonTestServer) CommitQuota(ctx context.Context, com *proto.CommitQuotaRequest) (*proto.CommitQuotaResponse, error) {
|
||||||
|
@ -1266,3 +1226,38 @@ func (p *provisionerDaemonTestServer) FailJob(ctx context.Context, job *proto.Fa
|
||||||
func (p *provisionerDaemonTestServer) CompleteJob(ctx context.Context, job *proto.CompletedJob) (*proto.Empty, error) {
|
func (p *provisionerDaemonTestServer) CompleteJob(ctx context.Context, job *proto.CompletedJob) (*proto.Empty, error) {
|
||||||
return p.completeJob(ctx, job)
|
return p.completeJob(ctx, job)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// acquireOne provides a function that returns a single provisioner job, then subsequent calls block until canceled.
|
||||||
|
// The complete channel is closed on the 2nd call.
|
||||||
|
type acquireOne struct {
|
||||||
|
t *testing.T
|
||||||
|
mu sync.Mutex
|
||||||
|
job *proto.AcquiredJob
|
||||||
|
called int
|
||||||
|
complete chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAcquireOne(t *testing.T, job *proto.AcquiredJob) *acquireOne {
|
||||||
|
return &acquireOne{
|
||||||
|
t: t,
|
||||||
|
job: job,
|
||||||
|
complete: make(chan struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *acquireOne) acquireWithCancel(stream proto.DRPCProvisionerDaemon_AcquireJobWithCancelStream) error {
|
||||||
|
a.mu.Lock()
|
||||||
|
defer a.mu.Unlock()
|
||||||
|
a.called++
|
||||||
|
if a.called == 2 {
|
||||||
|
close(a.complete)
|
||||||
|
}
|
||||||
|
if a.called > 1 {
|
||||||
|
_, _ = stream.Recv()
|
||||||
|
_ = stream.Send(&proto.AcquiredJob{})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := stream.Send(a.job)
|
||||||
|
assert.NoError(a.t, err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -49,7 +49,6 @@ export default defineConfig({
|
||||||
`--dangerous-disable-rate-limits ` +
|
`--dangerous-disable-rate-limits ` +
|
||||||
`--provisioner-daemons 10 ` +
|
`--provisioner-daemons 10 ` +
|
||||||
`--provisioner-daemons-echo ` +
|
`--provisioner-daemons-echo ` +
|
||||||
`--provisioner-daemon-poll-interval 50ms ` +
|
|
||||||
`--pprof-enable`,
|
`--pprof-enable`,
|
||||||
env: {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
|
|
Loading…
Reference in New Issue