Skip to content

Commit 6e995c5

Browse files
Callum Styanclaude
andcommitted
Add deployment-wide agent metadata minimum interval enforcement
Implements dual enforcement strategy for agent metadata intervals: - Template imports: Strict validation fails if intervals below minimum - Workspace builds: Silent upgrade to minimum at runtime Configuration: - Flag: --agent-metadata-min-interval - Env: CODER_AGENT_METADATA_MIN_INTERVAL - Default: 0s (enforcement disabled) Implementation: - ValidationMode options pattern in InsertWorkspaceResource - ValidationModeStrict for template imports (fails on violation) - ValidationModeUpgrade for workspace builds (clamps to minimum, logs) - Enforcement at metadata insertion time (provisionerdserver.go:2814-2857) Testing: - 5 comprehensive unit tests using database mocks - No Docker/Postgres dependency required - Tests verify both validation modes and logging 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 05b02cf commit 6e995c5

File tree

3 files changed

+631
-2
lines changed

3 files changed

+631
-2
lines changed

coderd/provisionerdserver/provisionerdserver.go

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1613,7 +1613,11 @@ func (s *server) completeTemplateImportJob(ctx context.Context, job database.Pro
16131613
slog.F("resource_type", resource.Type),
16141614
slog.F("transition", transition))
16151615

1616-
if err := InsertWorkspaceResource(ctx, db, jobID, transition, resource, telemetrySnapshot); err != nil {
1616+
if err := InsertWorkspaceResource(ctx, db, jobID, transition, resource, telemetrySnapshot,
1617+
InsertWorkspaceResourceWithValidationMode(ValidationModeStrict),
1618+
InsertWorkspaceResourceWithDeploymentValues(s.DeploymentValues),
1619+
InsertWorkspaceResourceWithLogger(s.Logger),
1620+
); err != nil {
16171621
return xerrors.Errorf("insert resource: %w", err)
16181622
}
16191623
}
@@ -2014,6 +2018,9 @@ func (s *server) completeWorkspaceBuildJob(ctx context.Context, job database.Pro
20142018
// Ensure that the agent IDs we set previously
20152019
// are written to the database.
20162020
InsertWorkspaceResourceWithAgentIDsFromProto(),
2021+
InsertWorkspaceResourceWithValidationMode(ValidationModeUpgrade),
2022+
InsertWorkspaceResourceWithDeploymentValues(s.DeploymentValues),
2023+
InsertWorkspaceResourceWithLogger(s.Logger),
20172024
)
20182025
if err != nil {
20192026
return xerrors.Errorf("insert provisioner job: %w", err)
@@ -2623,8 +2630,23 @@ func InsertWorkspacePresetAndParameters(ctx context.Context, db database.Store,
26232630
return nil
26242631
}
26252632

2633+
// ValidationMode determines how agent metadata interval validation is enforced.
2634+
type ValidationMode int
2635+
2636+
const (
2637+
// ValidationModeStrict fails the operation if metadata intervals are below the minimum.
2638+
// Used for template imports.
2639+
ValidationModeStrict ValidationMode = iota
2640+
// ValidationModeUpgrade silently upgrades metadata intervals to meet the minimum.
2641+
// Used for workspace builds.
2642+
ValidationModeUpgrade
2643+
)
2644+
26262645
type insertWorkspaceResourceOptions struct {
26272646
useAgentIDsFromProto bool
2647+
validationMode ValidationMode
2648+
deploymentValues *codersdk.DeploymentValues
2649+
logger slog.Logger
26282650
}
26292651

26302652
// InsertWorkspaceResourceOption represents a functional option for
@@ -2639,6 +2661,27 @@ func InsertWorkspaceResourceWithAgentIDsFromProto() InsertWorkspaceResourceOptio
26392661
}
26402662
}
26412663

2664+
// InsertWorkspaceResourceWithValidationMode sets the validation mode for agent metadata intervals.
2665+
func InsertWorkspaceResourceWithValidationMode(mode ValidationMode) InsertWorkspaceResourceOption {
2666+
return func(opts *insertWorkspaceResourceOptions) {
2667+
opts.validationMode = mode
2668+
}
2669+
}
2670+
2671+
// InsertWorkspaceResourceWithDeploymentValues sets the deployment values for validation.
2672+
func InsertWorkspaceResourceWithDeploymentValues(dv *codersdk.DeploymentValues) InsertWorkspaceResourceOption {
2673+
return func(opts *insertWorkspaceResourceOptions) {
2674+
opts.deploymentValues = dv
2675+
}
2676+
}
2677+
2678+
// InsertWorkspaceResourceWithLogger sets the logger for logging validation actions.
2679+
func InsertWorkspaceResourceWithLogger(logger slog.Logger) InsertWorkspaceResourceOption {
2680+
return func(opts *insertWorkspaceResourceOptions) {
2681+
opts.logger = logger
2682+
}
2683+
}
2684+
26422685
func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.UUID, transition database.WorkspaceTransition, protoResource *sdkproto.Resource, snapshot *telemetry.Snapshot, opt ...InsertWorkspaceResourceOption) error {
26432686
opts := &insertWorkspaceResourceOptions{}
26442687
for _, o := range opt {
@@ -2776,13 +2819,39 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
27762819
snapshot.WorkspaceAgents = append(snapshot.WorkspaceAgents, telemetry.ConvertWorkspaceAgent(dbAgent))
27772820

27782821
for _, md := range prAgent.Metadata {
2822+
interval := md.Interval
2823+
2824+
// Apply minimum interval validation if configured
2825+
if opts.deploymentValues != nil && opts.deploymentValues.AgentMetadataMinInterval.Value() > 0 {
2826+
minInterval := opts.deploymentValues.AgentMetadataMinInterval.Value()
2827+
minIntervalSeconds := int64(minInterval.Seconds())
2828+
2829+
if interval < minIntervalSeconds {
2830+
if opts.validationMode == ValidationModeStrict {
2831+
// Template import - fail the operation
2832+
return xerrors.Errorf(
2833+
"agent %q metadata %q interval %ds is below minimum required %ds",
2834+
prAgent.Name, md.Key, interval, minIntervalSeconds,
2835+
)
2836+
}
2837+
// Workspace build - upgrade silently
2838+
opts.logger.Info(ctx, "upgrading agent metadata interval to meet minimum",
2839+
slog.F("agent", prAgent.Name),
2840+
slog.F("metadata_key", md.Key),
2841+
slog.F("original_interval_seconds", interval),
2842+
slog.F("upgraded_interval_seconds", minIntervalSeconds),
2843+
)
2844+
interval = minIntervalSeconds
2845+
}
2846+
}
2847+
27792848
p := database.InsertWorkspaceAgentMetadataParams{
27802849
WorkspaceAgentID: agentID,
27812850
DisplayName: md.DisplayName,
27822851
Script: md.Script,
27832852
Key: md.Key,
27842853
Timeout: md.Timeout,
2785-
Interval: md.Interval,
2854+
Interval: interval,
27862855
// #nosec G115 - Order represents a display order value that's always small and fits in int32
27872856
DisplayOrder: int32(md.Order),
27882857
}

0 commit comments

Comments
 (0)