Repository: operator-framework/operator-lifecycle-manager
Pull request #2525 (2021-12-14 22:06:58)

chore(api): vendor the new version of api repo
Signed-off-by: Vu Dinh <vudinh@outlook.com>

<!--

Before making a PR, please read our contributing guidelines https://github.com/operator-framework/operator-lifecycle-manager/blob/master/CONTRIBUTING.md

Note: Make sure your branch is rebased to the latest upstream master.

-->

**Description of the change:**


**Motivation for the change:**

**Reviewer Checklist**
- [ ] Implementation matches the proposed design, or proposal is updated to match implementation
- [ ] Sufficient unit test coverage
- [ ] Sufficient end-to-end test coverage
- [ ] Docs updated or added to `/doc`
- [ ] Commit messages sensible and descriptive


<!--

Note: If this PR is fixing an issue make sure to add a note saying:
Closes #<ISSUE_NUMBER>

-->


openshift-ci[bot]: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/operator-framework/operator-lifecycle-manager/pull/2525#" title="Author self-approved">dinhxuanvu</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=operator-framework%2Foperator-lifecycle-manager).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/operator-framework/operator-lifecycle-manager/blob/master/OWNERS)~~ [dinhxuanvu]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
kevinrizza: /lgtm
awgreene: /lgtm
openshift-ci[bot]: New changes are detected. LGTM label has been removed.
openshift-ci[bot]: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/operator-framework/operator-lifecycle-manager/pull/2525#pullrequestreview-833115848" title="LGTM">ankitathomas</a>*, *<a href="https://github.com/operator-framework/operator-lifecycle-manager/pull/2525#" title="Author self-approved">dinhxuanvu</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=operator-framework%2Foperator-lifecycle-manager).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/operator-framework/operator-lifecycle-manager/blob/master/OWNERS)~~ [dinhxuanvu]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
openshift-bot: /retest-required

Please review the [full test history](https://prow.ci.openshift.org/pr-history/?org=operator-framework&repo=operator-lifecycle-manager&pr=2525) for this PR and help us cut down flakes.

Patch:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index c7fa0fc9b..738064565 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,12 +1,13 @@
 name: build
 on:
   pull_request:
+  workflow_dispatch:
 jobs:
   image:
     runs-on: ubuntu-latest
     steps:
     - name: Check out the repo
-      uses: actions/checkout@v2
+      uses: actions/checkout@v1
     - name: Build the container image
       uses: docker/build-push-action@v2
       with:
diff --git a/.github/workflows/goreleaser.yaml b/.github/workflows/goreleaser.yaml
index 536a2b1e5..762534912 100644
--- a/.github/workflows/goreleaser.yaml
+++ b/.github/workflows/goreleaser.yaml
@@ -4,13 +4,13 @@ on:
   push:
     tags:
       - 'v*'
-
+  workflow_dispatch:
 jobs:
   release:
     runs-on: ubuntu-latest
     steps:
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v1
         with:
           fetch-depth: 0
       - name: Set up Go
diff --git a/.github/workflows/quickstart.yml b/.github/workflows/quickstart.yml
index 9e6f6bf16..9df3656a9 100644
--- a/.github/workflows/quickstart.yml
+++ b/.github/workflows/quickstart.yml
@@ -3,11 +3,12 @@ on:
   pull_request:
     paths:
     - 'deploy/upstream/quickstart/**'
+  workflow_dispatch:
 jobs:
   install-quickstart:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v1
     - run: |
         curl -sLo kind "$(curl -sL https://api.github.com/repos/kubernetes-sigs/kind/releases/latest | jq -r '[.assets[] | select(.name == "kind-linux-amd64")] | first | .browser_download_url')"
         chmod +x kind
diff --git a/.github/workflows/run-kind-local.yml b/.github/workflows/run-kind-local.yml
index 2eaa7c6a4..920d19218 100644
--- a/.github/workflows/run-kind-local.yml
+++ b/.github/workflows/run-kind-local.yml
@@ -3,11 +3,12 @@ on:
   pull_request:
   schedule:
   - cron: '0 0 * * *' # daily to pick up releases
+  workflow_dispatch:
 jobs:
   e2e-kind:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v1
     - uses: actions/setup-go@v2
       with:
         go-version: '~1.16'
diff --git a/.github/workflows/run-minikube-local.yml b/.github/workflows/run-minikube-local.yml
index e2fedf48a..19cbfaa84 100644
--- a/.github/workflows/run-minikube-local.yml
+++ b/.github/workflows/run-minikube-local.yml
@@ -3,11 +3,12 @@ on:
   pull_request:
   schedule:
   - cron: '0 0 * * *' # daily to pick up releases
+  workflow_dispatch:
 jobs:
   e2e-minikube:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v1
     - uses: actions/setup-go@v2
       with:
         go-version: '~1.16'
diff --git a/.github/workflows/sanity.yaml b/.github/workflows/sanity.yaml
index 3ef7943f8..f3dec41de 100644
--- a/.github/workflows/sanity.yaml
+++ b/.github/workflows/sanity.yaml
@@ -4,11 +4,12 @@ on:
     branches:
       - '**'
   pull_request:
+  workflow_dispatch:
 jobs:
   sanity:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v1
       - uses: actions/setup-go@v2
         with:
           go-version: '~1.16'
diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml
index 33b7f3f9c..7f1fe801a 100644
--- a/.github/workflows/unit.yml
+++ b/.github/workflows/unit.yml
@@ -4,11 +4,12 @@ on:
     branches:
       - master
   pull_request:
+  workflow_dispatch:
 jobs:
   unit:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v1
       - uses: actions/setup-go@v2
         with:
           go-version: '~1.16'
diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml
index ea69dd344..8cab62ada 100644
--- a/.github/workflows/verify.yml
+++ b/.github/workflows/verify.yml
@@ -4,11 +4,12 @@ on:
     branches:
       - master
   pull_request:
+  workflow_dispatch:
 jobs:
   verify:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v1
       - uses: actions/setup-go@v2
         with:
           go-version: '~1.16'
diff --git a/MAINTAINERS.md b/MAINTAINERS.md
index 84221e2ea..15c0a9fc9 100644
--- a/MAINTAINERS.md
+++ b/MAINTAINERS.md
@@ -5,8 +5,8 @@ for general contribution guidelines.
 
 ## Maintainers (in alphabetical order)
 
-- [Alexander Greene](github.com/awgreene), Red Hat
-- [Evan Cordell](github.com/ecordell), Authzed
-- [Kevin Rizza](github.com/kevinrizza), Red Hat
-- [Nick Hale](github.com/njhale), Red Hat
-- [Vu Dinh](github.com/dinhxuanvu), Red Hat
+- [Alexander Greene](https://github.com/awgreene), Red Hat
+- [Evan Cordell](https://github.com/ecordell), Authzed
+- [Kevin Rizza](https://github.com/kevinrizza), Red Hat
+- [Nick Hale](https://github.com/njhale), Red Hat
+- [Vu Dinh](https://github.com/dinhxuanvu), Red Hat
diff --git a/go.mod b/go.mod
index cfdb77c54..2dbacfe18 100644
--- a/go.mod
+++ b/go.mod
@@ -25,7 +25,7 @@ require (
 	github.com/onsi/gomega v1.15.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.10.8-0.20211210205029-40cb9fd4036a
+	github.com/operator-framework/api v0.11.0
 	github.com/operator-framework/operator-registry v1.17.5
 	github.com/otiai10/copy v1.2.0
 	github.com/pkg/errors v0.9.1
diff --git a/go.sum b/go.sum
index cabfccd98..9ac571e71 100644
--- a/go.sum
+++ b/go.sum
@@ -927,8 +927,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.7.1/go.mod h1:L7IvLd/ckxJEJg/t4oTTlnHKAJIP/p51AvEslW3wYdY=
-github.com/operator-framework/api v0.10.8-0.20211210205029-40cb9fd4036a h1:tX+zUHdTkn7NfLt9Y8zjsfmSeZkE+sKQEZRqYkVdHYo=
-github.com/operator-framework/api v0.10.8-0.20211210205029-40cb9fd4036a/go.mod h1:FTiYGm11fZQ3cSX+EQHc/UWoGZAwkGfyeHU+wMJ8jmA=
+github.com/operator-framework/api v0.11.0 h1:W9V1NNwl3LWPQL9S1pDaFONCarJLl8Xu6gdF9w54hTE=
+github.com/operator-framework/api v0.11.0/go.mod h1:FTiYGm11fZQ3cSX+EQHc/UWoGZAwkGfyeHU+wMJ8jmA=
 github.com/operator-framework/operator-registry v1.17.5 h1:LR8m1rFz5Gcyje8WK6iYt+gIhtzqo52zMRALdmTYHT0=
 github.com/operator-framework/operator-registry v1.17.5/go.mod h1:sRQIgDMZZdUcmHltzyCnM6RUoDF+WS8Arj1BQIARDS8=
 github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k=
diff --git a/pkg/controller/operators/catalog/operator.go b/pkg/controller/operators/catalog/operator.go
index d152b399a..a749a9c11 100644
--- a/pkg/controller/operators/catalog/operator.go
+++ b/pkg/controller/operators/catalog/operator.go
@@ -803,6 +803,7 @@ func (o *Operator) syncConnection(logger *logrus.Entry, in *v1alpha1.CatalogSour
 		// Set connection status and return.
 		out.Status.GRPCConnectionState.LastConnectTime = now
 		out.Status.GRPCConnectionState.LastObservedState = source.ConnectionState.String()
+		out.Status.GRPCConnectionState.Address = source.Address
 	}
 
 	return
diff --git a/pkg/controller/registry/reconciler/grpc.go b/pkg/controller/registry/reconciler/grpc.go
index 8406740be..145d29bce 100644
--- a/pkg/controller/registry/reconciler/grpc.go
+++ b/pkg/controller/registry/reconciler/grpc.go
@@ -191,7 +191,7 @@ func (c *GrpcRegistryReconciler) EnsureRegistryServer(catalogSource *v1alpha1.Ca
 	source := grpcCatalogSourceDecorator{catalogSource}
 
 	// if service status is nil, we force create every object to ensure they're created the first time
-	overwrite := source.Status.RegistryServiceStatus == nil
+	overwrite := source.Status.RegistryServiceStatus == nil || !isRegistryServiceStatusValid(&source)
 
 	//TODO: if any of these error out, we should write a status back (possibly set RegistryServiceStatus to nil so they get recreated)
 	sa, err := c.ensureSA(source)
@@ -216,17 +216,33 @@ func (c *GrpcRegistryReconciler) EnsureRegistryServer(catalogSource *v1alpha1.Ca
 
 	if overwritePod {
 		now := c.now()
+		service := source.Service()
 		catalogSource.Status.RegistryServiceStatus = &v1alpha1.RegistryServiceStatus{
 			CreatedAt:        now,
 			Protocol:         "grpc",
-			ServiceName:      source.Service().GetName(),
+			ServiceName:      service.GetName(),
 			ServiceNamespace: source.GetNamespace(),
-			Port:             fmt.Sprintf("%d", source.Service().Spec.Ports[0].Port),
+			Port:             getPort(service),
 		}
 	}
 	return nil
 }
 
+func getPort(service *corev1.Service) string {
+	return fmt.Sprintf("%d", service.Spec.Ports[0].Port)
+}
+
+func isRegistryServiceStatusValid(source *grpcCatalogSourceDecorator) bool {
+	service := source.Service()
+	if source.Status.RegistryServiceStatus.ServiceName != service.GetName() ||
+		source.Status.RegistryServiceStatus.ServiceNamespace != service.GetNamespace() ||
+		source.Status.RegistryServiceStatus.Port != getPort(service) ||
+		source.Status.RegistryServiceStatus.Protocol != "grpc" {
+		return false
+	}
+	return true
+}
+
 func (c *GrpcRegistryReconciler) ensurePod(source grpcCatalogSourceDecorator, saName string, overwrite bool) error {
 	// currentLivePods refers to the currently live instances of the catalog source
 	currentLivePods := c.currentPods(source)
diff --git a/pkg/controller/registry/resolver/hooks.go b/pkg/controller/registry/resolver/hooks.go
new file mode 100644
index 000000000..aaa98e64c
--- /dev/null
+++ b/pkg/controller/registry/resolver/hooks.go
@@ -0,0 +1,9 @@
+package resolver
+
+// stepResolverInitHook provides a way for the downstream
+// to modify the step resolver at creation time.
+// This is a bit of a hack to enable system constraints downstream
+// without affecting the upstream. We may want to clean this up when
+// either we have a more pluggable architecture; or system constraints
+// come to the upstream
+type stepResolverInitHook func(*OperatorStepResolver) error
diff --git a/pkg/controller/registry/resolver/resolver.go b/pkg/controller/registry/resolver/resolver.go
index f1b5d42a0..427bbfaed 100644
--- a/pkg/controller/registry/resolver/resolver.go
+++ b/pkg/controller/registry/resolver/resolver.go
@@ -26,9 +26,10 @@ type OperatorResolver interface {
 }
 
 type SatResolver struct {
-	cache cache.OperatorCacheProvider
-	log   logrus.FieldLogger
-	pc    *predicateConverter
+	cache                     cache.OperatorCacheProvider
+	log                       logrus.FieldLogger
+	pc                        *predicateConverter
+	systemConstraintsProvider solver.ConstraintProvider
 }
 
 func NewDefaultSatResolver(rcp cache.SourceProvider, catsrcLister v1alpha1listers.CatalogSourceLister, logger logrus.FieldLogger) *SatResolver {
@@ -177,6 +178,25 @@ func (r *SatResolver) SolveOperators(namespaces []string, csvs []*v1alpha1.Clust
 	return operators, nil
 }
 
+// newBundleInstallableFromEntry converts an entry into a bundle installable with
+// system constraints applied, if they are defined for the entry
+func (r *SatResolver) newBundleInstallableFromEntry(entry *cache.Entry) (*BundleInstallable, error) {
+	bundleInstalleble, err := NewBundleInstallableFromOperator(entry)
+	if err != nil {
+		return nil, err
+	}
+
+	// apply system constraints if necessary
+	if r.systemConstraintsProvider != nil && !(entry.SourceInfo.Catalog.Virtual()) {
+		systemConstraints, err := r.systemConstraintsProvider.Constraints(entry)
+		if err != nil {
+			return nil, err
+		}
+		bundleInstalleble.constraints = append(bundleInstalleble.constraints, systemConstraints...)
+	}
+	return &bundleInstalleble, nil
+}
+
 func (r *SatResolver) getSubscriptionInstallables(sub *v1alpha1.Subscription, current *cache.Entry, namespacedCache cache.MultiCatalogOperatorFinder, visited map[*cache.Entry]*BundleInstallable) (map[solver.Identifier]solver.Installable, error) {
 	var cachePredicates, channelPredicates []cache.Predicate
 	installables := make(map[solver.Identifier]solver.Installable, 0)
@@ -334,13 +354,13 @@ func (r *SatResolver) getBundleInstallables(preferredNamespace string, bundleSta
 			continue
 		}
 
-		bundleInstallable, err := NewBundleInstallableFromOperator(bundle)
+		bundleInstallable, err := r.newBundleInstallableFromEntry(bundle)
 		if err != nil {
 			errs = append(errs, err)
 			continue
 		}
 
-		visited[bundle] = &bundleInstallable
+		visited[bundle] = bundleInstallable
 
 		dependencyPredicates, err := r.pc.convertDependencyProperties(bundle.Properties)
 		if err != nil {
@@ -389,12 +409,12 @@ func (r *SatResolver) getBundleInstallables(preferredNamespace string, bundleSta
 			// (after sorting) to remove all bundles that
 			// don't satisfy the dependency.
 			for _, b := range cache.Filter(sortedBundles, d) {
-				i, err := NewBundleInstallableFromOperator(b)
+				i, err := r.newBundleInstallableFromEntry(b)
 				if err != nil {
 					errs = append(errs, err)
 					continue
 				}
-				installables[i.Identifier()] = &i
+				installables[i.Identifier()] = i
 				bundleDependencies = append(bundleDependencies, i.Identifier())
 				bundleStack = append(bundleStack, b)
 			}
@@ -404,7 +424,7 @@ func (r *SatResolver) getBundleInstallables(preferredNamespace string, bundleSta
 			))
 		}
 
-		installables[bundleInstallable.Identifier()] = &bundleInstallable
+		installables[bundleInstallable.Identifier()] = bundleInstallable
 	}
 
 	if len(errs) > 0 {
diff --git a/pkg/controller/registry/resolver/solver/constraint_provider.go b/pkg/controller/registry/resolver/solver/constraint_provider.go
new file mode 100644
index 000000000..ce119abed
--- /dev/null
+++ b/pkg/controller/registry/resolver/solver/constraint_provider.go
@@ -0,0 +1,19 @@
+package solver
+
+import "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/cache"
+
+// ConstraintProvider knows how to provide solver constraints for a given cache entry.
+// For instance, it could be used to surface additional constraints against an entry given some
+// properties it may expose. E.g. olm.maxOpenShiftVersion could be checked against the cluster version
+// and prohibit any entry that doesn't meet the requirement
+type ConstraintProvider interface {
+	// Constraints returns a set of solver constraints for a cache entry.
+	Constraints(e *cache.Entry) ([]Constraint, error)
+}
+
+// ConstraintProviderFunc is a simple implementation of ConstraintProvider
+type ConstraintProviderFunc func(e *cache.Entry) ([]Constraint, error)
+
+func (c ConstraintProviderFunc) Constraints(e *cache.Entry) ([]Constraint, error) {
+	return c(e)
+}
diff --git a/pkg/controller/registry/resolver/step_resolver.go b/pkg/controller/registry/resolver/step_resolver.go
index 079b92806..224cc277b 100644
--- a/pkg/controller/registry/resolver/step_resolver.go
+++ b/pkg/controller/registry/resolver/step_resolver.go
@@ -26,6 +26,9 @@ const (
 	BundleLookupConditionPacked v1alpha1.BundleLookupConditionType = "BundleLookupNotPersisted"
 )
 
+// init hooks provides the downstream a way to modify the upstream behavior
+var initHooks []stepResolverInitHook
+
 var timeNow = func() metav1.Time { return metav1.NewTime(time.Now().UTC()) }
 
 type StepResolver interface {
@@ -48,7 +51,7 @@ var _ StepResolver = &OperatorStepResolver{}
 
 func NewOperatorStepResolver(lister operatorlister.OperatorLister, client versioned.Interface, kubeclient kubernetes.Interface,
 	globalCatalogNamespace string, provider RegistryClientProvider, log logrus.FieldLogger) *OperatorStepResolver {
-	return &OperatorStepResolver{
+	stepResolver := &OperatorStepResolver{
 		subLister:              lister.OperatorsV1alpha1().SubscriptionLister(),
 		csvLister:              lister.OperatorsV1alpha1().ClusterServiceVersionLister(),
 		ipLister:               lister.OperatorsV1alpha1().InstallPlanLister(),
@@ -58,6 +61,15 @@ func NewOperatorStepResolver(lister operatorlister.OperatorLister, client versio
 		satResolver:            NewDefaultSatResolver(SourceProviderFromRegistryClientProvider(provider, log), lister.OperatorsV1alpha1().CatalogSourceLister(), log),
 		log:                    log,
 	}
+
+	// init hooks can be added to the downstream to
+	// modify resolver behaviour
+	for _, initHook := range initHooks {
+		if err := initHook(stepResolver); err != nil {
+			panic(err)
+		}
+	}
+	return stepResolver
 }
 
 func (r *OperatorStepResolver) Expire(key cache.SourceKey) {
diff --git a/vendor/github.com/operator-framework/api/pkg/validation/doc.go b/vendor/github.com/operator-framework/api/pkg/validation/doc.go
index 5ab90e996..a22d76b51 100644
--- a/vendor/github.com/operator-framework/api/pkg/validation/doc.go
+++ b/vendor/github.com/operator-framework/api/pkg/validation/doc.go
@@ -8,8 +8,11 @@
 // by exported functions for missing mandatory and optional fields,
 // respectively. Each Error implements the error interface.
 //
-// Bundle format: https://github.com/operator-framework/operator-registry/#manifest-format
-// ClusterServiceVersion documentation: https://github.com/operator-framework/operator-lifecycle-manager/blob/master/Documentation/design/building-your-csv.md
-// Package manifest documentation: https://github.com/operator-framework/operator-lifecycle-manager#discovery-catalogs-and-automated-upgrades
-// CustomResourceDefinition documentation: https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/
+// - Bundle format: https://github.com/operator-framework/operator-registry/#manifest-format
+//
+// - ClusterServiceVersion documentation: https://github.com/operator-framework/operator-lifecycle-manager/blob/master/Documentation/design/building-your-csv.md
+//
+// - Package manifest documentation: https://github.com/operator-framework/operator-lifecycle-manager#discovery-catalogs-and-automated-upgrades
+
+// - CustomResourceDefinition documentation: https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/
 package validation
diff --git a/vendor/modules.txt b/vendor/modules.txt
index b4edece47..8e0da1300 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -515,7 +515,7 @@ github.com/openshift/client-go/config/informers/externalversions/config
 github.com/openshift/client-go/config/informers/externalversions/config/v1
 github.com/openshift/client-go/config/informers/externalversions/internalinterfaces
 github.com/openshift/client-go/config/listers/config/v1
-# github.com/operator-framework/api v0.10.8-0.20211210205029-40cb9fd4036a
+# github.com/operator-framework/api v0.11.0
 ## explicit
 github.com/operator-framework/api/crds
 github.com/operator-framework/api/pkg/constraints


Test patch:
diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml
index 6745d785d..3b7a86fba 100644
--- a/.github/workflows/e2e-tests.yml
+++ b/.github/workflows/e2e-tests.yml
@@ -4,11 +4,12 @@ on:
     branches:
       - master
   pull_request:
+  workflow_dispatch:
 jobs:
   e2e-tests:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v1
       - uses: actions/setup-go@v2
         with:
           go-version: '~1.16'
diff --git a/pkg/controller/operators/catalog/operator_test.go b/pkg/controller/operators/catalog/operator_test.go
index f6aceb8bc..8abd022d9 100644
--- a/pkg/controller/operators/catalog/operator_test.go
+++ b/pkg/controller/operators/catalog/operator_test.go
@@ -49,6 +49,7 @@ import (
 	"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned/fake"
 	"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/informers/externalversions"
 	olmerrors "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/errors"
+	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
 	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/grpc"
 	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/reconciler"
 	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver"
@@ -758,6 +759,12 @@ func TestExecutePlanDynamicResources(t *testing.T) {
 	}
 }
 
+func withStatus(catalogSource v1alpha1.CatalogSource, status v1alpha1.CatalogSourceStatus) *v1alpha1.CatalogSource {
+	copy := catalogSource.DeepCopy()
+	copy.Status = status
+	return copy
+}
+
 func TestSyncCatalogSources(t *testing.T) {
 	clockFake := utilclock.NewFakeClock(time.Date(2018, time.January, 26, 20, 40, 0, 0, time.UTC))
 	now := metav1.NewTime(clockFake.Now())
@@ -786,14 +793,15 @@ func TestSyncCatalogSources(t *testing.T) {
 		},
 	}
 	tests := []struct {
-		testName       string
-		namespace      string
-		catalogSource  *v1alpha1.CatalogSource
-		k8sObjs        []runtime.Object
-		configMap      *corev1.ConfigMap
-		expectedStatus *v1alpha1.CatalogSourceStatus
-		expectedObjs   []runtime.Object
-		expectedError  error
+		testName        string
+		namespace       string
+		catalogSource   *v1alpha1.CatalogSource
+		k8sObjs         []runtime.Object
+		configMap       *corev1.ConfigMap
+		expectedStatus  *v1alpha1.CatalogSourceStatus
+		expectedObjs    []runtime.Object
+		expectedError   error
+		existingSources []sourceAddress
 	}{
 		{
 			testName:  "CatalogSourceWithInvalidSourceType",
@@ -1013,6 +1021,47 @@ func TestSyncCatalogSources(t *testing.T) {
 			},
 			expectedError: nil,
 		},
+		{
+			testName:  "GRPCConnectionStateAddressIsUpdated",
+			namespace: "cool-namespace",
+			catalogSource: withStatus(*grpcCatalog, v1alpha1.CatalogSourceStatus{
+				RegistryServiceStatus: &v1alpha1.RegistryServiceStatus{
+					Protocol:         "grpc",
+					ServiceName:      "cool-catalog",
+					ServiceNamespace: "cool-namespace",
+					Port:             "50051",
+					CreatedAt:        now,
+				},
+				GRPCConnectionState: &v1alpha1.GRPCConnectionState{
+					Address: "..svc:", // Needs to be updated to cool-catalog.cool-namespace.svc:50051
+				},
+			}),
+			k8sObjs: []runtime.Object{
+				pod(*grpcCatalog),
+				service(grpcCatalog.GetName(), grpcCatalog.GetNamespace()),
+			},
+			existingSources: []sourceAddress{
+				{
+					sourceKey: registry.CatalogKey{Name: "cool-catalog", Namespace: "cool-namespace"},
+					address:   "cool-catalog.cool-namespace.svc:50051",
+				},
+			},
+			expectedStatus: &v1alpha1.CatalogSourceStatus{
+				RegistryServiceStatus: &v1alpha1.RegistryServiceStatus{
+					Protocol:         "grpc",
+					ServiceName:      "cool-catalog",
+					ServiceNamespace: "cool-namespace",
+					Port:             "50051",
+					CreatedAt:        now,
+				},
+				GRPCConnectionState: &v1alpha1.GRPCConnectionState{
+					Address:           "cool-catalog.cool-namespace.svc:50051",
+					LastObservedState: "",
+					LastConnectTime:   now,
+				},
+			},
+			expectedError: nil,
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.testName, func(t *testing.T) {
@@ -1023,7 +1072,7 @@ func TestSyncCatalogSources(t *testing.T) {
 			ctx, cancel := context.WithCancel(context.TODO())
 			defer cancel()
 
-			op, err := NewFakeOperator(ctx, tt.namespace, []string{tt.namespace}, withClock(clockFake), withClientObjs(clientObjs...), withK8sObjs(tt.k8sObjs...))
+			op, err := NewFakeOperator(ctx, tt.namespace, []string{tt.namespace}, withClock(clockFake), withClientObjs(clientObjs...), withK8sObjs(tt.k8sObjs...), withSources(tt.existingSources...))
 			require.NoError(t, err)
 
 			// Run sync
@@ -1040,6 +1089,13 @@ func TestSyncCatalogSources(t *testing.T) {
 			require.NotEmpty(t, updated)
 
 			if tt.expectedStatus != nil {
+				if tt.expectedStatus.GRPCConnectionState != nil {
+					updated.Status.GRPCConnectionState.LastConnectTime = now
+					// Ignore LastObservedState difference if an expected LastObservedState is no provided
+					if tt.expectedStatus.GRPCConnectionState.LastObservedState == "" {
+						updated.Status.GRPCConnectionState.LastObservedState = ""
+					}
+				}
 				require.NotEmpty(t, updated.Status)
 				require.Equal(t, *tt.expectedStatus, updated.Status)
 
@@ -1384,6 +1440,7 @@ type fakeOperatorConfig struct {
 	resolver      resolver.StepResolver
 	recorder      record.EventRecorder
 	reconciler    reconciler.RegistryReconcilerFactory
+	sources       []sourceAddress
 }
 
 // fakeOperatorOption applies an option to the given fake operator configuration.
@@ -1395,6 +1452,12 @@ func withResolver(res resolver.StepResolver) fakeOperatorOption {
 	}
 }
 
+func withSources(sources ...sourceAddress) fakeOperatorOption {
+	return func(config *fakeOperatorConfig) {
+		config.sources = sources
+	}
+}
+
 func withReconciler(rec reconciler.RegistryReconcilerFactory) fakeOperatorOption {
 	return func(config *fakeOperatorConfig) {
 		config.reconciler = rec
@@ -1431,6 +1494,11 @@ func withFakeClientOptions(options ...clientfake.Option) fakeOperatorOption {
 	}
 }
 
+type sourceAddress struct {
+	address   string
+	sourceKey registry.CatalogKey
+}
+
 // NewFakeOperator creates a new operator using fake clients.
 func NewFakeOperator(ctx context.Context, namespace string, namespaces []string, fakeOptions ...fakeOperatorOption) (*Operator, error) {
 	// Apply options to default config
@@ -1548,6 +1616,9 @@ func NewFakeOperator(ctx context.Context, namespace string, namespaces []string,
 
 	op.RunInformers(ctx)
 	op.sources.Start(ctx)
+	for _, source := range config.sources {
+		op.sources.Add(source.sourceKey, source.address)
+	}
 
 	if ok := cache.WaitForCacheSync(ctx.Done(), op.HasSynced); !ok {
 		return nil, fmt.Errorf("failed to wait for caches to sync")
diff --git a/pkg/controller/registry/reconciler/grpc_test.go b/pkg/controller/registry/reconciler/grpc_test.go
index 882728621..6ac340648 100644
--- a/pkg/controller/registry/reconciler/grpc_test.go
+++ b/pkg/controller/registry/reconciler/grpc_test.go
@@ -49,6 +49,11 @@ func grpcCatalogSourceWithSecret(secretNames []string) *v1alpha1.CatalogSource {
 		},
 	}
 }
+func grpcCatalogSourceWithStatus(status v1alpha1.CatalogSourceStatus) *v1alpha1.CatalogSource {
+	catsrc := validGrpcCatalogSource("image", "")
+	catsrc.Status = status
+	return catsrc
+}
 
 func grpcCatalogSourceWithAnnotations(annotations map[string]string) *v1alpha1.CatalogSource {
 	catsrc := validGrpcCatalogSource("image", "")
@@ -284,6 +289,29 @@ func TestGrpcRegistryReconciler(t *testing.T) {
 				},
 			},
 		},
+		{
+			testName: "Grpc/ExistingRegistry/UpdateInvalidRegistryServiceStatus",
+			in: in{
+				cluster: cluster{
+					k8sObjs: objectsForCatalogSource(validGrpcCatalogSource("image", "")),
+				},
+				catsrc: grpcCatalogSourceWithStatus(v1alpha1.CatalogSourceStatus{
+					RegistryServiceStatus: &v1alpha1.RegistryServiceStatus{
+						CreatedAt: now(),
+						Protocol:  "grpc",
+					},
+				}),
+			},
+			out: out{
+				status: &v1alpha1.RegistryServiceStatus{
+					CreatedAt:        now(),
+					Protocol:         "grpc",
+					ServiceName:      "img-catalog",
+					ServiceNamespace: testNamespace,
+					Port:             "50051",
+				},
+			},
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.testName, func(t *testing.T) {
diff --git a/pkg/controller/registry/resolver/resolver_test.go b/pkg/controller/registry/resolver/resolver_test.go
index 776f88e6d..6e0fc6aef 100644
--- a/pkg/controller/registry/resolver/resolver_test.go
+++ b/pkg/controller/registry/resolver/resolver_test.go
@@ -66,6 +66,109 @@ func TestSolveOperators(t *testing.T) {
 	require.EqualValues(t, expected, operators)
 }
 
+func TestSolveOperators_WithSystemConstraints(t *testing.T) {
+	const namespace = "test-namespace"
+	catalog := cache.SourceKey{Name: "test-catalog", Namespace: namespace}
+
+	packageASub := newSub(namespace, "packageA", "alpha", catalog)
+	packageDSub := existingSub(namespace, "packageD.v1", "packageD", "alpha", catalog)
+
+	APISet := cache.APISet{opregistry.APIKey{Group: "g", Version: "v", Kind: "k", Plural: "ks"}: struct{}{}}
+
+	// packageA requires an API that can be provided by B or C
+	packageA := genOperator("packageA.v1", "0.0.1", "", "packageA", "alpha", catalog.Name, catalog.Namespace, APISet, nil, nil, "", false)
+	packageB := genOperator("packageB.v1", "1.0.0", "", "packageB", "alpha", catalog.Name, catalog.Namespace, nil, APISet, nil, "", false)
+	packageC := genOperator("packageC.v1", "1.0.0", "", "packageC", "alpha", catalog.Name, catalog.Namespace, nil, APISet, nil, "", false)
+
+	// Existing operators
+	packageD := genOperator("packageD.v1", "1.0.0", "", "packageD", "alpha", catalog.Name, catalog.Namespace, nil, nil, nil, "", false)
+	existingPackageD := existingOperator(namespace, "packageD.v1", "packageD", "alpha", "", nil, nil, nil, nil)
+	existingPackageD.Annotations = map[string]string{"operatorframework.io/properties": `{"properties":[{"type":"olm.package","value":{"packageName":"packageD","version":"1.0.0"}}]}`}
+
+	whiteListConstraintProvider := func(whiteList ...*cache.Entry) solver.ConstraintProviderFunc {
+		return func(entry *cache.Entry) ([]solver.Constraint, error) {
+			for _, whiteListedEntry := range whiteList {
+				if whiteListedEntry.Package() == entry.Package() &&
+					whiteListedEntry.Name == entry.Name &&
+					whiteListedEntry.Version == entry.Version {
+					return nil, nil
+				}
+			}
+			return []solver.Constraint{PrettyConstraint(
+				solver.Prohibited(),
+				fmt.Sprintf("package: %s is not white listed", entry.Package()),
+			)}, nil
+		}
+	}
+
+	testCases := []struct {
+		title                     string
+		systemConstraintsProvider solver.ConstraintProvider
+		expectedOperators         cache.OperatorSet
+		csvs                      []*v1alpha1.ClusterServiceVersion
+		subs                      []*v1alpha1.Subscription
+		snapshotEntries           []*cache.Entry
+		err                       string
+	}{
+		{
+			title:                     "No runtime constraints",
+			snapshotEntries:           []*cache.Entry{packageA, packageB, packageC, packageD},
+			systemConstraintsProvider: nil,
+			expectedOperators:         cache.OperatorSet{"packageA.v1": packageA, "packageB.v1": packageB},
+			csvs:                      nil,
+			subs:                      []*v1alpha1.Subscription{packageASub},
+			err:                       "",
+		},
+		{
+			title:                     "Runtime constraints only accept packages A and C",
+			snapshotEntries:           []*cache.Entry{packageA, packageB, packageC, packageD},
+			systemConstraintsProvider: whiteListConstraintProvider(packageA, packageC),
+			expectedOperators:         cache.OperatorSet{"packageA.v1": packageA, "packageC.v1": packageC},
+			csvs:                      nil,
+			subs:                      []*v1alpha1.Subscription{packageASub},
+			err:                       "",
+		},
+		{
+			title:                     "Existing packages are ignored",
+			snapshotEntries:           []*cache.Entry{packageA, packageB, packageC, packageD},
+			systemConstraintsProvider: whiteListConstraintProvider(packageA, packageC),
+			expectedOperators:         cache.OperatorSet{"packageA.v1": packageA, "packageC.v1": packageC},
+			csvs:                      []*v1alpha1.ClusterServiceVersion{existingPackageD},
+			subs:                      []*v1alpha1.Subscription{packageASub, packageDSub},
+			err:                       "",
+		},
+		{
+			title:                     "Runtime constraints don't allow A",
+			snapshotEntries:           []*cache.Entry{packageA, packageB, packageC, packageD},
+			systemConstraintsProvider: whiteListConstraintProvider(),
+			expectedOperators:         nil,
+			csvs:                      nil,
+			subs:                      []*v1alpha1.Subscription{packageASub},
+			err:                       "packageA is not white listed",
+		},
+	}
+
+	for _, testCase := range testCases {
+		satResolver := SatResolver{
+			cache: cache.New(cache.StaticSourceProvider{
+				catalog: &cache.Snapshot{
+					Entries: testCase.snapshotEntries,
+				},
+			}),
+			log:                       logrus.New(),
+			systemConstraintsProvider: testCase.systemConstraintsProvider,
+		}
+		operators, err := satResolver.SolveOperators([]string{namespace}, testCase.csvs, testCase.subs)
+
+		if testCase.err != "" {
+			require.Containsf(t, err.Error(), testCase.err, "Test %s failed", testCase.title)
+		} else {
+			require.NoErrorf(t, err, "Test %s failed", testCase.title)
+		}
+		require.EqualValuesf(t, testCase.expectedOperators, operators, "Test %s failed", testCase.title)
+	}
+}
+
 func TestDisjointChannelGraph(t *testing.T) {
 	const namespace = "test-namespace"
 	catalog := cache.SourceKey{Name: "test-catalog", Namespace: namespace}
diff --git a/pkg/controller/registry/resolver/step_resolver_test.go b/pkg/controller/registry/resolver/step_resolver_test.go
index 6c29c127e..1839bccbd 100644
--- a/pkg/controller/registry/resolver/step_resolver_test.go
+++ b/pkg/controller/registry/resolver/step_resolver_test.go
@@ -47,6 +47,33 @@ var (
 	Requires4 = APISet4
 )
 
+func TestInitHooks(t *testing.T) {
+	clientFake := fake.NewSimpleClientset()
+	lister := operatorlister.NewLister()
+	kClientFake := k8sfake.NewSimpleClientset()
+	log := logrus.New()
+
+	// no init hooks
+	resolver := NewOperatorStepResolver(lister, clientFake, kClientFake, "", nil, log)
+	require.NotNil(t, resolver.satResolver)
+
+	// with init hook
+	var testHook stepResolverInitHook = func(resolver *OperatorStepResolver) error {
+		resolver.satResolver = nil
+		return nil
+	}
+
+	// defined in step_resolver.go
+	initHooks = append(initHooks, testHook)
+	defer func() {
+		// reset initHooks
+		initHooks = nil
+	}()
+
+	resolver = NewOperatorStepResolver(lister, clientFake, kClientFake, "", nil, log)
+	require.Nil(t, resolver.satResolver)
+}
+
 func TestResolver(t *testing.T) {
 	const namespace = "catsrc-namespace"
 	catalog := resolvercache.SourceKey{Name: "catsrc", Namespace: namespace}
diff --git a/test/e2e/metrics_e2e_test.go b/test/e2e/metrics_e2e_test.go
index 2769091e3..ee03e3c30 100644
--- a/test/e2e/metrics_e2e_test.go
+++ b/test/e2e/metrics_e2e_test.go
@@ -149,9 +149,13 @@ var _ = Describe("Metrics are generated for OLM managed resources", func() {
 					restartDeploymentWithLabel(c, "app=olm-operator")
 				})
 				It("CSV metric is preserved", func() {
-					Expect(getMetricsFromPod(c, getPodWithLabel(c, "app=olm-operator"))).To(
-						ContainElement(LikeMetric(WithFamily("csv_succeeded"), WithName(csv.Name), WithValue(1))),
-					)
+					Eventually(func() []Metric {
+						return getMetricsFromPod(c, getPodWithLabel(c, "app=olm-operator"))
+					}).Should(ContainElement(LikeMetric(
+						WithFamily("csv_succeeded"),
+						WithName(csv.Name),
+						WithValue(1),
+					)))
 				})
 			})
 		})