feat: add support for telemetry-required licenses (#6194)

This commit is contained in:
Ammar Bandukwala
2023-02-14 14:26:47 -06:00
committed by GitHub
parent 15c862fcb5
commit 6e3330a03f
10 changed files with 49 additions and 21 deletions

3
coderd/apidoc/docs.go generated
View File

@ -6422,6 +6422,9 @@ const docTemplate = `{
"has_license": { "has_license": {
"type": "boolean" "type": "boolean"
}, },
"require_telemetry": {
"type": "boolean"
},
"trial": { "trial": {
"type": "boolean" "type": "boolean"
}, },

View File

@ -5742,6 +5742,9 @@
"has_license": { "has_license": {
"type": "boolean" "type": "boolean"
}, },
"require_telemetry": {
"type": "boolean"
},
"trial": { "trial": {
"type": "boolean" "type": "boolean"
}, },

View File

@ -81,11 +81,12 @@ type Feature struct {
} }
type Entitlements struct { type Entitlements struct {
Features map[FeatureName]Feature `json:"features"` Features map[FeatureName]Feature `json:"features"`
Warnings []string `json:"warnings"` Warnings []string `json:"warnings"`
Errors []string `json:"errors"` Errors []string `json:"errors"`
HasLicense bool `json:"has_license"` HasLicense bool `json:"has_license"`
Trial bool `json:"trial"` Trial bool `json:"trial"`
RequireTelemetry bool `json:"require_telemetry"`
// DEPRECATED: use Experiments instead. // DEPRECATED: use Experiments instead.
Experimental bool `json:"experimental"` Experimental bool `json:"experimental"`

View File

@ -128,6 +128,7 @@ curl -X GET http://coder-server:8080/api/v2/entitlements \
} }
}, },
"has_license": true, "has_license": true,
"require_telemetry": true,
"trial": true, "trial": true,
"warnings": ["string"] "warnings": ["string"]
} }

View File

@ -2806,6 +2806,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a
} }
}, },
"has_license": true, "has_license": true,
"require_telemetry": true,
"trial": true, "trial": true,
"warnings": ["string"] "warnings": ["string"]
} }
@ -2813,15 +2814,16 @@ CreateParameterRequest is a structure used to create a new parameter value for a
### Properties ### Properties
| Name | Type | Required | Restrictions | Description | | Name | Type | Required | Restrictions | Description |
| ------------------ | ------------------------------------ | -------- | ------------ | ------------------------------------- | | ------------------- | ------------------------------------ | -------- | ------------ | ------------------------------------- |
| `errors` | array of string | false | | | | `errors` | array of string | false | | |
| `experimental` | boolean | false | | Experimental use Experiments instead. | | `experimental` | boolean | false | | Experimental use Experiments instead. |
| `features` | object | false | | | | `features` | object | false | | |
| » `[any property]` | [codersdk.Feature](#codersdkfeature) | false | | | | » `[any property]` | [codersdk.Feature](#codersdkfeature) | false | | |
| `has_license` | boolean | false | | | | `has_license` | boolean | false | | |
| `trial` | boolean | false | | | | `require_telemetry` | boolean | false | | |
| `warnings` | array of string | false | | | | `trial` | boolean | false | | |
| `warnings` | array of string | false | | |
## codersdk.Experiment ## codersdk.Experiment

View File

@ -254,6 +254,17 @@ func (api *API) updateEntitlements(ctx context.Context) error {
if err != nil { if err != nil {
return err 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 entitlements.Experimental = api.DeploymentConfig.Experimental.Value || len(api.AGPL.Experiments) != 0
featureChanged := func(featureName codersdk.FeatureName) (changed bool, enabled bool) { featureChanged := func(featureName codersdk.FeatureName) (changed bool, enabled bool) {

View File

@ -98,6 +98,7 @@ func Entitlements(
if claims.AllFeatures { if claims.AllFeatures {
allFeatures = true allFeatures = true
} }
entitlements.RequireTelemetry = entitlements.RequireTelemetry || claims.RequireTelemetry
} }
if allFeatures { 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 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 // 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. // processing libraries to consider the token "valid" until then.
LicenseExpires *jwt.NumericDate `json:"license_expires,omitempty"` LicenseExpires *jwt.NumericDate `json:"license_expires,omitempty"`
AccountType string `json:"account_type,omitempty"` AccountType string `json:"account_type,omitempty"`
AccountID string `json:"account_id,omitempty"` AccountID string `json:"account_id,omitempty"`
Trial bool `json:"trial"` Trial bool `json:"trial"`
AllFeatures bool `json:"all_features"` AllFeatures bool `json:"all_features"`
Version uint64 `json:"version"` Version uint64 `json:"version"`
Features Features `json:"features"` Features Features `json:"features"`
RequireTelemetry bool `json:"require_telemetry,omitempty"`
} }
// ParseRaw consumes a license and returns the claims. // ParseRaw consumes a license and returns the claims.

View File

@ -654,6 +654,7 @@ export const getEntitlements = async (): Promise<TypesGen.Entitlements> => {
experimental: false, experimental: false,
features: withDefaultFeatures({}), features: withDefaultFeatures({}),
has_license: false, has_license: false,
require_telemetry: false,
trial: false, trial: false,
warnings: [], warnings: [],
} }

View File

@ -359,6 +359,7 @@ export interface Entitlements {
readonly errors: string[] readonly errors: string[]
readonly has_license: boolean readonly has_license: boolean
readonly trial: boolean readonly trial: boolean
readonly require_telemetry: boolean
readonly experimental: boolean readonly experimental: boolean
} }

View File

@ -1119,6 +1119,7 @@ export const MockEntitlements: TypesGen.Entitlements = {
has_license: false, has_license: false,
features: withDefaultFeatures({}), features: withDefaultFeatures({}),
experimental: false, experimental: false,
require_telemetry: false,
trial: false, trial: false,
} }
@ -1128,6 +1129,7 @@ export const MockEntitlementsWithWarnings: TypesGen.Entitlements = {
has_license: true, has_license: true,
experimental: false, experimental: false,
trial: false, trial: false,
require_telemetry: false,
features: withDefaultFeatures({ features: withDefaultFeatures({
user_limit: { user_limit: {
enabled: true, enabled: true,
@ -1151,6 +1153,7 @@ export const MockEntitlementsWithAuditLog: TypesGen.Entitlements = {
warnings: [], warnings: [],
has_license: true, has_license: true,
experimental: false, experimental: false,
require_telemetry: false,
trial: false, trial: false,
features: withDefaultFeatures({ features: withDefaultFeatures({
audit_log: { audit_log: {