Skip to content

Commit 18458e2

Browse files
committed
[wsman-mk2] Replace in memory state handling
1 parent 1049676 commit 18458e2

File tree

5 files changed

+143
-72
lines changed

5 files changed

+143
-72
lines changed

components/ws-daemon/pkg/controller/snapshot_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ func (ssc *SnapshotReconciler) Reconcile(ctx context.Context, req ctrl.Request)
8585
return ctrl.Result{}, nil
8686
}
8787

88-
snapshotURL, snapshotName, snapshotErr := ssc.operations.SnapshotIDs(snapshot.Spec.WorkspaceID)
88+
snapshotURL, snapshotName, snapshotErr := ssc.operations.SnapshotIDs(ctx, snapshot.Spec.WorkspaceID)
8989
if snapshotErr != nil {
9090
return ctrl.Result{}, snapshotErr
9191
}

components/ws-daemon/pkg/controller/workspace_controller.go

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ func (wsc *WorkspaceController) handleWorkspaceInit(ctx context.Context, ws *wor
148148
}
149149

150150
initStart := time.Now()
151-
alreadyInit, failure, initErr := wsc.operations.InitWorkspaceContent(ctx, InitContentOptions{
151+
failure, initErr := wsc.operations.InitWorkspaceContent(ctx, InitContentOptions{
152152
Meta: WorkspaceMeta{
153153
Owner: ws.Spec.Ownership.Owner,
154154
WorkspaceId: ws.Spec.Ownership.WorkspaceID,
@@ -158,10 +158,6 @@ func (wsc *WorkspaceController) handleWorkspaceInit(ctx context.Context, ws *wor
158158
Headless: ws.IsHeadless(),
159159
})
160160

161-
if alreadyInit {
162-
return ctrl.Result{}, nil
163-
}
164-
165161
err = retry.RetryOnConflict(retryParams, func() error {
166162
if err := wsc.Get(ctx, req.NamespacedName, ws); err != nil {
167163
return err
@@ -226,7 +222,7 @@ func (wsc *WorkspaceController) handleWorkspaceStop(ctx context.Context, ws *wor
226222
if ws.Spec.Type == workspacev1.WorkspaceTypeRegular {
227223
snapshotName = storage.DefaultBackup
228224
} else {
229-
snapshotUrl, snapshotName, err = wsc.operations.SnapshotIDs(ws.Name)
225+
snapshotUrl, snapshotName, err = wsc.operations.SnapshotIDs(ctx, ws.Name)
230226
if err != nil {
231227
return ctrl.Result{}, err
232228
}
@@ -250,7 +246,7 @@ func (wsc *WorkspaceController) handleWorkspaceStop(ctx context.Context, ws *wor
250246
}
251247
}
252248

253-
alreadyDisposing, gitStatus, disposeErr := wsc.operations.DisposeWorkspace(ctx, DisposeOptions{
249+
gitStatus, disposeErr := wsc.operations.DisposeWorkspace(ctx, DisposeOptions{
254250
Meta: WorkspaceMeta{
255251
Owner: ws.Spec.Ownership.Owner,
256252
WorkspaceId: ws.Spec.Ownership.WorkspaceID,
@@ -262,10 +258,6 @@ func (wsc *WorkspaceController) handleWorkspaceStop(ctx context.Context, ws *wor
262258
UpdateGitStatus: ws.Spec.Type == workspacev1.WorkspaceTypeRegular,
263259
})
264260

265-
if alreadyDisposing {
266-
return ctrl.Result{}, nil
267-
}
268-
269261
err = retry.RetryOnConflict(retryParams, func() error {
270262
if err := wsc.Get(ctx, req.NamespacedName, ws); err != nil {
271263
return err

components/ws-daemon/pkg/controller/workspace_operations.go

Lines changed: 40 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,9 @@ const (
4141
)
4242

4343
type WorkspaceOperations struct {
44-
config content.Config
45-
store *session.Store
44+
config content.Config
45+
//store *session.Store
46+
provider *WorkspaceProvider
4647
backupWorkspaceLimiter chan struct{}
4748
metrics *content.Metrics
4849
}
@@ -67,15 +68,15 @@ type DisposeOptions struct {
6768
SnapshotName string
6869
}
6970

70-
func NewWorkspaceOperations(config content.Config, store *session.Store, reg prometheus.Registerer) (*WorkspaceOperations, error) {
71+
func NewWorkspaceOperations(config content.Config, store *WorkspaceProvider, reg prometheus.Registerer) (*WorkspaceOperations, error) {
7172
waitingTimeHist, waitingTimeoutCounter, err := content.RegisterConcurrentBackupMetrics(reg, "_mk2")
7273
if err != nil {
7374
return nil, err
7475
}
7576

7677
return &WorkspaceOperations{
7778
config: config,
78-
store: store,
79+
//store: store,
7980
metrics: &content.Metrics{
8081
BackupWaitingTimeHist: waitingTimeHist,
8182
BackupWaitingTimeoutCounter: waitingTimeoutCounter,
@@ -85,34 +86,26 @@ func NewWorkspaceOperations(config content.Config, store *session.Store, reg pro
8586
}, nil
8687
}
8788

88-
func (wso *WorkspaceOperations) InitWorkspaceContent(ctx context.Context, options InitContentOptions) (bool, string, error) {
89-
res, err := wso.store.NewWorkspace(
90-
ctx, options.Meta.InstanceId, filepath.Join(wso.store.Location, options.Meta.InstanceId),
89+
func (wso *WorkspaceOperations) InitWorkspaceContent(ctx context.Context, options InitContentOptions) (string, error) {
90+
ws, err := wso.provider.Create(ctx, options.Meta.InstanceId, filepath.Join(wso.provider.Location, options.Meta.InstanceId),
9191
wso.creator(options.Meta.Owner, options.Meta.WorkspaceId, options.Meta.InstanceId, options.Initializer, false))
92-
if errors.Is(err, storage.ErrNotFound) {
93-
return false, "", nil
94-
}
95-
96-
if errors.Is(err, session.ErrAlreadyExists) {
97-
return true, "", nil
98-
}
9992

10093
if err != nil {
101-
return false, "bug: cannot add workspace to store", xerrors.Errorf("cannot add workspace to store: %w", err)
94+
return "bug: cannot add workspace to store", xerrors.Errorf("cannot add workspace to store: %w", err)
10295
}
10396

104-
rs, ok := res.NonPersistentAttrs[session.AttrRemoteStorage].(storage.DirectAccess)
97+
rs, ok := ws.NonPersistentAttrs[session.AttrRemoteStorage].(storage.DirectAccess)
10598
if rs == nil || !ok {
106-
return false, "bug: workspace has no remote storage", xerrors.Errorf("workspace has no remote storage")
99+
return "bug: workspace has no remote storage", xerrors.Errorf("workspace has no remote storage")
107100
}
108101
ps, err := storage.NewPresignedAccess(&wso.config.Storage)
109102
if err != nil {
110-
return false, "bug: no presigned storage available", xerrors.Errorf("no presigned storage available: %w", err)
103+
return "bug: no presigned storage available", xerrors.Errorf("no presigned storage available: %w", err)
111104
}
112105

113106
remoteContent, err := content.CollectRemoteContent(ctx, rs, ps, options.Meta.Owner, options.Initializer)
114107
if err != nil {
115-
return false, "remote content error", xerrors.Errorf("remote content error: %w", err)
108+
return "remote content error", xerrors.Errorf("remote content error: %w", err)
116109
}
117110

118111
// Initialize workspace.
@@ -138,13 +131,18 @@ func (wso *WorkspaceOperations) InitWorkspaceContent(ctx context.Context, option
138131
},
139132
}
140133

141-
err = content.RunInitializer(ctx, res.Location, options.Initializer, remoteContent, opts)
134+
err = content.RunInitializer(ctx, ws.Location, options.Initializer, remoteContent, opts)
142135
if err != nil {
143136
glog.Infof("error running initializer %v", err)
144-
return false, err.Error(), err
137+
return err.Error(), err
138+
}
139+
140+
err = ws.Persist()
141+
if err != nil {
142+
return "cannot persist workspace", err
145143
}
146144

147-
return false, "", nil
145+
return "", nil
148146
}
149147

150148
func (wso *WorkspaceOperations) creator(owner, workspaceId, instanceId string, init *csapi.WorkspaceInitializer, storageDisabled bool) session.WorkspaceFactory {
@@ -173,25 +171,14 @@ func (wso *WorkspaceOperations) creator(owner, workspaceId, instanceId string, i
173171
}
174172
}
175173

176-
func (wso *WorkspaceOperations) DisposeWorkspace(ctx context.Context, opts DisposeOptions) (bool, *csapi.GitStatus, error) {
177-
sess := wso.store.Get(opts.Meta.InstanceId)
178-
if sess == nil {
179-
return false, nil, fmt.Errorf("cannot find workspace %s during DisposeWorkspace", opts.Meta.InstanceId)
180-
}
181-
182-
// Maybe there's someone else already trying to dispose the workspace.
183-
// In that case we'll simply wait for that to happen.
184-
done, repo, err := sess.WaitOrMarkForDisposal(ctx)
174+
func (wso *WorkspaceOperations) DisposeWorkspace(ctx context.Context, opts DisposeOptions) (*csapi.GitStatus, error) {
175+
ws, err := wso.provider.Get(ctx, opts.Meta.InstanceId)
185176
if err != nil {
186-
return false, nil, fmt.Errorf("failed to wait for workspace disposal of %s", opts.Meta.InstanceId)
187-
}
188-
189-
if done {
190-
return true, repo, nil
177+
return nil, fmt.Errorf("cannot find workspace %s during DisposeWorkspace", opts.Meta.InstanceId)
191178
}
192179

193-
if sess.RemoteStorageDisabled {
194-
return false, nil, xerrors.Errorf("workspace has no remote storage")
180+
if ws.RemoteStorageDisabled {
181+
return nil, fmt.Errorf("workspace has no remote storage")
195182
}
196183

197184
if opts.BackupLogs {
@@ -202,14 +189,15 @@ func (wso *WorkspaceOperations) DisposeWorkspace(ctx context.Context, opts Dispo
202189
}
203190
}
204191

205-
err = wso.uploadWorkspaceContent(ctx, sess, opts.SnapshotName)
192+
err = wso.uploadWorkspaceContent(ctx, ws, opts.SnapshotName)
206193
if err != nil {
207-
return false, nil, xerrors.Errorf("final backup failed for workspace %s", opts.Meta.InstanceId)
194+
return nil, fmt.Errorf("final backup failed for workspace %s", opts.Meta.InstanceId)
208195
}
209196

197+
var repo *csapi.GitStatus
210198
if opts.UpdateGitStatus {
211199
// Update the git status prior to deleting the workspace
212-
repo, err = sess.UpdateGitStatus(ctx, false)
200+
repo, err = ws.UpdateGitStatus(ctx, false)
213201
if err != nil {
214202
// do not fail workspace because we were unable to get git status
215203
// which can happen for various reasons, including user corrupting his .git folder somehow
@@ -219,22 +207,22 @@ func (wso *WorkspaceOperations) DisposeWorkspace(ctx context.Context, opts Dispo
219207
}
220208
}
221209

222-
if err = sess.Dispose(ctx); err != nil {
210+
if err = ws.Dispose(ctx); err != nil {
223211
glog.WithError(err).Error("cannot dispose session")
224212
}
225213

226214
// remove workspace daemon directory in the node
227-
if err := os.RemoveAll(sess.ServiceLocDaemon); err != nil {
215+
if err := os.RemoveAll(ws.ServiceLocDaemon); err != nil {
228216
glog.WithError(err).Error("cannot delete workspace daemon directory")
229217
}
230218

231-
return false, repo, nil
219+
return repo, nil
232220
}
233221

234-
func (wso *WorkspaceOperations) SnapshotIDs(workspaceID string) (snapshotUrl, snapshotName string, err error) {
235-
sess := wso.store.Get(workspaceID)
236-
if sess == nil {
237-
return "", "", fmt.Errorf("cannot find workspace %s during SnapshotName", workspaceID)
222+
func (wso *WorkspaceOperations) SnapshotIDs(ctx context.Context, workspaceID string) (snapshotUrl, snapshotName string, err error) {
223+
sess, err := wso.provider.Get(ctx, workspaceID)
224+
if err != nil {
225+
return "", "", fmt.Errorf("cannot find workspace %s during SnapshotName: %w", workspaceID, err)
238226
}
239227

240228
baseName := fmt.Sprintf("snapshot-%d", time.Now().UnixNano())
@@ -258,16 +246,16 @@ func (wso *WorkspaceOperations) TakeSnapshot(ctx context.Context, workspaceID, s
258246
return fmt.Errorf("workspaceID is required")
259247
}
260248

261-
sess := wso.store.Get(workspaceID)
262-
if sess == nil {
249+
ws, err := wso.provider.Get(ctx, workspaceID)
250+
if err != nil {
263251
return fmt.Errorf("cannot find workspace %s during DisposeWorkspace", workspaceID)
264252
}
265253

266-
if sess.RemoteStorageDisabled {
254+
if ws.RemoteStorageDisabled {
267255
return fmt.Errorf("workspace has no remote storage")
268256
}
269257

270-
err = wso.uploadWorkspaceContent(ctx, sess, snapshotName)
258+
err = wso.uploadWorkspaceContent(ctx, ws, snapshotName)
271259
if err != nil {
272260
return fmt.Errorf("snapshot failed for workspace %s", workspaceID)
273261
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright (c) 2023 Gitpod GmbH. All rights reserved.
2+
// Licensed under the GNU Affero General Public License (AGPL).
3+
// See License.AGPL.txt in the project root for license information.
4+
5+
package controller
6+
7+
import (
8+
"context"
9+
"encoding/json"
10+
"fmt"
11+
"os"
12+
"path/filepath"
13+
14+
"github.com/gitpod-io/gitpod/common-go/log"
15+
"github.com/gitpod-io/gitpod/common-go/tracing"
16+
"github.com/opentracing/opentracing-go"
17+
18+
"github.com/gitpod-io/gitpod/ws-daemon/pkg/internal/session"
19+
)
20+
21+
type WorkspaceProvider struct {
22+
hooks map[session.WorkspaceState][]session.WorkspaceLivecycleHook
23+
Location string
24+
}
25+
26+
func NewWorkspaceProvider(hooks map[session.WorkspaceState][]session.WorkspaceLivecycleHook, location string) *WorkspaceProvider {
27+
return &WorkspaceProvider{
28+
hooks: hooks,
29+
Location: location,
30+
}
31+
}
32+
33+
func (wf *WorkspaceProvider) Create(ctx context.Context, instanceID, location string, create session.WorkspaceFactory) (ws *session.Workspace, err error) {
34+
span, ctx := opentracing.StartSpanFromContext(ctx, "Store.NewWorkspace")
35+
tracing.ApplyOWI(span, log.OWI("", "", instanceID))
36+
defer tracing.FinishSpan(span, &err)
37+
38+
ws, err = create(ctx, location)
39+
if err != nil {
40+
return nil, err
41+
}
42+
43+
err = wf.runLifecycleHooks(ctx, ws, session.WorkspaceInitializing)
44+
if err != nil {
45+
return nil, err
46+
}
47+
48+
return nil, nil
49+
}
50+
51+
func (wf *WorkspaceProvider) Get(ctx context.Context, instanceID string) (*session.Workspace, error) {
52+
path := filepath.Join(wf.Location, fmt.Sprintf("%s.workspace.json", instanceID))
53+
return loadWorkspace(ctx, path)
54+
}
55+
56+
func (s *WorkspaceProvider) runLifecycleHooks(ctx context.Context, ws *session.Workspace, state session.WorkspaceState) error {
57+
hooks := s.hooks[state]
58+
log.WithFields(ws.OWI()).WithField("state", state).WithField("hooks", len(hooks)).Debug("running lifecycle hooks")
59+
60+
for _, h := range hooks {
61+
err := h(ctx, ws)
62+
if err != nil {
63+
return err
64+
}
65+
}
66+
return nil
67+
}
68+
69+
func loadWorkspace(ctx context.Context, path string) (ws *session.Workspace, err error) {
70+
span, _ := opentracing.StartSpanFromContext(ctx, "loadWorkspace")
71+
defer tracing.FinishSpan(span, &err)
72+
73+
fc, err := os.ReadFile(path)
74+
if err != nil {
75+
return nil, fmt.Errorf("cannot load session file: %w", err)
76+
}
77+
78+
err = json.Unmarshal(fc, ws)
79+
if err != nil {
80+
return nil, fmt.Errorf("cannot unmarshal session file: %w", err)
81+
}
82+
83+
return ws, nil
84+
}

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

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ import (
3535
"github.com/gitpod-io/gitpod/ws-daemon/pkg/cpulimit"
3636
"github.com/gitpod-io/gitpod/ws-daemon/pkg/diskguard"
3737
"github.com/gitpod-io/gitpod/ws-daemon/pkg/dispatch"
38-
"github.com/gitpod-io/gitpod/ws-daemon/pkg/internal/session"
3938
"github.com/gitpod-io/gitpod/ws-daemon/pkg/iws"
4039
"github.com/gitpod-io/gitpod/ws-daemon/pkg/netlimit"
4140
"github.com/gitpod-io/gitpod/ws-daemon/pkg/quota"
@@ -193,18 +192,26 @@ func NewDaemon(config Config) (*Daemon, error) {
193192
return nil, err
194193
}
195194

196-
store, err := session.NewStore(context.Background(), contentCfg.WorkingArea, content.WorkspaceLifecycleHooks(
195+
hooks := content.WorkspaceLifecycleHooks(
197196
contentCfg,
198197
func(instanceID string) bool { return true },
199198
&iws.Uidmapper{Config: config.Uidmapper, Runtime: containerRuntime},
200199
xfs,
201200
config.CPULimit.CGroupBasePath,
202-
))
203-
if err != nil {
204-
return nil, err
205-
}
206-
207-
workspaceOps, err := controller.NewWorkspaceOperations(contentCfg, store, wrappedReg)
201+
)
202+
203+
// store, err := session.NewStore(context.Background(), contentCfg.WorkingArea, content.WorkspaceLifecycleHooks(
204+
// contentCfg,
205+
// func(instanceID string) bool { return true },
206+
// &iws.Uidmapper{Config: config.Uidmapper, Runtime: containerRuntime},
207+
// xfs,
208+
// config.CPULimit.CGroupBasePath,
209+
// ))
210+
// if err != nil {
211+
// return nil, err
212+
// }
213+
214+
workspaceOps, err := controller.NewWorkspaceOperations(contentCfg, controller.NewWorkspaceProvider(hooks, contentCfg.WorkingArea), wrappedReg)
208215
if err != nil {
209216
return nil, err
210217
}

0 commit comments

Comments
 (0)