Skip to content

Commit 1596b85

Browse files
committed
[ws-daemon/manager] Use feature flag
1 parent 0bf6aca commit 1596b85

40 files changed

+190
-177
lines changed

components/common-go/kubernetes/kubernetes.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ const (
6565
// WorkspaceSSHPublicKeys contains all authorized ssh public keys that can be connected to the workspace
6666
WorkspaceSSHPublicKeys = "gitpod.io/sshPublicKeys"
6767

68-
// workspaceCpuLimit denotes the cpu limit of a workspace
69-
WorkspaceCpuLimitAnnotation = "gitpod.io/cpuLimit"
68+
// workspaceCpuMinLimitAnnotation denotes the minimum cpu limit of a workspace i.e. the minimum amount of resources it is guaranteed to get
69+
WorkspaceCpuMinLimitAnnotation = "gitpod.io/cpuMinLimit"
7070

7171
// workspaceCpuBurstLimit denotes the cpu burst limit of a workspace
7272
WorkspaceCpuBurstLimitAnnotation = "gitpod.io/cpuBurstLimit"

components/ee/agent-smith/pkg/agent/actions.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ func (agent *Smith) limitCPUUse(podname string) error {
7070
return err
7171
}
7272

73-
pod.Annotations[wsk8s.WorkspaceCpuLimitAnnotation] = agent.Config.Enforcement.CPULimitPenalty
73+
pod.Annotations[wsk8s.WorkspaceCpuMinLimitAnnotation] = agent.Config.Enforcement.CPULimitPenalty
74+
pod.Annotations[wsk8s.WorkspaceCpuBurstLimitAnnotation] = agent.Config.Enforcement.CPULimitPenalty
7475
_, err = pods.Update(ctx, pod, corev1.UpdateOptions{})
7576
if err != nil {
7677
return err

components/gitpod-protocol/src/protocol.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ export const WorkspaceFeatureFlags = {
274274
fixed_resources: undefined,
275275
persistent_volume_claim: undefined,
276276
protected_secrets: undefined,
277+
workspace_class_limiting: undefined,
277278
};
278279
export type NamedWorkspaceFeatureFlag = keyof typeof WorkspaceFeatureFlags;
279280

components/server/src/workspace/workspace-starter.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -755,14 +755,6 @@ export class WorkspaceStarter {
755755
featureFlags = featureFlags.concat(["persistent_volume_claim"]);
756756
}
757757

758-
if (!!featureFlags) {
759-
// only set feature flags if there actually are any. Otherwise we waste the
760-
// few bytes of JSON in the database for no good reason.
761-
configuration.featureFlags = featureFlags;
762-
}
763-
764-
const usageAttributionId = await this.userService.getWorkspaceUsageAttributionId(user, workspace.projectId);
765-
766758
let workspaceClass = "";
767759
let classesEnabled = await getExperimentsClientForBackend().getValueAsync("workspace_classes", false, {
768760
user: user,
@@ -802,8 +794,18 @@ export class WorkspaceStarter {
802794
workspaceClass = workspaceClass + "-pvc";
803795
}
804796
}
797+
798+
featureFlags = featureFlags.concat(["workspace_class_limiting"]);
805799
}
806800

801+
if (!!featureFlags) {
802+
// only set feature flags if there actually are any. Otherwise we waste the
803+
// few bytes of JSON in the database for no good reason.
804+
configuration.featureFlags = featureFlags;
805+
}
806+
807+
const usageAttributionId = await this.userService.getWorkspaceUsageAttributionId(user, workspace.projectId);
808+
807809
const now = new Date().toISOString();
808810
const instance: WorkspaceInstance = {
809811
id: uuidv4(),

components/ws-daemon/pkg/cpulimit/cpulimit.go

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package cpulimit
77
import (
88
"context"
99
"sort"
10+
"strings"
1011
"time"
1112

1213
"github.com/gitpod-io/gitpod/common-go/log"
@@ -179,7 +180,7 @@ func (d *Distributor) Tick(dt time.Duration) (DistributorDebug, error) {
179180
ws := d.History[id]
180181
limit, err := d.Limiter.Limit(ws)
181182
if err != nil {
182-
log.Errorf("unable to apply min limit: %v", err)
183+
log.WithError(err).Errorf("unable to apply min limit")
183184
continue
184185
}
185186

@@ -191,7 +192,7 @@ func (d *Distributor) Tick(dt time.Duration) (DistributorDebug, error) {
191192
if totalBandwidth < d.TotalBandwidth && ws.Throttled() {
192193
limit, err = d.BurstLimiter.Limit(ws)
193194
if err != nil {
194-
log.Errorf("unable to apply burst limit: %v", err)
195+
log.WithError(err).Errorf("unable to apply burst limit")
195196
continue
196197
}
197198

@@ -222,6 +223,12 @@ type ResourceLimiter interface {
222223
Limit(wsh *WorkspaceHistory) (Bandwidth, error)
223224
}
224225

226+
var _ ResourceLimiter = (*fixedLimiter)(nil)
227+
var _ ResourceLimiter = (*annotationLimiter)(nil)
228+
var _ ResourceLimiter = (*BucketLimiter)(nil)
229+
var _ ResourceLimiter = (*ClampingBucketLimiter)(nil)
230+
var _ ResourceLimiter = (*compositeLimiter)(nil)
231+
225232
// FixedLimiter returns a fixed limit
226233
func FixedLimiter(limit Bandwidth) ResourceLimiter {
227234
return fixedLimiter{limit}
@@ -306,7 +313,7 @@ type ClampingBucketLimiter struct {
306313
}
307314

308315
// Limit decides on a CPU use limit
309-
func (bl *ClampingBucketLimiter) Limit(wsh *WorkspaceHistory) (newLimit Bandwidth) {
316+
func (bl *ClampingBucketLimiter) Limit(wsh *WorkspaceHistory) (Bandwidth, error) {
310317
budgetSpent := wsh.Usage()
311318

312319
if bl.lastBucketLock {
@@ -315,25 +322,54 @@ func (bl *ClampingBucketLimiter) Limit(wsh *WorkspaceHistory) (newLimit Bandwidt
315322
}
316323
}
317324
if bl.lastBucketLock {
318-
return bl.Buckets[len(bl.Buckets)-1].Limit
325+
return bl.Buckets[len(bl.Buckets)-1].Limit, nil
319326
}
320327

321328
for i, bkt := range bl.Buckets {
322329
if i+1 == len(bl.Buckets) {
323330
// We've reached the last bucket - budget doesn't matter anymore
324331
bl.lastBucketLock = true
325-
return bkt.Limit
332+
return bkt.Limit, nil
326333
}
327334

328335
budgetSpent -= bkt.Budget
329336
if budgetSpent <= 0 {
330337
// BudgetSpent value is in this bucket, hence we have found our current bucket
331-
return bkt.Limit
338+
return bkt.Limit, nil
332339
}
333340
}
334341

335342
// empty bucket list
336-
return 0
343+
return 0, nil
344+
}
345+
346+
type compositeLimiter struct {
347+
limiters []ResourceLimiter
348+
}
349+
350+
func CompositeLimiter(limiters ...ResourceLimiter) ResourceLimiter {
351+
return &compositeLimiter{
352+
limiters: limiters,
353+
}
354+
}
355+
356+
func (cl *compositeLimiter) Limit(wsh *WorkspaceHistory) (Bandwidth, error) {
357+
var errs []error
358+
for _, limiter := range cl.limiters {
359+
limit, err := limiter.Limit(wsh)
360+
if err != nil {
361+
errs = append(errs, err)
362+
continue
363+
}
364+
365+
return limit, nil
366+
}
367+
368+
allerr := make([]string, len(errs))
369+
for i, err := range errs {
370+
allerr[i] = err.Error()
371+
}
372+
return 0, xerrors.Errorf("no limiter was able to provide a limit", strings.Join(allerr, ", "))
337373
}
338374

339375
type CFSController interface {

components/ws-daemon/pkg/cpulimit/dispatch.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import (
2929
type Config struct {
3030
Enabled bool `json:"enabled"`
3131
TotalBandwidth resource.Quantity `json:"totalBandwidth"`
32+
Limit resource.Quantity `json:"limit"`
33+
BurstLimit resource.Quantity `json:"burstLimit"`
3234

3335
ControlPeriod util.Duration `json:"controlPeriod"`
3436
CGroupBasePath string `json:"cgroupBasePath"`
@@ -65,8 +67,8 @@ func NewDispatchListener(cfg *Config, prom prometheus.Registerer) *DispatchListe
6567

6668
if cfg.Enabled {
6769
dist := NewDistributor(d.source, d.sink,
68-
AnnotationLimiter(kubernetes.WorkspaceCpuLimitAnnotation),
69-
AnnotationLimiter(kubernetes.WorkspaceCpuBurstLimitAnnotation),
70+
CompositeLimiter(AnnotationLimiter(kubernetes.WorkspaceCpuMinLimitAnnotation), FixedLimiter(BandwidthFromQuantity(d.Config.Limit))),
71+
CompositeLimiter(AnnotationLimiter(kubernetes.WorkspaceCpuBurstLimitAnnotation), FixedLimiter(BandwidthFromQuantity(d.Config.BurstLimit))),
7072
BandwidthFromQuantity(d.Config.TotalBandwidth),
7173
)
7274
go dist.Run(context.Background(), time.Duration(d.Config.ControlPeriod))

components/ws-daemon/pkg/cpulimit/limiter_test.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,18 @@ func TestBucketLimiter(t *testing.T) {
3737

3838
for _, test := range tests {
3939
t.Run(test.Desc, func(t *testing.T) {
40-
//limiter := cpulimit.BucketLimiter(test.Buckets)
41-
// ws := &cpulimit.WorkspaceHistory {
42-
43-
// }
40+
limiter := cpulimit.BucketLimiter(test.Buckets)
41+
ws := &cpulimit.WorkspaceHistory{
42+
LastUpdate: &cpulimit.Workspace{
43+
Usage: test.BudgetSpent,
44+
},
45+
UsageT0: 0,
46+
}
4447

45-
// limit,_ := limiter.Limit(test.BudgetSpent)
46-
// if limit != test.ExpectedLimit {
47-
// t.Errorf("unexpected limit %d: expected %d", limit, test.ExpectedLimit)
48-
// }
48+
limit, _ := limiter.Limit(ws)
49+
if limit != test.ExpectedLimit {
50+
t.Errorf("unexpected limit %d: expected %d", limit, test.ExpectedLimit)
51+
}
4952
})
5053
}
5154
}

components/ws-manager-api/core.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,9 @@ enum WorkspaceFeatureFlag {
597597

598598
// PROTECTED_SECRETS feature flag for enable secrets support
599599
PROTECTED_SECRETS = 8;
600+
601+
// WORKSPACE_CLASS_LIMITING feature flag for enabling resuorce limiting based on workspace class
602+
WORKSPACE_CLASS_LIMITING = 9;
600603
}
601604

602605
// GitSpec configures the Git available within the workspace

0 commit comments

Comments
 (0)