diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 06583721c9679..54f7fe0c9d6e4 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -6422,6 +6422,9 @@ const docTemplate = `{ "has_license": { "type": "boolean" }, + "require_telemetry": { + "type": "boolean" + }, "trial": { "type": "boolean" }, diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 44a964ff435eb..6eb855dab5455 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -5742,6 +5742,9 @@ "has_license": { "type": "boolean" }, + "require_telemetry": { + "type": "boolean" + }, "trial": { "type": "boolean" }, diff --git a/codersdk/deployment.go b/codersdk/deployment.go index ced725f43a38a..76182f2925140 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -81,11 +81,12 @@ type Feature struct { } type Entitlements struct { - Features map[FeatureName]Feature `json:"features"` - Warnings []string `json:"warnings"` - Errors []string `json:"errors"` - HasLicense bool `json:"has_license"` - Trial bool `json:"trial"` + Features map[FeatureName]Feature `json:"features"` + Warnings []string `json:"warnings"` + Errors []string `json:"errors"` + HasLicense bool `json:"has_license"` + Trial bool `json:"trial"` + RequireTelemetry bool `json:"require_telemetry"` // DEPRECATED: use Experiments instead. Experimental bool `json:"experimental"` diff --git a/docs/api/enterprise.md b/docs/api/enterprise.md index 94214ad59f20f..be461cc606e44 100644 --- a/docs/api/enterprise.md +++ b/docs/api/enterprise.md @@ -128,6 +128,7 @@ curl -X GET http://coder-server:8080/api/v2/entitlements \ } }, "has_license": true, + "require_telemetry": true, "trial": true, "warnings": ["string"] } diff --git a/docs/api/schemas.md b/docs/api/schemas.md index c9d98afbc7e07..afa6e754c76af 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -2806,6 +2806,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a } }, "has_license": true, + "require_telemetry": true, "trial": true, "warnings": ["string"] } @@ -2813,15 +2814,16 @@ CreateParameterRequest is a structure used to create a new parameter value for a ### Properties -| Name | Type | Required | Restrictions | Description | -| ------------------ | ------------------------------------ | -------- | ------------ | ------------------------------------- | -| `errors` | array of string | false | | | -| `experimental` | boolean | false | | Experimental use Experiments instead. | -| `features` | object | false | | | -| » `[any property]` | [codersdk.Feature](#codersdkfeature) | false | | | -| `has_license` | boolean | false | | | -| `trial` | boolean | false | | | -| `warnings` | array of string | false | | | +| Name | Type | Required | Restrictions | Description | +| ------------------- | ------------------------------------ | -------- | ------------ | ------------------------------------- | +| `errors` | array of string | false | | | +| `experimental` | boolean | false | | Experimental use Experiments instead. | +| `features` | object | false | | | +| » `[any property]` | [codersdk.Feature](#codersdkfeature) | false | | | +| `has_license` | boolean | false | | | +| `require_telemetry` | boolean | false | | | +| `trial` | boolean | false | | | +| `warnings` | array of string | false | | | ## codersdk.Experiment diff --git a/enterprise/coderd/coderd.go b/enterprise/coderd/coderd.go index 67d5b543aef31..0bcf7a40e185e 100644 --- a/enterprise/coderd/coderd.go +++ b/enterprise/coderd/coderd.go @@ -252,6 +252,17 @@ func (api *API) updateEntitlements(ctx context.Context) error { if err != nil { return err } + + if entitlements.RequireTelemetry && !api.DeploymentConfig.Telemetry.Enable.Value { + // We can't fail because then the user couldn't remove the offending + // license w/o a restart. + api.entitlements.Errors = []string{ + "License requires telemetry but telemetry is disabled", + } + api.Logger.Error(ctx, "license requires telemetry enabled") + return nil + } + entitlements.Experimental = api.DeploymentConfig.Experimental.Value || len(api.AGPL.Experiments) != 0 featureChanged := func(featureName codersdk.FeatureName) (changed bool, enabled bool) { diff --git a/enterprise/coderd/license/license.go b/enterprise/coderd/license/license.go index 4e66ab578fad6..39bb0bb65ffd6 100644 --- a/enterprise/coderd/license/license.go +++ b/enterprise/coderd/license/license.go @@ -98,6 +98,7 @@ func Entitlements( if claims.AllFeatures { allFeatures = true } + entitlements.RequireTelemetry = entitlements.RequireTelemetry || claims.RequireTelemetry } if allFeatures { @@ -224,13 +225,14 @@ type Claims struct { // the end of the grace period (identical to LicenseExpires if there is no grace period). // The reason we use the standard claim for the end of the grace period is that we want JWT // processing libraries to consider the token "valid" until then. - LicenseExpires *jwt.NumericDate `json:"license_expires,omitempty"` - AccountType string `json:"account_type,omitempty"` - AccountID string `json:"account_id,omitempty"` - Trial bool `json:"trial"` - AllFeatures bool `json:"all_features"` - Version uint64 `json:"version"` - Features Features `json:"features"` + LicenseExpires *jwt.NumericDate `json:"license_expires,omitempty"` + AccountType string `json:"account_type,omitempty"` + AccountID string `json:"account_id,omitempty"` + Trial bool `json:"trial"` + AllFeatures bool `json:"all_features"` + Version uint64 `json:"version"` + Features Features `json:"features"` + RequireTelemetry bool `json:"require_telemetry,omitempty"` } // ParseRaw consumes a license and returns the claims. diff --git a/site/src/api/api.ts b/site/src/api/api.ts index 2b13f2ded771d..37d89bf22288f 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -654,6 +654,7 @@ export const getEntitlements = async (): Promise => { experimental: false, features: withDefaultFeatures({}), has_license: false, + require_telemetry: false, trial: false, warnings: [], } diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 64ad8a3e5f8b8..6c2606e0ca581 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -359,6 +359,7 @@ export interface Entitlements { readonly errors: string[] readonly has_license: boolean readonly trial: boolean + readonly require_telemetry: boolean readonly experimental: boolean } diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index cce61e6c75876..8876bd169a7a2 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -1119,6 +1119,7 @@ export const MockEntitlements: TypesGen.Entitlements = { has_license: false, features: withDefaultFeatures({}), experimental: false, + require_telemetry: false, trial: false, } @@ -1128,6 +1129,7 @@ export const MockEntitlementsWithWarnings: TypesGen.Entitlements = { has_license: true, experimental: false, trial: false, + require_telemetry: false, features: withDefaultFeatures({ user_limit: { enabled: true, @@ -1151,6 +1153,7 @@ export const MockEntitlementsWithAuditLog: TypesGen.Entitlements = { warnings: [], has_license: true, experimental: false, + require_telemetry: false, trial: false, features: withDefaultFeatures({ audit_log: {