Skip to content

Commit 58df83b

Browse files
refactor: address review feedback on registry and command patterns
- Make Register methods atomic (validate-then-insert) in check, action, and dependency registries to avoid partial state on error - Change CommandOption to return error instead of panicking - Remove dead IsSet() guard in formatAndOutputResults (lint mode only) - Collapse duplicate if/else branches in backup command - Extract newDefaultActionRegistry() helper to DRY up migrate constructors - Update stale Command Structure docs in architecture.md Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent a92e656 commit 58df83b

File tree

11 files changed

+66
-61
lines changed

11 files changed

+66
-61
lines changed

docs/lint/architecture.md

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -344,15 +344,27 @@ The lint command uses a `Command` struct (not `Options`) with constructor `NewCo
344344

345345
```go
346346
type Command struct {
347-
shared *SharedOptions
348-
targetVersion string
347+
*SharedOptions
348+
TargetVersion version.SemVersion // implements pflag.Value
349+
registry *check.CheckRegistry
349350
}
350351

351-
func NewCommand(opts CommandOptions) *Command {
352-
return &Command{
353-
shared: opts.Shared,
354-
targetVersion: opts.TargetVersion,
352+
func NewCommand(
353+
streams genericiooptions.IOStreams,
354+
configFlags *genericclioptions.ConfigFlags,
355+
options ...CommandOption,
356+
) (*Command, error) {
357+
registry, err := check.NewRegistry(/* checks... */)
358+
if err != nil {
359+
return nil, fmt.Errorf("registering checks: %w", err)
360+
}
361+
362+
c := &Command{
363+
SharedOptions: NewSharedOptions(streams, configFlags),
364+
registry: registry,
355365
}
366+
// ... apply options ...
367+
return c, nil
356368
}
357369
```
358370

pkg/backup/command.go

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -96,25 +96,18 @@ func (c *Command) Complete() error {
9696
}
9797
}
9898

99+
var resolvers []dependencies.Resolver
99100
if c.Dependencies {
100-
registry, err := dependencies.NewRegistry(
101-
notebooks.NewResolver(),
102-
dspa.NewResolver(),
103-
)
104-
if err != nil {
105-
return fmt.Errorf("registering dependency resolvers: %w", err)
106-
}
107-
108-
c.depRegistry = registry
109-
} else {
110-
registry, err := dependencies.NewRegistry()
111-
if err != nil {
112-
return fmt.Errorf("creating dependency registry: %w", err)
113-
}
101+
resolvers = append(resolvers, notebooks.NewResolver(), dspa.NewResolver())
102+
}
114103

115-
c.depRegistry = registry
104+
registry, err := dependencies.NewRegistry(resolvers...)
105+
if err != nil {
106+
return fmt.Errorf("creating dependency registry: %w", err)
116107
}
117108

109+
c.depRegistry = registry
110+
118111
return nil
119112
}
120113

pkg/backup/dependencies/registry.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,17 @@ func NewRegistry(resolvers ...Resolver) (*Registry, error) {
2727
}
2828

2929
// Register adds one or more resolvers to the registry.
30+
// The operation is atomic: either all resolvers are registered or none are.
3031
// Returns an error if any resolver is nil.
3132
func (r *Registry) Register(resolvers ...Resolver) error {
3233
for _, resolver := range resolvers {
3334
if resolver == nil {
3435
return errors.New("cannot register nil resolver")
3536
}
36-
37-
r.resolvers = append(r.resolvers, resolver)
3837
}
3938

39+
r.resolvers = append(r.resolvers, resolvers...)
40+
4041
return nil
4142
}
4243

pkg/lint/check/registry.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ func NewRegistry(checks ...Check) (*CheckRegistry, error) {
2626
}
2727

2828
// Register adds one or more checks to the registry.
29+
// The operation is atomic: either all checks are registered or none are.
2930
// Returns an error if a check with the same ID already exists.
3031
func (r *CheckRegistry) Register(checks ...Check) error {
3132
r.mu.Lock()
@@ -35,7 +36,9 @@ func (r *CheckRegistry) Register(checks ...Check) error {
3536
if _, exists := r.checks[c.ID()]; exists {
3637
return fmt.Errorf("check with ID %s already registered", c.ID())
3738
}
39+
}
3840

41+
for _, c := range checks {
3942
r.checks[c.ID()] = c
4043
}
4144

pkg/lint/command.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ func NewCommand(
111111
}
112112

113113
for _, opt := range options {
114-
opt(c)
114+
if err := opt(c); err != nil {
115+
return nil, fmt.Errorf("applying command option: %w", err)
116+
}
115117
}
116118

117119
return c, nil
@@ -405,13 +407,7 @@ func (c *Command) formatAndOutputResults(
405407
resultsByGroup map[check.CheckGroup][]check.CheckExecution,
406408
) error {
407409
clusterVer := &c.currentClusterVersion
408-
409-
var targetVer *string
410-
if c.TargetVersion.IsSet() {
411-
s := c.TargetVersion.String()
412-
targetVer = &s
413-
}
414-
410+
var targetVer *string // always nil in lint mode
415411
ocpVer := c.openShiftVersionPtr()
416412

417413
// Flatten results to sorted array

pkg/lint/command_options.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -210,18 +210,19 @@ func ValidateCheckSelector(selector string) error {
210210
//
211211
// Example:
212212
//
213-
// cmd := lint.NewCommand(streams, configFlags,
213+
// cmd, err := lint.NewCommand(streams, configFlags,
214214
// lint.WithTargetVersion("3.0"),
215215
// )
216-
type CommandOption func(*Command)
216+
type CommandOption func(*Command) error
217217

218218
// WithTargetVersion returns a CommandOption that sets the target version.
219-
// Panics if version is not a valid semver string (programmatic API).
220219
func WithTargetVersion(v string) CommandOption {
221-
return func(c *Command) {
220+
return func(c *Command) error {
222221
if err := c.TargetVersion.Set(v); err != nil {
223-
panic(fmt.Sprintf("WithTargetVersion: %v", err))
222+
return fmt.Errorf("setting target version: %w", err)
224223
}
224+
225+
return nil
225226
}
226227
}
227228

pkg/migrate/action/registry.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,21 @@ func NewActionRegistry(actions ...Action) (*ActionRegistry, error) {
2424
return r, nil
2525
}
2626

27+
// Register adds one or more actions to the registry.
28+
// The operation is atomic: either all actions are registered or none are.
29+
// Returns an error if an action with the same ID already exists.
2730
func (r *ActionRegistry) Register(actions ...Action) error {
2831
r.mu.Lock()
2932
defer r.mu.Unlock()
3033

3134
for _, a := range actions {
32-
id := a.ID()
33-
if _, exists := r.actions[id]; exists {
34-
return fmt.Errorf("action with ID %q already registered", id)
35+
if _, exists := r.actions[a.ID()]; exists {
36+
return fmt.Errorf("action with ID %q already registered", a.ID())
3537
}
38+
}
3639

37-
r.actions[id] = a
40+
for _, a := range actions {
41+
r.actions[a.ID()] = a
3842
}
3943

4044
return nil

pkg/migrate/command_list.go

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414

1515
"github.com/opendatahub-io/odh-cli/pkg/cmd"
1616
"github.com/opendatahub-io/odh-cli/pkg/migrate/action"
17-
"github.com/opendatahub-io/odh-cli/pkg/migrate/actions/kueue/rhbok"
1817
"github.com/opendatahub-io/odh-cli/pkg/printer/table"
1918
"github.com/opendatahub-io/odh-cli/pkg/util/iostreams"
2019
"github.com/opendatahub-io/odh-cli/pkg/util/version"
@@ -43,17 +42,13 @@ type ListCommand struct {
4342
}
4443

4544
func NewListCommand(streams genericiooptions.IOStreams) (*ListCommand, error) {
46-
shared := NewSharedOptions(streams)
47-
48-
registry, err := action.NewActionRegistry(
49-
&rhbok.RHBOKMigrationAction{},
50-
)
45+
registry, err := newDefaultActionRegistry()
5146
if err != nil {
5247
return nil, fmt.Errorf("registering actions: %w", err)
5348
}
5449

5550
return &ListCommand{
56-
SharedOptions: shared,
51+
SharedOptions: NewSharedOptions(streams),
5752
registry: registry,
5853
}, nil
5954
}

pkg/migrate/command_options.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"k8s.io/cli-runtime/pkg/genericclioptions"
99
"k8s.io/cli-runtime/pkg/genericiooptions"
1010

11+
"github.com/opendatahub-io/odh-cli/pkg/migrate/action"
12+
"github.com/opendatahub-io/odh-cli/pkg/migrate/actions/kueue/rhbok"
1113
"github.com/opendatahub-io/odh-cli/pkg/util/client"
1214
"github.com/opendatahub-io/odh-cli/pkg/util/iostreams"
1315
)
@@ -84,3 +86,11 @@ func (o *SharedOptions) Validate() error {
8486

8587
return nil
8688
}
89+
90+
// newDefaultActionRegistry creates an action registry with all known migration actions.
91+
// Centralizes the action list so additions are made in one place.
92+
func newDefaultActionRegistry() (*action.ActionRegistry, error) {
93+
return action.NewActionRegistry(
94+
&rhbok.RHBOKMigrationAction{},
95+
)
96+
}

pkg/migrate/command_prepare.go

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414

1515
"github.com/opendatahub-io/odh-cli/pkg/cmd"
1616
"github.com/opendatahub-io/odh-cli/pkg/migrate/action"
17-
"github.com/opendatahub-io/odh-cli/pkg/migrate/actions/kueue/rhbok"
1817
"github.com/opendatahub-io/odh-cli/pkg/util/version"
1918
)
2019

@@ -37,17 +36,13 @@ type PrepareCommand struct {
3736
}
3837

3938
func NewPrepareCommand(streams genericiooptions.IOStreams) (*PrepareCommand, error) {
40-
shared := NewSharedOptions(streams)
41-
42-
registry, err := action.NewActionRegistry(
43-
&rhbok.RHBOKMigrationAction{},
44-
)
39+
registry, err := newDefaultActionRegistry()
4540
if err != nil {
4641
return nil, fmt.Errorf("registering actions: %w", err)
4742
}
4843

4944
return &PrepareCommand{
50-
SharedOptions: shared,
45+
SharedOptions: NewSharedOptions(streams),
5146
registry: registry,
5247
}, nil
5348
}

0 commit comments

Comments
 (0)