Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit915bb41

Browse files
authored
feat: Add trial property to licenses (#4372)
* feat: Add trial property to licensesThis allows the frontend to display whether the user is ona trial license of Coder. This is useful for advertisingEnterprise functionality.* Improve tests for license enablement code* Add all features property
1 parent05670d1 commit915bb41

File tree

16 files changed

+504
-329
lines changed

16 files changed

+504
-329
lines changed

‎.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
"cliflag",
1010
"cliui",
1111
"codecov",
12-
"Codespaces",
1312
"coderd",
13+
"coderdenttest",
1414
"coderdtest",
1515
"codersdk",
1616
"cronstrue",
@@ -24,6 +24,7 @@
2424
"drpcmux",
2525
"drpcserver",
2626
"Dsts",
27+
"enablements",
2728
"fatih",
2829
"Formik",
2930
"gitsshkey",

‎codersdk/features.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ type Entitlements struct {
4242
Warnings []string`json:"warnings"`
4343
HasLicensebool`json:"has_license"`
4444
Experimentalbool`json:"experimental"`
45+
Trialbool`json:"trial"`
4546
}
4647

4748
func (c*Client)Entitlements(ctx context.Context) (Entitlements,error) {

‎enterprise/cli/features_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func TestFeaturesList(t *testing.T) {
5757
varentitlements codersdk.Entitlements
5858
err:=json.Unmarshal(buf.Bytes(),&entitlements)
5959
require.NoError(t,err,"unmarshal JSON output")
60-
assert.Len(t,entitlements.Features,4)
60+
assert.Len(t,entitlements.Features,5)
6161
assert.Empty(t,entitlements.Warnings)
6262
assert.Equal(t,codersdk.EntitlementNotEntitled,
6363
entitlements.Features[codersdk.FeatureUserLimit].Entitlement)
@@ -67,6 +67,8 @@ func TestFeaturesList(t *testing.T) {
6767
entitlements.Features[codersdk.FeatureBrowserOnly].Entitlement)
6868
assert.Equal(t,codersdk.EntitlementNotEntitled,
6969
entitlements.Features[codersdk.FeatureWorkspaceQuota].Entitlement)
70+
assert.Equal(t,codersdk.EntitlementNotEntitled,
71+
entitlements.Features[codersdk.FeatureSCIM].Entitlement)
7072
assert.False(t,entitlements.HasLicense)
7173
assert.False(t,entitlements.Experimental)
7274
})

‎enterprise/coderd/coderd.go

Lines changed: 35 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package coderd
33
import (
44
"context"
55
"crypto/ed25519"
6-
"fmt"
76
"net/http"
87
"sync"
98
"time"
@@ -15,11 +14,14 @@ import (
1514

1615
"cdr.dev/slog"
1716
"github.com/coder/coder/coderd"
17+
agplaudit"github.com/coder/coder/coderd/audit"
1818
"github.com/coder/coder/coderd/httpapi"
1919
"github.com/coder/coder/coderd/httpmw"
20+
"github.com/coder/coder/coderd/workspacequota"
2021
"github.com/coder/coder/codersdk"
2122
"github.com/coder/coder/enterprise/audit"
2223
"github.com/coder/coder/enterprise/audit/backends"
24+
"github.com/coder/coder/enterprise/coderd/license"
2325
)
2426

2527
// New constructs an Enterprise coderd API instance.
@@ -34,19 +36,8 @@ func New(ctx context.Context, options *Options) (*API, error) {
3436
}
3537
ctx,cancelFunc:=context.WithCancel(ctx)
3638
api:=&API{
37-
AGPL:coderd.New(options.Options),
38-
Options:options,
39-
40-
entitlements:entitlements{
41-
activeUsers: codersdk.Feature{
42-
Entitlement:codersdk.EntitlementNotEntitled,
43-
Enabled:false,
44-
},
45-
auditLogs:codersdk.EntitlementNotEntitled,
46-
browserOnly:codersdk.EntitlementNotEntitled,
47-
scim:codersdk.EntitlementNotEntitled,
48-
workspaceQuota:codersdk.EntitlementNotEntitled,
49-
},
39+
AGPL:coderd.New(options.Options),
40+
Options:options,
5041
cancelEntitlementsLoop:cancelFunc,
5142
}
5243
oauthConfigs:=&httpmw.OAuth2Configs{
@@ -117,16 +108,7 @@ type API struct {
117108

118109
cancelEntitlementsLoopfunc()
119110
entitlementsMu sync.RWMutex
120-
entitlementsentitlements
121-
}
122-
123-
typeentitlementsstruct {
124-
hasLicensebool
125-
activeUsers codersdk.Feature
126-
auditLogs codersdk.Entitlement
127-
browserOnly codersdk.Entitlement
128-
scim codersdk.Entitlement
129-
workspaceQuota codersdk.Entitlement
111+
entitlements codersdk.Entitlements
130112
}
131113

132114
func (api*API)Close()error {
@@ -135,94 +117,57 @@ func (api *API) Close() error {
135117
}
136118

137119
func (api*API)updateEntitlements(ctx context.Context)error {
138-
licenses,err:=api.Database.GetUnexpiredLicenses(ctx)
139-
iferr!=nil {
140-
returnerr
141-
}
142120
api.entitlementsMu.Lock()
143121
deferapi.entitlementsMu.Unlock()
144-
now:=time.Now()
145122

146-
// Default all entitlements to be disabled.
147-
entitlements:=entitlements{
148-
hasLicense:false,
149-
activeUsers: codersdk.Feature{
150-
Enabled:false,
151-
Entitlement:codersdk.EntitlementNotEntitled,
152-
},
153-
auditLogs:codersdk.EntitlementNotEntitled,
154-
scim:codersdk.EntitlementNotEntitled,
155-
browserOnly:codersdk.EntitlementNotEntitled,
156-
workspaceQuota:codersdk.EntitlementNotEntitled,
123+
entitlements,err:=license.Entitlements(ctx,api.Database,api.Logger,api.Keys,map[string]bool{
124+
codersdk.FeatureAuditLog:api.AuditLogging,
125+
codersdk.FeatureBrowserOnly:api.BrowserOnly,
126+
codersdk.FeatureSCIM:len(api.SCIMAPIKey)!=0,
127+
codersdk.FeatureWorkspaceQuota:api.UserWorkspaceQuota!=0,
128+
})
129+
iferr!=nil {
130+
returnerr
157131
}
158132

159-
// Here we loop through licenses to detect enabled features.
160-
for_,l:=rangelicenses {
161-
claims,err:=validateDBLicense(l,api.Keys)
162-
iferr!=nil {
163-
api.Logger.Debug(ctx,"skipping invalid license",
164-
slog.F("id",l.ID),slog.Error(err))
165-
continue
133+
featureChanged:=func(featureNamestring) (changedbool,enabledbool) {
134+
ifapi.entitlements.Features==nil {
135+
returntrue,entitlements.Features[featureName].Enabled
166136
}
167-
entitlements.hasLicense=true
168-
entitlement:=codersdk.EntitlementEntitled
169-
ifnow.After(claims.LicenseExpires.Time) {
170-
// if the grace period were over, the validation fails, so if we are after
171-
// LicenseExpires we must be in grace period.
172-
entitlement=codersdk.EntitlementGracePeriod
173-
}
174-
ifclaims.Features.UserLimit>0 {
175-
entitlements.activeUsers= codersdk.Feature{
176-
Enabled:true,
177-
Entitlement:entitlement,
178-
}
179-
currentLimit:=int64(0)
180-
ifentitlements.activeUsers.Limit!=nil {
181-
currentLimit=*entitlements.activeUsers.Limit
182-
}
183-
limit:=max(currentLimit,claims.Features.UserLimit)
184-
entitlements.activeUsers.Limit=&limit
185-
}
186-
ifclaims.Features.AuditLog>0 {
187-
entitlements.auditLogs=entitlement
188-
}
189-
ifclaims.Features.BrowserOnly>0 {
190-
entitlements.browserOnly=entitlement
191-
}
192-
ifclaims.Features.SCIM>0 {
193-
entitlements.scim=entitlement
194-
}
195-
ifclaims.Features.WorkspaceQuota>0 {
196-
entitlements.workspaceQuota=entitlement
137+
oldFeature:=api.entitlements.Features[featureName]
138+
newFeature:=entitlements.Features[featureName]
139+
ifoldFeature.Enabled!=newFeature.Enabled {
140+
returntrue,newFeature.Enabled
197141
}
142+
returnfalse,newFeature.Enabled
198143
}
199144

200-
ifentitlements.auditLogs!=api.entitlements.auditLogs {
201-
// A flag could be added to the options that would allow disabling
202-
// enhanced audit logging here!
203-
ifentitlements.auditLogs!=codersdk.EntitlementNotEntitled&&api.AuditLogging {
204-
auditor:=audit.NewAuditor(
145+
ifchanged,enabled:=featureChanged(codersdk.FeatureAuditLog);changed {
146+
auditor:=agplaudit.NewNop()
147+
ifenabled {
148+
auditor=audit.NewAuditor(
205149
audit.DefaultFilter,
206150
backends.NewPostgres(api.Database,true),
207151
backends.NewSlog(api.Logger),
208152
)
209-
api.AGPL.Auditor.Store(&auditor)
210153
}
154+
api.AGPL.Auditor.Store(&auditor)
211155
}
212156

213-
ifentitlements.browserOnly!=api.entitlements.browserOnly {
157+
ifchanged,enabled:=featureChanged(codersdk.FeatureBrowserOnly);changed {
214158
varhandlerfunc(rw http.ResponseWriter)bool
215-
ifentitlements.browserOnly!=codersdk.EntitlementNotEntitled&&api.BrowserOnly {
159+
ifenabled {
216160
handler=api.shouldBlockNonBrowserConnections
217161
}
218162
api.AGPL.WorkspaceClientCoordinateOverride.Store(&handler)
219163
}
220164

221-
ifentitlements.workspaceQuota!=api.entitlements.workspaceQuota {
222-
ifentitlements.workspaceQuota!=codersdk.EntitlementNotEntitled&&api.UserWorkspaceQuota>0 {
223-
enforcer:=NewEnforcer(api.Options.UserWorkspaceQuota)
224-
api.AGPL.WorkspaceQuotaEnforcer.Store(&enforcer)
165+
ifchanged,enabled:=featureChanged(codersdk.FeatureWorkspaceQuota);changed {
166+
enforcer:=workspacequota.NewNop()
167+
ifenabled {
168+
enforcer=NewEnforcer(api.Options.UserWorkspaceQuota)
225169
}
170+
api.AGPL.WorkspaceQuotaEnforcer.Store(&enforcer)
226171
}
227172

228173
api.entitlements=entitlements
@@ -235,82 +180,7 @@ func (api *API) serveEntitlements(rw http.ResponseWriter, r *http.Request) {
235180
api.entitlementsMu.RLock()
236181
entitlements:=api.entitlements
237182
api.entitlementsMu.RUnlock()
238-
239-
resp:= codersdk.Entitlements{
240-
Features:make(map[string]codersdk.Feature),
241-
Warnings:make([]string,0),
242-
HasLicense:entitlements.hasLicense,
243-
Experimental:api.Experimental,
244-
}
245-
246-
ifentitlements.activeUsers.Limit!=nil {
247-
activeUserCount,err:=api.Database.GetActiveUserCount(ctx)
248-
iferr!=nil {
249-
httpapi.Write(ctx,rw,http.StatusInternalServerError, codersdk.Response{
250-
Message:"Unable to query database",
251-
Detail:err.Error(),
252-
})
253-
return
254-
}
255-
entitlements.activeUsers.Actual=&activeUserCount
256-
ifactiveUserCount>*entitlements.activeUsers.Limit {
257-
resp.Warnings=append(resp.Warnings,
258-
fmt.Sprintf(
259-
"Your deployment has %d active users but is only licensed for %d.",
260-
activeUserCount,*entitlements.activeUsers.Limit))
261-
}
262-
}
263-
resp.Features[codersdk.FeatureUserLimit]=entitlements.activeUsers
264-
265-
// Audit logs
266-
resp.Features[codersdk.FeatureAuditLog]= codersdk.Feature{
267-
Entitlement:entitlements.auditLogs,
268-
Enabled:api.AuditLogging,
269-
}
270-
// Audit logging is enabled by default. We don't want to display
271-
// a warning if they don't have a license.
272-
ifentitlements.hasLicense&&api.AuditLogging {
273-
ifentitlements.auditLogs==codersdk.EntitlementNotEntitled {
274-
resp.Warnings=append(resp.Warnings,
275-
"Audit logging is enabled but your license is not entitled to this feature.")
276-
}
277-
ifentitlements.auditLogs==codersdk.EntitlementGracePeriod {
278-
resp.Warnings=append(resp.Warnings,
279-
"Audit logging is enabled but your license for this feature is expired.")
280-
}
281-
}
282-
283-
resp.Features[codersdk.FeatureBrowserOnly]= codersdk.Feature{
284-
Entitlement:entitlements.browserOnly,
285-
Enabled:api.BrowserOnly,
286-
}
287-
ifapi.BrowserOnly {
288-
ifentitlements.browserOnly==codersdk.EntitlementNotEntitled {
289-
resp.Warnings=append(resp.Warnings,
290-
"Browser only connections are enabled but your license is not entitled to this feature.")
291-
}
292-
ifentitlements.browserOnly==codersdk.EntitlementGracePeriod {
293-
resp.Warnings=append(resp.Warnings,
294-
"Browser only connections are enabled but your license for this feature is expired.")
295-
}
296-
}
297-
298-
resp.Features[codersdk.FeatureWorkspaceQuota]= codersdk.Feature{
299-
Entitlement:entitlements.workspaceQuota,
300-
Enabled:api.UserWorkspaceQuota>0,
301-
}
302-
ifapi.UserWorkspaceQuota>0 {
303-
ifentitlements.workspaceQuota==codersdk.EntitlementNotEntitled {
304-
resp.Warnings=append(resp.Warnings,
305-
"Workspace quotas are enabled but your license is not entitled to this feature.")
306-
}
307-
ifentitlements.workspaceQuota==codersdk.EntitlementGracePeriod {
308-
resp.Warnings=append(resp.Warnings,
309-
"Workspace quotas are enabled but your license for this feature is expired.")
310-
}
311-
}
312-
313-
httpapi.Write(ctx,rw,http.StatusOK,resp)
183+
httpapi.Write(ctx,rw,http.StatusOK,entitlements)
314184
}
315185

316186
func (api*API)runEntitlementsLoop(ctx context.Context) {
@@ -374,10 +244,3 @@ func (api *API) runEntitlementsLoop(ctx context.Context) {
374244
}
375245
}
376246
}
377-
378-
funcmax(a,bint64)int64 {
379-
ifa>b {
380-
returna
381-
}
382-
returnb
383-
}

‎enterprise/coderd/coderd_test.go

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -86,48 +86,6 @@ func TestEntitlements(t *testing.T) {
8686
assert.Equal(t,codersdk.EntitlementNotEntitled,al.Entitlement)
8787
assert.True(t,al.Enabled)
8888
})
89-
t.Run("Warnings",func(t*testing.T) {
90-
t.Parallel()
91-
client:=coderdenttest.New(t,&coderdenttest.Options{
92-
AuditLogging:true,
93-
BrowserOnly:true,
94-
})
95-
first:=coderdtest.CreateFirstUser(t,client)
96-
fori:=0;i<4;i++ {
97-
coderdtest.CreateAnotherUser(t,client,first.OrganizationID)
98-
}
99-
coderdenttest.AddLicense(t,client, coderdenttest.LicenseOptions{
100-
UserLimit:4,
101-
AuditLog:true,
102-
BrowserOnly:true,
103-
GraceAt:time.Now().Add(-time.Second),
104-
})
105-
res,err:=client.Entitlements(context.Background())
106-
require.NoError(t,err)
107-
assert.True(t,res.HasLicense)
108-
ul:=res.Features[codersdk.FeatureUserLimit]
109-
assert.Equal(t,codersdk.EntitlementGracePeriod,ul.Entitlement)
110-
assert.Equal(t,int64(4),*ul.Limit)
111-
assert.Equal(t,int64(5),*ul.Actual)
112-
assert.True(t,ul.Enabled)
113-
al:=res.Features[codersdk.FeatureAuditLog]
114-
assert.Equal(t,codersdk.EntitlementGracePeriod,al.Entitlement)
115-
assert.True(t,al.Enabled)
116-
assert.Nil(t,al.Limit)
117-
assert.Nil(t,al.Actual)
118-
bo:=res.Features[codersdk.FeatureBrowserOnly]
119-
assert.Equal(t,codersdk.EntitlementGracePeriod,bo.Entitlement)
120-
assert.True(t,bo.Enabled)
121-
assert.Nil(t,bo.Limit)
122-
assert.Nil(t,bo.Actual)
123-
assert.Len(t,res.Warnings,3)
124-
assert.Contains(t,res.Warnings,
125-
"Your deployment has 5 active users but is only licensed for 4.")
126-
assert.Contains(t,res.Warnings,
127-
"Audit logging is enabled but your license for this feature is expired.")
128-
assert.Contains(t,res.Warnings,
129-
"Browser only connections are enabled but your license for this feature is expired.")
130-
})
13189
t.Run("Pubsub",func(t*testing.T) {
13290
t.Parallel()
13391
client,_,api:=coderdenttest.NewWithAPI(t,nil)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp