Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 501cfa9

Browse files
authored
refactor: deduplicate / type license feature code (#5734)
1 parent ea1b03f commit 501cfa9

39 files changed

+648
-396
lines changed

codersdk/features.go

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"encoding/json"
66
"net/http"
7+
"strings"
78
)
89

910
type Entitlement string
@@ -14,19 +15,24 @@ const (
1415
EntitlementNotEntitled Entitlement = "not_entitled"
1516
)
1617

18+
// To add a new feature, modify this set of enums as well as the FeatureNames
19+
// array below.
20+
type FeatureName string
21+
1722
const (
18-
FeatureUserLimit = "user_limit"
19-
FeatureAuditLog = "audit_log"
20-
FeatureBrowserOnly = "browser_only"
21-
FeatureSCIM = "scim"
22-
FeatureTemplateRBAC = "template_rbac"
23-
FeatureHighAvailability = "high_availability"
24-
FeatureMultipleGitAuth = "multiple_git_auth"
25-
FeatureExternalProvisionerDaemons = "external_provisioner_daemons"
26-
FeatureAppearance = "appearance"
23+
FeatureUserLimit FeatureName = "user_limit"
24+
FeatureAuditLog FeatureName = "audit_log"
25+
FeatureBrowserOnly FeatureName = "browser_only"
26+
FeatureSCIM FeatureName = "scim"
27+
FeatureTemplateRBAC FeatureName = "template_rbac"
28+
FeatureHighAvailability FeatureName = "high_availability"
29+
FeatureMultipleGitAuth FeatureName = "multiple_git_auth"
30+
FeatureExternalProvisionerDaemons FeatureName = "external_provisioner_daemons"
31+
FeatureAppearance FeatureName = "appearance"
2732
)
2833

29-
var FeatureNames = []string{
34+
// FeatureNames must be kept in-sync with the Feature enum above.
35+
var FeatureNames = []FeatureName{
3036
FeatureUserLimit,
3137
FeatureAuditLog,
3238
FeatureBrowserOnly,
@@ -38,6 +44,29 @@ var FeatureNames = []string{
3844
FeatureAppearance,
3945
}
4046

47+
// Humanize returns the feature name in a human-readable format.
48+
func (n FeatureName) Humanize() string {
49+
switch n {
50+
case FeatureTemplateRBAC:
51+
return "Template RBAC"
52+
case FeatureSCIM:
53+
return "SCIM"
54+
default:
55+
return strings.Title(strings.ReplaceAll(string(n), "_", " "))
56+
}
57+
}
58+
59+
// AlwaysEnable returns if the feature is always enabled if entitled.
60+
// Warning: We don't know if we need this functionality.
61+
// This method may disappear at any time.
62+
func (n FeatureName) AlwaysEnable() bool {
63+
return map[FeatureName]bool{
64+
FeatureMultipleGitAuth: true,
65+
FeatureExternalProvisionerDaemons: true,
66+
FeatureAppearance: true,
67+
}[n]
68+
}
69+
4170
type Feature struct {
4271
Entitlement Entitlement `json:"entitlement"`
4372
Enabled bool `json:"enabled"`
@@ -46,12 +75,12 @@ type Feature struct {
4675
}
4776

4877
type Entitlements struct {
49-
Features map[string]Feature `json:"features"`
50-
Warnings []string `json:"warnings"`
51-
Errors []string `json:"errors"`
52-
HasLicense bool `json:"has_license"`
53-
Experimental bool `json:"experimental"`
54-
Trial bool `json:"trial"`
78+
Features map[FeatureName]Feature `json:"features"`
79+
Warnings []string `json:"warnings"`
80+
Errors []string `json:"errors"`
81+
HasLicense bool `json:"has_license"`
82+
Experimental bool `json:"experimental"`
83+
Trial bool `json:"trial"`
5584
}
5685

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

codersdk/licenses.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"time"
99

1010
"github.com/google/uuid"
11+
"golang.org/x/xerrors"
1112
)
1213

1314
type AddLicenseRequest struct {
@@ -25,6 +26,30 @@ type License struct {
2526
Claims map[string]interface{} `json:"claims"`
2627
}
2728

29+
// Features provides the feature claims in license.
30+
func (l *License) Features() (map[FeatureName]int64, error) {
31+
strMap, ok := l.Claims["features"].(map[string]interface{})
32+
if !ok {
33+
return nil, xerrors.New("features key is unexpected type")
34+
}
35+
fMap := make(map[FeatureName]int64)
36+
for k, v := range strMap {
37+
jn, ok := v.(json.Number)
38+
if !ok {
39+
return nil, xerrors.Errorf("feature %q has unexpected type", k)
40+
}
41+
42+
n, err := jn.Int64()
43+
if err != nil {
44+
return nil, err
45+
}
46+
47+
fMap[FeatureName(k)] = n
48+
}
49+
50+
return fMap, nil
51+
}
52+
2853
func (c *Client) AddLicense(ctx context.Context, r AddLicenseRequest) (License, error) {
2954
res, err := c.Request(ctx, http.MethodPost, "/api/v2/licenses", r)
3055
if err != nil {

enterprise/cli/features.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,17 +88,17 @@ func featuresList() *cobra.Command {
8888
}
8989

9090
type featureRow struct {
91-
Name string `table:"name"`
92-
Entitlement string `table:"entitlement"`
93-
Enabled bool `table:"enabled"`
94-
Limit *int64 `table:"limit"`
95-
Actual *int64 `table:"actual"`
91+
Name codersdk.FeatureName `table:"name"`
92+
Entitlement string `table:"entitlement"`
93+
Enabled bool `table:"enabled"`
94+
Limit *int64 `table:"limit"`
95+
Actual *int64 `table:"actual"`
9696
}
9797

9898
// displayFeatures will return a table displaying all features passed in.
9999
// filterColumns must be a subset of the feature fields and will determine which
100100
// columns to display
101-
func displayFeatures(filterColumns []string, features map[string]codersdk.Feature) (string, error) {
101+
func displayFeatures(filterColumns []string, features map[codersdk.FeatureName]codersdk.Feature) (string, error) {
102102
rows := make([]featureRow, 0, len(features))
103103
for name, feat := range features {
104104
rows = append(rows, featureRow{

enterprise/cli/groupcreate_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ import (
99
"github.com/coder/coder/cli/clitest"
1010
"github.com/coder/coder/cli/cliui"
1111
"github.com/coder/coder/coderd/coderdtest"
12+
"github.com/coder/coder/codersdk"
1213
"github.com/coder/coder/enterprise/cli"
1314
"github.com/coder/coder/enterprise/coderd/coderdenttest"
15+
"github.com/coder/coder/enterprise/coderd/license"
1416
"github.com/coder/coder/pty/ptytest"
1517
)
1618

@@ -23,7 +25,9 @@ func TestCreateGroup(t *testing.T) {
2325
client := coderdenttest.New(t, nil)
2426
coderdtest.CreateFirstUser(t, client)
2527
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
26-
TemplateRBAC: true,
28+
Features: license.Features{
29+
codersdk.FeatureTemplateRBAC: 1,
30+
},
2731
})
2832

2933
var (

enterprise/cli/groupdelete_test.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/coder/coder/codersdk"
1313
"github.com/coder/coder/enterprise/cli"
1414
"github.com/coder/coder/enterprise/coderd/coderdenttest"
15+
"github.com/coder/coder/enterprise/coderd/license"
1516
"github.com/coder/coder/pty/ptytest"
1617
"github.com/coder/coder/testutil"
1718
)
@@ -26,7 +27,9 @@ func TestGroupDelete(t *testing.T) {
2627
admin := coderdtest.CreateFirstUser(t, client)
2728

2829
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
29-
TemplateRBAC: true,
30+
Features: license.Features{
31+
codersdk.FeatureTemplateRBAC: 1,
32+
},
3033
})
3134

3235
ctx, _ := testutil.Context(t)
@@ -57,7 +60,9 @@ func TestGroupDelete(t *testing.T) {
5760
_ = coderdtest.CreateFirstUser(t, client)
5861

5962
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
60-
TemplateRBAC: true,
63+
Features: license.Features{
64+
codersdk.FeatureTemplateRBAC: 1,
65+
},
6166
})
6267

6368
cmd, root := clitest.NewWithSubcommands(t, cli.EnterpriseSubcommands(),

enterprise/cli/groupedit_test.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/coder/coder/codersdk"
1313
"github.com/coder/coder/enterprise/cli"
1414
"github.com/coder/coder/enterprise/coderd/coderdenttest"
15+
"github.com/coder/coder/enterprise/coderd/license"
1516
"github.com/coder/coder/pty/ptytest"
1617
"github.com/coder/coder/testutil"
1718
)
@@ -26,7 +27,9 @@ func TestGroupEdit(t *testing.T) {
2627
admin := coderdtest.CreateFirstUser(t, client)
2728

2829
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
29-
TemplateRBAC: true,
30+
Features: license.Features{
31+
codersdk.FeatureTemplateRBAC: 1,
32+
},
3033
})
3134

3235
ctx, _ := testutil.Context(t)
@@ -77,7 +80,9 @@ func TestGroupEdit(t *testing.T) {
7780
admin := coderdtest.CreateFirstUser(t, client)
7881

7982
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
80-
TemplateRBAC: true,
83+
Features: license.Features{
84+
codersdk.FeatureTemplateRBAC: 1,
85+
},
8186
})
8287

8388
ctx, _ := testutil.Context(t)
@@ -106,7 +111,9 @@ func TestGroupEdit(t *testing.T) {
106111
_ = coderdtest.CreateFirstUser(t, client)
107112

108113
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
109-
TemplateRBAC: true,
114+
Features: license.Features{
115+
codersdk.FeatureTemplateRBAC: 1,
116+
},
110117
})
111118

112119
cmd, root := clitest.NewWithSubcommands(t, cli.EnterpriseSubcommands(), "groups", "edit")

enterprise/cli/grouplist_test.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/coder/coder/codersdk"
1111
"github.com/coder/coder/enterprise/cli"
1212
"github.com/coder/coder/enterprise/coderd/coderdenttest"
13+
"github.com/coder/coder/enterprise/coderd/license"
1314
"github.com/coder/coder/pty/ptytest"
1415
"github.com/coder/coder/testutil"
1516
)
@@ -24,7 +25,9 @@ func TestGroupList(t *testing.T) {
2425
admin := coderdtest.CreateFirstUser(t, client)
2526

2627
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
27-
TemplateRBAC: true,
28+
Features: license.Features{
29+
codersdk.FeatureTemplateRBAC: 1,
30+
},
2831
})
2932

3033
ctx, _ := testutil.Context(t)
@@ -81,7 +84,9 @@ func TestGroupList(t *testing.T) {
8184
_ = coderdtest.CreateFirstUser(t, client)
8285

8386
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
84-
TemplateRBAC: true,
87+
Features: license.Features{
88+
codersdk.FeatureTemplateRBAC: 1,
89+
},
8590
})
8691

8792
cmd, root := clitest.NewWithSubcommands(t, cli.EnterpriseSubcommands(), "groups", "list")

enterprise/cli/licenses_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ func (s *fakeLicenseAPI) deleteLicense(rw http.ResponseWriter, r *http.Request)
338338
}
339339

340340
func (*fakeLicenseAPI) entitlements(rw http.ResponseWriter, r *http.Request) {
341-
features := make(map[string]codersdk.Feature)
341+
features := make(map[codersdk.FeatureName]codersdk.Feature)
342342
for _, f := range codersdk.FeatureNames {
343343
features[f] = codersdk.Feature{
344344
Entitlement: codersdk.EntitlementEntitled,

enterprise/coderd/appearance_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/coder/coder/coderd/coderdtest"
1212
"github.com/coder/coder/codersdk"
1313
"github.com/coder/coder/enterprise/coderd/coderdenttest"
14+
"github.com/coder/coder/enterprise/coderd/license"
1415
"github.com/coder/coder/testutil"
1516
)
1617

@@ -30,7 +31,9 @@ func TestServiceBanners(t *testing.T) {
3031
require.False(t, sb.ServiceBanner.Enabled)
3132

3233
coderdenttest.AddLicense(t, adminClient, coderdenttest.LicenseOptions{
33-
ServiceBanners: true,
34+
Features: license.Features{
35+
codersdk.FeatureAppearance: 1,
36+
},
3437
})
3538

3639
// Default state

enterprise/coderd/authorize_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/coder/coder/coderd/rbac"
1212
"github.com/coder/coder/codersdk"
1313
"github.com/coder/coder/enterprise/coderd/coderdenttest"
14+
"github.com/coder/coder/enterprise/coderd/license"
1415
"github.com/coder/coder/testutil"
1516
)
1617

@@ -28,7 +29,9 @@ func TestCheckACLPermissions(t *testing.T) {
2829
// Create adminClient, member, and org adminClient
2930
adminUser := coderdtest.CreateFirstUser(t, adminClient)
3031
_ = coderdenttest.AddLicense(t, adminClient, coderdenttest.LicenseOptions{
31-
TemplateRBAC: true,
32+
Features: license.Features{
33+
codersdk.FeatureTemplateRBAC: 1,
34+
},
3235
})
3336

3437
memberClient := coderdtest.CreateAnotherUser(t, adminClient, adminUser.OrganizationID)

enterprise/coderd/coderd.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ func (api *API) updateEntitlements(ctx context.Context) error {
238238
api.entitlementsMu.Lock()
239239
defer api.entitlementsMu.Unlock()
240240

241-
entitlements, err := license.Entitlements(ctx, api.Database, api.Logger, len(api.replicaManager.All()), len(api.GitAuthConfigs), api.Keys, map[string]bool{
241+
entitlements, err := license.Entitlements(ctx, api.Database, api.Logger, len(api.replicaManager.All()), len(api.GitAuthConfigs), api.Keys, map[codersdk.FeatureName]bool{
242242
codersdk.FeatureAuditLog: api.AuditLogging,
243243
codersdk.FeatureBrowserOnly: api.BrowserOnly,
244244
codersdk.FeatureSCIM: len(api.SCIMAPIKey) != 0,
@@ -252,7 +252,7 @@ func (api *API) updateEntitlements(ctx context.Context) error {
252252
}
253253
entitlements.Experimental = api.DeploymentConfig.Experimental.Value
254254

255-
featureChanged := func(featureName string) (changed bool, enabled bool) {
255+
featureChanged := func(featureName codersdk.FeatureName) (changed bool, enabled bool) {
256256
if api.entitlements.Features == nil {
257257
return true, entitlements.Features[featureName].Enabled
258258
}

0 commit comments

Comments
 (0)