Skip to content

Commit 05f5417

Browse files
committed
Addresses the bug where updating the packageserver fails with a unable to adopt APIService status message. This is a result of ownership mismatches between the packageserver (owner) and the APIService (the owned object). This can be resolved by setting the actual k8s owner references or the label-based references that OLM uses. This commit ensures that ownership references for the APIService are cleaned up on OLM restart and that the olm.owner:packageserver label is set on the corresponding APIService object.
1 parent 2b93a4b commit 05f5417

File tree

4 files changed

+181
-6
lines changed

4 files changed

+181
-6
lines changed

cmd/olm/cleanup.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,23 @@ package main
33
import (
44
"time"
55

6+
"k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset"
7+
68
"github.com/sirupsen/logrus"
79
rbacv1 "k8s.io/api/rbac/v1"
810
"k8s.io/apimachinery/pkg/api/errors"
11+
k8serrors "k8s.io/apimachinery/pkg/api/errors"
912
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1013
"k8s.io/apimachinery/pkg/types"
1114
"k8s.io/apimachinery/pkg/util/wait"
1215
"k8s.io/client-go/util/retry"
16+
apiregv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
1317

1418
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
1519
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned"
20+
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm"
1621
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
22+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
1723
)
1824

1925
const (
@@ -53,6 +59,10 @@ func cleanup(logger *logrus.Logger, c operatorclient.ClientInterface, crc versio
5359
if err := cleanupOwnerReferences(c, crc); err != nil {
5460
logger.WithError(err).Fatal("couldn't cleanup cross-namespace ownerreferences")
5561
}
62+
63+
if err := ensureAPIServiceLabels(c.ApiregistrationV1Interface()); err != nil {
64+
logger.WithError(err).Fatal("couldn't ensure APIService labels")
65+
}
5666
}
5767

5868
func waitForDelete(checkResource checkResourceFunc, deleteResource deleteResourceFunc) error {
@@ -171,6 +181,11 @@ func cleanupOwnerReferences(c operatorclient.ClientInterface, crc versioned.Inte
171181
objs = append(objs, &obj)
172182
}
173183

184+
apiServices, _ := c.ApiregistrationV1Interface().ApiregistrationV1().APIServices().List(listOpts)
185+
for _, obj := range apiServices.Items {
186+
objs = append(objs, &obj)
187+
}
188+
174189
for _, obj := range objs {
175190
if !removeBadRefs(obj) {
176191
continue
@@ -206,6 +221,11 @@ func cleanupOwnerReferences(c operatorclient.ClientInterface, crc versioned.Inte
206221
_, err = c.KubernetesInterface().RbacV1().RoleBindings(v.GetNamespace()).Update(v)
207222
return err
208223
}
224+
case *apiregv1.APIService:
225+
update = func() error {
226+
_, err = c.ApiregistrationV1Interface().ApiregistrationV1().APIServices().Update(v)
227+
return err
228+
}
209229
}
210230

211231
if err := retry.RetryOnConflict(retry.DefaultBackoff, update); err != nil {
@@ -239,3 +259,35 @@ func crossNamespaceOwnerReferenceRemoval(kind string, uidNamespaces map[types.UI
239259
return
240260
}
241261
}
262+
263+
// ensureAPIServiceLabels checks the labels of existing APIService objects to ensure ownership is set correctly.
264+
// If the APIService label does not correspond to the expected packageserver owner the APIService labels are updated
265+
func ensureAPIServiceLabels(c clientset.Interface) error {
266+
apiService, err := c.ApiregistrationV1().APIServices().Get(olm.PackageserverName, metav1.GetOptions{})
267+
if err != nil && !k8serrors.IsNotFound(err) {
268+
logrus.WithField("apiService", "packageserver").Debugf("get error: %s", err)
269+
return err
270+
}
271+
if apiService == nil || k8serrors.IsNotFound(err) || apiService.Name == "" {
272+
logrus.WithField("apiService", "packageserver").Debugf("not found")
273+
return nil
274+
}
275+
276+
if l := apiService.GetLabels(); l == nil {
277+
apiService.Labels = make(map[string]string)
278+
}
279+
280+
if apiService.Labels[ownerutil.OwnerKey] != ownerutil.OwnerPackageServer {
281+
apiService.Labels[ownerutil.OwnerKey] = ownerutil.OwnerPackageServer
282+
update := func() error {
283+
_, err := c.ApiregistrationV1().APIServices().Update(apiService)
284+
return err
285+
}
286+
if err := retry.RetryOnConflict(retry.DefaultBackoff, update); err != nil {
287+
logrus.WithField("apiService", apiService.Name).Warnf("update error: %s", err)
288+
return err
289+
}
290+
}
291+
292+
return nil
293+
}

cmd/olm/cleanup_test.go

Lines changed: 123 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ import (
1010
"k8s.io/apimachinery/pkg/runtime"
1111
"k8s.io/apimachinery/pkg/types"
1212
k8sfake "k8s.io/client-go/kubernetes/fake"
13+
apiregv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
1314
apiregistrationfake "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/fake"
1415

1516
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
1617
operatorsfake "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned/fake"
1718
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
19+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
1820
)
1921

2022
func TestCrossOwnerReferenceRemoval(t *testing.T) {
@@ -352,8 +354,9 @@ func TestCleanupOwnerReferences(t *testing.T) {
352354
clusterRoleKind := "ClusterRole"
353355

354356
type fields struct {
355-
k8sObjs []runtime.Object
356-
clientObjs []runtime.Object
357+
k8sObjs []runtime.Object
358+
clientObjs []runtime.Object
359+
apiServices []runtime.Object
357360
}
358361
type expected struct {
359362
err error
@@ -362,6 +365,7 @@ func TestCleanupOwnerReferences(t *testing.T) {
362365
clusterRoleBindings []rbacv1.ClusterRoleBinding
363366
roles []rbacv1.Role
364367
roleBindings []rbacv1.RoleBinding
368+
apiServices []apiregv1.APIService
365369
}
366370
tests := []struct {
367371
description string
@@ -381,6 +385,9 @@ func TestCleanupOwnerReferences(t *testing.T) {
381385
newClusterServiceVersion("ns-b", "csv-b", "csv-b-uid"),
382386
newClusterServiceVersion("ns-a", "csv-a", "csv-a-uid", newOwnerReference(v1alpha1.ClusterServiceVersionKind, "csv-b-uid")),
383387
},
388+
apiServices: []runtime.Object{
389+
newAPIService("apisvc", "apisvc-a-uid", nil, newOwnerReference(v1alpha1.ClusterServiceVersionKind, "csv-b-uid")),
390+
},
384391
},
385392
expected: expected{
386393
csvs: []v1alpha1.ClusterServiceVersion{
@@ -399,6 +406,9 @@ func TestCleanupOwnerReferences(t *testing.T) {
399406
roleBindings: []rbacv1.RoleBinding{
400407
*newRoleBinding("ns-a", "rb", "rb-a-uid"),
401408
},
409+
apiServices: []apiregv1.APIService{
410+
*newAPIService("apisvc", "apisvc-a-uid", nil),
411+
},
402412
},
403413
},
404414
{
@@ -462,7 +472,7 @@ func TestCleanupOwnerReferences(t *testing.T) {
462472
for _, tt := range tests {
463473
t.Run(tt.description, func(t *testing.T) {
464474
k8sClient := k8sfake.NewSimpleClientset(tt.fields.k8sObjs...)
465-
c := operatorclient.NewClient(k8sClient, apiextensionsfake.NewSimpleClientset(), apiregistrationfake.NewSimpleClientset())
475+
c := operatorclient.NewClient(k8sClient, apiextensionsfake.NewSimpleClientset(), apiregistrationfake.NewSimpleClientset(tt.fields.apiServices...))
466476
crc := operatorsfake.NewSimpleClientset(tt.fields.clientObjs...)
467477
require.Equal(t, tt.expected.err, cleanupOwnerReferences(c, crc))
468478

@@ -486,10 +496,111 @@ func TestCleanupOwnerReferences(t *testing.T) {
486496
roleBindings, err := c.KubernetesInterface().RbacV1().RoleBindings(metav1.NamespaceAll).List(listOpts)
487497
require.NoError(t, err)
488498
require.ElementsMatch(t, tt.expected.roleBindings, roleBindings.Items)
499+
500+
apiService, err := c.ApiregistrationV1Interface().ApiregistrationV1().APIServices().List(listOpts)
501+
require.NoError(t, err)
502+
require.ElementsMatch(t, tt.expected.apiServices, apiService.Items)
489503
})
490504
}
491505
}
492506

507+
func TestCheckAPIServiceLabels(t *testing.T) {
508+
type fields struct {
509+
apiServices []runtime.Object
510+
}
511+
512+
type expected struct {
513+
err error
514+
apiServices []apiregv1.APIService
515+
}
516+
517+
tests := []struct {
518+
description string
519+
fields fields
520+
expected expected
521+
}{
522+
{
523+
description: "NoLabel/UpdateAPIService",
524+
fields: fields{
525+
apiServices: []runtime.Object{
526+
newAPIService("v1.packages.operators.coreos.com", "apisvc-a-uid", map[string]string{}),
527+
},
528+
},
529+
expected: expected{
530+
apiServices: []apiregv1.APIService{
531+
*newAPIService("v1.packages.operators.coreos.com", "apisvc-a-uid", map[string]string{ownerutil.OwnerKey: ownerutil.OwnerPackageServer}),
532+
},
533+
},
534+
},
535+
{
536+
description: "WrongLabel/UpdateAPIService",
537+
fields: fields{
538+
apiServices: []runtime.Object{
539+
newAPIService("v1.packages.operators.coreos.com", "apisvc-a-uid", map[string]string{ownerutil.OwnerKey: "banana"}),
540+
},
541+
},
542+
expected: expected{
543+
apiServices: []apiregv1.APIService{
544+
*newAPIService("v1.packages.operators.coreos.com", "apisvc-a-uid", map[string]string{ownerutil.OwnerKey: ownerutil.OwnerPackageServer}),
545+
},
546+
},
547+
},
548+
{
549+
description: "CorrectLabel/NoUpdate",
550+
fields: fields{
551+
apiServices: []runtime.Object{
552+
newAPIService("v1.packages.operators.coreos.com", "apisvc-a-uid", map[string]string{ownerutil.OwnerKey: ownerutil.OwnerPackageServer}),
553+
},
554+
},
555+
expected: expected{
556+
apiServices: []apiregv1.APIService{
557+
*newAPIService("v1.packages.operators.coreos.com", "apisvc-a-uid", map[string]string{ownerutil.OwnerKey: ownerutil.OwnerPackageServer}),
558+
},
559+
},
560+
},
561+
{
562+
description: "WrongAPIService/NoUpdate",
563+
fields: fields{
564+
apiServices: []runtime.Object{
565+
newAPIService("banana", "apisvc-a-uid", map[string]string{ownerutil.OwnerKey: "banana"}),
566+
},
567+
},
568+
expected: expected{
569+
apiServices: []apiregv1.APIService{
570+
*newAPIService("banana", "apisvc-a-uid", map[string]string{ownerutil.OwnerKey: "banana"}),
571+
},
572+
},
573+
},
574+
{
575+
description: "NoLabels/Update",
576+
fields: fields{
577+
apiServices: []runtime.Object{
578+
newAPIService("v1.packages.operators.coreos.com", "apisvc-a-uid", nil),
579+
},
580+
},
581+
expected: expected{
582+
apiServices: []apiregv1.APIService{
583+
*newAPIService("v1.packages.operators.coreos.com", "apisvc-a-uid", map[string]string{ownerutil.OwnerKey: ownerutil.OwnerPackageServer}),
584+
},
585+
},
586+
},
587+
}
588+
589+
for _, tt := range tests {
590+
t.Run(tt.description, func(t *testing.T) {
591+
listOpts := metav1.ListOptions{}
592+
k8sClient := k8sfake.NewSimpleClientset()
593+
c := operatorclient.NewClient(k8sClient, apiextensionsfake.NewSimpleClientset(), apiregistrationfake.NewSimpleClientset(tt.fields.apiServices...))
594+
require.Equal(t, tt.expected.err, ensureAPIServiceLabels(c.ApiregistrationV1Interface()))
595+
596+
apiService, err := c.ApiregistrationV1Interface().ApiregistrationV1().APIServices().List(listOpts)
597+
require.NoError(t, err)
598+
require.ElementsMatch(t, tt.expected.apiServices, apiService.Items)
599+
})
600+
}
601+
602+
}
603+
493604
func newOwnerReference(kind string, uid types.UID) metav1.OwnerReference {
494605
return metav1.OwnerReference{
495606
Kind: kind,
@@ -540,3 +651,12 @@ func newRoleBinding(namespace, name string, uid types.UID, ownerRefs ...metav1.O
540651
roleBinding.SetOwnerReferences(ownerRefs)
541652
return roleBinding
542653
}
654+
655+
func newAPIService(name string, uid types.UID, labels map[string]string, ownerRefs ...metav1.OwnerReference) *apiregv1.APIService {
656+
apiService := &apiregv1.APIService{}
657+
apiService.SetUID(uid)
658+
apiService.SetName(name)
659+
apiService.SetOwnerReferences(ownerRefs)
660+
apiService.SetLabels(labels)
661+
return apiService
662+
}

pkg/controller/operators/olm/apiservices.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ const (
3232
OLMCAHashAnnotationKey = "olmcahash"
3333
// Organization is the organization name used in the generation of x509 certs
3434
Organization = "Red Hat, Inc."
35+
// Name of packageserver API service
36+
PackageserverName = "v1.packages.operators.coreos.com"
3537
)
3638

3739
func (a *Operator) shouldRotateCerts(csv *v1alpha1.ClusterServiceVersion) bool {

pkg/lib/ownerutil/util.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ import (
1616
)
1717

1818
const (
19-
OwnerKey = "olm.owner"
20-
OwnerNamespaceKey = "olm.owner.namespace"
21-
OwnerKind = "olm.owner.kind"
19+
OwnerKey = "olm.owner"
20+
OwnerNamespaceKey = "olm.owner.namespace"
21+
OwnerKind = "olm.owner.kind"
22+
OwnerPackageServer = "packageserver"
2223
)
2324

2425
var (

0 commit comments

Comments
 (0)