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

Skip to content

Commit 1cfe5de

Browse files
authored
Add Service Banners (#5272)
1 parent df389d4 commit 1cfe5de

36 files changed

+959
-4
lines changed

coderd/database/databasefake/databasefake.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ type data struct {
122122
deploymentID string
123123
derpMeshKey string
124124
lastUpdateCheck []byte
125+
serviceBanner []byte
125126
lastLicenseID int32
126127
}
127128

@@ -3331,6 +3332,25 @@ func (q *fakeQuerier) GetLastUpdateCheck(_ context.Context) (string, error) {
33313332
return string(q.lastUpdateCheck), nil
33323333
}
33333334

3335+
func (q *fakeQuerier) InsertOrUpdateServiceBanner(_ context.Context, data string) error {
3336+
q.mutex.RLock()
3337+
defer q.mutex.RUnlock()
3338+
3339+
q.serviceBanner = []byte(data)
3340+
return nil
3341+
}
3342+
3343+
func (q *fakeQuerier) GetServiceBanner(_ context.Context) (string, error) {
3344+
q.mutex.RLock()
3345+
defer q.mutex.RUnlock()
3346+
3347+
if q.serviceBanner == nil {
3348+
return "", sql.ErrNoRows
3349+
}
3350+
3351+
return string(q.serviceBanner), nil
3352+
}
3353+
33343354
func (q *fakeQuerier) InsertLicense(
33353355
_ context.Context, arg database.InsertLicenseParams,
33363356
) (database.License, error) {

coderd/database/querier.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries.sql.go

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/siteconfig.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,10 @@ ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'last_update
1616

1717
-- name: GetLastUpdateCheck :one
1818
SELECT value FROM site_configs WHERE key = 'last_update_check';
19+
20+
-- name: InsertOrUpdateServiceBanner :exec
21+
INSERT INTO site_configs (key, value) VALUES ('service_banner', $1)
22+
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'service_banner';
23+
24+
-- name: GetServiceBanner :one
25+
SELECT value FROM site_configs WHERE key = 'service_banner';

codersdk/features.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const (
2323
FeatureHighAvailability = "high_availability"
2424
FeatureMultipleGitAuth = "multiple_git_auth"
2525
FeatureExternalProvisionerDaemons = "external_provisioner_daemons"
26+
FeatureServiceBanners = "service_banners"
2627
)
2728

2829
var FeatureNames = []string{
@@ -34,6 +35,7 @@ var FeatureNames = []string{
3435
FeatureHighAvailability,
3536
FeatureMultipleGitAuth,
3637
FeatureExternalProvisionerDaemons,
38+
FeatureServiceBanners,
3739
}
3840

3941
type Feature struct {

codersdk/servicebanner.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package codersdk
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"net/http"
7+
)
8+
9+
type ServiceBanner struct {
10+
Enabled bool `json:"enabled"`
11+
Message string `json:"message,omitempty"`
12+
BackgroundColor string `json:"background_color,omitempty"`
13+
}
14+
15+
func (c *Client) ServiceBanner(ctx context.Context) (*ServiceBanner, error) {
16+
res, err := c.Request(ctx, http.MethodGet, "/api/v2/service-banner", nil)
17+
if err != nil {
18+
return nil, err
19+
}
20+
defer res.Body.Close()
21+
if res.StatusCode != http.StatusOK {
22+
return nil, readBodyAsError(res)
23+
}
24+
var b ServiceBanner
25+
return &b, json.NewDecoder(res.Body).Decode(&b)
26+
}
27+
28+
func (c *Client) SetServiceBanner(ctx context.Context, s *ServiceBanner) error {
29+
res, err := c.Request(ctx, http.MethodPut, "/api/v2/service-banner", s)
30+
if err != nil {
31+
return err
32+
}
33+
defer res.Body.Close()
34+
if res.StatusCode != http.StatusOK {
35+
return readBodyAsError(res)
36+
}
37+
return nil
38+
}

docs/admin/service-banners.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Service Banners
2+
3+
Service Banners let admins post important messages to all site users. Only Site Owners may set the service banner.
4+
5+
![service banners](../images/admin/service-banners.png)
6+
7+
You can access the Service Banner settings by navigating to
8+
`Deployment > Service Banners`.
9+
10+
## Up next
11+
12+
- [Enterprise](../enterprise.md)

docs/enterprise.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Contact [email protected] to obtain a license.
1313

1414
- [High Availability](./admin/high-availability.md)
1515
- [Browser Only Connections](./networking.md#browser-only-connections)
16+
- [Service Banners](./admin/service-banners.md)
1617

1718
### Other
1819

docs/images/admin/service-banners.png

910 KB
Loading

docs/images/icons/info.svg

Lines changed: 1 addition & 0 deletions
Loading

docs/manifest.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,13 @@
276276
"icon_path": "./images/icons/speed.svg",
277277
"path": "./admin/prometheus.md"
278278
},
279+
{
280+
"title": "Service Banners",
281+
"description": "Learn how to configure Service Banners",
282+
"icon_path": "./images/icons/info.svg",
283+
"path": "./admin/service-banners.md",
284+
"state": "enterprise"
285+
},
279286
{
280287
"title": "Telemetry",
281288
"description": "Learn what usage telemetry Coder collects",

enterprise/coderd/coderd.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,13 @@ func New(ctx context.Context, options *Options) (*API, error) {
127127
r.Get("/", api.workspaceQuota)
128128
})
129129
})
130+
r.Route("/service-banner", func(r chi.Router) {
131+
r.Use(
132+
apiKeyMiddleware,
133+
)
134+
r.Get("/", api.serviceBanner)
135+
r.Put("/", api.putServiceBanner)
136+
})
130137
})
131138

132139
if len(options.SCIMAPIKey) != 0 {

enterprise/coderd/coderdenttest/coderdenttest.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ type LicenseOptions struct {
113113
HighAvailability bool
114114
MultipleGitAuth bool
115115
ExternalProvisionerDaemons bool
116+
ServiceBanners bool
116117
}
117118

118119
// AddLicense generates a new license with the options provided and inserts it.
@@ -164,6 +165,11 @@ func GenerateLicense(t *testing.T, options LicenseOptions) string {
164165
externalProvisionerDaemons = 1
165166
}
166167

168+
serviceBanners := int64(0)
169+
if options.ServiceBanners {
170+
serviceBanners = 1
171+
}
172+
167173
c := &license.Claims{
168174
RegisteredClaims: jwt.RegisteredClaims{
169175
Issuer: "[email protected]",
@@ -186,6 +192,7 @@ func GenerateLicense(t *testing.T, options LicenseOptions) string {
186192
TemplateRBAC: rbacEnabled,
187193
MultipleGitAuth: multipleGitAuth,
188194
ExternalProvisionerDaemons: externalProvisionerDaemons,
195+
ServiceBanners: serviceBanners,
189196
},
190197
}
191198
tok := jwt.NewWithClaims(jwt.SigningMethodEdDSA, c)

enterprise/coderd/coderdenttest/coderdenttest_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ func TestAuthorizeAllEndpoints(t *testing.T) {
4949

5050
skipRoutes, assertRoute := coderdtest.AGPLRoutes(a)
5151
skipRoutes["GET:/api/v2/organizations/{organization}/provisionerdaemons/serve"] = "This route checks for RBAC dependent on input parameters!"
52+
skipRoutes["GET:/api/v2/service-banner/"] = "This route is available to all users"
5253

5354
assertRoute["GET:/api/v2/entitlements"] = coderdtest.RouteCheck{
5455
NoAuthorize: true,

enterprise/coderd/license/license.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,12 @@ func Entitlements(
123123
Enabled: true,
124124
}
125125
}
126+
if claims.Features.ServiceBanners > 0 {
127+
entitlements.Features[codersdk.FeatureServiceBanners] = codersdk.Feature{
128+
Entitlement: entitlement,
129+
Enabled: true,
130+
}
131+
}
126132
if claims.AllFeatures {
127133
allFeatures = true
128134
}
@@ -252,6 +258,7 @@ type Features struct {
252258
HighAvailability int64 `json:"high_availability"`
253259
MultipleGitAuth int64 `json:"multiple_git_auth"`
254260
ExternalProvisionerDaemons int64 `json:"external_provisioner_daemons"`
261+
ServiceBanners int64 `json:"service_banners"`
255262
}
256263

257264
type Claims struct {

enterprise/coderd/license/license_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ func TestEntitlements(t *testing.T) {
2727
codersdk.FeatureTemplateRBAC: true,
2828
codersdk.FeatureMultipleGitAuth: true,
2929
codersdk.FeatureExternalProvisionerDaemons: true,
30+
codersdk.FeatureServiceBanners: true,
3031
}
3132

3233
t.Run("Defaults", func(t *testing.T) {
@@ -70,6 +71,7 @@ func TestEntitlements(t *testing.T) {
7071
TemplateRBAC: true,
7172
MultipleGitAuth: true,
7273
ExternalProvisionerDaemons: true,
74+
ServiceBanners: true,
7375
}),
7476
Exp: time.Now().Add(time.Hour),
7577
})
@@ -78,7 +80,7 @@ func TestEntitlements(t *testing.T) {
7880
require.True(t, entitlements.HasLicense)
7981
require.False(t, entitlements.Trial)
8082
for _, featureName := range codersdk.FeatureNames {
81-
require.Equal(t, codersdk.EntitlementEntitled, entitlements.Features[featureName].Entitlement)
83+
require.Equal(t, codersdk.EntitlementEntitled, entitlements.Features[featureName].Entitlement, featureName)
8284
}
8385
})
8486
t.Run("SingleLicenseGrace", func(t *testing.T) {
@@ -93,6 +95,7 @@ func TestEntitlements(t *testing.T) {
9395
HighAvailability: true,
9496
TemplateRBAC: true,
9597
ExternalProvisionerDaemons: true,
98+
ServiceBanners: true,
9699
GraceAt: time.Now().Add(-time.Hour),
97100
ExpiresAt: time.Now().Add(time.Hour),
98101
}),

enterprise/coderd/licenses_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ func TestGetLicense(t *testing.T) {
109109
codersdk.FeatureTemplateRBAC: json.Number("1"),
110110
codersdk.FeatureMultipleGitAuth: json.Number("0"),
111111
codersdk.FeatureExternalProvisionerDaemons: json.Number("0"),
112+
codersdk.FeatureServiceBanners: json.Number("0"),
112113
}, licenses[0].Claims["features"])
113114
assert.Equal(t, int32(2), licenses[1].ID)
114115
assert.Equal(t, "testing2", licenses[1].Claims["account_id"])
@@ -122,6 +123,7 @@ func TestGetLicense(t *testing.T) {
122123
codersdk.FeatureTemplateRBAC: json.Number("0"),
123124
codersdk.FeatureMultipleGitAuth: json.Number("0"),
124125
codersdk.FeatureExternalProvisionerDaemons: json.Number("0"),
126+
codersdk.FeatureServiceBanners: json.Number("0"),
125127
}, licenses[1].Claims["features"])
126128
})
127129
}

0 commit comments

Comments
 (0)