@@ -25,7 +25,6 @@ import (
2525
2626 "github.com/go-logr/logr"
2727 opv1a1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
28- "k8s.io/apimachinery/pkg/api/errors"
2928 "k8s.io/utils/ptr"
3029 ctrl "sigs.k8s.io/controller-runtime"
3130 "sigs.k8s.io/controller-runtime/pkg/client"
@@ -47,6 +46,11 @@ type ClusterServiceVersionDeploymentScaler struct {
4746 odfOwnedCsvNames map [string ]bool
4847}
4948
49+ const (
50+ // keyReplicas is the key for the replicas in the defaults map
51+ keyReplicas = "replicas"
52+ )
53+
5054//+kubebuilder:rbac:groups=operators.coreos.com,resources=clusterserviceversions,verbs=get;patch
5155//+kubebuilder:rbac:groups="",resources=configmaps,verbs=get
5256
@@ -71,19 +75,35 @@ func (r *ClusterServiceVersionDeploymentScaler) Handle(ctx context.Context, req
7175 return admission .Allowed ("csv is not managed by ODF" )
7276 }
7377
74- running , err := r .isPreviousCsvHasRunningDeployments (ctx , logger , csv )
75- if err != nil {
76- logger .Error (err , "failed getting replicas from csv" )
77- return admission .Errored (http .StatusInternalServerError , fmt .Errorf ("failed getting replicas from csv: %v" , err ))
78+ var isPrevCsvFound bool
79+ if csv .Spec .Replaces != "" {
80+ prevCsv := & opv1a1.ClusterServiceVersion {}
81+ key := client.ObjectKey {Name : csv .Spec .Replaces , Namespace : csv .Namespace }
82+ if err := r .Client .Get (ctx , key , prevCsv ); err != nil {
83+ if client .IgnoreNotFound (err ) != nil {
84+ logger .Error (err , "failed to get previous CSV" , "csv" , csv .Spec .Replaces )
85+ return admission .Errored (http .StatusInternalServerError , fmt .Errorf ("failed getting previous csv %q: %w" , csv .Spec .Replaces , err ))
86+ }
87+ } else {
88+ logger .Info ("previous CSV found" , "csv" , csv .Spec .Replaces )
89+ isPrevCsvFound = true
90+ defaultsMap := map [string ]any {}
91+ // in case previours csv has n deployments and current csv has n+1 deployments the the defaults map will be used to scale down replicas of the new deployment
92+ // also the defaults map can be used to set default values for new deployments in the future
93+ if r .isCsvHasRunningDeployments (prevCsv ) {
94+ defaultsMap [keyReplicas ] = 1
95+ } else {
96+ defaultsMap [keyReplicas ] = 0
97+ }
98+ r .syncNewCsvWithPrevCsv (prevCsv , csv , defaultsMap )
99+ }
78100 }
79101
80- if running {
81- logger .Info ("ignoring csv as the previous csv deployments are running " )
82- return admission . Allowed ( "previous csv deployments are running" )
102+ if ! isPrevCsvFound {
103+ logger .Info ("scaling down deployments" )
104+ r . scaleDownCsvDeployments ( logger , csv )
83105 }
84106
85- r .scaleDownCsvDeployments (logger , csv )
86-
87107 marshaledCsv , err := json .Marshal (csv )
88108 if err != nil {
89109 logger .Error (err , "failed marshaling csv" )
@@ -123,35 +143,51 @@ func (r *ClusterServiceVersionDeploymentScaler) loadOdfConfigMapData(ctx context
123143 return nil
124144}
125145
126- func (r * ClusterServiceVersionDeploymentScaler ) isPreviousCsvHasRunningDeployments (ctx context.Context , logger logr.Logger , csv * opv1a1.ClusterServiceVersion ) (bool , error ) {
146+ // syncNewCsvWithPrevCsv copies the required fields from the previous csv that are required after upgrade in the new csv
147+ func (r * ClusterServiceVersionDeploymentScaler ) syncNewCsvWithPrevCsv (
148+ prevCsv * opv1a1.ClusterServiceVersion ,
149+ newCsv * opv1a1.ClusterServiceVersion ,
150+ defaultsMap map [string ]any ) {
127151
128- if csv .Spec .Replaces == "" {
129- logger .Info ("csv.Spec.Replaces is not populated" )
130- return false , nil
131- }
152+ prevDeployments := prevCsv .Spec .InstallStrategy .StrategySpec .DeploymentSpecs
153+ newDeployments := newCsv .Spec .InstallStrategy .StrategySpec .DeploymentSpecs
132154
133- prevCsv := & opv1a1. ClusterServiceVersion {}
134- key := client. ObjectKey { Name : csv . Spec . Replaces , Namespace : csv . Namespace }
155+ for i := range newDeployments {
156+ newDeployment := & newDeployments [ i ]
135157
136- if err := r .Client .Get (ctx , key , prevCsv ); errors .IsNotFound (err ) {
137- // new install where an previous csv does not exists
138- return false , nil
139- } else if err != nil {
140- logger .Error (err , "failed getting previous csv" )
141- return false , err
158+ var deploymentFound bool
159+
160+ for j := range prevDeployments {
161+ prevDeployment := & prevDeployments [j ]
162+
163+ if newDeployment .Name == prevDeployment .Name {
164+ newDeployment .Spec .Template .Spec .HostNetwork = prevDeployment .Spec .Template .Spec .HostNetwork
165+ newDeployment .Spec .Template .Spec .DNSPolicy = prevDeployment .Spec .Template .Spec .DNSPolicy
166+ newDeployment .Spec .Replicas = prevDeployment .Spec .Replicas
167+ deploymentFound = true
168+ break
169+ }
170+ }
171+ // if the deployment is not found in the previous csv, then the defaults map will be used to set values in the new csv
172+ if ! deploymentFound {
173+ if val , ok := defaultsMap [keyReplicas ].(int32 ); ok {
174+ newDeployment .Spec .Replicas = ptr .To (val )
175+ }
176+ }
142177 }
178+ }
179+
180+ func (r * ClusterServiceVersionDeploymentScaler ) isCsvHasRunningDeployments (csv * opv1a1.ClusterServiceVersion ) bool {
143181
144- deployments := prevCsv .Spec .InstallStrategy .StrategySpec .DeploymentSpecs
182+ deployments := csv .Spec .InstallStrategy .StrategySpec .DeploymentSpecs
145183 for i := range deployments {
146184 deployment := & deployments [i ]
147185 if deployment .Spec .Replicas == nil || * deployment .Spec .Replicas > 0 {
148- // upgrade case where an older csv is found with replica 1
149- return true , nil
186+ return true
150187 }
151188 }
152189
153- // upgrade case where an older csv is found with replica 0
154- return false , nil
190+ return false
155191}
156192
157193func (r * ClusterServiceVersionDeploymentScaler ) scaleDownCsvDeployments (logger logr.Logger , csv * opv1a1.ClusterServiceVersion ) {
0 commit comments