Skip to content

Commit 73fdc37

Browse files
author
Per Goncalves da Silva
committed
mask helm conflict errors
Signed-off-by: Per Goncalves da Silva <[email protected]>
1 parent 7cc9872 commit 73fdc37

File tree

4 files changed

+134
-4
lines changed

4 files changed

+134
-4
lines changed

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ require (
1414
github.com/operator-framework/helm-operator-plugins v0.2.2-0.20240520180534-f463c36fedf9
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
1719
github.com/spf13/pflag v1.0.5
1820
github.com/stretchr/testify v1.9.0
1921
go.uber.org/zap v1.27.0
@@ -190,7 +192,6 @@ require (
190192
github.com/otiai10/copy v1.14.0 // indirect
191193
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
192194
github.com/pjbgf/sha1cd v0.3.0 // indirect
193-
github.com/pkg/errors v0.9.1 // indirect
194195
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
195196
github.com/prometheus/client_golang v1.19.1 // indirect
196197
github.com/prometheus/client_model v0.6.1 // indirect
@@ -201,7 +202,6 @@ require (
201202
github.com/russross/blackfriday/v2 v2.1.0 // indirect
202203
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
203204
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/controllers/clusterextension_controller.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ import (
7474
catalogfilter "github.com/operator-framework/operator-controller/internal/catalogmetadata/filter"
7575
catalogsort "github.com/operator-framework/operator-controller/internal/catalogmetadata/sort"
7676
"github.com/operator-framework/operator-controller/internal/conditionsets"
77+
olmv1error "github.com/operator-framework/operator-controller/internal/error"
7778
"github.com/operator-framework/operator-controller/internal/httputil"
7879
"github.com/operator-framework/operator-controller/internal/labels"
7980
)
@@ -338,7 +339,7 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
338339
case stateNeedsInstall:
339340
err := preflight.Install(ctx, desiredRel)
340341
if err != nil {
341-
setInstalledStatusConditionFailed(ext, fmt.Sprintf("%s:%v", ocv1alpha1.ReasonInstallationFailed, err))
342+
setInstalledStatusConditionFailed(ext, fmt.Sprintf("%s:%v", ocv1alpha1.ReasonInstallationFailed, olmv1error.AsOlmErr(err)))
342343
return ctrl.Result{}, err
343344
}
344345
case stateNeedsUpgrade:
@@ -358,7 +359,8 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
358359
return nil
359360
}, helmclient.AppendInstallPostRenderer(post))
360361
if err != nil {
361-
setInstalledStatusConditionFailed(ext, fmt.Sprintf("%s:%v", ocv1alpha1.ReasonInstallationFailed, err))
362+
log.FromContext(ctx).Error(err, "failed to install bundle")
363+
setInstalledStatusConditionFailed(ext, fmt.Sprintf("%s:%v", ocv1alpha1.ReasonInstallationFailed, olmv1error.AsOlmErr(err)))
362364
return ctrl.Result{}, err
363365
}
364366
case stateNeedsUpgrade:

internal/error/errors.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package error
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
7+
"github.com/pkg/errors"
8+
"github.com/sirupsen/logrus"
9+
)
10+
11+
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: .*`
14+
)
15+
16+
// InstallConflictErr indicates one or more resources already exist in the cluster
17+
type InstallConflictErr error
18+
19+
// UnknownErr indicates an error that cannot be classified
20+
type UnknownErr error
21+
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+
}
33+
34+
// let's mark any unmatched errors as unknown
35+
return UnknownErr(err)
36+
}
37+
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
41+
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
45+
46+
// rule links a matcher to a translator
47+
type rule struct {
48+
matchFunc errorMatcher
49+
translator errorTranslator
50+
}
51+
52+
func (r rule) MatchError(err error) []string {
53+
return r.matchFunc(err)
54+
}
55+
56+
func (r rule) TranslateError(err error, params ...string) error {
57+
return r.translator(err, params...)
58+
}
59+
60+
// rules is a list of rules for error translation
61+
var rules = []rule{
62+
{regexErrorMatcher(installConflictErrorPattern), installConflictErrorTranslator},
63+
{regexErrorMatcher(installConflictErrorGeneralPattern), installConflictErrorTranslator},
64+
}
65+
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)
74+
}
75+
return InstallConflictErr(errors.New(msg))
76+
}
77+
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+
}
84+
}

internal/error/errors_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package error
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
)
7+
8+
func TestAsOlmErr(t *testing.T) {
9+
tests := []struct {
10+
name string
11+
err error
12+
expected error
13+
}{
14+
{
15+
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'")),
18+
},
19+
{
20+
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.")),
23+
},
24+
{
25+
name: "Unknown error",
26+
err: fmt.Errorf("some unknown error"),
27+
expected: UnknownErr(fmt.Errorf("some unknown error")),
28+
},
29+
{
30+
name: "Nil error",
31+
err: nil,
32+
expected: nil,
33+
},
34+
}
35+
36+
for _, tt := range tests {
37+
t.Run(tt.name, func(t *testing.T) {
38+
result := AsOlmErr(tt.err)
39+
if result != nil && result.Error() != tt.expected.Error() {
40+
t.Errorf("Expected: %v, got: %v", tt.expected, result)
41+
}
42+
})
43+
}
44+
}

0 commit comments

Comments
 (0)