Skip to content

Commit 5a2d772

Browse files
author
Per Goncalves da Silva
committed
refactor
Signed-off-by: Per Goncalves da Silva <[email protected]>
1 parent 1599593 commit 5a2d772

File tree

7 files changed

+198
-133
lines changed

7 files changed

+198
-133
lines changed

cmd/manager/main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import (
4747
"github.com/operator-framework/rukpak/pkg/storage"
4848

4949
ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1"
50+
"github.com/operator-framework/operator-controller/internal/action"
5051
"github.com/operator-framework/operator-controller/internal/catalogmetadata/cache"
5152
catalogclient "github.com/operator-framework/operator-controller/internal/catalogmetadata/client"
5253
"github.com/operator-framework/operator-controller/internal/controllers"
@@ -176,9 +177,10 @@ func main() {
176177
os.Exit(1)
177178
}
178179

179-
acg, err := helmclient.NewActionClientGetter(cfgGetter,
180+
acg, err := action.NewWrappedActionClientGetter(cfgGetter,
180181
helmclient.WithFailureRollbacks(false),
181182
)
183+
182184
if err != nil {
183185
setupLog.Error(err, "unable to create helm client")
184186
os.Exit(1)

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ require (
1414
github.com/operator-framework/helm-operator-plugins v0.3.0
1515
github.com/operator-framework/operator-registry v1.44.0
1616
github.com/operator-framework/rukpak v0.24.0
17-
github.com/pkg/errors v0.9.1
18-
github.com/sirupsen/logrus v1.9.3
1917
github.com/spf13/pflag v1.0.5
2018
github.com/stretchr/testify v1.9.0
2119
go.uber.org/zap v1.27.0
@@ -192,6 +190,7 @@ require (
192190
github.com/otiai10/copy v1.14.0 // indirect
193191
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
194192
github.com/pjbgf/sha1cd v0.3.0 // indirect
193+
github.com/pkg/errors v0.9.1 // indirect
195194
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
196195
github.com/prometheus/client_golang v1.19.1 // indirect
197196
github.com/prometheus/client_model v0.6.1 // indirect
@@ -202,6 +201,7 @@ require (
202201
github.com/russross/blackfriday/v2 v2.1.0 // indirect
203202
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
204203
github.com/shopspring/decimal v1.3.1 // indirect
204+
github.com/sirupsen/logrus v1.9.3 // indirect
205205
github.com/skeema/knownhosts v1.2.2 // indirect
206206
github.com/spf13/cast v1.5.0 // indirect
207207
github.com/spf13/cobra v1.8.1 // indirect

internal/action/error/errors.go

Lines changed: 48 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,82 +3,75 @@ package error
33
import (
44
"fmt"
55
"regexp"
6-
7-
"github.com/pkg/errors"
8-
"github.com/sirupsen/logrus"
96
)
107

118
const (
12-
installConflictErrorPattern = `Unable to continue with install: (\w+) "(.*)" in namespace "(.*)" exists and cannot be imported into the current release.*`
13-
installConflictErrorGeneralPattern = `Unable to continue with install: .*`
9+
defaultErrMessage = "An unexpected error prevented the operator from succeeding"
1410
)
1511

16-
// InstallConflictErr indicates one or more resources already exist in the cluster
17-
type InstallConflictErr error
12+
var (
13+
installConflictErrorPattern = regexp.MustCompile(`Unable to continue with install: (\w+) "(.*)" in namespace "(.*)" exists and cannot be imported into the current release.*`)
14+
)
1815

19-
// UnknownErr indicates an error that cannot be classified
20-
type UnknownErr error
16+
type Olmv1Err struct {
17+
originalErr error
18+
message string
19+
}
2120

22-
func AsOlmErr(err error) error {
23-
if err == nil {
24-
return nil
25-
}
26-
for _, rule := range rules {
27-
if matches := rule.MatchError(err); len(matches) > 0 {
28-
// first index is the full match
29-
logrus.Debugf("found match with params %v\n", matches)
30-
return rule.TranslateError(err, matches[1:]...)
31-
}
32-
}
21+
func (o Olmv1Err) Error() string {
22+
return o.message
23+
}
3324

34-
// let's mark any unmatched errors as unknown
35-
return UnknownErr(err)
25+
func (o Olmv1Err) Cause() error {
26+
return o.originalErr
3627
}
3728

38-
// errorTranslator is a function that translates an error into a more specific error
39-
// typically to hide implementation details
40-
type errorTranslator func(error, ...string) error
29+
func newOlmv1Err(originalErr error, message string) error {
30+
return &Olmv1Err{
31+
originalErr: originalErr,
32+
message: message,
33+
}
34+
}
4135

42-
// errorMatcher is a function that matches an error to a specific pattern and extracts any
43-
// relevant information - if nil is returned, no match was found
44-
type errorMatcher func(error) []string
36+
func AsOlmErr(originalErr error) error {
37+
if originalErr == nil {
38+
return nil
39+
}
4540

46-
// rule links a matcher to a translator
47-
type rule struct {
48-
matchFunc errorMatcher
49-
translator errorTranslator
50-
}
41+
for _, exec := range rules {
42+
if err := exec(originalErr); err != nil {
43+
return err
44+
}
45+
}
5146

52-
func (r rule) MatchError(err error) []string {
53-
return r.matchFunc(err)
47+
// let's mark any unmatched errors as unknown
48+
return defaultErrTranslator(originalErr)
5449
}
5550

56-
func (r rule) TranslateError(err error, params ...string) error {
57-
return r.translator(err, params...)
58-
}
51+
// rule is a function that translates an error into a more specific error
52+
// typically to hide internal implementation details
53+
// in: helm error
54+
// out: nil -> no translation | !nil -> translated error
55+
type rule func(originalErr error) error
5956

6057
// rules is a list of rules for error translation
6158
var rules = []rule{
62-
{regexErrorMatcher(installConflictErrorPattern), installConflictErrorTranslator},
63-
{regexErrorMatcher(installConflictErrorGeneralPattern), installConflictErrorTranslator},
59+
helmInstallConflictErr,
6460
}
6561

66-
// installConflictErrorTranslator c
67-
func installConflictErrorTranslator(_ error, params ...string) error {
68-
msg := "A conflict prevented the installation from proceeding. Please ensure there are no conflicting resources already present in the cluster."
69-
if len(params) == 3 {
70-
kind := params[0]
71-
name := params[1]
72-
namespace := params[2]
73-
msg = fmt.Sprintf("%s '%s' already exists in namespace '%s'", kind, name, namespace)
62+
// installConflictErrorTranslator
63+
func helmInstallConflictErr(originalErr error) error {
64+
matches := installConflictErrorPattern.FindStringSubmatch(originalErr.Error())
65+
if len(matches) != 4 {
66+
// there was no match
67+
return nil
7468
}
75-
return InstallConflictErr(errors.New(msg))
69+
kind := matches[1]
70+
name := matches[2]
71+
namespace := matches[3]
72+
return newOlmv1Err(originalErr, fmt.Sprintf("%s '%s' already exists in namespace '%s'", kind, name, namespace))
7673
}
7774

78-
// takes a valid regexp and calls FindStringSubmatch to assert match and extract parameters
79-
func regexErrorMatcher(regex string) errorMatcher {
80-
re := regexp.MustCompile(regex)
81-
return func(err error) []string {
82-
return re.FindStringSubmatch(err.Error())
83-
}
75+
func defaultErrTranslator(originalErr error) error {
76+
return newOlmv1Err(originalErr, defaultErrMessage)
8477
}

internal/action/error/errors_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package error
22

33
import (
4-
"fmt"
4+
"errors"
55
"testing"
66
)
77

@@ -13,18 +13,18 @@ func TestAsOlmErr(t *testing.T) {
1313
}{
1414
{
1515
name: "Install conflict error (match)",
16-
err: fmt.Errorf("Unable to continue with install: Deployment \"my-deploy\" in namespace \"my-namespace\" exists and cannot be imported into the current release"),
17-
expected: InstallConflictErr(fmt.Errorf("Deployment 'my-deploy' already exists in namespace 'my-namespace'")),
16+
err: errors.New("Unable to continue with install: Deployment \"my-deploy\" in namespace \"my-namespace\" exists and cannot be imported into the current release"),
17+
expected: errors.New("Deployment 'my-deploy' already exists in namespace 'my-namespace'"),
1818
},
1919
{
2020
name: "Install conflict error (no match)",
21-
err: fmt.Errorf("Unable to continue with install: because of something"),
22-
expected: InstallConflictErr(fmt.Errorf("A conflict prevented the installation from proceeding. Please ensure there are no conflicting resources already present in the cluster.")),
21+
err: errors.New("Unable to continue with install: because of something"),
22+
expected: errors.New("An unexpected error prevented the operator from succeeding"),
2323
},
2424
{
2525
name: "Unknown error",
26-
err: fmt.Errorf("some unknown error"),
27-
expected: UnknownErr(fmt.Errorf("some unknown error")),
26+
err: errors.New("some unknown error"),
27+
expected: errors.New("An unexpected error prevented the operator from succeeding"),
2828
},
2929
{
3030
name: "Nil error",

internal/action/helm.go

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,80 @@
11
package action
22

33
import (
4+
"context"
5+
46
"helm.sh/helm/v3/pkg/chart"
57
"helm.sh/helm/v3/pkg/release"
8+
"sigs.k8s.io/controller-runtime/pkg/client"
9+
10+
actionclient "github.com/operator-framework/helm-operator-plugins/pkg/client"
611

7-
"github.com/operator-framework/helm-operator-plugins/pkg/client"
12+
olmv1error "github.com/operator-framework/operator-controller/internal/action/error"
813
)
914

15+
type ActionClientGetter struct {
16+
actionclient.ActionClientGetter
17+
}
18+
19+
func (a ActionClientGetter) ActionClientFor(ctx context.Context, obj client.Object) (actionclient.ActionInterface, error) {
20+
ac, err := a.ActionClientGetter.ActionClientFor(ctx, obj)
21+
if err != nil {
22+
return nil, err
23+
}
24+
return ActionClient{
25+
ActionInterface: ac,
26+
actionClientErrorTranslator: olmv1error.AsOlmErr,
27+
}, nil
28+
}
29+
30+
func NewWrappedActionClientGetter(acg actionclient.ActionConfigGetter, opts ...actionclient.ActionClientGetterOption) (*ActionClientGetter, error) {
31+
ag, err := actionclient.NewActionClientGetter(acg, opts...)
32+
if err != nil {
33+
return nil, err
34+
}
35+
return &ActionClientGetter{
36+
ActionClientGetter: ag,
37+
}, nil
38+
}
39+
40+
type ActionClientErrorTranslator func(err error) error
41+
1042
type ActionClient struct {
11-
client.ActionInterface
43+
actionclient.ActionInterface
44+
actionClientErrorTranslator ActionClientErrorTranslator
1245
}
1346

14-
func (a ActionClient) Install(name, namespace string, chrt *chart.Chart, vals map[string]interface{}, opts ...client.InstallOption) (*release.Release, error) {
47+
func NewWrappedActionClient(ca actionclient.ActionInterface, errTranslator ActionClientErrorTranslator) *ActionClient {
48+
return &ActionClient{
49+
ActionInterface: ca,
50+
actionClientErrorTranslator: errTranslator,
51+
}
52+
}
53+
54+
func (a ActionClient) Install(name, namespace string, chrt *chart.Chart, vals map[string]interface{}, opts ...actionclient.InstallOption) (*release.Release, error) {
1555
rel, err := a.ActionInterface.Install(name, namespace, chrt, vals, opts...)
56+
err = a.actionClientErrorTranslator(err)
57+
return rel, err
58+
}
59+
60+
func (a ActionClient) Upgrade(name, namespace string, chrt *chart.Chart, vals map[string]interface{}, opts ...actionclient.UpgradeOption) (*release.Release, error) {
61+
rel, err := a.ActionInterface.Upgrade(name, namespace, chrt, vals, opts...)
62+
err = a.actionClientErrorTranslator(err)
1663
return rel, err
1764
}
1865

19-
func (a ActionClient) Upgrade(name, namespace string, chrt *chart.Chart, vals map[string]interface{}, opts ...client.UpgradeOption) (*release.Release, error) {
20-
re, err := a.ActionInterface.Upgrade(name, namespace, chrt, vals, opts...)
21-
return re, err
66+
func (a ActionClient) Uninstall(name string, opts ...actionclient.UninstallOption) (*release.UninstallReleaseResponse, error) {
67+
resp, err := a.ActionInterface.Uninstall(name, opts...)
68+
err = a.actionClientErrorTranslator(err)
69+
return resp, err
70+
}
71+
72+
func (a ActionClient) Get(name string, opts ...actionclient.GetOption) (*release.Release, error) {
73+
resp, err := a.ActionInterface.Get(name, opts...)
74+
err = a.actionClientErrorTranslator(err)
75+
return resp, err
76+
}
77+
78+
func (a ActionClient) Reconcile(rel *release.Release) error {
79+
return a.actionClientErrorTranslator(a.ActionInterface.Reconcile(rel))
2280
}

internal/action/helm_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package action
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
"helm.sh/helm/v3/pkg/chart"
9+
"helm.sh/helm/v3/pkg/release"
10+
11+
"github.com/operator-framework/helm-operator-plugins/pkg/client"
12+
)
13+
14+
var _ client.ActionInterface = &fakeActionClient{}
15+
16+
type fakeActionClient struct {
17+
err error
18+
}
19+
20+
func (f fakeActionClient) Get(name string, opts ...client.GetOption) (*release.Release, error) {
21+
return nil, f.err
22+
}
23+
24+
func (f fakeActionClient) Install(name, namespace string, chrt *chart.Chart, vals map[string]interface{}, opts ...client.InstallOption) (*release.Release, error) {
25+
return nil, f.err
26+
}
27+
28+
func (f fakeActionClient) Upgrade(name, namespace string, chrt *chart.Chart, vals map[string]interface{}, opts ...client.UpgradeOption) (*release.Release, error) {
29+
return nil, f.err
30+
}
31+
32+
func (f fakeActionClient) Uninstall(name string, opts ...client.UninstallOption) (*release.UninstallReleaseResponse, error) {
33+
return nil, f.err
34+
}
35+
36+
func (f fakeActionClient) Reconcile(rel *release.Release) error {
37+
return f.err
38+
}
39+
40+
func TestActionClientErrorTranslation(t *testing.T) {
41+
expectedErr := fmt.Errorf("something other error")
42+
errTranslator := func(originalErr error) error {
43+
return expectedErr
44+
}
45+
46+
ac := fakeActionClient{
47+
err: fmt.Errorf("some error"),
48+
}
49+
50+
wrappedAc := NewWrappedActionClient(ac, errTranslator)
51+
52+
// Get
53+
_, err := wrappedAc.Get("something")
54+
assert.Equal(t, expectedErr, err, "expected Get() to return translated error")
55+
56+
// Install
57+
_, err = wrappedAc.Install("something", "somethingelse", nil, nil)
58+
assert.Equal(t, expectedErr, err, "expected Install() to return translated error")
59+
60+
// Uninstall
61+
_, err = wrappedAc.Uninstall("something")
62+
assert.Equal(t, expectedErr, err, "expected Uninstall() to return translated error")
63+
64+
// Upgrade
65+
_, err = wrappedAc.Upgrade("something", "somethingelse", nil, nil)
66+
assert.Equal(t, expectedErr, err, "expected Upgrade() to return translated error")
67+
68+
// Reconcile
69+
err = wrappedAc.Reconcile(nil)
70+
assert.Equal(t, expectedErr, err, "expected Reconcile() to return translated error")
71+
}

0 commit comments

Comments
 (0)