Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cli/testdata/coder_server_--help.golden
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ SUBCOMMANDS:
PostgreSQL deployment.

OPTIONS:
--agent-metadata-min-interval duration, $CODER_AGENT_METADATA_MIN_INTERVAL (default: 0s)
Minimum interval for agent metadata collection. Template-defined
intervals below this value will cause template import to fail.
Existing workspaces with lower intervals will be silently upgraded on
restart. Set to 0 to disable enforcement.

--allow-workspace-renames bool, $CODER_ALLOW_WORKSPACE_RENAMES (default: false)
DEPRECATED: Allow users to rename their workspaces. Use only for
temporary compatibility reasons, this will be removed in a future
Expand Down
5 changes: 5 additions & 0 deletions cli/testdata/server-config.yaml.golden
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,11 @@ sshKeygenAlgorithm: ed25519
# URL to use for agent troubleshooting when not set in the template.
# (default: https://coder.com/docs/admin/templates/troubleshooting, type: url)
agentFallbackTroubleshootingURL: https://coder.com/docs/admin/templates/troubleshooting
# Minimum interval for agent metadata collection. Template-defined intervals below
# this value will cause template import to fail. Existing workspaces with lower
# intervals will be silently upgraded on restart. Set to 0 to disable enforcement.
# (default: 0s, type: duration)
agentMetadataMinInterval: 0s
# Disable workspace apps that are not served from subdomains. Path-based apps can
# make requests to the Coder API and pose a security risk when the workspace
# serves malicious JavaScript. This is recommended for security purposes if a
Expand Down
3 changes: 3 additions & 0 deletions coderd/apidoc/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions coderd/apidoc/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

73 changes: 71 additions & 2 deletions coderd/provisionerdserver/provisionerdserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1613,7 +1613,11 @@ func (s *server) completeTemplateImportJob(ctx context.Context, job database.Pro
slog.F("resource_type", resource.Type),
slog.F("transition", transition))

if err := InsertWorkspaceResource(ctx, db, jobID, transition, resource, telemetrySnapshot); err != nil {
if err := InsertWorkspaceResource(ctx, db, jobID, transition, resource, telemetrySnapshot,
InsertWorkspaceResourceWithValidationMode(ValidationModeStrict),
InsertWorkspaceResourceWithDeploymentValues(s.DeploymentValues),
InsertWorkspaceResourceWithLogger(s.Logger),
); err != nil {
return xerrors.Errorf("insert resource: %w", err)
}
}
Expand Down Expand Up @@ -2014,6 +2018,9 @@ func (s *server) completeWorkspaceBuildJob(ctx context.Context, job database.Pro
// Ensure that the agent IDs we set previously
// are written to the database.
InsertWorkspaceResourceWithAgentIDsFromProto(),
InsertWorkspaceResourceWithValidationMode(ValidationModeUpgrade),
InsertWorkspaceResourceWithDeploymentValues(s.DeploymentValues),
InsertWorkspaceResourceWithLogger(s.Logger),
)
if err != nil {
return xerrors.Errorf("insert provisioner job: %w", err)
Expand Down Expand Up @@ -2623,8 +2630,23 @@ func InsertWorkspacePresetAndParameters(ctx context.Context, db database.Store,
return nil
}

// ValidationMode determines how agent metadata interval validation is enforced.
type ValidationMode int

const (
// ValidationModeStrict fails the operation if metadata intervals are below the minimum.
// Used for template imports.
ValidationModeStrict ValidationMode = iota
// ValidationModeUpgrade silently upgrades metadata intervals to meet the minimum.
// Used for workspace builds.
ValidationModeUpgrade
)

type insertWorkspaceResourceOptions struct {
useAgentIDsFromProto bool
validationMode ValidationMode
deploymentValues *codersdk.DeploymentValues
logger slog.Logger
}

// InsertWorkspaceResourceOption represents a functional option for
Expand All @@ -2639,6 +2661,27 @@ func InsertWorkspaceResourceWithAgentIDsFromProto() InsertWorkspaceResourceOptio
}
}

// InsertWorkspaceResourceWithValidationMode sets the validation mode for agent metadata intervals.
func InsertWorkspaceResourceWithValidationMode(mode ValidationMode) InsertWorkspaceResourceOption {
return func(opts *insertWorkspaceResourceOptions) {
opts.validationMode = mode
}
}

// InsertWorkspaceResourceWithDeploymentValues sets the deployment values for validation.
func InsertWorkspaceResourceWithDeploymentValues(dv *codersdk.DeploymentValues) InsertWorkspaceResourceOption {
return func(opts *insertWorkspaceResourceOptions) {
opts.deploymentValues = dv
}
}

// InsertWorkspaceResourceWithLogger sets the logger for logging validation actions.
func InsertWorkspaceResourceWithLogger(logger slog.Logger) InsertWorkspaceResourceOption {
return func(opts *insertWorkspaceResourceOptions) {
opts.logger = logger
}
}

func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.UUID, transition database.WorkspaceTransition, protoResource *sdkproto.Resource, snapshot *telemetry.Snapshot, opt ...InsertWorkspaceResourceOption) error {
opts := &insertWorkspaceResourceOptions{}
for _, o := range opt {
Expand Down Expand Up @@ -2776,13 +2819,39 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
snapshot.WorkspaceAgents = append(snapshot.WorkspaceAgents, telemetry.ConvertWorkspaceAgent(dbAgent))

for _, md := range prAgent.Metadata {
interval := md.Interval

// Apply minimum interval validation if configured
if opts.deploymentValues != nil && opts.deploymentValues.AgentMetadataMinInterval.Value() > 0 {
minInterval := opts.deploymentValues.AgentMetadataMinInterval.Value()
minIntervalSeconds := int64(minInterval.Seconds())

if interval < minIntervalSeconds {
if opts.validationMode == ValidationModeStrict {
// Template import - fail the operation
return xerrors.Errorf(
"agent %q metadata %q interval %ds is below minimum required %ds",
prAgent.Name, md.Key, interval, minIntervalSeconds,
)
}
// Workspace build - upgrade silently
opts.logger.Info(ctx, "upgrading agent metadata interval to meet minimum",
slog.F("agent", prAgent.Name),
slog.F("metadata_key", md.Key),
slog.F("original_interval_seconds", interval),
slog.F("upgraded_interval_seconds", minIntervalSeconds),
)
interval = minIntervalSeconds
}
}

p := database.InsertWorkspaceAgentMetadataParams{
WorkspaceAgentID: agentID,
DisplayName: md.DisplayName,
Script: md.Script,
Key: md.Key,
Timeout: md.Timeout,
Interval: md.Interval,
Interval: interval,
// #nosec G115 - Order represents a display order value that's always small and fits in int32
DisplayOrder: int32(md.Order),
}
Expand Down
Loading
Loading