Skip to content

Commit b613fb9

Browse files
committed
Replace custom deployment controller
1 parent bd620ce commit b613fb9

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)