mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix: don't create autostart workspace builds with no available provisioners (#19067)
This should fix https://github.com/coder/coder/issues/17941 by introducing a check for whether there are any valid (non-stale provisioners for a job in the autobuild executor code path. --------- Signed-off-by: Callum Styan <callumstyan@gmail.com>
This commit is contained in:
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/database/provisionerjobs"
|
||||
"github.com/coder/coder/v2/coderd/database/pubsub"
|
||||
"github.com/coder/coder/v2/coderd/notifications"
|
||||
"github.com/coder/coder/v2/coderd/provisionerdserver"
|
||||
"github.com/coder/coder/v2/coderd/schedule"
|
||||
"github.com/coder/coder/v2/coderd/wsbuilder"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
@@ -132,6 +133,39 @@ func (e *Executor) Run() {
|
||||
})
|
||||
}
|
||||
|
||||
// hasValidProvisioner checks whether there is at least one valid (non-stale, correct tags) provisioner
|
||||
// based on time t and the tags maps (such as from a templateVersionJob).
|
||||
func (e *Executor) hasValidProvisioner(ctx context.Context, tx database.Store, t time.Time, ws database.Workspace, tags map[string]string) (bool, error) {
|
||||
queryParams := database.GetProvisionerDaemonsByOrganizationParams{
|
||||
OrganizationID: ws.OrganizationID,
|
||||
WantTags: tags,
|
||||
}
|
||||
|
||||
// nolint: gocritic // The user (in this case, the user/context for autostart builds) may not have the full
|
||||
// permissions to read provisioner daemons, but we need to check if there's any for the job prior to the
|
||||
// execution of the job via autostart to fix: https://github.com/coder/coder/issues/17941
|
||||
provisionerDaemons, err := tx.GetProvisionerDaemonsByOrganization(dbauthz.AsSystemReadProvisionerDaemons(ctx), queryParams)
|
||||
if err != nil {
|
||||
return false, xerrors.Errorf("get provisioner daemons: %w", err)
|
||||
}
|
||||
|
||||
logger := e.log.With(slog.F("tags", tags))
|
||||
// Check if any provisioners are active (not stale)
|
||||
for _, pd := range provisionerDaemons {
|
||||
if pd.LastSeenAt.Valid {
|
||||
age := t.Sub(pd.LastSeenAt.Time)
|
||||
if age <= provisionerdserver.StaleInterval {
|
||||
logger.Debug(ctx, "hasValidProvisioner: found active provisioner",
|
||||
slog.F("daemon_id", pd.ID),
|
||||
)
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.Debug(ctx, "hasValidProvisioner: no active provisioners found")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (e *Executor) runOnce(t time.Time) Stats {
|
||||
stats := Stats{
|
||||
Transitions: make(map[uuid.UUID]database.WorkspaceTransition),
|
||||
@@ -281,6 +315,22 @@ func (e *Executor) runOnce(t time.Time) Stats {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get the template version job to access tags
|
||||
templateVersionJob, err := tx.GetProvisionerJobByID(e.ctx, activeTemplateVersion.JobID)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get template version job: %w", err)
|
||||
}
|
||||
|
||||
// Before creating the workspace build, check for available provisioners
|
||||
hasProvisioners, err := e.hasValidProvisioner(e.ctx, tx, t, ws, templateVersionJob.Tags)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("check provisioner availability: %w", err)
|
||||
}
|
||||
if !hasProvisioners {
|
||||
log.Warn(e.ctx, "skipping autostart - no available provisioners")
|
||||
return nil // Skip this workspace
|
||||
}
|
||||
|
||||
if nextTransition != "" {
|
||||
builder := wsbuilder.New(ws, nextTransition, *e.buildUsageChecker.Load()).
|
||||
SetLastWorkspaceBuildInTx(&latestBuild).
|
||||
|
||||
Reference in New Issue
Block a user