diff options
| author | Paul Buetow <paul@buetow.org> | 2026-07-08 23:17:39 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-07-08 23:17:39 +0300 |
| commit | 3f8b79f2b1384e3abc2b1bb2fe913688c7ec251c (patch) | |
| tree | 5cff57875a1f5b63536423c0b7fe7a3788ff15ac /internal | |
| parent | 920f2ea88c45e972cd87b56580c90a0277b7130e (diff) | |
add multi which can return multiple resources
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/resource/multi.go | 39 | ||||
| -rw-r--r-- | internal/resource/multi_test.go | 81 | ||||
| -rw-r--r-- | internal/resource/repository.go | 2 | ||||
| -rw-r--r-- | internal/resource/repository_test.go | 14 | ||||
| -rw-r--r-- | internal/resource/resource.go | 8 |
5 files changed, 134 insertions, 10 deletions
diff --git a/internal/resource/multi.go b/internal/resource/multi.go new file mode 100644 index 0000000..1694814 --- /dev/null +++ b/internal/resource/multi.go @@ -0,0 +1,39 @@ +package resource + +import ( + "errors" + "strings" +) + +// Multi is a collection of resources that satisfies the api.Resource interface. +type Multi []Resource + +func (m Multi) String() string { + strs := make([]string, 0, len(m)) + + for _, res := range m { + strs = append(strs, res.String()) + } + + return strings.Join(strs, ", ") +} + +func (m Multi) ID() string { + ids := make([]string, 0, len(m)) + + for _, res := range m { + ids = append(ids, res.String()) + } + + return strings.Join(ids, "+") +} + +func (m Multi) Apply() error { + var errs []error + + for _, res := range m { + errs = append(errs, res.Apply()) + } + + return errors.Join(errs...) +} diff --git a/internal/resource/multi_test.go b/internal/resource/multi_test.go new file mode 100644 index 0000000..3443afb --- /dev/null +++ b/internal/resource/multi_test.go @@ -0,0 +1,81 @@ +package resource + +import ( + "errors" + "testing" +) + +func TestMultiString(t *testing.T) { + m := Multi{ + Resource{Type: "File", Name: "/tmp/a"}, + Resource{Type: "File", Name: "/tmp/b"}, + } + + want := "File[/tmp/a], File[/tmp/b]" + if got := m.String(); got != want { + t.Errorf("Multi.String() = %q, want %q", got, want) + } +} + +func TestMultiID(t *testing.T) { + m := Multi{ + Resource{Type: "File", Name: "/tmp/a"}, + Resource{Type: "File", Name: "/tmp/b"}, + } + + want := "File[/tmp/a]+File[/tmp/b]" + if got := m.ID(); got != want { + t.Errorf("Multi.ID() = %q, want %q", got, want) + } +} + +func TestMultiApply(t *testing.T) { + tests := []struct { + name string + appliers []Applier + wantErr bool + }{ + { + name: "all success", + appliers: []Applier{ + ApplierFunc(func() error { return nil }), + ApplierFunc(func() error { return nil }), + }, + wantErr: false, + }, + { + name: "one failure", + appliers: []Applier{ + ApplierFunc(func() error { return nil }), + ApplierFunc(func() error { return errors.New("fail 1") }), + }, + wantErr: true, + }, + { + name: "multiple failures", + appliers: []Applier{ + ApplierFunc(func() error { return errors.New("fail 1") }), + ApplierFunc(func() error { return errors.New("fail 2") }), + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var resources []Resource + for _, app := range tt.appliers { + resources = append(resources, Resource{ + applier: app, + }) + } + + m := Multi(resources) + err := m.Apply() + + if (err != nil) != tt.wantErr { + t.Errorf("Multi.Apply() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/internal/resource/repository.go b/internal/resource/repository.go index 215f7e1..0771ef8 100644 --- a/internal/resource/repository.go +++ b/internal/resource/repository.go @@ -89,7 +89,7 @@ func (r *repository) apply() error { for _, res := range order { log.Printf("Applying resource %v", res) - if err := res.Apply.Apply(); err != nil { + if err := res.Apply(); err != nil { return fmt.Errorf("failed to apply %v: %w", res, err) } } diff --git a/internal/resource/repository_test.go b/internal/resource/repository_test.go index 8f1aef6..f939fca 100644 --- a/internal/resource/repository_test.go +++ b/internal/resource/repository_test.go @@ -32,17 +32,17 @@ func TestApply(t *testing.T) { // A depends on B, B depends on C r.registered["A"] = Resource{ Type: "T", Name: "A", - Apply: &mockApplier{name: "A", logs: logs}, + applier: &mockApplier{name: "A", logs: logs}, dependsOn: map[string]struct{}{"B": {}}, } r.registered["B"] = Resource{ Type: "T", Name: "B", - Apply: &mockApplier{name: "B", logs: logs}, + applier: &mockApplier{name: "B", logs: logs}, dependsOn: map[string]struct{}{"C": {}}, } r.registered["C"] = Resource{ Type: "T", Name: "C", - Apply: &mockApplier{name: "C", logs: logs}, + applier: &mockApplier{name: "C", logs: logs}, } }, wantOrder: []string{"C", "B", "A"}, @@ -52,12 +52,12 @@ func TestApply(t *testing.T) { setup: func(r *repository, logs *[]string) { r.registered["A"] = Resource{ Type: "T", Name: "A", - Apply: &mockApplier{name: "A", logs: logs}, + applier: &mockApplier{name: "A", logs: logs}, dependsOn: map[string]struct{}{"B": {}}, } r.registered["B"] = Resource{ Type: "T", Name: "B", - Apply: &mockApplier{name: "B", logs: logs}, + applier: &mockApplier{name: "B", logs: logs}, dependsOn: map[string]struct{}{"A": {}}, } }, @@ -69,7 +69,7 @@ func TestApply(t *testing.T) { setup: func(r *repository, logs *[]string) { r.registered["A"] = Resource{ Type: "T", Name: "A", - Apply: &mockApplier{name: "A", logs: logs}, + applier: &mockApplier{name: "A", logs: logs}, dependsOn: map[string]struct{}{"Missing": {}}, } }, @@ -81,7 +81,7 @@ func TestApply(t *testing.T) { setup: func(r *repository, logs *[]string) { r.registered["A"] = Resource{ Type: "T", Name: "A", - Apply: &mockApplier{name: "A", err: errors.New("fail A"), logs: logs}, + applier: &mockApplier{name: "A", err: errors.New("fail A"), logs: logs}, } }, wantError: true, diff --git a/internal/resource/resource.go b/internal/resource/resource.go index 4e72485..2a01629 100644 --- a/internal/resource/resource.go +++ b/internal/resource/resource.go @@ -18,7 +18,7 @@ func (f ApplierFunc) Apply() error { type Resource struct { Type string Name string - Apply Applier + applier Applier dependsOn map[string]struct{} } @@ -26,7 +26,7 @@ func Register(type_, name string, apply Applier) Resource { r := Resource{ Type: type_, Name: name, - Apply: apply, + applier: apply, dependsOn: make(map[string]struct{}), } @@ -44,3 +44,7 @@ func (r Resource) String() string { func (r Resource) ID() string { return fmt.Sprintf("%s[%s]", r.Type, r.Name) } + +func (r Resource) Apply() error { + return r.applier.Apply() +} |
