Skip to content

Commit 76754c9

Browse files
Merge pull request #1114 from jpeeler/fix-status-invalid-csv
Bug 1767004: defer provided api update in operator groups
2 parents 16619cd + ad49491 commit 76754c9

File tree

4 files changed

+155
-8
lines changed

4 files changed

+155
-8
lines changed

pkg/controller/operators/olm/operator.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,6 +1252,12 @@ func (a *Operator) transitionCSVState(in v1alpha1.ClusterServiceVersion) (out *v
12521252
if unionedAnnotations == nil {
12531253
unionedAnnotations = make(map[string]string)
12541254
}
1255+
if unionedAnnotations[v1.OperatorGroupProvidedAPIsAnnotationKey] == union.String() {
1256+
// resolver may think apis need adding with invalid input, so continue when there's no work
1257+
// to be done so that the CSV can progress far enough to get requirements checked
1258+
a.logger.Debug("operator group annotations up to date, continuing")
1259+
break
1260+
}
12551261
unionedAnnotations[v1.OperatorGroupProvidedAPIsAnnotationKey] = union.String()
12561262
operatorGroup.SetAnnotations(unionedAnnotations)
12571263
if _, err := a.client.OperatorsV1().OperatorGroups(operatorGroup.GetNamespace()).Update(operatorGroup); err != nil && !k8serrors.IsNotFound(err) {

pkg/controller/operators/olm/operator_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4264,7 +4264,7 @@ func TestSyncOperatorGroups(t *testing.T) {
42644264
require.NoError(t, err)
42654265

42664266
// Sync csvs enough to get them back to succeeded state
4267-
for i := 0; i < 8; i++ {
4267+
for i := 0; i < 16; i++ {
42684268
opGroupCSVs, err := op.client.OperatorsV1alpha1().ClusterServiceVersions(operatorNamespace).List(metav1.ListOptions{})
42694269
require.NoError(t, err)
42704270

@@ -4292,7 +4292,7 @@ func TestSyncOperatorGroups(t *testing.T) {
42924292
require.NoError(t, err)
42934293
}
42944294

4295-
if i == 8 {
4295+
if i == 16 {
42964296
err = wait.PollImmediate(1*time.Millisecond, 10*time.Second, func() (bool, error) {
42974297
for namespace, objects := range tt.final.objects {
42984298
if err := RequireObjectsInCache(t, op.lister, namespace, objects, true); err != nil {

pkg/controller/operators/olm/operatorgroup.go

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
2020
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver"
2121
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
22+
opregistry "github.com/operator-framework/operator-registry/pkg/registry"
2223
)
2324

2425
const (
@@ -126,7 +127,13 @@ func (a *Operator) syncOperatorGroups(obj interface{}) error {
126127
groupSurface := resolver.NewOperatorGroup(op)
127128
groupProvidedAPIs := groupSurface.ProvidedAPIs()
128129
providedAPIsForCSVs := a.providedAPIsFromCSVs(op, logger)
129-
providedAPIsForGroup := providedAPIsForCSVs.Union(groupProvidedAPIs)
130+
providedAPIsForGroup := make(resolver.APISet)
131+
for api := range providedAPIsForCSVs {
132+
providedAPIsForGroup[api] = struct{}{}
133+
}
134+
for api := range groupProvidedAPIs {
135+
providedAPIsForGroup[api] = struct{}{}
136+
}
130137

131138
csvs, err := a.findCSVsThatProvideAnyOf(providedAPIsForGroup)
132139
if err != nil {
@@ -212,9 +219,9 @@ func (a *Operator) annotateCSVs(group *v1.OperatorGroup, targetNamespaces []stri
212219
return errors.NewAggregate(updateErrs)
213220
}
214221

215-
func (a *Operator) providedAPIsFromCSVs(group *v1.OperatorGroup, logger *logrus.Entry) resolver.APISet {
222+
func (a *Operator) providedAPIsFromCSVs(group *v1.OperatorGroup, logger *logrus.Entry) map[opregistry.APIKey]*v1alpha1.ClusterServiceVersion {
216223
set := a.csvSet(group.Namespace, v1alpha1.CSVPhaseAny)
217-
providedAPIsFromCSVs := make(resolver.APISet)
224+
providedAPIsFromCSVs := make(map[opregistry.APIKey]*v1alpha1.ClusterServiceVersion)
218225
for _, csv := range set {
219226
// Don't union providedAPIsFromCSVs if the CSV is copied (member of another OperatorGroup)
220227
if csv.IsCopied() {
@@ -230,20 +237,40 @@ func (a *Operator) providedAPIsFromCSVs(group *v1.OperatorGroup, logger *logrus.
230237
logger.WithError(err).Warn("could not create OperatorSurface from csv")
231238
continue
232239
}
233-
providedAPIsFromCSVs = providedAPIsFromCSVs.Union(operatorSurface.ProvidedAPIs().StripPlural())
240+
for providedAPI := range operatorSurface.ProvidedAPIs().StripPlural() {
241+
providedAPIsFromCSVs[providedAPI] = csv
242+
}
234243
}
235244
return providedAPIsFromCSVs
236245
}
237246

238-
func (a *Operator) pruneProvidedAPIs(group *v1.OperatorGroup, groupProvidedAPIs, providedAPIsFromCSVs resolver.APISet, logger *logrus.Entry) {
247+
func (a *Operator) pruneProvidedAPIs(group *v1.OperatorGroup, groupProvidedAPIs resolver.APISet, providedAPIsFromCSVs map[opregistry.APIKey]*v1alpha1.ClusterServiceVersion, logger *logrus.Entry) {
239248
// Don't prune providedAPIsFromCSVs if static
240249
if group.Spec.StaticProvidedAPIs {
241250
a.logger.Debug("group has static provided apis. skipping provided api pruning")
242251
return
243252
}
244253

254+
intersection := make(resolver.APISet)
255+
for api := range providedAPIsFromCSVs {
256+
if _, ok := groupProvidedAPIs[api]; ok {
257+
intersection[api] = struct{}{}
258+
} else {
259+
csv := providedAPIsFromCSVs[api]
260+
_, err := a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(csv.GetNamespace()).Get(csv.GetName())
261+
if k8serrors.IsNotFound(err) {
262+
continue
263+
}
264+
if csv.DeletionTimestamp == nil && (csv.Status.Phase == v1alpha1.CSVPhaseNone || csv.Status.Phase == v1alpha1.CSVPhasePending) {
265+
logger.Debugf("aborting operator group provided API update due to CSV %v in phase %v", csv.GetName(), csv.Status.Phase)
266+
return
267+
}
268+
}
269+
}
270+
245271
// Prune providedAPIs annotation if the cluster has fewer providedAPIs (handles CSV deletion)
246-
if intersection := groupProvidedAPIs.Intersection(providedAPIsFromCSVs); len(intersection) < len(groupProvidedAPIs) {
272+
//if intersection := groupProvidedAPIs.Intersection(providedAPIsFromCSVs); len(intersection) < len(groupProvidedAPIs) {
273+
if len(intersection) < len(groupProvidedAPIs) {
247274
difference := groupProvidedAPIs.Difference(intersection)
248275
logger := logger.WithFields(logrus.Fields{
249276
"providedAPIsOnCluster": providedAPIsFromCSVs,

test/e2e/csv_e2e_test.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3212,3 +3212,117 @@ func TestCreateCSVRequirementsEvents(t *testing.T) {
32123212
}
32133213

32143214
// TODO: test behavior when replaces field doesn't point to existing CSV
3215+
3216+
func TestCSVStatusInvalidCSV(t *testing.T) {
3217+
defer cleaner.NotifyTestComplete(t, true)
3218+
3219+
c := newKubeClient(t)
3220+
crc := newCRClient(t)
3221+
3222+
// Create CRD
3223+
crdPlural := genName("ins")
3224+
crdName := crdPlural + ".cluster.com"
3225+
cleanupCRD, err := createCRD(c, apiextensions.CustomResourceDefinition{
3226+
ObjectMeta: metav1.ObjectMeta{
3227+
Name: crdName,
3228+
},
3229+
Spec: apiextensions.CustomResourceDefinitionSpec{
3230+
Group: "cluster.com",
3231+
Version: "v1alpha1",
3232+
Names: apiextensions.CustomResourceDefinitionNames{
3233+
Plural: crdPlural,
3234+
Singular: crdPlural,
3235+
Kind: crdPlural,
3236+
ListKind: "list" + crdPlural,
3237+
},
3238+
Scope: "Namespaced",
3239+
},
3240+
})
3241+
require.NoError(t, err)
3242+
defer cleanupCRD()
3243+
3244+
// create CSV
3245+
strategy := install.StrategyDetailsDeployment{
3246+
DeploymentSpecs: []install.StrategyDeploymentSpec{
3247+
{
3248+
Name: genName("dep-"),
3249+
Spec: newNginxDeployment(genName("nginx-")),
3250+
},
3251+
},
3252+
}
3253+
strategyRaw, err := json.Marshal(strategy)
3254+
require.NoError(t, err)
3255+
3256+
csv := v1alpha1.ClusterServiceVersion{
3257+
TypeMeta: metav1.TypeMeta{
3258+
Kind: v1alpha1.ClusterServiceVersionKind,
3259+
APIVersion: v1alpha1.ClusterServiceVersionAPIVersion,
3260+
},
3261+
ObjectMeta: metav1.ObjectMeta{
3262+
Name: genName("csv"),
3263+
},
3264+
Spec: v1alpha1.ClusterServiceVersionSpec{
3265+
InstallModes: []v1alpha1.InstallMode{
3266+
{
3267+
Type: v1alpha1.InstallModeTypeOwnNamespace,
3268+
Supported: true,
3269+
},
3270+
{
3271+
Type: v1alpha1.InstallModeTypeSingleNamespace,
3272+
Supported: true,
3273+
},
3274+
{
3275+
Type: v1alpha1.InstallModeTypeMultiNamespace,
3276+
Supported: true,
3277+
},
3278+
{
3279+
Type: v1alpha1.InstallModeTypeAllNamespaces,
3280+
Supported: true,
3281+
},
3282+
},
3283+
InstallStrategy: v1alpha1.NamedInstallStrategy{
3284+
StrategyName: install.InstallStrategyNameDeployment,
3285+
StrategySpecRaw: strategyRaw,
3286+
},
3287+
CustomResourceDefinitions: v1alpha1.CustomResourceDefinitions{
3288+
Owned: []v1alpha1.CRDDescription{
3289+
{
3290+
Name: crdName,
3291+
Version: "apiextensions.k8s.io/v1alpha1", // purposely invalid, should be just v1alpha1 to match CRD
3292+
Kind: crdPlural,
3293+
DisplayName: crdName,
3294+
Description: "In the cluster2",
3295+
},
3296+
},
3297+
},
3298+
},
3299+
}
3300+
3301+
cleanupCSV, err := createCSV(t, c, crc, csv, testNamespace, true, false)
3302+
require.NoError(t, err)
3303+
defer cleanupCSV()
3304+
3305+
notServedStatus := v1alpha1.RequirementStatus{
3306+
Group: "apiextensions.k8s.io",
3307+
Version: "v1beta1",
3308+
Kind: "CustomResourceDefinition",
3309+
Name: crdName,
3310+
Status: v1alpha1.RequirementStatusReasonNotPresent,
3311+
Message: "CRD version not served",
3312+
}
3313+
csvCheckPhaseAndRequirementStatus := func(csv *v1alpha1.ClusterServiceVersion) bool {
3314+
if csv.Status.Phase == v1alpha1.CSVPhasePending {
3315+
for _, status := range csv.Status.RequirementStatus {
3316+
if status.Message == notServedStatus.Message {
3317+
return true
3318+
}
3319+
}
3320+
}
3321+
return false
3322+
}
3323+
3324+
fetchedCSV, err := fetchCSV(t, crc, csv.Name, testNamespace, csvCheckPhaseAndRequirementStatus)
3325+
require.NoError(t, err)
3326+
3327+
require.Contains(t, fetchedCSV.Status.RequirementStatus, notServedStatus)
3328+
}

0 commit comments

Comments
 (0)