99 "os"
1010 "os/exec"
1111 "path/filepath"
12+ "regexp"
1213 "strings"
1314 "time"
1415
@@ -21,6 +22,8 @@ import (
2122const sessionLeaseDuration = 2 * time .Minute
2223const sessionHeartbeatInterval = 5 * time .Minute
2324
25+ var invalidOwnerChars = regexp .MustCompile (`[^a-z0-9._-]` )
26+
2427func loadConfigAndNamespace (opts * Options ) (* config.DevEnvironment , string , error ) {
2528 cfg , path , err := config .Load (opts .ConfigPath )
2629 if err != nil {
@@ -48,11 +51,8 @@ func resolveSessionName(opts *Options, cfg *config.DevEnvironment) (string, erro
4851 return session .Resolve (opts .Session , cfg .Spec .Session .DefaultNameTemplate )
4952}
5053
51- func labelsForSession (cfg * config.DevEnvironment , sessionName string ) map [string ]string {
52- owner := os .Getenv ("USER" )
53- if owner == "" {
54- owner = "dev"
55- }
54+ func labelsForSession (opts * Options , cfg * config.DevEnvironment , sessionName string ) map [string ]string {
55+ owner := currentOwner (opts )
5656 repo := "unknown"
5757 if root , err := session .RepoRoot (); err == nil && root != "" {
5858 repo = filepath .Base (root )
@@ -63,12 +63,24 @@ func labelsForSession(cfg *config.DevEnvironment, sessionName string) map[string
6363 "okdev.io/session" : sessionName ,
6464 "okdev.io/owner" : owner ,
6565 "okdev.io/repo" : repo ,
66+ "okdev.io/shareable" : func () string {
67+ if cfg .Spec .Session .Shareable {
68+ return "true"
69+ }
70+ return "false"
71+ }(),
6672 }
6773}
6874
6975func annotationsForSession (cfg * config.DevEnvironment ) map [string ]string {
7076 out := map [string ]string {
7177 "okdev.io/last-attach" : time .Now ().UTC ().Format (time .RFC3339 ),
78+ "okdev.io/shareable" : func () string {
79+ if cfg .Spec .Session .Shareable {
80+ return "true"
81+ }
82+ return "false"
83+ }(),
7284 }
7385 if cfg .Spec .Session .TTLHours > 0 {
7486 out ["okdev.io/ttl-hours" ] = fmt .Sprintf ("%d" , cfg .Spec .Session .TTLHours )
@@ -252,17 +264,69 @@ func startSessionMaintenanceWithClient(k *kube.Client, cfg *config.DevEnvironmen
252264}
253265
254266func sessionHolderIdentity () string {
255- user := os .Getenv ("USER" )
256- if user == "" {
257- user = "dev"
258- }
267+ user := currentOwner (nil )
259268 host , err := os .Hostname ()
260269 if err != nil || host == "" {
261270 host = "unknown-host"
262271 }
263272 return user + "@" + host
264273}
265274
275+ func currentOwner (opts * Options ) string {
276+ if opts != nil {
277+ if v := normalizeOwner (opts .Owner ); v != "" {
278+ return v
279+ }
280+ }
281+ if v := normalizeOwner (os .Getenv ("OKDEV_OWNER" )); v != "" {
282+ return v
283+ }
284+ if v := normalizeOwner (os .Getenv ("USER" )); v != "" {
285+ return v
286+ }
287+ return "dev"
288+ }
289+
290+ func normalizeOwner (v string ) string {
291+ s := strings .ToLower (strings .TrimSpace (v ))
292+ s = strings .ReplaceAll (s , " " , "-" )
293+ s = invalidOwnerChars .ReplaceAllString (s , "-" )
294+ s = strings .Trim (s , "-" )
295+ return s
296+ }
297+
298+ func ownerLabelSelector (opts * Options ) string {
299+ return "okdev.io/owner=" + currentOwner (opts )
300+ }
301+
302+ func isSessionShareable (p kube.PodSummary ) bool {
303+ if strings .EqualFold (strings .TrimSpace (p .Annotations ["okdev.io/shareable" ]), "true" ) {
304+ return true
305+ }
306+ return strings .EqualFold (strings .TrimSpace (p .Labels ["okdev.io/shareable" ]), "true" )
307+ }
308+
309+ func ensureSessionOwnership (opts * Options , k * kube.Client , namespace , sessionName string , allowShareable bool ) error {
310+ ctx , cancel := context .WithTimeout (context .Background (), 15 * time .Second )
311+ defer cancel ()
312+ pods , err := k .ListPods (ctx , namespace , false , "okdev.io/managed=true,okdev.io/session=" + sessionName )
313+ if err != nil {
314+ return err
315+ }
316+ if len (pods ) == 0 {
317+ return nil
318+ }
319+ owner := currentOwner (opts )
320+ otherOwner := strings .TrimSpace (pods [0 ].Labels ["okdev.io/owner" ])
321+ if otherOwner == "" || otherOwner == owner {
322+ return nil
323+ }
324+ if allowShareable && isSessionShareable (pods [0 ]) {
325+ return nil
326+ }
327+ return fmt .Errorf ("session %q is owned by %q (current owner: %q); set --owner %s or mark session as shareable" , sessionName , otherOwner , owner , otherOwner )
328+ }
329+
266330func ensureCommand (name string ) error {
267331 if _ , err := exec .LookPath (name ); err != nil {
268332 return fmt .Errorf ("required command %q not found in PATH" , name )
0 commit comments