Skip to content

Commit 1a5375b

Browse files
Merge pull request #2102 from dinhxuanvu/resolver-events
fix(catalog): Reduce namespace resync in resolution failure
2 parents f5292da + b9ae658 commit 1a5375b

File tree

2 files changed

+173
-0
lines changed

2 files changed

+173
-0
lines changed

pkg/controller/operators/catalog/operator.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import (
5050
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/grpc"
5151
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/reconciler"
5252
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver"
53+
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/solver"
5354
controllerclient "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/controller-runtime/client"
5455
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/event"
5556
index "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/index"
@@ -886,6 +887,16 @@ func (o *Operator) syncResolvingNamespace(obj interface{}) error {
886887
steps, bundleLookups, updatedSubs, err := o.resolver.ResolveSteps(namespace, querier)
887888
if err != nil {
888889
go o.recorder.Event(ns, corev1.EventTypeWarning, "ResolutionFailed", err.Error())
890+
// If the error is constraints not satisfiable, then simply project the
891+
// resolution failure event and move on without returning the error.
892+
// Returning the error only triggers the namespace resync which is unnecessary
893+
// given not-satisfiable error is terminal and most likely require intervention
894+
// from users/admins. Resyncing the namespace again is unlikely to resolve
895+
// not-satisfiable error
896+
if _, ok := err.(solver.NotSatisfiable); ok {
897+
logger.WithError(err).Debug("resolution failed")
898+
return nil
899+
}
889900
return err
890901
}
891902

pkg/controller/operators/catalog/operator_test.go

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
k8sfake "k8s.io/client-go/kubernetes/fake"
3838
"k8s.io/client-go/rest"
3939
"k8s.io/client-go/tools/cache"
40+
"k8s.io/client-go/tools/record"
4041
"k8s.io/client-go/util/workqueue"
4142
apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
4243
apiregistrationfake "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/fake"
@@ -49,6 +50,7 @@ import (
4950
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/grpc"
5051
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/reconciler"
5152
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver"
53+
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/solver"
5254
"github.com/operator-framework/operator-lifecycle-manager/pkg/fakes"
5355
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/clientfake"
5456
controllerclient "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/controller-runtime/client"
@@ -1052,6 +1054,163 @@ func TestSyncCatalogSources(t *testing.T) {
10521054
}
10531055
}
10541056

1057+
func TestSyncResolvingNamespace(t *testing.T) {
1058+
clockFake := utilclock.NewFakeClock(time.Date(2018, time.January, 26, 20, 40, 0, 0, time.UTC))
1059+
testNamespace := "testNamespace"
1060+
1061+
type fields struct {
1062+
clientOptions []clientfake.Option
1063+
sourcesLastUpdate metav1.Time
1064+
resolveErr error
1065+
existingOLMObjs []runtime.Object
1066+
existingObjects []runtime.Object
1067+
}
1068+
type args struct {
1069+
obj interface{}
1070+
}
1071+
tests := []struct {
1072+
name string
1073+
fields fields
1074+
wantErr error
1075+
}{
1076+
{
1077+
name: "NoError",
1078+
fields: fields{
1079+
clientOptions: []clientfake.Option{clientfake.WithSelfLinks(t)},
1080+
existingOLMObjs: []runtime.Object{
1081+
&v1alpha1.Subscription{
1082+
TypeMeta: metav1.TypeMeta{
1083+
Kind: v1alpha1.SubscriptionKind,
1084+
APIVersion: v1alpha1.SchemeGroupVersion.String(),
1085+
},
1086+
ObjectMeta: metav1.ObjectMeta{
1087+
Name: "sub",
1088+
Namespace: testNamespace,
1089+
},
1090+
Spec: &v1alpha1.SubscriptionSpec{
1091+
CatalogSource: "src",
1092+
CatalogSourceNamespace: testNamespace,
1093+
},
1094+
Status: v1alpha1.SubscriptionStatus{
1095+
CurrentCSV: "",
1096+
State: "",
1097+
},
1098+
},
1099+
},
1100+
},
1101+
},
1102+
{
1103+
name: "NotSatisfiableError",
1104+
fields: fields{
1105+
clientOptions: []clientfake.Option{clientfake.WithSelfLinks(t)},
1106+
existingOLMObjs: []runtime.Object{
1107+
&v1alpha1.Subscription{
1108+
TypeMeta: metav1.TypeMeta{
1109+
Kind: v1alpha1.SubscriptionKind,
1110+
APIVersion: v1alpha1.SchemeGroupVersion.String(),
1111+
},
1112+
ObjectMeta: metav1.ObjectMeta{
1113+
Name: "sub",
1114+
Namespace: testNamespace,
1115+
},
1116+
Spec: &v1alpha1.SubscriptionSpec{
1117+
CatalogSource: "src",
1118+
CatalogSourceNamespace: testNamespace,
1119+
},
1120+
Status: v1alpha1.SubscriptionStatus{
1121+
CurrentCSV: "",
1122+
State: "",
1123+
},
1124+
},
1125+
},
1126+
resolveErr: solver.NotSatisfiable{
1127+
{
1128+
Installable: resolver.NewSubscriptionInstallable("a", nil),
1129+
Constraint: resolver.PrettyConstraint(solver.Mandatory(), "something"),
1130+
},
1131+
},
1132+
},
1133+
},
1134+
{
1135+
name: "OtherError",
1136+
fields: fields{
1137+
clientOptions: []clientfake.Option{clientfake.WithSelfLinks(t)},
1138+
existingOLMObjs: []runtime.Object{
1139+
&v1alpha1.ClusterServiceVersion{
1140+
ObjectMeta: metav1.ObjectMeta{
1141+
Name: "csv.v.1",
1142+
Namespace: testNamespace,
1143+
},
1144+
Status: v1alpha1.ClusterServiceVersionStatus{
1145+
Phase: v1alpha1.CSVPhaseSucceeded,
1146+
},
1147+
},
1148+
&v1alpha1.Subscription{
1149+
TypeMeta: metav1.TypeMeta{
1150+
Kind: v1alpha1.SubscriptionKind,
1151+
APIVersion: v1alpha1.SchemeGroupVersion.String(),
1152+
},
1153+
ObjectMeta: metav1.ObjectMeta{
1154+
Name: "sub",
1155+
Namespace: testNamespace,
1156+
},
1157+
Spec: &v1alpha1.SubscriptionSpec{
1158+
CatalogSource: "src",
1159+
CatalogSourceNamespace: testNamespace,
1160+
},
1161+
Status: v1alpha1.SubscriptionStatus{
1162+
CurrentCSV: "",
1163+
State: "",
1164+
},
1165+
},
1166+
},
1167+
resolveErr: fmt.Errorf("some error"),
1168+
},
1169+
wantErr: fmt.Errorf("some error"),
1170+
},
1171+
}
1172+
for _, tt := range tests {
1173+
t.Run(tt.name, func(t *testing.T) {
1174+
// Create test operator
1175+
ctx, cancel := context.WithCancel(context.TODO())
1176+
defer cancel()
1177+
1178+
o, err := NewFakeOperator(ctx, testNamespace, []string{testNamespace}, withClock(clockFake), withClientObjs(tt.fields.existingOLMObjs...), withK8sObjs(tt.fields.existingObjects...), withFakeClientOptions(tt.fields.clientOptions...))
1179+
require.NoError(t, err)
1180+
1181+
o.reconciler = &fakes.FakeRegistryReconcilerFactory{
1182+
ReconcilerForSourceStub: func(source *v1alpha1.CatalogSource) reconciler.RegistryReconciler {
1183+
return &fakes.FakeRegistryReconciler{
1184+
EnsureRegistryServerStub: func(source *v1alpha1.CatalogSource) error {
1185+
return nil
1186+
},
1187+
}
1188+
},
1189+
}
1190+
1191+
o.sourcesLastUpdate.Set(tt.fields.sourcesLastUpdate.Time)
1192+
o.resolver = &fakes.FakeStepResolver{
1193+
ResolveStepsStub: func(string, resolver.SourceQuerier) ([]*v1alpha1.Step, []v1alpha1.BundleLookup, []*v1alpha1.Subscription, error) {
1194+
return nil, nil, nil, tt.fields.resolveErr
1195+
},
1196+
}
1197+
1198+
namespace := &corev1.Namespace{
1199+
ObjectMeta: metav1.ObjectMeta{
1200+
Name: testNamespace,
1201+
},
1202+
}
1203+
1204+
err = o.syncResolvingNamespace(namespace)
1205+
if tt.wantErr != nil {
1206+
require.Equal(t, tt.wantErr, err)
1207+
} else {
1208+
require.NoError(t, err)
1209+
}
1210+
})
1211+
}
1212+
}
1213+
10551214
func TestCompetingCRDOwnersExist(t *testing.T) {
10561215

10571216
testNamespace := "default"
@@ -1146,6 +1305,7 @@ type fakeOperatorConfig struct {
11461305
clientOptions []clientfake.Option
11471306
logger *logrus.Logger
11481307
resolver resolver.StepResolver
1308+
recorder record.EventRecorder
11491309
reconciler reconciler.RegistryReconcilerFactory
11501310
}
11511311

@@ -1201,6 +1361,7 @@ func NewFakeOperator(ctx context.Context, namespace string, namespaces []string,
12011361
logger: logrus.StandardLogger(),
12021362
clock: utilclock.RealClock{},
12031363
resolver: &fakes.FakeStepResolver{},
1364+
recorder: &record.FakeRecorder{},
12041365
}
12051366
for _, option := range fakeOptions {
12061367
option(config)
@@ -1286,6 +1447,7 @@ func NewFakeOperator(ctx context.Context, namespace string, namespaces []string,
12861447
), "resolver"),
12871448
resolver: config.resolver,
12881449
reconciler: config.reconciler,
1450+
recorder: config.recorder,
12891451
clientAttenuator: scoped.NewClientAttenuator(logger, &rest.Config{}, opClientFake, clientFake, dynamicClientFake),
12901452
serviceAccountQuerier: scoped.NewUserDefinedServiceAccountQuerier(logger, clientFake),
12911453
catsrcQueueSet: queueinformer.NewEmptyResourceQueueSet(),

0 commit comments

Comments
 (0)