Skip to content

[chore] architecture refactor#863

Merged
ldmonster merged 3 commits intomainfrom
chore/architecture-refactor
Apr 8, 2026
Merged

[chore] architecture refactor#863
ldmonster merged 3 commits intomainfrom
chore/architecture-refactor

Conversation

@ldmonster
Copy link
Copy Markdown
Collaborator

@ldmonster ldmonster commented Apr 7, 2026

Overview

Followed: #862

Implements architecture refactoring roadmap (review.md): seven focused, medium-risk improvements covering a data race, a deferred context, two OCP violations, three interface splits, and two structural decompositions. Build is clean; all 26 test packages pass; 5 new test files added (709 insertions, 218 deletions across 16 files).


What this PR does / why we need it

2.1 — Extract HookTaskFactory (operator.go, new hook_task_factory.go)
initHookManager, initValidatingWebhookManager, and conversionEventHandler each contained an identical anonymous callback building a task.NewTask(HookRun).WithMetadata(HookMetadata{...}).WithLogLabels(...).WithQueueName(...).WithCompactionID(...) chain (~15 duplicated lines, three times). Extracted to HookTaskFactory.NewHookTask(hook, bindingType, info, logLabels). The factory is a zero-value struct field on ShellOperator; no constructor wiring required.

2.2 — Decompose loadHook() into three discrete methods (hook_manager.go)
loadHook() was ~80 lines performing path resolution, --config execution, config parsing, metadata label injection for four binding types, controller wiring, and validation. Refactored into:

Method Responsibility
fetchHookConfig(hook) Execute hook --config, parse output, populate hook.Config
enrichHookMetadata(hook) Inject hook.Name log/metric labels into all four binding type configs
wireHookController(hook) Construct HookController, init all binding subsystems, set TmpDir

loadHook() now orchestrates only: construct hook → fetchHookConfigenrichHookMetadatawireHookController → validate → log.

2.3 — IOProvider interface abstracts temp-file I/O from Hook.Run() (hook.go, new io_provider.go)
Hook.Run() previously called five private prepare*File() helpers that created real OS temp files, making the method impossible to unit-test without a filesystem and manual cleanup. Introduced:

type IOProvider interface {
    PrepareFiles(context BindingContextList, safeName, tmpDir string) (HookRunFiles, func(), error)
}

FileIOProvider{} is the production implementation (writes to tmpDir). Tests can supply any alternative implementation. NewHook injects FileIOProvider{} by default; the field is exported so test code can substitute it. The five prepare* methods are removed; Run() calls h.IOProvider.PrepareFiles(...) and uses the returned HookRunFiles struct for all path references.

2.4 — VersionedConverter registry; remove version switch (config.go)
HookConfig.ConvertAndCheck contained a switch c.Version { case "v0": ... case "v1": ... } — a textbook OCP violation. Introduced the VersionedConverter interface (one method: ConvertAndCheck(data []byte, c *HookConfig) error) and a var versionedConverters = map[string]VersionedConverter{ "v0": v0Converter{}, "v1": v1Converter{} } registry. The switch is replaced by a single map lookup. New versions are added by registering a new struct — no changes to ConvertAndCheck needed.

2.5 — Split KubeEventsManager and ScheduleManager interfaces
Both interfaces previously mixed lifecycle control, entry/monitor management, and channel emission into a single surface.

  • KubeEventsManagerMonitorRegistry (CRUD: AddMonitor, HasMonitor, GetMonitor, StartMonitor, StopMonitor, WithMetricStorage, MetricStorage) + EventEmitter (Ch, Stop, Wait). KubeEventsManager composes both.
  • ScheduleManagerScheduleRegistry (Add, Remove) + ScheduleEmitter (Ch, Start, Stop). ScheduleManager composes both.

Callers that only need event consumption can now depend on EventEmitter/ScheduleEmitter; callers that only register watches depend on MonitorRegistry/ScheduleRegistry. The concrete types satisfy all sub-interfaces (verified by compile-time var _ X = (*impl)(nil) assertions in tests).

2.6 — Propagate context.Context through bootstrap.Init() (bootstrap.go, start.go)
Init() previously accepted no context and called context.TODO() in six places — four logger.Log calls, NewShellOperator, and RunDefaultDebugServer. The function signature is now Init(ctx context.Context, logger *log.Logger) and every internal call uses the provided context. The single call-site in cmd/shell-operator/start.go passes the context.Background() that already existed there.

2.7 — Fix CronEntry.Ids data race (schedule_manager.go)
sm.Entries was map[string]CronEntry (value). Mutating cronEntry.Ids[newEntry.Id] on a copy retrieved from the map silently discarded the write; the stored entry was never updated. Under concurrent access the check-then-modify pair was also racy. Changed to map[string]*CronEntry so all reads and writes operate on the same heap object under the existing mutex.

@ldmonster ldmonster self-assigned this Apr 7, 2026
@ldmonster ldmonster added the enhancement New feature or request label Apr 7, 2026
Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
@ldmonster ldmonster force-pushed the chore/architecture-refactor branch from bdd07c8 to fd8c50e Compare April 8, 2026 07:44
Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
@ldmonster ldmonster merged commit a8dc73a into main Apr 8, 2026
9 checks passed
@ldmonster ldmonster deleted the chore/architecture-refactor branch April 8, 2026 08:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant