@@ -1613,7 +1613,11 @@ func (s *server) completeTemplateImportJob(ctx context.Context, job database.Pro
16131613slog .F ("resource_type" ,resource .Type ),
16141614slog .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 {
16171621return 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.
20162020InsertWorkspaceResourceWithAgentIDsFromProto (),
2021+ InsertWorkspaceResourceWithValidationMode (ValidationModeUpgrade ),
2022+ InsertWorkspaceResourceWithDeploymentValues (s .DeploymentValues ),
2023+ InsertWorkspaceResourceWithLogger (s .Logger ),
20172024)
20182025if err != nil {
20192026return xerrors .Errorf ("insert provisioner job: %w" ,err )
@@ -2623,8 +2630,23 @@ func InsertWorkspacePresetAndParameters(ctx context.Context, db database.Store,
26232630return 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+
26262645type insertWorkspaceResourceOptions struct {
26272646useAgentIDsFromProto 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+
26422685func InsertWorkspaceResource (ctx context.Context ,db database.Store ,jobID uuid.UUID ,transition database.WorkspaceTransition ,protoResource * sdkproto.Resource ,snapshot * telemetry.Snapshot ,opt ... InsertWorkspaceResourceOption )error {
26432686opts := & insertWorkspaceResourceOptions {}
26442687for _ ,o := range opt {
@@ -2776,13 +2819,39 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
27762819snapshot .WorkspaceAgents = append (snapshot .WorkspaceAgents ,telemetry .ConvertWorkspaceAgent (dbAgent ))
27772820
27782821for _ ,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+
27792848p := database.InsertWorkspaceAgentMetadataParams {
27802849WorkspaceAgentID :agentID ,
27812850DisplayName :md .DisplayName ,
27822851Script :md .Script ,
27832852Key :md .Key ,
27842853Timeout :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
27872856DisplayOrder :int32 (md .Order ),
27882857}