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

Bug 1784024: Use generations to prevent duplicate InstallPlans #1316

Merged
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
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,8 @@ clean:

# Generate manifests for CRDs
manifests: vendor
$(CONTROLLER_GEN) schemapatch:manifests=./deploy/chart/templates paths=./pkg/api/apis/operators/v1alpha1/... output:dir=./deploy/chart/templates
$(CONTROLLER_GEN) schemapatch:manifests=./deploy/chart/templates paths=./pkg/api/apis/operators/v1/... output:dir=./deploy/chart/templates
$(CONTROLLER_GEN) schemapatch:manifests=./deploy/chart/templates paths=./pkg/api/apis/operators/... output:dir=./deploy/chart/templates

$(YQ_INTERNAL) w --inplace deploy/chart/templates/0000_50_olm_03-clusterserviceversion.crd.yaml spec.validation.openAPIV3Schema.properties.spec.properties.install.properties.spec.properties.deployments.items.properties.spec.properties.template.properties.metadata.x-kubernetes-preserve-unknown-fields true

# Generate clients, listers, and informers
Expand Down
18 changes: 15 additions & 3 deletions deploy/chart/templates/0000_50_olm_04-installplan.crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ spec:
- approval
- approved
- clusterServiceVersionNames
- generation
properties:
approval:
description: Approval is the user approval policy for an InstallPlan.
Expand All @@ -77,6 +78,8 @@ spec:
type: array
items:
type: string
generation:
type: integer
source:
type: string
sourceNamespace:
Expand Down Expand Up @@ -128,17 +131,21 @@ spec:
description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
type: string
bundleLookups:
description: BundleLookups is the set of in-progress requests to pull
and unpackage bundle content to the cluster.
type: array
items:
description: BundleLookup is a request to pull and unpackage the content
of a bundle to the cluster.
type: object
required:
- catalogSourceRef
- path
- replaces
properties:
catalogSourceRef:
description: ObjectReference contains enough information to let
you inspect or modify the referred object.
description: CatalogSourceRef is a reference to the CatalogSource
the bundle path was resolved from.
type: object
properties:
apiVersion:
Expand Down Expand Up @@ -174,6 +181,7 @@ spec:
description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
type: string
conditions:
description: Conditions represents the overall state of a BundleLookup.
type: array
items:
type: object
Expand All @@ -187,7 +195,7 @@ spec:
type: string
format: date-time
lastUpdateTime:
description: Last time the condition was probed
description: Last time the condition was probed.
type: string
format: date-time
message:
Expand All @@ -205,8 +213,12 @@ spec:
description: Type of condition.
type: string
path:
description: Path refers to the location of a bundle to pull.
It's typically an image reference.
type: string
replaces:
description: Replaces is the name of the bundle to replace with
the one found at Path.
type: string
catalogSources:
type: array
Expand Down
4 changes: 4 additions & 0 deletions deploy/chart/templates/0000_50_olm_05-subscription.crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1702,6 +1702,10 @@ spec:
currentCSV:
description: CurrentCSV is the CSV the Subscription is progressing to.
type: string
installPlanGeneration:
description: InstallPlanGeneration is the current generation of the
installplan
type: integer
installPlanRef:
description: InstallPlanRef is a reference to the latest InstallPlan
that contains the Subscription's current CSV.
Expand Down
1 change: 1 addition & 0 deletions pkg/api/apis/operators/installplan_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type InstallPlanSpec struct {
ClusterServiceVersionNames []string
Approval Approval
Approved bool
Generation int
}

// InstallPlanPhase is the current status of a InstallPlan as a whole.
Expand Down
4 changes: 4 additions & 0 deletions pkg/api/apis/operators/subscription_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ type SubscriptionStatus struct {
// +optional
Reason ConditionReason

// InstallPlanGeneration is the current generation of the installplan
// +optional
InstallPlanGeneration int `json:"installPlanGeneration,omitempty"`

// InstallPlanRef is a reference to the latest InstallPlan that contains the Subscription's current CSV.
// +optional
InstallPlanRef *corev1.ObjectReference
Expand Down
34 changes: 1 addition & 33 deletions pkg/api/apis/operators/v1alpha1/installplan_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type InstallPlanSpec struct {
ClusterServiceVersionNames []string `json:"clusterServiceVersionNames"`
Approval Approval `json:"approval"`
Approved bool `json:"approved"`
Generation int `json:"generation"`
}

// InstallPlanPhase is the current status of a InstallPlan as a whole.
Expand Down Expand Up @@ -305,39 +306,6 @@ func (b *BundleLookup) SetCondition(cond BundleLookupCondition) BundleLookupCond
return cond
}

// ManifestsMatch returns true if the CSV manifests in the StepResources of the given list of steps
// matches those in the InstallPlanStatus.
func (s *InstallPlanStatus) CSVManifestsMatch(steps []*Step) bool {
if s.Plan == nil && steps == nil {
return true
}
if s.Plan == nil || steps == nil {
return false
}

manifests := make(map[string]struct{})
for _, step := range s.Plan {
resource := step.Resource
if resource.Kind != ClusterServiceVersionKind {
continue
}
manifests[resource.Manifest] = struct{}{}
}

for _, step := range steps {
resource := step.Resource
if resource.Kind != ClusterServiceVersionKind {
continue
}
if _, ok := manifests[resource.Manifest]; !ok {
return false
}
delete(manifests, resource.Manifest)
}

return len(manifests) == 0
}

func (s *Step) String() string {
return fmt.Sprintf("%s: %s (%s)", s.Resolving, s.Resource, s.Status)
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/api/apis/operators/v1alpha1/subscription_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@ type SubscriptionStatus struct {
// +optional
Reason ConditionReason `json:"reason,omitempty"`

// InstallPlanGeneration is the current generation of the installplan
// +optional
InstallPlanGeneration int `json:"installPlanGeneration,omitempty"`

// InstallPlanRef is a reference to the latest InstallPlan that contains the Subscription's current CSV.
// +optional
InstallPlanRef *corev1.ObjectReference `json:"installPlanRef,omitempty"`
Expand Down
4 changes: 4 additions & 0 deletions pkg/api/apis/operators/v1alpha1/zz_generated.conversion.go

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

6 changes: 6 additions & 0 deletions pkg/api/apis/operators/v1alpha2/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// +k8s:deepcopy-gen=package
// +k8s:conversion-gen=github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators
// +groupName=operators.coreos.com

// Package v1alpha2 contains resources types for version v1alpha2 of the operators.coreos.com API group.
package v1alpha2
97 changes: 97 additions & 0 deletions pkg/api/apis/operators/v1alpha2/operatorgroup_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package v1alpha2

import (
"sort"
"strings"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
OperatorGroupAnnotationKey = "olm.operatorGroup"
OperatorGroupNamespaceAnnotationKey = "olm.operatorNamespace"
OperatorGroupTargetsAnnotationKey = "olm.targetNamespaces"
OperatorGroupProvidedAPIsAnnotationKey = "olm.providedAPIs"

OperatorGroupKind = "OperatorGroup"
)

// OperatorGroupSpec is the spec for an OperatorGroup resource.
type OperatorGroupSpec struct {
// Selector selects the OperatorGroup's target namespaces.
// +optional
Selector *metav1.LabelSelector `json:"selector,omitempty"`

// TargetNamespaces is an explicit set of namespaces to target.
// If it is set, Selector is ignored.
// +optional
TargetNamespaces []string `json:"targetNamespaces,omitempty"`

// ServiceAccountName is the admin specified service account which will be
// used to deploy operator(s) in this operator group.
ServiceAccountName string `json:"serviceAccountName,omitempty"`

// Static tells OLM not to update the OperatorGroup's providedAPIs annotation
// +optional
StaticProvidedAPIs bool `json:"staticProvidedAPIs,omitempty"`
}

// OperatorGroupStatus is the status for an OperatorGroupResource.
type OperatorGroupStatus struct {
// Namespaces is the set of target namespaces for the OperatorGroup.
Namespaces []string `json:"namespaces,omitempty"`

// ServiceAccountRef references the service account object specified.
ServiceAccountRef *corev1.ObjectReference `json:"serviceAccountRef,omitempty"`

// LastUpdated is a timestamp of the last time the OperatorGroup's status was Updated.
LastUpdated *metav1.Time `json:"lastUpdated"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient

// OperatorGroup is the unit of multitenancy for OLM managed operators.
// It constrains the installation of operators in its namespace to a specified set of target namespaces.
type OperatorGroup struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`

// +optional
Spec OperatorGroupSpec `json:"spec"`
Status OperatorGroupStatus `json:"status,omitempty"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// OperatorGroupList is a list of OperatorGroup resources.
type OperatorGroupList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`

Items []OperatorGroup `json:"items"`
}

func (o *OperatorGroup) BuildTargetNamespaces() string {
sort.Strings(o.Status.Namespaces)
return strings.Join(o.Status.Namespaces, ",")
}

// IsServiceAccountSpecified returns true if the spec has a service account name specified.
func (o *OperatorGroup) IsServiceAccountSpecified() bool {
if o.Spec.ServiceAccountName == "" {
return false
}

return true
}

// HasServiceAccountSynced returns true if the service account specified has been synced.
func (o *OperatorGroup) HasServiceAccountSynced() bool {
if o.IsServiceAccountSpecified() && o.Status.ServiceAccountRef != nil {
return true
}

return false
}