Skip to content

Commit b31c4ba

Browse files
Merge pull request #2189 from openshift-cherrypick-robot/cherry-pick-2107-to-release-4.6
[release-4.6] Bug 1969412: fix(catalog): Reduce namespace resync in resolution failure
2 parents bf72c5d + 65ac3f0 commit b31c4ba

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"
@@ -884,6 +885,16 @@ func (o *Operator) syncResolvingNamespace(obj interface{}) error {
884885
steps, bundleLookups, updatedSubs, err := o.resolver.ResolveSteps(namespace, querier)
885886
if err != nil {
886887
go o.recorder.Event(ns, corev1.EventTypeWarning, "ResolutionFailed", err.Error())
888+
// If the error is constraints not satisfiable, then simply project the
889+
// resolution failure event and move on without returning the error.
890+
// Returning the error only triggers the namespace resync which is unnecessary
891+
// given not-satisfiable error is terminal and most likely require intervention
892+
// from users/admins. Resyncing the namespace again is unlikely to resolve
893+
// not-satisfiable error
894+
if _, ok := err.(solver.NotSatisfiable); ok {
895+
logger.WithError(err).Debug("resolution failed")
896+
return nil
897+
}
887898
return err
888899
}
889900

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"
@@ -48,6 +49,7 @@ import (
4849
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/grpc"
4950
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/reconciler"
5051
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver"
52+
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/solver"
5153
"github.com/operator-framework/operator-lifecycle-manager/pkg/fakes"
5254
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/clientfake"
5355
controllerclient "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/controller-runtime/client"
@@ -1008,6 +1010,163 @@ func TestSyncCatalogSources(t *testing.T) {
10081010
}
10091011
}
10101012

1013+
func TestSyncResolvingNamespace(t *testing.T) {
1014+
clockFake := utilclock.NewFakeClock(time.Date(2018, time.January, 26, 20, 40, 0, 0, time.UTC))
1015+
testNamespace := "testNamespace"
1016+
1017+
type fields struct {
1018+
clientOptions []clientfake.Option
1019+
sourcesLastUpdate metav1.Time
1020+
resolveErr error
1021+
existingOLMObjs []runtime.Object
1022+
existingObjects []runtime.Object
1023+
}
1024+
type args struct {
1025+
obj interface{}
1026+
}
1027+
tests := []struct {
1028+
name string
1029+
fields fields
1030+
wantErr error
1031+
}{
1032+
{
1033+
name: "NoError",
1034+
fields: fields{
1035+
clientOptions: []clientfake.Option{clientfake.WithSelfLinks(t)},
1036+
existingOLMObjs: []runtime.Object{
1037+
&v1alpha1.Subscription{
1038+
TypeMeta: metav1.TypeMeta{
1039+
Kind: v1alpha1.SubscriptionKind,
1040+
APIVersion: v1alpha1.SchemeGroupVersion.String(),
1041+
},
1042+
ObjectMeta: metav1.ObjectMeta{
1043+
Name: "sub",
1044+
Namespace: testNamespace,
1045+
},
1046+
Spec: &v1alpha1.SubscriptionSpec{
1047+
CatalogSource: "src",
1048+
CatalogSourceNamespace: testNamespace,
1049+
},
1050+
Status: v1alpha1.SubscriptionStatus{
1051+
CurrentCSV: "",
1052+
State: "",
1053+
},
1054+
},
1055+
},
1056+
},
1057+
},
1058+
{
1059+
name: "NotSatisfiableError",
1060+
fields: fields{
1061+
clientOptions: []clientfake.Option{clientfake.WithSelfLinks(t)},
1062+
existingOLMObjs: []runtime.Object{
1063+
&v1alpha1.Subscription{
1064+
TypeMeta: metav1.TypeMeta{
1065+
Kind: v1alpha1.SubscriptionKind,
1066+
APIVersion: v1alpha1.SchemeGroupVersion.String(),
1067+
},
1068+
ObjectMeta: metav1.ObjectMeta{
1069+
Name: "sub",
1070+
Namespace: testNamespace,
1071+
},
1072+
Spec: &v1alpha1.SubscriptionSpec{
1073+
CatalogSource: "src",
1074+
CatalogSourceNamespace: testNamespace,
1075+
},
1076+
Status: v1alpha1.SubscriptionStatus{
1077+
CurrentCSV: "",
1078+
State: "",
1079+
},
1080+
},
1081+
},
1082+
resolveErr: solver.NotSatisfiable{
1083+
{
1084+
Installable: resolver.NewSubscriptionInstallable("a", nil),
1085+
Constraint: resolver.PrettyConstraint(solver.Mandatory(), "something"),
1086+
},
1087+
},
1088+
},
1089+
},
1090+
{
1091+
name: "OtherError",
1092+
fields: fields{
1093+
clientOptions: []clientfake.Option{clientfake.WithSelfLinks(t)},
1094+
existingOLMObjs: []runtime.Object{
1095+
&v1alpha1.ClusterServiceVersion{
1096+
ObjectMeta: metav1.ObjectMeta{
1097+
Name: "csv.v.1",
1098+
Namespace: testNamespace,
1099+
},
1100+
Status: v1alpha1.ClusterServiceVersionStatus{
1101+
Phase: v1alpha1.CSVPhaseSucceeded,
1102+
},
1103+
},
1104+
&v1alpha1.Subscription{
1105+
TypeMeta: metav1.TypeMeta{
1106+
Kind: v1alpha1.SubscriptionKind,
1107+
APIVersion: v1alpha1.SchemeGroupVersion.String(),
1108+
},
1109+
ObjectMeta: metav1.ObjectMeta{
1110+
Name: "sub",
1111+
Namespace: testNamespace,
1112+
},
1113+
Spec: &v1alpha1.SubscriptionSpec{
1114+
CatalogSource: "src",
1115+
CatalogSourceNamespace: testNamespace,
1116+
},
1117+
Status: v1alpha1.SubscriptionStatus{
1118+
CurrentCSV: "",
1119+
State: "",
1120+
},
1121+
},
1122+
},
1123+
resolveErr: fmt.Errorf("some error"),
1124+
},
1125+
wantErr: fmt.Errorf("some error"),
1126+
},
1127+
}
1128+
for _, tt := range tests {
1129+
t.Run(tt.name, func(t *testing.T) {
1130+
// Create test operator
1131+
ctx, cancel := context.WithCancel(context.TODO())
1132+
defer cancel()
1133+
1134+
o, err := NewFakeOperator(ctx, testNamespace, []string{testNamespace}, withClock(clockFake), withClientObjs(tt.fields.existingOLMObjs...), withK8sObjs(tt.fields.existingObjects...), withFakeClientOptions(tt.fields.clientOptions...))
1135+
require.NoError(t, err)
1136+
1137+
o.reconciler = &fakes.FakeRegistryReconcilerFactory{
1138+
ReconcilerForSourceStub: func(source *v1alpha1.CatalogSource) reconciler.RegistryReconciler {
1139+
return &fakes.FakeRegistryReconciler{
1140+
EnsureRegistryServerStub: func(source *v1alpha1.CatalogSource) error {
1141+
return nil
1142+
},
1143+
}
1144+
},
1145+
}
1146+
1147+
o.sourcesLastUpdate.Set(tt.fields.sourcesLastUpdate.Time)
1148+
o.resolver = &fakes.FakeStepResolver{
1149+
ResolveStepsStub: func(string, resolver.SourceQuerier) ([]*v1alpha1.Step, []v1alpha1.BundleLookup, []*v1alpha1.Subscription, error) {
1150+
return nil, nil, nil, tt.fields.resolveErr
1151+
},
1152+
}
1153+
1154+
namespace := &corev1.Namespace{
1155+
ObjectMeta: metav1.ObjectMeta{
1156+
Name: testNamespace,
1157+
},
1158+
}
1159+
1160+
err = o.syncResolvingNamespace(namespace)
1161+
if tt.wantErr != nil {
1162+
require.Equal(t, tt.wantErr, err)
1163+
} else {
1164+
require.NoError(t, err)
1165+
}
1166+
})
1167+
}
1168+
}
1169+
10111170
func TestCompetingCRDOwnersExist(t *testing.T) {
10121171

10131172
testNamespace := "default"
@@ -1102,6 +1261,7 @@ type fakeOperatorConfig struct {
11021261
clientOptions []clientfake.Option
11031262
logger *logrus.Logger
11041263
resolver resolver.StepResolver
1264+
recorder record.EventRecorder
11051265
reconciler reconciler.RegistryReconcilerFactory
11061266
}
11071267

@@ -1157,6 +1317,7 @@ func NewFakeOperator(ctx context.Context, namespace string, namespaces []string,
11571317
logger: logrus.StandardLogger(),
11581318
clock: utilclock.RealClock{},
11591319
resolver: &fakes.FakeStepResolver{},
1320+
recorder: &record.FakeRecorder{},
11601321
}
11611322
for _, option := range fakeOptions {
11621323
option(config)
@@ -1242,6 +1403,7 @@ func NewFakeOperator(ctx context.Context, namespace string, namespaces []string,
12421403
), "resolver"),
12431404
resolver: config.resolver,
12441405
reconciler: config.reconciler,
1406+
recorder: config.recorder,
12451407
clientAttenuator: scoped.NewClientAttenuator(logger, &rest.Config{}, opClientFake, clientFake, dynamicClientFake),
12461408
serviceAccountQuerier: scoped.NewUserDefinedServiceAccountQuerier(logger, clientFake),
12471409
catsrcQueueSet: queueinformer.NewEmptyResourceQueueSet(),

0 commit comments

Comments
 (0)