Skip to content

Commit a33db32

Browse files
author
Prince Rachit Sinha
committed
Add maximum lifetime constraint for a workspace
1 parent 26e2777 commit a33db32

File tree

10 files changed

+31
-7
lines changed

10 files changed

+31
-7
lines changed

chart/templates/ws-manager-configmap.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ data:
8282
"headlessWorkspace": "60m",
8383
"initialization": "30m",
8484
"regularWorkspace": "30m",
85+
"maxLifetime": "2160m",
8586
"startup": "60m",
8687
"contentFinalization": "60m",
8788
"stopping": "60m",

components/installer/pkg/components/ws-manager/configmap.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) {
8686
HeadlessWorkspace: util.Duration(1 * time.Hour),
8787
Initialization: util.Duration(30 * time.Minute),
8888
RegularWorkspace: util.Duration(30 * time.Minute),
89+
MaxLifetime: util.Duration(2160 * time.Minute),
8990
TotalStartup: util.Duration(1 * time.Hour),
9091
ContentFinalization: util.Duration(1 * time.Hour),
9192
Stopping: util.Duration(1 * time.Hour),

components/ws-manager-api/go/config/config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ type WorkspaceTimeoutConfiguration struct {
122122
Initialization util.Duration `json:"initialization"`
123123
// RegularWorkspace is the time a regular workspace can be without activity before it's shutdown
124124
RegularWorkspace util.Duration `json:"regularWorkspace"`
125+
// MaxLifetime is the maximum lifetime of a regular workspace
126+
MaxLifetime util.Duration `json:"maxLifetime"`
125127
// HeadlessWorkspace is the maximum runtime a headless workspace can have (including startup)
126128
HeadlessWorkspace util.Duration `json:"headlessWorkspace"`
127129
// AfterClose is the time a workspace lives after it has been marked closed
@@ -188,6 +190,7 @@ func (c *Configuration) Validate() error {
188190
validation.Field(&c.Timeouts.HeadlessWorkspace, validation.Required),
189191
validation.Field(&c.Timeouts.Initialization, validation.Required),
190192
validation.Field(&c.Timeouts.RegularWorkspace, validation.Required),
193+
validation.Field(&c.Timeouts.MaxLifetime, validation.Required),
191194
validation.Field(&c.Timeouts.TotalStartup, validation.Required),
192195
validation.Field(&c.Timeouts.ContentFinalization, validation.Required),
193196
validation.Field(&c.Timeouts.Stopping, validation.Required),

components/ws-manager/config-schema.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@
173173
"startup",
174174
"initialization",
175175
"regularWorkspace",
176+
"maxLifetime",
176177
"headlessWorkspace",
177178
"afterClose",
178179
"stopping",
@@ -194,6 +195,9 @@
194195
"regularWorkspace": {
195196
"type": "string"
196197
},
198+
"maxLifetime": {
199+
"type": "string"
200+
},
197201
"startup": {
198202
"type": "string"
199203
},

components/ws-manager/example-config.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"headlessWorkspace": "60m",
4242
"initialization": "30m",
4343
"regularWorkspace": "30m",
44+
"maxLifetime": "2160m",
4445
"startup": "30m"
4546
},
4647
"eventTraceLog": "-",

components/ws-manager/pkg/manager/integration_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ func forIntegrationTestGetManager(t *testing.T) *Manager {
8787
Initialization: util.Duration(30 * time.Minute),
8888
TotalStartup: util.Duration(45 * time.Minute),
8989
RegularWorkspace: util.Duration(60 * time.Minute),
90+
MaxLifetime: util.Duration(2160 * time.Minute),
9091
HeadlessWorkspace: util.Duration(90 * time.Minute),
9192
Stopping: util.Duration(60 * time.Minute),
9293
ContentFinalization: util.Duration(15 * time.Minute),

components/ws-manager/pkg/manager/monitor.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -976,7 +976,7 @@ func (m *Monitor) finalizeWorkspaceContent(ctx context.Context, wso *workspaceOb
976976
}
977977
}
978978

979-
// markTimedoutWorkspaces finds workspaces which haven't been active recently and marks them as timed out
979+
// markTimedoutWorkspaces finds workspaces which can be timeout due to inactivity or max lifetime allowed
980980
func (m *Monitor) markTimedoutWorkspaces(ctx context.Context) (err error) {
981981
span, ctx := tracing.FromContext(ctx, "markTimedoutWorkspaces")
982982
defer tracing.FinishSpan(span, &err)

components/ws-manager/pkg/manager/status.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,7 @@ const (
667667
activityPullingImages activity = "pulling images"
668668
activityRunningHeadless activity = "running the headless workspace"
669669
activityNone activity = "period of inactivity"
670+
activityMaxLifetime activity = "maximum lifetime"
670671
activityClosed activity = "after being closed"
671672
activityInterrupted activity = "workspace interruption"
672673
activityStopping activity = "stopping"
@@ -718,22 +719,32 @@ func (m *Manager) isWorkspaceTimedOut(wso workspaceObjects) (reason string, err
718719
case api.WorkspacePhase_RUNNING:
719720
timeout := m.Config.Timeouts.RegularWorkspace
720721
activity := activityNone
722+
if ctv, ok := wso.Pod.Annotations[customTimeoutAnnotation]; ok {
723+
if ct, err := time.ParseDuration(ctv); err == nil {
724+
timeout = util.Duration(ct)
725+
} else {
726+
log.WithError(err).WithField("customTimeout", ctv).WithFields(wsk8s.GetOWIFromObject(&wso.Pod.ObjectMeta)).Warn("pod had custom timeout annotation set, but could not parse its value. Defaulting to ws-manager config.")
727+
}
728+
}
721729
if wso.IsWorkspaceHeadless() {
722730
timeout = m.Config.Timeouts.HeadlessWorkspace
723731
lastActivity = &start
724732
activity = activityRunningHeadless
733+
return decide(*lastActivity, timeout, activity)
725734
} else if lastActivity == nil {
726735
// the workspace is up and running, but the user has never produced any activity
727736
return decide(start, m.Config.Timeouts.TotalStartup, activityNone)
728737
} else if isClosed {
729738
return decide(*lastActivity, m.Config.Timeouts.AfterClose, activityClosed)
730739
}
731-
if ctv, ok := wso.Pod.Annotations[customTimeoutAnnotation]; ok {
732-
if ct, err := time.ParseDuration(ctv); err == nil {
733-
timeout = util.Duration(ct)
734-
} else {
735-
log.WithError(err).WithField("customTimeout", ctv).WithFields(wsk8s.GetOWIFromObject(&wso.Pod.ObjectMeta)).Warn("pod had custom timeout annotation set, but could not parse its value. Defaulting to ws-manager config.")
736-
}
740+
// check if workspace can be inactivity timed out
741+
// if not then check if the workspace has reached max lifetime
742+
timedOut, err := decide(*lastActivity, timeout, activity)
743+
if err != nil {
744+
return timedOut, err
745+
} else {
746+
timeout = m.Config.Timeouts.MaxLifetime
747+
activity = activityMaxLifetime
737748
}
738749
return decide(*lastActivity, timeout, activity)
739750

components/ws-manager/pkg/manager/status_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ func TestIsWorkspaceTimedout(t *testing.T) {
4848
Initialization: util.Duration(30 * time.Minute),
4949
TotalStartup: util.Duration(45 * time.Minute),
5050
RegularWorkspace: util.Duration(60 * time.Minute),
51+
MaxLifetime: util.Duration(2160 * time.Minute),
5152
HeadlessWorkspace: util.Duration(90 * time.Minute),
5253
Stopping: util.Duration(60 * time.Minute),
5354
ContentFinalization: util.Duration(55 * time.Minute),

components/ws-manager/pkg/manager/testing_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ func forTestingOnlyManagerConfig() config.Configuration {
5656
Initialization: util.Duration(30 * time.Minute),
5757
TotalStartup: util.Duration(45 * time.Minute),
5858
RegularWorkspace: util.Duration(60 * time.Minute),
59+
MaxLifetime: util.Duration(2160 * time.Minute),
5960
HeadlessWorkspace: util.Duration(90 * time.Minute),
6061
Stopping: util.Duration(60 * time.Minute),
6162
ContentFinalization: util.Duration(55 * time.Minute),

0 commit comments

Comments
 (0)