Skip to content

Commit 60cccc2

Browse files
committed
implement license checks for external agents in workspace builds and templates
1 parent 07a9c42 commit 60cccc2

File tree

2 files changed

+69
-6
lines changed

2 files changed

+69
-6
lines changed

enterprise/coderd/coderd.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,18 @@ func (api *API) CheckBuildUsage(ctx context.Context, store database.Store, templ
952952
// When there are no licenses installed, a noop usage checker is used
953953
// instead.
954954

955+
// If the template version has an external agent, we need to check that the
956+
// license is entitled to this feature.
957+
if templateVersion.HasExternalAgent.Valid && templateVersion.HasExternalAgent.Bool {
958+
feature, ok := api.Entitlements.Feature(codersdk.FeatureWorkspaceExternalAgent)
959+
if !ok || !feature.Enabled {
960+
return wsbuilder.UsageCheckResponse{
961+
Permitted: false,
962+
Message: "You have a template which uses external agents but your license is not entitled to this feature. You will be unable to create new workspaces from these templates.",
963+
}, nil
964+
}
965+
}
966+
955967
// If the template version doesn't have an AI task, we don't need to check
956968
// usage.
957969
if !templateVersion.HasAITask.Valid || !templateVersion.HasAITask.Bool {

enterprise/coderd/license/license.go

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package license
33
import (
44
"context"
55
"crypto/ed25519"
6+
"database/sql"
67
"fmt"
78
"math"
89
"time"
@@ -94,10 +95,34 @@ func Entitlements(
9495
return codersdk.Entitlements{}, xerrors.Errorf("query active user count: %w", err)
9596
}
9697

98+
// nolint:gocritic // Getting external workspaces is a system function.
99+
externalWorkspaces, err := db.GetWorkspaces(dbauthz.AsSystemRestricted(ctx), database.GetWorkspacesParams{
100+
HasExternalAgent: sql.NullBool{
101+
Bool: true,
102+
Valid: true,
103+
},
104+
})
105+
if err != nil {
106+
return codersdk.Entitlements{}, xerrors.Errorf("query external workspaces: %w", err)
107+
}
108+
109+
// nolint:gocritic // Getting external templates is a system function.
110+
externalTemplates, err := db.GetTemplatesWithFilter(dbauthz.AsSystemRestricted(ctx), database.GetTemplatesWithFilterParams{
111+
HasExternalAgent: sql.NullBool{
112+
Bool: true,
113+
Valid: true,
114+
},
115+
})
116+
if err != nil {
117+
return codersdk.Entitlements{}, xerrors.Errorf("query external templates: %w", err)
118+
}
119+
97120
entitlements, err := LicensesEntitlements(ctx, now, licenses, enablements, keys, FeatureArguments{
98-
ActiveUserCount: activeUserCount,
99-
ReplicaCount: replicaCount,
100-
ExternalAuthCount: externalAuthCount,
121+
ActiveUserCount: activeUserCount,
122+
ReplicaCount: replicaCount,
123+
ExternalAuthCount: externalAuthCount,
124+
ExternalWorkspaceCount: int64(len(externalWorkspaces)),
125+
ExternalTemplateCount: int64(len(externalTemplates)),
101126
ManagedAgentCountFn: func(ctx context.Context, startTime time.Time, endTime time.Time) (int64, error) {
102127
// nolint:gocritic // Requires permission to read all workspaces to read managed agent count.
103128
return db.GetManagedAgentCount(dbauthz.AsSystemRestricted(ctx), database.GetManagedAgentCountParams{
@@ -114,9 +139,11 @@ func Entitlements(
114139
}
115140

116141
type FeatureArguments struct {
117-
ActiveUserCount int64
118-
ReplicaCount int
119-
ExternalAuthCount int
142+
ActiveUserCount int64
143+
ReplicaCount int
144+
ExternalAuthCount int
145+
ExternalWorkspaceCount int64
146+
ExternalTemplateCount int64
120147
// Unfortunately, managed agent count is not a simple count of the current
121148
// state of the world, but a count between two points in time determined by
122149
// the licenses.
@@ -418,6 +445,30 @@ func LicensesEntitlements(
418445
}
419446
}
420447

448+
if featureArguments.ExternalWorkspaceCount > 0 {
449+
feature := entitlements.Features[codersdk.FeatureWorkspaceExternalAgent]
450+
switch feature.Entitlement {
451+
case codersdk.EntitlementNotEntitled:
452+
entitlements.Errors = append(entitlements.Errors,
453+
"You have external workspaces but your license is not entitled to this feature.")
454+
case codersdk.EntitlementGracePeriod:
455+
entitlements.Warnings = append(entitlements.Warnings,
456+
"You have external workspaces but your license is expired.")
457+
}
458+
}
459+
460+
if featureArguments.ExternalTemplateCount > 0 {
461+
feature := entitlements.Features[codersdk.FeatureWorkspaceExternalAgent]
462+
switch feature.Entitlement {
463+
case codersdk.EntitlementNotEntitled:
464+
entitlements.Errors = append(entitlements.Errors,
465+
"You have templates which use external agents but your license is not entitled to this feature.")
466+
case codersdk.EntitlementGracePeriod:
467+
entitlements.Warnings = append(entitlements.Warnings,
468+
"You have templates which use external agents but your license is expired.")
469+
}
470+
}
471+
421472
// Managed agent warnings are applied based on usage period. We only
422473
// generate a warning if the license actually has managed agents.
423474
// Note that agents are free when unlicensed.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy