diff --git a/pkg/lifecycle/mode.go b/pkg/lifecycle/mode.go new file mode 100644 index 00000000..65986061 --- /dev/null +++ b/pkg/lifecycle/mode.go @@ -0,0 +1,52 @@ +package lifecycle + +import ( + "os" + "strings" +) + +type AppMode string + +const ( + ModeManager AppMode = "manager" + ModeWorker AppMode = "worker" +) + +const ( + appModeEnv = "CORE_APP_MODE" + ciEnv = "CI" +) + +// DetectMode returns ModeWorker when CORE_APP_MODE=worker. +// +// mode := DetectMode() +func DetectMode() AppMode { + if value, ok := os.LookupEnv(appModeEnv); ok { + if mode, valid := parseAppMode(value); valid { + return mode + } + } + + if value, ok := os.LookupEnv(ciEnv); ok && isTrue(value) { + return ModeWorker + } + + return ModeManager +} + +func parseAppMode(value string) (AppMode, bool) { + switch strings.ToLower(strings.TrimSpace(value)) { + case string(ModeManager): + return ModeManager, true + case string(ModeWorker): + return ModeWorker, true + case "": + return "", false + default: + return ModeManager, true + } +} + +func isTrue(value string) bool { + return strings.EqualFold(strings.TrimSpace(value), "true") +} diff --git a/pkg/lifecycle/mode_test.go b/pkg/lifecycle/mode_test.go new file mode 100644 index 00000000..32ddc74d --- /dev/null +++ b/pkg/lifecycle/mode_test.go @@ -0,0 +1,57 @@ +package lifecycle + +import ( + "os" + "testing" +) + +func TestMode_DetectMode_Good(t *testing.T) { + unsetEnv(t, appModeEnv) + t.Setenv(ciEnv, "") + + mode := DetectMode() + if mode != ModeManager { + t.Fatalf("expected manager mode, got %q", mode) + } +} + +func TestMode_DetectMode_Bad(t *testing.T) { + t.Setenv(appModeEnv, "bogus") + t.Setenv(ciEnv, "") + + mode := DetectMode() + if mode != ModeManager { + t.Fatalf("expected manager mode after invalid env value, got %q", mode) + } +} + +func TestMode_DetectMode_Ugly(t *testing.T) { + t.Setenv(appModeEnv, "") + t.Setenv(ciEnv, "true") + + mode := DetectMode() + if mode != ModeWorker { + t.Fatalf("expected worker mode in CI headless context, got %q", mode) + } +} + +func unsetEnv(t *testing.T, key string) { + t.Helper() + + value, ok := os.LookupEnv(key) + if err := os.Unsetenv(key); err != nil { + t.Fatalf("unset %s: %v", key, err) + } + + t.Cleanup(func() { + if ok { + if err := os.Setenv(key, value); err != nil { + t.Fatalf("restore %s: %v", key, err) + } + return + } + if err := os.Unsetenv(key); err != nil { + t.Fatalf("restore unset %s: %v", key, err) + } + }) +}