Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OLM support for the Upgradeable OperatorCondition and Admin overrides #1885

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -25,7 +25,7 @@ require (
github.com/onsi/gomega v1.9.0
github.com/openshift/api v0.0.0-20200331152225-585af27e34fd
github.com/openshift/client-go v0.0.0-20200326155132-2a6cd50aedd0
github.com/operator-framework/api v0.3.23
github.com/operator-framework/api v0.3.25
github.com/operator-framework/operator-registry v1.13.6
github.com/otiai10/copy v1.2.0
github.com/pkg/errors v0.9.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Expand Up @@ -756,8 +756,8 @@ github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJ
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/operator-framework/api v0.3.7-0.20200602203552-431198de9fc2/go.mod h1:Xbje9x0SHmh0nihE21kpesB38vk3cyxnE6JdDS8Jo1Q=
github.com/operator-framework/api v0.3.23 h1:+QiJ0m5SjfV+iMv8uzuGac+PoJFlTaMFIN8eVccUnoY=
github.com/operator-framework/api v0.3.23/go.mod h1:GVNiB6AQucwdZz3ZFXNv9HtcLOzcFnr6O/QldzKG93g=
github.com/operator-framework/api v0.3.25 h1:d6WgHCshCffT37okVZeL+IbGlhrsHy57xdfMnopC8rI=
github.com/operator-framework/api v0.3.25/go.mod h1:GVNiB6AQucwdZz3ZFXNv9HtcLOzcFnr6O/QldzKG93g=
github.com/operator-framework/operator-registry v1.13.6 h1:h/dIjQQS7uneQNRifrSz7h0xg4Xyjg6C9f6XZofbMPg=
github.com/operator-framework/operator-registry v1.13.6/go.mod h1:YhnIzOVjRU2ZwZtzt+fjcjW8ujJaSFynBEu7QVKaSdU=
github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k=
Expand Down
25 changes: 25 additions & 0 deletions pkg/controller/operators/olm/operator.go
Expand Up @@ -220,6 +220,22 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat
return nil, err
}

// Register OperatorCondition QueueInformer
opConditionInformer := extInformerFactory.Operators().V1().OperatorConditions()
op.lister.OperatorsV1().RegisterOperatorConditionLister(namespace, opConditionInformer.Lister())
opConditionQueueInformer, err := queueinformer.NewQueueInformer(
ctx,
queueinformer.WithLogger(op.logger),
queueinformer.WithInformer(opConditionInformer.Informer()),
queueinformer.WithSyncer(k8sSyncer),
)
if err != nil {
return nil, err
}
if err := op.RegisterQueueInformer(opConditionQueueInformer); err != nil {
return nil, err
}

subInformer := extInformerFactory.Operators().V1alpha1().Subscriptions()
op.lister.OperatorsV1alpha1().RegisterSubscriptionLister(namespace, subInformer.Lister())
subQueueInformer, err := queueinformer.NewQueueInformer(
Expand Down Expand Up @@ -1426,6 +1442,15 @@ func (a *Operator) transitionCSVState(in v1alpha1.ClusterServiceVersion) (out *v
logger.Info("scheduling ClusterServiceVersion for requirement verification")
out.SetPhaseWithEvent(v1alpha1.CSVPhasePending, v1alpha1.CSVReasonRequirementsUnknown, "requirements not yet checked", now, a.recorder)
case v1alpha1.CSVPhasePending:
// Check previous version's Upgradeable condition
replacedCSV := a.isReplacing(out)
if replacedCSV != nil {
operatorUpgradeable, condErr := a.isOperatorUpgradeable(replacedCSV)
if !operatorUpgradeable {
out.SetPhaseWithEventIfChanged(v1alpha1.CSVPhasePending, v1alpha1.CSVReasonOperatorConditionNotUpgradeable, fmt.Sprintf("operator is not upgradeable: %s", condErr), now, a.recorder)
return
}
}
met, statuses, err := a.requirementAndPermissionStatus(out)
if err != nil {
// TODO: account for Bad Rule as well
Expand Down
45 changes: 45 additions & 0 deletions pkg/controller/operators/olm/operatorconditions.go
@@ -0,0 +1,45 @@
package olm

import (
"fmt"

k8serrors "k8s.io/apimachinery/pkg/api/errors"
meta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

operatorsv1 "github.com/operator-framework/api/pkg/operators/v1"
"github.com/operator-framework/api/pkg/operators/v1alpha1"
)

func (a *Operator) isOperatorUpgradeable(csv *v1alpha1.ClusterServiceVersion) (bool, error) {
if csv == nil {
return false, fmt.Errorf("CSV is invalid")
}

cond, err := a.lister.OperatorsV1().OperatorConditionLister().OperatorConditions(csv.GetNamespace()).Get(csv.GetName())
if err != nil {
if k8serrors.IsNotFound(err) {
return true, nil
}
return false, err
}

// Check condition overrides
for _, override := range cond.Spec.Overrides {
ecordell marked this conversation as resolved.
Show resolved Hide resolved
if override.Type == operatorsv1.OperatorUpgradeable {
if override.Status == metav1.ConditionTrue {
return true, nil
}
return false, fmt.Errorf("The operator is not upgradeable: %s", override.Message)
}
}

// Check for OperatorUpgradeable condition status
if c := meta.FindStatusCondition(cond.Status.Conditions, operatorsv1.OperatorUpgradeable); c != nil {
if c.Status == metav1.ConditionFalse {
return false, fmt.Errorf("The operator is not upgradeable: %s", c.Message)
}
}

return true, nil
}
8 changes: 6 additions & 2 deletions pkg/lib/operatorlister/lister.go
Expand Up @@ -102,8 +102,10 @@ type OperatorsV1alpha1Lister interface {
//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . OperatorsV1Lister
type OperatorsV1Lister interface {
RegisterOperatorGroupLister(namespace string, lister v1.OperatorGroupLister)
RegisterOperatorConditionLister(namespace string, lister v1.OperatorConditionLister)

OperatorGroupLister() v1.OperatorGroupLister
OperatorConditionLister() v1.OperatorConditionLister
}

type appsV1Lister struct {
Expand Down Expand Up @@ -189,12 +191,14 @@ func newOperatorsV1alpha1Lister() *operatorsV1alpha1Lister {
}

type operatorsV1Lister struct {
operatorGroupLister *UnionOperatorGroupLister
operatorGroupLister *UnionOperatorGroupLister
operatorConditionLister *UnionOperatorConditionLister
}

func newOperatorsV1Lister() *operatorsV1Lister {
return &operatorsV1Lister{
operatorGroupLister: &UnionOperatorGroupLister{},
operatorGroupLister: &UnionOperatorGroupLister{},
operatorConditionLister: &UnionOperatorConditionLister{},
}
}

Expand Down
96 changes: 96 additions & 0 deletions pkg/lib/operatorlister/operatorcondition.go
@@ -0,0 +1,96 @@
package operatorlister

import (
"fmt"
"sync"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"

v1 "github.com/operator-framework/api/pkg/operators/v1"
listers "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/listers/operators/v1"
)

type UnionOperatorConditionLister struct {
opConditionListers map[string]listers.OperatorConditionLister
opConditionLock sync.RWMutex
}

// List lists all OperatorConditions in the indexer.
func (uol *UnionOperatorConditionLister) List(selector labels.Selector) (ret []*v1.OperatorCondition, err error) {
uol.opConditionLock.RLock()
defer uol.opConditionLock.RUnlock()

set := make(map[types.UID]*v1.OperatorCondition)
for _, cl := range uol.opConditionListers {
csvs, err := cl.List(selector)
if err != nil {
return nil, err
}

for _, csv := range csvs {
set[csv.GetUID()] = csv
}
}

for _, csv := range set {
ret = append(ret, csv)
}

return
}

// OperatorConditions returns an object that can list and get OperatorConditions.
func (uol *UnionOperatorConditionLister) OperatorConditions(namespace string) listers.OperatorConditionNamespaceLister {
uol.opConditionLock.RLock()
defer uol.opConditionLock.RUnlock()

// Check for specific namespace listers
if cl, ok := uol.opConditionListers[namespace]; ok {
return cl.OperatorConditions(namespace)
}

// Check for any namespace-all listers
if cl, ok := uol.opConditionListers[metav1.NamespaceAll]; ok {
return cl.OperatorConditions(namespace)
}

return &NullOperatorConditionNamespaceLister{}
}

func (uol *UnionOperatorConditionLister) RegisterOperatorConditionLister(namespace string, lister listers.OperatorConditionLister) {
uol.opConditionLock.Lock()
defer uol.opConditionLock.Unlock()

if uol.opConditionListers == nil {
uol.opConditionListers = make(map[string]listers.OperatorConditionLister)
}

uol.opConditionListers[namespace] = lister
}

func (l *operatorsV1Lister) RegisterOperatorConditionLister(namespace string, lister listers.OperatorConditionLister) {
l.operatorConditionLister.RegisterOperatorConditionLister(namespace, lister)
}

func (l *operatorsV1Lister) OperatorConditionLister() listers.OperatorConditionLister {
return l.operatorConditionLister
}

// NullOperatorConditionNamespaceLister is an implementation of a null OperatorConditionNamespaceLister. It is
// used to prevent nil pointers when no OperatorConditionNamespaceLister has been registered for a given
// namespace.
type NullOperatorConditionNamespaceLister struct {
listers.OperatorConditionNamespaceLister
}

// List returns nil and an error explaining that this is a NullOperatorConditionNamespaceLister.
func (n *NullOperatorConditionNamespaceLister) List(selector labels.Selector) (ret []*v1.OperatorCondition, err error) {
return nil, fmt.Errorf("cannot list OperatorConditions with a NullOperatorConditionNamespaceLister")
}

// Get returns nil and an error explaining that this is a NullOperatorConditionNamespaceLister.
func (n *NullOperatorConditionNamespaceLister) Get(name string) (*v1.OperatorCondition, error) {
return nil, fmt.Errorf("cannot get OperatorCondition with a NullOperatorConditionNamespaceLister")
}
104 changes: 104 additions & 0 deletions pkg/lib/operatorlister/operatorlisterfakes/fake_operators_v1lister.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.