Skip to content

Commit f630ac8

Browse files
committed
Replace custom deployment controller
Use DeploymentController to manage Deployment. Remove the custom controller. Ad move functions from the original controller to global functions, so they can be used by the hooks.
1 parent 0a13850 commit f630ac8

File tree

4 files changed

+128
-393
lines changed

4 files changed

+128
-393
lines changed

pkg/operator/csidriveroperator/deploymentcontroller.go

Lines changed: 58 additions & 218 deletions
Original file line numberDiff line numberDiff line change
@@ -2,165 +2,31 @@ package csidriveroperator
22

33
import (
44
"bytes"
5-
"context"
65
"fmt"
76
"strconv"
87
"strings"
98
"time"
109

1110
appsv1 "k8s.io/api/apps/v1"
12-
"k8s.io/client-go/kubernetes"
13-
"k8s.io/klog/v2"
1411

1512
configv1 "github.com/openshift/api/config/v1"
1613
operatorv1 "github.com/openshift/api/operator/v1"
1714
configv1listers "github.com/openshift/client-go/config/listers/config/v1"
15+
"github.com/openshift/cluster-storage-operator/assets"
16+
"github.com/openshift/cluster-storage-operator/pkg/csoclients"
17+
"github.com/openshift/cluster-storage-operator/pkg/operator/configobservation/util"
18+
"github.com/openshift/cluster-storage-operator/pkg/operator/csidriveroperator/csioperatorclient"
1819
"github.com/openshift/library-go/pkg/controller/factory"
1920
"github.com/openshift/library-go/pkg/operator/deploymentcontroller"
2021
"github.com/openshift/library-go/pkg/operator/events"
2122
"github.com/openshift/library-go/pkg/operator/loglevel"
22-
"github.com/openshift/library-go/pkg/operator/resource/resourceapply"
23-
"github.com/openshift/library-go/pkg/operator/resource/resourcemerge"
2423
"github.com/openshift/library-go/pkg/operator/status"
25-
"github.com/openshift/library-go/pkg/operator/v1helpers"
26-
27-
"github.com/openshift/cluster-storage-operator/pkg/csoclients"
28-
"github.com/openshift/cluster-storage-operator/pkg/operator/configobservation/util"
29-
"github.com/openshift/cluster-storage-operator/pkg/operator/csidriveroperator/csioperatorclient"
30-
csoutils "github.com/openshift/cluster-storage-operator/pkg/utils"
3124
)
3225

3326
const (
3427
deploymentControllerName = "CSIDriverOperatorDeployment"
3528
)
3629

37-
type CommonCSIDeploymentController struct {
38-
name string
39-
operatorClient v1helpers.OperatorClient
40-
commonClients *csoclients.Clients
41-
csiOperatorConfig csioperatorclient.CSIOperatorConfig
42-
kubeClient kubernetes.Interface
43-
versionGetter status.VersionGetter
44-
targetVersion string
45-
eventRecorder events.Recorder
46-
infraLister configv1listers.InfrastructureLister
47-
resyncInterval time.Duration
48-
factory *factory.Factory
49-
replacers []*strings.Replacer
50-
manifestHooks []deploymentcontroller.ManifestHookFunc
51-
deploymentHooks []deploymentcontroller.DeploymentHookFunc
52-
}
53-
54-
func (c *CommonCSIDeploymentController) initController(factoryHookFunc func(*factory.Factory)) *factory.Factory {
55-
f := factory.New()
56-
f = f.ResyncEvery(c.resyncInterval)
57-
f = f.WithSyncDegradedOnError(c.operatorClient)
58-
// Necessary to do initial Sync after the controller starts.
59-
f = f.WithPostStartHooks(initalSync)
60-
// Add informers to the factory now, but the actual event handlers
61-
// are added later in CSIDriverOperatorDeploymentController.Run(),
62-
// when we're 100% sure the controller is going to start (because it
63-
// depends on the platform).
64-
// If we added the event handlers now, all events would pile up in the
65-
// controller queue, without anything reading it.
66-
f = f.WithInformers(
67-
c.commonClients.OperatorClient.Informer())
68-
factoryHookFunc(f)
69-
return f
70-
}
71-
72-
func (c *CommonCSIDeploymentController) preCheckSync(
73-
ctx context.Context,
74-
syncCtx factory.SyncContext) (bool, *operatorv1.OperatorStatus, *operatorv1.OperatorSpec, error) {
75-
76-
opSpec, opStatus, _, err := c.operatorClient.GetOperatorState()
77-
if err != nil {
78-
return false, opStatus, opSpec, err
79-
}
80-
if opSpec.ManagementState != operatorv1.Managed {
81-
return false, opStatus, opSpec, nil
82-
}
83-
return true, opStatus, opSpec, nil
84-
}
85-
86-
func (c *CommonCSIDeploymentController) postSync(ctx context.Context, deployment *appsv1.Deployment) error {
87-
progressingCondition := operatorv1.OperatorCondition{
88-
Type: c.name + operatorv1.OperatorStatusTypeProgressing,
89-
Status: operatorv1.ConditionFalse,
90-
}
91-
92-
if ok, msg := isProgressing(deployment); ok {
93-
progressingCondition.Status = operatorv1.ConditionTrue
94-
progressingCondition.Message = msg
95-
progressingCondition.Reason = "Deploying"
96-
}
97-
98-
updateStatusFn := func(newStatus *operatorv1.OperatorStatus) error {
99-
resourcemerge.SetDeploymentGeneration(&newStatus.Generations, deployment)
100-
return nil
101-
}
102-
103-
_, _, err := v1helpers.UpdateStatus(
104-
ctx,
105-
c.operatorClient,
106-
updateStatusFn,
107-
v1helpers.UpdateConditionFn(progressingCondition),
108-
)
109-
return err
110-
}
111-
112-
func initCommonDeploymentParams(
113-
client *csoclients.Clients,
114-
csiOperatorConfig csioperatorclient.CSIOperatorConfig,
115-
resyncInterval time.Duration,
116-
versionGetter status.VersionGetter,
117-
targetVersion string,
118-
eventRecorder events.Recorder) CommonCSIDeploymentController {
119-
c := CommonCSIDeploymentController{
120-
name: csiOperatorConfig.ConditionPrefix,
121-
operatorClient: client.OperatorClient,
122-
kubeClient: client.KubeClient,
123-
csiOperatorConfig: csiOperatorConfig,
124-
commonClients: client,
125-
versionGetter: versionGetter,
126-
targetVersion: targetVersion,
127-
resyncInterval: resyncInterval,
128-
eventRecorder: eventRecorder.WithComponentSuffix(csiOperatorConfig.ConditionPrefix),
129-
infraLister: client.ConfigInformers.Config().V1().Infrastructures().Lister(),
130-
}
131-
c.manifestHooks = []deploymentcontroller.ManifestHookFunc{
132-
c.getReplacerHook(),
133-
c.getLogLevelHook(),
134-
}
135-
c.deploymentHooks = []deploymentcontroller.DeploymentHookFunc{
136-
c.getProxyHook(),
137-
}
138-
139-
// Common replacers
140-
c.replacers = []*strings.Replacer{sidecarReplacer}
141-
if csiOperatorConfig.ImageReplacer != nil {
142-
c.replacers = append(c.replacers, csiOperatorConfig.ImageReplacer)
143-
}
144-
145-
return c
146-
}
147-
148-
// This CSIDriverStarterController installs and syncs CSI driver operator Deployment.
149-
// It replace ${LOG_LEVEL} in the Deployment with current log level.
150-
// It replaces images in the Deployment using CSIOperatorConfig.ImageReplacer.
151-
// It produces following Conditions:
152-
// <CSI driver name>CSIDriverOperatorDeploymentProgressing
153-
// <CSI driver name>CSIDriverOperatorDeploymentDegraded
154-
// This controller doesn't set the Available condition to avoid prematurely cascading
155-
// up to the clusteroperator CR a potential Available=false. On the other hand it
156-
// does a better in making sure the Degraded condition is properly set if
157-
// Deployment isn't healthy.
158-
type CSIDriverOperatorDeploymentController struct {
159-
CommonCSIDeploymentController
160-
}
161-
162-
var _ factory.Controller = &CSIDriverOperatorDeploymentController{}
163-
16430
func NewCSIDriverOperatorDeploymentController(
16531
clients *csoclients.Clients,
16632
csiOperatorConfig csioperatorclient.CSIOperatorConfig,
@@ -169,28 +35,65 @@ func NewCSIDriverOperatorDeploymentController(
16935
eventRecorder events.Recorder,
17036
resyncInterval time.Duration,
17137
) factory.Controller {
172-
c := &CSIDriverOperatorDeploymentController{
173-
CommonCSIDeploymentController: initCommonDeploymentParams(clients, csiOperatorConfig, resyncInterval, versionGetter, targetVersion, eventRecorder),
38+
39+
deploymentBytes, err := assets.ReadFile(csiOperatorConfig.DeploymentAsset)
40+
if err != nil {
41+
panic(err)
42+
}
43+
44+
manifestHooks, deploymentHooks := getCommonHooks(getCommonReplacers(csiOperatorConfig))
45+
deploymentHooks = append(deploymentHooks, getStandaloneNodeSelectorHook(clients.ConfigInformers.Config().V1().Infrastructures().Lister()))
46+
47+
c, err := deploymentcontroller.NewDeploymentControllerBuilder(
48+
csiOperatorConfig.ConditionPrefix+deploymentControllerName,
49+
deploymentBytes,
50+
eventRecorder,
51+
clients.OperatorClient,
52+
clients.KubeClient,
53+
clients.KubeInformers.InformersFor(csoclients.CSIOperatorNamespace).Apps().V1().Deployments(),
54+
).WithConditions(
55+
// Explicitly disable Available condition to avoid prematurely cascading
56+
// up to the clusteroperator CR a potential Available=false.
57+
operatorv1.OperatorStatusTypeProgressing,
58+
operatorv1.OperatorStatusTypeDegraded,
59+
).WithExtraInformers(
60+
clients.ConfigInformers.Config().V1().Infrastructures().Informer(),
61+
).WithManifestHooks(
62+
manifestHooks...,
63+
).WithDeploymentHooks(
64+
deploymentHooks...,
65+
).WithPostStartHooks(
66+
initalSync,
67+
).ToController()
68+
if err != nil {
69+
panic(err)
17470
}
175-
f := c.initController(func(f *factory.Factory) {
176-
f.WithInformers(
177-
c.commonClients.KubeInformers.InformersFor(csoclients.CSIOperatorNamespace).Apps().V1().Deployments().Informer(),
178-
c.commonClients.ConfigInformers.Config().V1().Infrastructures().Informer())
179-
})
71+
return c
72+
}
18073

181-
// Standalone specific deployment hooks
182-
c.deploymentHooks = append(c.deploymentHooks, c.getStandaloneNodeSelectorHook())
74+
func getCommonReplacers(csiOperatorConfig csioperatorclient.CSIOperatorConfig) []*strings.Replacer {
75+
replacers := []*strings.Replacer{sidecarReplacer}
76+
if csiOperatorConfig.ImageReplacer != nil {
77+
replacers = append(replacers, csiOperatorConfig.ImageReplacer)
78+
}
79+
return replacers
80+
}
18381

184-
c.factory = f
185-
return c
82+
func getCommonHooks(replacers []*strings.Replacer) ([]deploymentcontroller.ManifestHookFunc, []deploymentcontroller.DeploymentHookFunc) {
83+
return []deploymentcontroller.ManifestHookFunc{
84+
getReplacerHook(replacers),
85+
getLogLevelHook(),
86+
}, []deploymentcontroller.DeploymentHookFunc{
87+
getProxyHook(),
88+
}
18689
}
18790

188-
func (c *CommonCSIDeploymentController) getReplacerHook() deploymentcontroller.ManifestHookFunc {
91+
func getReplacerHook(replacers []*strings.Replacer) deploymentcontroller.ManifestHookFunc {
18992
return func(spec *operatorv1.OperatorSpec, deploymentBytes []byte) ([]byte, error) {
19093
deploymentString := string(deploymentBytes)
19194

19295
// Replace images
193-
for _, replacer := range c.replacers {
96+
for _, replacer := range replacers {
19497
// Replace images
19598
if replacer != nil {
19699
deploymentString = replacer.Replace(deploymentString)
@@ -201,23 +104,23 @@ func (c *CommonCSIDeploymentController) getReplacerHook() deploymentcontroller.M
201104
}
202105
}
203106

204-
func (c *CommonCSIDeploymentController) getLogLevelHook() deploymentcontroller.ManifestHookFunc {
107+
func getLogLevelHook() deploymentcontroller.ManifestHookFunc {
205108
return func(spec *operatorv1.OperatorSpec, deploymentBytes []byte) ([]byte, error) {
206109
logLevel := loglevel.LogLevelToVerbosity(spec.LogLevel)
207110
deploymentBytes = bytes.ReplaceAll(deploymentBytes, []byte("${LOG_LEVEL}"), []byte(strconv.Itoa(logLevel)))
208111
return deploymentBytes, nil
209112
}
210113
}
211114

212-
func (c *CommonCSIDeploymentController) getProxyHook() deploymentcontroller.DeploymentHookFunc {
115+
func getProxyHook() deploymentcontroller.DeploymentHookFunc {
213116
return func(spec *operatorv1.OperatorSpec, deployment *appsv1.Deployment) error {
214117
return util.InjectObservedProxyInDeploymentContainers(deployment, spec)
215118
}
216119
}
217120

218-
func (c *CSIDriverOperatorDeploymentController) getStandaloneNodeSelectorHook() deploymentcontroller.DeploymentHookFunc {
121+
func getStandaloneNodeSelectorHook(infraLister configv1listers.InfrastructureLister) deploymentcontroller.DeploymentHookFunc {
219122
return func(spec *operatorv1.OperatorSpec, deployment *appsv1.Deployment) error {
220-
infra, err := c.infraLister.Get(infraConfigName)
123+
infra, err := infraLister.Get(infraConfigName)
221124
if err != nil {
222125
return fmt.Errorf("failed to get infrastructure resource: %w", err)
223126
}
@@ -227,66 +130,3 @@ func (c *CSIDriverOperatorDeploymentController) getStandaloneNodeSelectorHook()
227130
return nil
228131
}
229132
}
230-
231-
func (c *CSIDriverOperatorDeploymentController) Sync(ctx context.Context, syncCtx factory.SyncContext) error {
232-
klog.V(4).Infof("CSIDriverOperatorDeploymentController sync started")
233-
defer klog.V(4).Infof("CSIDriverOperatorDeploymentController sync finished")
234-
235-
runSync, opStatus, opSpec, err := c.preCheckSync(ctx, syncCtx)
236-
if err != nil {
237-
return err
238-
}
239-
240-
if !runSync {
241-
return nil
242-
}
243-
244-
required, err := csoutils.GetRequiredDeployment(c.csiOperatorConfig.DeploymentAsset, opSpec, c.manifestHooks, nil)
245-
if err != nil {
246-
return fmt.Errorf("failed to generate required Deployment: %s", err)
247-
}
248-
249-
requiredCopy := required.DeepCopy()
250-
251-
lastGeneration := resourcemerge.ExpectedDeploymentGeneration(requiredCopy, opStatus.Generations)
252-
deployment, _, err := resourceapply.ApplyDeployment(ctx, c.kubeClient.AppsV1(), c.eventRecorder, requiredCopy, lastGeneration)
253-
if err != nil {
254-
return err
255-
}
256-
257-
err = c.postSync(ctx, deployment)
258-
if err != nil {
259-
return err
260-
}
261-
return checkDeploymentHealth(ctx, c.kubeClient.AppsV1(), deployment)
262-
}
263-
264-
func (c *CSIDriverOperatorDeploymentController) Run(ctx context.Context, workers int) {
265-
// This adds event handlers to informers.
266-
ctrl := c.factory.WithSync(c.Sync).ToController(c.Name(), c.eventRecorder)
267-
ctrl.Run(ctx, workers)
268-
}
269-
270-
func (c *CSIDriverOperatorDeploymentController) Name() string {
271-
return c.name + deploymentControllerName
272-
}
273-
274-
// TODO: create a common function in library-go
275-
func isProgressing(deployment *appsv1.Deployment) (bool, string) {
276-
var deploymentExpectedReplicas int32
277-
if deployment.Spec.Replicas != nil {
278-
deploymentExpectedReplicas = *deployment.Spec.Replicas
279-
}
280-
281-
switch {
282-
case deployment.Generation != deployment.Status.ObservedGeneration:
283-
return true, "Waiting for Deployment to act on changes"
284-
case deployment.Status.UnavailableReplicas > 0:
285-
return true, "Waiting for Deployment to deploy pods"
286-
case deployment.Status.UpdatedReplicas < deploymentExpectedReplicas:
287-
return true, "Waiting for Deployment to update pods"
288-
case deployment.Status.AvailableReplicas < deploymentExpectedReplicas:
289-
return true, "Waiting for Deployment to deploy pods"
290-
}
291-
return false, ""
292-
}

0 commit comments

Comments
 (0)