Skip to content

Commit 1f8b74a

Browse files
committed
refactor(core): remove lease-based session locking mechanism
1 parent eb8b027 commit 1f8b74a

13 files changed

Lines changed: 25 additions & 261 deletions

File tree

docs/okdev-design.md

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,6 @@ Ownership model:
263263

264264
- `okdev down [--delete-pvc]`
265265
- stop session; default preserves PVC for continuity
266-
- always releases the session Lease immediately for fast handoff/restart
267266
- supports `--dry-run` to preview deletions
268267

269268
- `okdev prune [--dry-run]`
@@ -281,11 +280,6 @@ okdev approach:
281280
- Local machine stores only ephemeral client metadata.
282281
- Any machine with kube access and repo can run `okdev up --attach`.
283282

284-
Optional lock modes:
285-
- `none`
286-
- `advisory` (warn if another active client)
287-
- `exclusive` (single active client lease with timeout)
288-
289283
Ownership model:
290284
- Owner identity resolution: `--owner` flag -> `OKDEV_OWNER` -> local `USER`.
291285
- Sessions are labeled with `okdev.io/owner=<owner>`.
@@ -404,7 +398,7 @@ This guarantees isolation between projects while keeping shared cluster usage si
404398
### Phase 2
405399
- improved sync engine with change detection and conflict handling
406400
- idle timeout and TTL pruning
407-
- advisory/exclusive lease mode
401+
- stricter owner-scoped access controls
408402
- richer status diagnostics
409403

410404
### Phase 3

internal/cli/common.go

Lines changed: 6 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import (
1919
"github.com/acmore/okdev/internal/session"
2020
)
2121

22-
const sessionLeaseDuration = 2 * time.Minute
2322
const sessionHeartbeatInterval = 5 * time.Minute
2423

2524
var invalidOwnerChars = regexp.MustCompile(`[^a-z0-9._-]`)
@@ -125,37 +124,6 @@ func runConnectWithClient(k *kube.Client, namespace, sessionName string, command
125124
return connect.Run(ctx, k, namespace, podName(sessionName), command, tty, os.Stdin, os.Stdout, os.Stderr)
126125
}
127126

128-
func ensureSessionLock(opts *Options, cfg *config.DevEnvironment, namespace, sessionName string, out io.Writer) error {
129-
stopRenew, err := acquireSessionLockWithClient(newKubeClient(opts), cfg, namespace, sessionName, out)
130-
if err != nil {
131-
return err
132-
}
133-
stopRenew()
134-
return nil
135-
}
136-
137-
func acquireSessionLock(opts *Options, cfg *config.DevEnvironment, namespace, sessionName string, out io.Writer) (func(), error) {
138-
return acquireSessionLockWithClient(newKubeClient(opts), cfg, namespace, sessionName, out)
139-
}
140-
141-
func acquireSessionLockWithClient(k *kube.Client, cfg *config.DevEnvironment, namespace, sessionName string, out io.Writer) (func(), error) {
142-
if cfg.Spec.Session.LockMode == "none" {
143-
return func() {}, nil
144-
}
145-
holder := sessionHolderIdentity()
146-
leaseName := "okdev-" + sessionName
147-
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
148-
defer cancel()
149-
res, err := k.AcquireLease(ctx, namespace, leaseName, holder, cfg.Spec.Session.LockMode, sessionLeaseDuration)
150-
if err != nil {
151-
return nil, err
152-
}
153-
if !res.Acquired && cfg.Spec.Session.LockMode == "advisory" {
154-
fmt.Fprintf(out, "warning: session lock currently held by %s\n", res.CurrentHolder)
155-
}
156-
return func() {}, nil
157-
}
158-
159127
func startSessionHeartbeat(opts *Options, namespace, sessionName string, out io.Writer, interval time.Duration) func() {
160128
return startSessionHeartbeatWithClient(newKubeClient(opts), namespace, sessionName, out, interval)
161129
}
@@ -192,52 +160,19 @@ func startSessionMaintenance(opts *Options, cfg *config.DevEnvironment, namespac
192160
}
193161

194162
func startSessionMaintenanceWithClient(k *kube.Client, cfg *config.DevEnvironment, namespace, sessionName string, out io.Writer, renewLock bool, emitHeartbeat bool) func() {
195-
if !renewLock && !emitHeartbeat {
163+
_ = cfg
164+
_ = renewLock
165+
if !emitHeartbeat {
196166
return func() {}
197167
}
198-
holder := sessionHolderIdentity()
199-
leaseName := "okdev-" + sessionName
200168
pod := podName(sessionName)
201169
ctx, cancel := context.WithCancel(context.Background())
202170

203171
go func() {
204-
var renewTicker *time.Ticker
205-
var renewCh <-chan time.Time
206-
if renewLock && cfg.Spec.Session.LockMode != "none" {
207-
renewTicker = time.NewTicker(sessionLeaseDuration / 2)
208-
renewCh = renewTicker.C
209-
defer renewTicker.Stop()
210-
}
211-
var heartbeatTicker *time.Ticker
212-
var heartbeatCh <-chan time.Time
213-
if emitHeartbeat {
214-
heartbeatTicker = time.NewTicker(sessionHeartbeatInterval)
215-
heartbeatCh = heartbeatTicker.C
216-
defer heartbeatTicker.Stop()
217-
}
218-
219-
doRenew := func() {
220-
if !renewLock || cfg.Spec.Session.LockMode == "none" {
221-
return
222-
}
223-
renewCtx, renewCancel := context.WithTimeout(context.Background(), 15*time.Second)
224-
renewRes, renewErr := k.AcquireLease(renewCtx, namespace, leaseName, holder, cfg.Spec.Session.LockMode, sessionLeaseDuration)
225-
renewCancel()
226-
if renewErr != nil {
227-
slog.Warn("session lock renewal failed", "namespace", namespace, "session", sessionName, "holder", holder, "error", renewErr)
228-
fmt.Fprintf(out, "warning: failed to renew session lock: %v\n", renewErr)
229-
return
230-
}
231-
if !renewRes.Acquired && cfg.Spec.Session.LockMode == "advisory" {
232-
slog.Warn("session advisory lock not acquired on renewal", "namespace", namespace, "session", sessionName, "holder", holder, "current_holder", renewRes.CurrentHolder)
233-
fmt.Fprintf(out, "warning: session lock now held by %s\n", renewRes.CurrentHolder)
234-
}
235-
}
172+
heartbeatTicker := time.NewTicker(sessionHeartbeatInterval)
173+
defer heartbeatTicker.Stop()
236174

237175
doHeartbeat := func() {
238-
if !emitHeartbeat {
239-
return
240-
}
241176
beatCtx, beatCancel := context.WithTimeout(context.Background(), 10*time.Second)
242177
err := k.TouchPodActivity(beatCtx, namespace, pod)
243178
beatCancel()
@@ -252,9 +187,7 @@ func startSessionMaintenanceWithClient(k *kube.Client, cfg *config.DevEnvironmen
252187
select {
253188
case <-ctx.Done():
254189
return
255-
case <-renewCh:
256-
doRenew()
257-
case <-heartbeatCh:
190+
case <-heartbeatTicker.C:
258191
doHeartbeat()
259192
}
260193
}
@@ -263,15 +196,6 @@ func startSessionMaintenanceWithClient(k *kube.Client, cfg *config.DevEnvironmen
263196
return cancel
264197
}
265198

266-
func sessionHolderIdentity() string {
267-
user := currentOwner(nil)
268-
host, err := os.Hostname()
269-
if err != nil || host == "" {
270-
host = "unknown-host"
271-
}
272-
return user + "@" + host
273-
}
274-
275199
func currentOwner(opts *Options) string {
276200
if opts != nil {
277201
if v := normalizeOwner(opts.Owner); v != "" {

internal/cli/connect.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,6 @@ func newConnectCmd(opts *Options) *cobra.Command {
2727
if err := ensureSessionOwnership(opts, k, ns, sn, true); err != nil {
2828
return err
2929
}
30-
stopRenew, err := acquireSessionLock(opts, cfg, ns, sn, cmd.OutOrStdout())
31-
if err != nil {
32-
return err
33-
}
34-
defer stopRenew()
3530
stopMaintenance := startSessionMaintenance(opts, cfg, ns, sn, cmd.OutOrStdout(), true, true)
3631
defer stopMaintenance()
3732

internal/cli/down.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ func newDownCmd(opts *Options) *cobra.Command {
3636
} else if !deletePVC && cfg.Spec.Workspace.PVC.ClaimName == "" {
3737
fmt.Fprintf(cmd.OutOrStdout(), "- would retain pvc/%s\n", pvcName(cfg, sn))
3838
}
39-
fmt.Fprintf(cmd.OutOrStdout(), "- would delete lease/%s\n", "okdev-"+sn)
4039
return nil
4140
}
4241

@@ -48,9 +47,6 @@ func newDownCmd(opts *Options) *cobra.Command {
4847
return fmt.Errorf("delete workspace pvc: %w", err)
4948
}
5049
}
51-
if err := k.Delete(ctx, ns, "lease", "okdev-"+sn, true); err != nil {
52-
return fmt.Errorf("delete session lease: %w", err)
53-
}
5450
fmt.Fprintf(cmd.OutOrStdout(), "Session stopped: %s\n", sn)
5551
if !deletePVC && cfg.Spec.Workspace.PVC.ClaimName == "" {
5652
fmt.Fprintf(cmd.OutOrStdout(), "Workspace PVC retained: %s (use --delete-pvc to remove)\n", pvcName(cfg, sn))

internal/cli/ports.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,6 @@ func newPortsCmd(opts *Options) *cobra.Command {
2828
if err := ensureSessionOwnership(opts, k, ns, sn, true); err != nil {
2929
return err
3030
}
31-
stopRenew, err := acquireSessionLock(opts, cfg, ns, sn, cmd.OutOrStdout())
32-
if err != nil {
33-
return err
34-
}
35-
defer stopRenew()
3631
stopMaintenance := startSessionMaintenance(opts, cfg, ns, sn, cmd.OutOrStdout(), true, true)
3732
defer stopMaintenance()
3833
if len(cfg.Spec.Ports) == 0 {

internal/cli/ssh.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,6 @@ func newSSHCmd(opts *Options) *cobra.Command {
3939
if err := ensureSessionOwnership(opts, k, ns, sn, true); err != nil {
4040
return err
4141
}
42-
stopRenew, err := acquireSessionLock(opts, cfg, ns, sn, cmd.OutOrStdout())
43-
if err != nil {
44-
return err
45-
}
46-
defer stopRenew()
4742
stopMaintenance := startSessionMaintenance(opts, cfg, ns, sn, cmd.OutOrStdout(), true, true)
4843
defer stopMaintenance()
4944

internal/cli/status.go

Lines changed: 17 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -48,33 +48,27 @@ func newStatusCmd(opts *Options) *cobra.Command {
4848
})
4949
if opts.Output == "json" {
5050
type statusRow struct {
51-
Session string `json:"session"`
52-
Owner string `json:"owner"`
53-
Pod string `json:"pod"`
54-
Phase string `json:"phase"`
55-
Age string `json:"age"`
56-
Ready string `json:"ready"`
57-
Restarts int32 `json:"restarts"`
58-
Reason string `json:"reason"`
59-
LockHolder string `json:"lockHolder,omitempty"`
51+
Session string `json:"session"`
52+
Owner string `json:"owner"`
53+
Pod string `json:"pod"`
54+
Phase string `json:"phase"`
55+
Age string `json:"age"`
56+
Ready string `json:"ready"`
57+
Restarts int32 `json:"restarts"`
58+
Reason string `json:"reason"`
6059
}
6160
rows := make([]statusRow, 0, len(pods))
6261
for _, p := range pods {
6362
sn := p.Labels["okdev.io/session"]
64-
lockHolder, err := k.LeaseHolder(ctx, p.Namespace, "okdev-"+sn)
65-
if err != nil {
66-
return err
67-
}
6863
rows = append(rows, statusRow{
69-
Session: sn,
70-
Owner: p.Labels["okdev.io/owner"],
71-
Pod: p.Name,
72-
Phase: p.Phase,
73-
Age: age(p.CreatedAt),
74-
Ready: p.Ready,
75-
Restarts: p.Restarts,
76-
Reason: p.Reason,
77-
LockHolder: lockHolder,
64+
Session: sn,
65+
Owner: p.Labels["okdev.io/owner"],
66+
Pod: p.Name,
67+
Phase: p.Phase,
68+
Age: age(p.CreatedAt),
69+
Ready: p.Ready,
70+
Restarts: p.Restarts,
71+
Reason: p.Reason,
7872
})
7973
}
8074
return outputJSON(cmd.OutOrStdout(), rows)
@@ -83,13 +77,6 @@ func newStatusCmd(opts *Options) *cobra.Command {
8377
rows := make([][]string, 0, len(pods))
8478
for _, p := range pods {
8579
sn := p.Labels["okdev.io/session"]
86-
lockHolder, err := k.LeaseHolder(ctx, p.Namespace, "okdev-"+sn)
87-
if err != nil {
88-
return err
89-
}
90-
if lockHolder == "" {
91-
lockHolder = "-"
92-
}
9380
rows = append(rows, []string{
9481
sn,
9582
p.Labels["okdev.io/owner"],
@@ -98,11 +85,10 @@ func newStatusCmd(opts *Options) *cobra.Command {
9885
p.Ready,
9986
fmt.Sprintf("%d", p.Restarts),
10087
p.Reason,
101-
lockHolder,
10288
age(p.CreatedAt),
10389
})
10490
}
105-
output.PrintTable(cmd.OutOrStdout(), []string{"SESSION", "OWNER", "POD", "PHASE", "READY", "RESTARTS", "REASON", "LOCK", "AGE"}, rows)
91+
output.PrintTable(cmd.OutOrStdout(), []string{"SESSION", "OWNER", "POD", "PHASE", "READY", "RESTARTS", "REASON", "AGE"}, rows)
10692
return nil
10793
},
10894
}

internal/cli/sync.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,6 @@ func newSyncCmd(opts *Options) *cobra.Command {
6868
}
6969
return nil
7070
}
71-
stopRenew, err := acquireSessionLock(opts, cfg, ns, sn, cmd.OutOrStdout())
72-
if err != nil {
73-
return err
74-
}
75-
defer stopRenew()
7671
stopMaintenance := startSessionMaintenance(opts, cfg, ns, sn, cmd.OutOrStdout(), renewLock, renewLock)
7772
defer stopMaintenance()
7873

internal/cli/up.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,6 @@ func newUpCmd(opts *Options) *cobra.Command {
5656
}
5757
return nil
5858
}
59-
stopRenew, err := acquireSessionLockWithClient(k, cfg, ns, sn, cmd.OutOrStdout())
60-
if err != nil {
61-
return err
62-
}
63-
defer stopRenew()
6459
if err := ensureSessionOwnership(opts, k, ns, sn, true); err != nil {
6560
return err
6661
}

internal/config/config.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ type SessionSpec struct {
4141
TTLHours int `yaml:"ttlHours"`
4242
IdleTimeoutMinutes int `yaml:"idleTimeoutMinutes"`
4343
Shareable bool `yaml:"shareable"`
44-
LockMode string `yaml:"lockMode"`
4544
}
4645

4746
type Workspace struct {
@@ -119,9 +118,6 @@ func (d *DevEnvironment) SetDefaults() {
119118
if d.Spec.SSH.LocalPort == 0 {
120119
d.Spec.SSH.LocalPort = 2222
121120
}
122-
if d.Spec.Session.LockMode == "" {
123-
d.Spec.Session.LockMode = "none"
124-
}
125121
}
126122

127123
func (d *DevEnvironment) Validate() error {
@@ -149,9 +145,6 @@ func (d *DevEnvironment) Validate() error {
149145
if d.Spec.Sync.Engine != "native" && d.Spec.Sync.Engine != "syncthing" {
150146
return fmt.Errorf("spec.sync.engine must be native or syncthing, got %q", d.Spec.Sync.Engine)
151147
}
152-
if d.Spec.Session.LockMode != "none" && d.Spec.Session.LockMode != "advisory" && d.Spec.Session.LockMode != "exclusive" {
153-
return fmt.Errorf("spec.session.lockMode must be none|advisory|exclusive, got %q", d.Spec.Session.LockMode)
154-
}
155148
if d.Spec.Session.TTLHours < 0 {
156149
return errors.New("spec.session.ttlHours must be >= 0")
157150
}

0 commit comments

Comments
 (0)