• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kubernetes-sigs / azuredisk-csi-driver / 3598308181

02 Dec 2022 02:34AM UTC coverage: 70.266% (+25.1%) from 45.203%
3598308181

push

github

GitHub
Merge pull request #1570 from hccheng72/uninstall-path

116 of 116 new or added lines in 6 files covered. (100.0%)

7014 of 9982 relevant lines covered (70.27%)

6.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.42
/pkg/controller/attach_detach.go
1
/*
2
Copyright 2021 The Kubernetes Authors.
3

4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7

8
    http://www.apache.org/licenses/LICENSE-2.0
9

10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16

17
package controller
18

19
import (
20
        "context"
21
        "strings"
22
        "sync"
23
        "sync/atomic"
24

25
        "github.com/go-logr/logr"
26
        "google.golang.org/grpc/codes"
27
        "google.golang.org/grpc/status"
28
        v1 "k8s.io/api/core/v1"
29
        storagev1 "k8s.io/api/storage/v1"
30
        "k8s.io/apimachinery/pkg/api/errors"
31
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32
        utilfeature "k8s.io/apiserver/pkg/util/feature"
33
        "k8s.io/kubernetes/pkg/features"
34
        azdiskv1beta2 "sigs.k8s.io/azuredisk-csi-driver/pkg/apis/azuredisk/v1beta2"
35
        consts "sigs.k8s.io/azuredisk-csi-driver/pkg/azureconstants"
36
        "sigs.k8s.io/azuredisk-csi-driver/pkg/azureutils"
37
        "sigs.k8s.io/azuredisk-csi-driver/pkg/provisioner"
38
        "sigs.k8s.io/azuredisk-csi-driver/pkg/util"
39
        "sigs.k8s.io/azuredisk-csi-driver/pkg/workflow"
40

41
        volerr "k8s.io/cloud-provider/volume/errors"
42
        "sigs.k8s.io/controller-runtime/pkg/client"
43
        "sigs.k8s.io/controller-runtime/pkg/controller"
44
        "sigs.k8s.io/controller-runtime/pkg/handler"
45
        "sigs.k8s.io/controller-runtime/pkg/manager"
46
        "sigs.k8s.io/controller-runtime/pkg/reconcile"
47
        "sigs.k8s.io/controller-runtime/pkg/source"
48
)
49

50
type CloudDiskAttachDetacher interface {
51
        PublishVolume(ctx context.Context, volumeID string, nodeID string, volumeContext map[string]string) provisioner.CloudAttachResult
52
        UnpublishVolume(ctx context.Context, volumeID string, nodeID string) error
53
}
54

55
type CrdDetacher interface {
56
        UnpublishVolume(ctx context.Context, volumeID string, nodeID string, secrets map[string]string, mode consts.UnpublishMode) error
57
        WaitForDetach(ctx context.Context, volumeID, nodeID string) error
58
}
59

60
/*
61
Attach Detach controller is responsible for
62
 1. attaching volume to a specified node upon creation of AzVolumeAttachment CRI
63
 2. promoting AzVolumeAttachment to primary upon spec update
64
 3. detaching volume upon deletions marked with certain annotations
65
*/
66
type ReconcileAttachDetach struct {
67
        *SharedState
68
        logger            logr.Logger
69
        crdDetacher       CrdDetacher
70
        cloudDiskAttacher CloudDiskAttachDetacher
71
        stateLock         *sync.Map
72
        retryInfo         *retryInfo
73
}
74

75
var _ reconcile.Reconciler = &ReconcileAttachDetach{}
76

77
var allowedTargetAttachmentStates = map[string][]string{
78
        "":                                       {string(azdiskv1beta2.AttachmentPending), string(azdiskv1beta2.Attaching), string(azdiskv1beta2.Detaching)},
79
        string(azdiskv1beta2.AttachmentPending):  {string(azdiskv1beta2.Attaching), string(azdiskv1beta2.Detaching)},
80
        string(azdiskv1beta2.Attaching):          {string(azdiskv1beta2.Attached), string(azdiskv1beta2.AttachmentFailed)},
81
        string(azdiskv1beta2.Detaching):          {string(azdiskv1beta2.Detached), string(azdiskv1beta2.DetachmentFailed)},
82
        string(azdiskv1beta2.Attached):           {string(azdiskv1beta2.Detaching)},
83
        string(azdiskv1beta2.Detached):           {},
84
        string(azdiskv1beta2.AttachmentFailed):   {string(azdiskv1beta2.Detaching)},
85
        string(azdiskv1beta2.DetachmentFailed):   {string(azdiskv1beta2.ForceDetachPending)},
86
        string(azdiskv1beta2.ForceDetachPending): {string(azdiskv1beta2.Detaching)},
87
}
88

89
func (r *ReconcileAttachDetach) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) {
6✔
90
        if !r.isRecoveryComplete() {
6✔
91
                return reconcile.Result{Requeue: true}, nil
×
92
        }
×
93

94
        azVolumeAttachment, err := azureutils.GetAzVolumeAttachment(ctx, r.cachedClient, r.azClient, request.Name, request.Namespace, true)
6✔
95
        // if object is not found, it means the object has been deleted. Log the deletion and do not requeue
6✔
96
        if errors.IsNotFound(err) {
6✔
97
                r.azVolumeAttachmentToVaMap.Delete(request.Name)
×
98
                return reconcileReturnOnSuccess(request.Name, r.retryInfo)
×
99
        } else if err != nil {
6✔
100
                azVolumeAttachment.Name = request.Name
×
101
                return reconcileReturnOnError(ctx, azVolumeAttachment, "get", err, r.retryInfo)
×
102
        }
×
103

104
        ctx, _ = workflow.GetWorkflowFromObj(ctx, azVolumeAttachment)
6✔
105

6✔
106
        // if underlying cloud operation already in process, skip until operation is completed
6✔
107
        if isOperationInProcess(azVolumeAttachment) {
6✔
108
                return reconcileReturnOnSuccess(azVolumeAttachment.Name, r.retryInfo)
×
109
        }
×
110

111
        // deletion request
112
        if deleteRequested, deleteAfter := objectDeletionRequested(azVolumeAttachment); deleteRequested {
7✔
113
                if deleteAfter > 0 {
1✔
114
                        return reconcileAfter(deleteAfter, request.Name, r.retryInfo)
×
115
                }
×
116
                if err := r.removeFinalizer(ctx, azVolumeAttachment); err != nil {
1✔
117
                        return reconcileReturnOnError(ctx, azVolumeAttachment, "delete", err, r.retryInfo)
×
118
                }
×
119
                // deletion of azVolumeAttachment is succeeded, the node's remaining capacity of disk attachment should be increased by 1
120
                r.incrementAttachmentCount(ctx, azVolumeAttachment.Spec.NodeName)
1✔
121
                // detachment request
122
        } else if volumeDetachRequested(azVolumeAttachment) {
6✔
123
                if err := r.triggerDetach(ctx, azVolumeAttachment); err != nil {
1✔
124
                        return reconcileReturnOnError(ctx, azVolumeAttachment, "detach", err, r.retryInfo)
×
125
                }
×
126
                // attachment request
127
        } else if azVolumeAttachment.Status.Detail == nil {
6✔
128
                if err := r.triggerAttach(ctx, azVolumeAttachment); err != nil {
3✔
129
                        return reconcileReturnOnError(ctx, azVolumeAttachment, "attach", err, r.retryInfo)
1✔
130
                }
1✔
131
                // promotion request
132
        } else if azVolumeAttachment.Spec.RequestedRole != azVolumeAttachment.Status.Detail.Role {
4✔
133
                switch azVolumeAttachment.Spec.RequestedRole {
2✔
134
                case azdiskv1beta2.PrimaryRole:
1✔
135
                        if err := r.promote(ctx, azVolumeAttachment); err != nil {
1✔
136
                                return reconcileReturnOnError(ctx, azVolumeAttachment, "promote", err, r.retryInfo)
×
137
                        }
×
138
                case azdiskv1beta2.ReplicaRole:
1✔
139
                        if err := r.demote(ctx, azVolumeAttachment); err != nil {
1✔
140
                                return reconcileReturnOnError(ctx, azVolumeAttachment, "demote", err, r.retryInfo)
×
141
                        }
×
142
                }
143
        }
144

145
        return reconcileReturnOnSuccess(azVolumeAttachment.Name, r.retryInfo)
5✔
146
}
147

148
func (r *ReconcileAttachDetach) triggerAttach(ctx context.Context, azVolumeAttachment *azdiskv1beta2.AzVolumeAttachment) error {
2✔
149
        var err error
2✔
150
        ctx, w := workflow.New(ctx)
2✔
151
        defer func() { w.Finish(err) }()
4✔
152

153
        // requeue if AzVolumeAttachment's state is being updated by a different worker
154
        if _, ok := r.stateLock.LoadOrStore(azVolumeAttachment.Name, nil); ok {
2✔
155
                err = getOperationRequeueError("attach", azVolumeAttachment)
×
156
                return err
×
157
        }
×
158
        defer r.stateLock.Delete(azVolumeAttachment.Name)
2✔
159

2✔
160
        if !volumeAttachRequested(azVolumeAttachment) {
3✔
161
                err = status.Errorf(codes.FailedPrecondition, "attach operation has not yet been requested")
1✔
162
                return err
1✔
163
        }
1✔
164

165
        var azVolume *azdiskv1beta2.AzVolume
1✔
166
        if azVolume, err = azureutils.GetAzVolume(ctx, r.cachedClient, r.azClient, strings.ToLower(azVolumeAttachment.Spec.VolumeName), r.config.ObjectNamespace, true); err != nil {
1✔
167
                if errors.IsNotFound(err) {
×
168
                        w.Logger().V(5).Infof("Aborting attach operation for AzVolumeAttachment (%s): AzVolume (%s) not found", azVolumeAttachment.Name, azVolumeAttachment.Spec.VolumeName)
×
169
                        err = nil
×
170
                        return nil
×
171
                }
×
172
                return err
×
173
        } else if deleteRequested, _ := objectDeletionRequested(azVolume); deleteRequested {
1✔
174
                w.Logger().V(5).Infof("Aborting attach operation for AzVolumeAttachment (%s): AzVolume (%s) scheduled for deletion", azVolumeAttachment.Name, azVolumeAttachment.Spec.VolumeName)
×
175
                return nil
×
176
        }
×
177

178
        // update status block
179
        updateFunc := func(obj client.Object) error {
2✔
180
                azv := obj.(*azdiskv1beta2.AzVolumeAttachment)
1✔
181
                // Update state to attaching, Initialize finalizer and add label to the object
1✔
182
                _, derr := updateState(azv, azdiskv1beta2.Attaching, normalUpdate)
1✔
183
                return derr
1✔
184
        }
1✔
185

186
        var updatedObj client.Object
1✔
187
        if updatedObj, err = azureutils.UpdateCRIWithRetry(ctx, nil, r.cachedClient, r.azClient, azVolumeAttachment, updateFunc, consts.NormalUpdateMaxNetRetry, azureutils.UpdateCRIStatus); err != nil {
1✔
188
                return err
×
189
        }
×
190
        azVolumeAttachment = updatedObj.(*azdiskv1beta2.AzVolumeAttachment)
1✔
191

1✔
192
        w.Logger().V(5).Info("Attaching volume")
1✔
193
        waitCh := make(chan goSignal)
1✔
194
        //nolint:contextcheck // call is asynchronous; context is not inherited by design
1✔
195
        go func() {
2✔
196
                var attachErr error
1✔
197
                _, goWorkflow := workflow.New(ctx)
1✔
198
                defer func() { goWorkflow.Finish(attachErr) }()
2✔
199
                waitCh <- goSignal{}
1✔
200

1✔
201
                goCtx := goWorkflow.SaveToContext(context.Background())
1✔
202
                cloudCtx, cloudCancel := context.WithTimeout(goCtx, cloudTimeout)
1✔
203
                defer cloudCancel()
1✔
204

1✔
205
                // attempt to attach the disk to a node
1✔
206
                var publishCtx map[string]string
1✔
207
                var pods []v1.Pod
1✔
208
                if azVolumeAttachment.Spec.RequestedRole == azdiskv1beta2.ReplicaRole {
1✔
209
                        var err error
×
210
                        pods, err = r.getPodsFromVolume(goCtx, r.cachedClient, azVolumeAttachment.Spec.VolumeName)
×
211
                        if err != nil {
×
212
                                goWorkflow.Logger().Error(err, "failed to list pods for volume")
×
213
                        }
×
214
                }
215

216
                var handleSuccess func(bool)
1✔
217
                var handleError func()
1✔
218

1✔
219
                attachAndUpdate := func() {
2✔
220
                        attachResult := r.attachVolume(cloudCtx, azVolumeAttachment.Spec.VolumeID, azVolumeAttachment.Spec.NodeName, azVolumeAttachment.Spec.VolumeContext)
1✔
221
                        if publishCtx = attachResult.PublishContext(); publishCtx != nil {
2✔
222
                                handleSuccess(false)
1✔
223
                        }
1✔
224
                        var ok bool
1✔
225
                        if attachErr, ok = <-attachResult.ResultChannel(); !ok || attachErr != nil {
1✔
226
                                handleError()
×
227
                        } else {
1✔
228
                                handleSuccess(true)
1✔
229
                        }
1✔
230
                }
231

232
                handleError = func() {
1✔
233
                        if len(pods) > 0 {
×
234
                                for _, pod := range pods {
×
235
                                        r.eventRecorder.Eventf(pod.DeepCopyObject(), v1.EventTypeWarning, consts.ReplicaAttachmentFailedEvent, "Replica mount for volume %s failed to be attached to node %s with error: %v", azVolumeAttachment.Spec.VolumeName, azVolumeAttachment.Spec.NodeName, attachErr)
×
236
                                }
×
237
                        }
238

239
                        // if the disk is attached to a different node
240
                        if danglingAttachErr, ok := attachErr.(*volerr.DanglingAttachError); ok {
×
241
                                // get disk, current node and attachment name
×
242
                                currentNodeName := string(danglingAttachErr.CurrentNode)
×
243
                                currentAttachmentName := azureutils.GetAzVolumeAttachmentName(azVolumeAttachment.Spec.VolumeName, currentNodeName)
×
244
                                goWorkflow.Logger().Infof("Dangling attach detected for %s", currentNodeName)
×
245

×
246
                                // check if AzVolumeAttachment exists for the existing attachment
×
247
                                _, err := r.azClient.DiskV1beta2().AzVolumeAttachments(r.config.ObjectNamespace).Get(cloudCtx, currentAttachmentName, metav1.GetOptions{})
×
248
                                var detachErr error
×
249
                                if errors.IsNotFound(err) {
×
250
                                        // AzVolumeAttachment doesn't exist so we only need to detach disk from cloud
×
251
                                        detachErr = r.cloudDiskAttacher.UnpublishVolume(goCtx, azVolumeAttachment.Spec.VolumeID, currentNodeName)
×
252
                                        if detachErr != nil {
×
253
                                                goWorkflow.Logger().Errorf(detachErr, "failed to detach dangling volume (%s) from node (%s). Error: %v", azVolumeAttachment.Spec.VolumeID, currentNodeName, err)
×
254
                                        }
×
255
                                } else {
×
256
                                        // AzVolumeAttachment exist so we need to detach disk through crdProvisioner
×
257
                                        detachErr = r.crdDetacher.UnpublishVolume(goCtx, azVolumeAttachment.Spec.VolumeID, currentNodeName, make(map[string]string), consts.Detach)
×
258
                                        if detachErr != nil {
×
259
                                                goWorkflow.Logger().Errorf(detachErr, "failed to make a unpublish request dangling AzVolumeAttachment for volume (%s) and node (%s)", azVolumeAttachment.Spec.VolumeID, currentNodeName)
×
260
                                        }
×
261
                                        detachErr = r.crdDetacher.WaitForDetach(goCtx, azVolumeAttachment.Spec.VolumeID, currentNodeName)
×
262
                                        if detachErr != nil {
×
263
                                                goWorkflow.Logger().Errorf(detachErr, "failed to unpublish dangling AzVolumeAttachment for volume (%s) and node (%s)", azVolumeAttachment.Spec.VolumeID, currentNodeName)
×
264
                                        }
×
265
                                }
266
                                // attempt to attach the disk to a node after detach
267
                                if detachErr == nil {
×
268
                                        attachAndUpdate()
×
269
                                        return
×
270
                                }
×
271
                        }
272

273
                        updateFunc := func(obj client.Object) error {
×
274
                                azv := obj.(*azdiskv1beta2.AzVolumeAttachment)
×
275
                                _, uerr := reportError(azv, azdiskv1beta2.AttachmentFailed, attachErr)
×
276
                                return uerr
×
277
                        }
×
278
                        //nolint:contextcheck // final status update of the CRI must occur even when the current context's deadline passes.
279
                        _, _ = azureutils.UpdateCRIWithRetry(goCtx, nil, r.cachedClient, r.azClient, azVolumeAttachment, updateFunc, consts.ForcedUpdateMaxNetRetry, azureutils.UpdateCRIStatus)
×
280
                }
281
                handleSuccess = func(asyncComplete bool) {
3✔
282
                        // Publish event to indicate attachment success
2✔
283
                        if asyncComplete {
3✔
284
                                if len(pods) > 0 {
1✔
285
                                        for _, pod := range pods {
×
286
                                                r.eventRecorder.Eventf(pod.DeepCopyObject(), v1.EventTypeNormal, consts.ReplicaAttachmentSuccessEvent, "Replica mount for volume %s successfully attached to node %s", azVolumeAttachment.Spec.VolumeName, azVolumeAttachment.Spec.NodeName)
×
287
                                        }
×
288
                                }
289
                                // the node's remaining capacity of disk attachment should be decreased by 1, since the disk attachment is succeeded.
290
                                r.decrementAttachmentCount(ctx, azVolumeAttachment.Spec.NodeName)
1✔
291
                        }
292

293
                        updateFunc := func(obj client.Object) error {
4✔
294
                                azv := obj.(*azdiskv1beta2.AzVolumeAttachment)
2✔
295
                                azv = updateStatusDetail(azv, publishCtx)
2✔
296
                                var uerr error
2✔
297
                                if asyncComplete {
3✔
298
                                        _, uerr = updateState(azv, azdiskv1beta2.Attached, forceUpdate)
1✔
299
                                }
1✔
300
                                return uerr
2✔
301
                        }
302

303
                        if asyncComplete && azVolumeAttachment.Spec.RequestedRole == azdiskv1beta2.PrimaryRole {
3✔
304
                                _ = r.updateVolumeAttachmentWithResult(goCtx, azVolumeAttachment)
1✔
305
                        }
1✔
306
                        updatedObj, _ = azureutils.UpdateCRIWithRetry(goCtx, nil, r.cachedClient, r.azClient, azVolumeAttachment, updateFunc, consts.ForcedUpdateMaxNetRetry, azureutils.UpdateCRIStatus)
2✔
307
                        azVolumeAttachment = updatedObj.(*azdiskv1beta2.AzVolumeAttachment)
2✔
308
                }
309

310
                attachAndUpdate()
1✔
311
        }()
312
        <-waitCh
1✔
313

1✔
314
        return nil
1✔
315
}
316

317
func (r *ReconcileAttachDetach) triggerDetach(ctx context.Context, azVolumeAttachment *azdiskv1beta2.AzVolumeAttachment) error {
1✔
318
        var err error
1✔
319
        ctx, w := workflow.New(ctx)
1✔
320
        defer func() { w.Finish(err) }()
2✔
321

322
        if _, ok := r.stateLock.LoadOrStore(azVolumeAttachment.Name, nil); ok {
1✔
323
                return getOperationRequeueError("detach", azVolumeAttachment)
×
324
        }
×
325
        defer r.stateLock.Delete(azVolumeAttachment.Name)
1✔
326

1✔
327
        updateFunc := func(obj client.Object) error {
2✔
328
                azv := obj.(*azdiskv1beta2.AzVolumeAttachment)
1✔
329
                // Update state to detaching
1✔
330
                _, derr := updateState(azv, azdiskv1beta2.Detaching, normalUpdate)
1✔
331
                return derr
1✔
332
        }
1✔
333

334
        var updatedObj client.Object
1✔
335
        if updatedObj, err = azureutils.UpdateCRIWithRetry(ctx, nil, r.cachedClient, r.azClient, azVolumeAttachment, updateFunc, consts.NormalUpdateMaxNetRetry, azureutils.UpdateCRIStatus); err != nil {
1✔
336
                return err
×
337
        }
×
338
        azVolumeAttachment = updatedObj.(*azdiskv1beta2.AzVolumeAttachment)
1✔
339

1✔
340
        w.Logger().V(5).Info("Detaching volume")
1✔
341
        waitCh := make(chan goSignal)
1✔
342
        //nolint:contextcheck // call is asynchronous; context is not inherited by design
1✔
343
        go func() {
2✔
344
                var detachErr error
1✔
345
                _, goWorkflow := workflow.New(ctx)
1✔
346
                defer func() { goWorkflow.Finish(detachErr) }()
2✔
347
                waitCh <- goSignal{}
1✔
348

1✔
349
                goCtx := goWorkflow.SaveToContext(context.Background())
1✔
350
                cloudCtx, cloudCancel := context.WithTimeout(goCtx, cloudTimeout)
1✔
351
                defer cloudCancel()
1✔
352

1✔
353
                var updateFunc func(obj client.Object) error
1✔
354
                detachErr = r.detachVolume(cloudCtx, azVolumeAttachment.Spec.VolumeID, azVolumeAttachment.Spec.NodeName)
1✔
355
                if detachErr != nil {
1✔
356
                        updateFunc = func(obj client.Object) error {
×
357
                                azv := obj.(*azdiskv1beta2.AzVolumeAttachment)
×
358
                                _, derr := reportError(azv, azdiskv1beta2.DetachmentFailed, detachErr)
×
359
                                return derr
×
360
                        }
×
361
                        //nolint:contextcheck // final status update of the CRI must occur even when the current context's deadline passes.
362
                        if _, uerr := azureutils.UpdateCRIWithRetry(goCtx, nil, r.cachedClient, r.azClient, azVolumeAttachment, updateFunc, consts.ForcedUpdateMaxNetRetry, azureutils.UpdateCRIStatus); uerr != nil {
×
363
                                w.Logger().Errorf(uerr, "failed to update final status of AzVolumeAttachement")
×
364
                        }
×
365
                } else {
1✔
366
                        //nolint:contextcheck // delete of the CRI must occur even when the current context's deadline passes.
1✔
367
                        if derr := r.cachedClient.Delete(goCtx, azVolumeAttachment); derr != nil {
1✔
368
                                w.Logger().Error(derr, "failed to delete AzVolumeAttachment")
×
369
                        }
×
370
                }
371
        }()
372
        <-waitCh
1✔
373
        return nil
1✔
374
}
375

376
func (r *ReconcileAttachDetach) removeFinalizer(ctx context.Context, azVolumeAttachment *azdiskv1beta2.AzVolumeAttachment) error {
1✔
377
        var err error
1✔
378
        ctx, w := workflow.New(ctx)
1✔
379
        defer func() { w.Finish(err) }()
2✔
380

381
        updateFunc := func(obj client.Object) error {
2✔
382
                azv := obj.(*azdiskv1beta2.AzVolumeAttachment)
1✔
383
                // delete finalizer
1✔
384
                _ = r.deleteFinalizer(azv)
1✔
385
                return nil
1✔
386
        }
1✔
387

388
        _, err = azureutils.UpdateCRIWithRetry(ctx, nil, r.cachedClient, r.azClient, azVolumeAttachment, updateFunc, consts.NormalUpdateMaxNetRetry, azureutils.UpdateCRI)
1✔
389
        return err
1✔
390
}
391

392
func (r *ReconcileAttachDetach) promote(ctx context.Context, azVolumeAttachment *azdiskv1beta2.AzVolumeAttachment) error {
1✔
393
        var err error
1✔
394
        ctx, w := workflow.New(ctx)
1✔
395
        defer func() { w.Finish(err) }()
2✔
396

397
        w.Logger().Infof("Promoting AzVolumeAttachment")
1✔
398
        if err = r.updateVolumeAttachmentWithResult(ctx, azVolumeAttachment); err != nil {
1✔
399
                return err
×
400
        }
×
401
        // initialize metadata and update status block
402
        updateFunc := func(obj client.Object) error {
2✔
403
                azv := obj.(*azdiskv1beta2.AzVolumeAttachment)
1✔
404
                _ = updateRole(azv, azdiskv1beta2.PrimaryRole)
1✔
405
                return nil
1✔
406
        }
1✔
407
        if _, err = azureutils.UpdateCRIWithRetry(ctx, nil, r.cachedClient, r.azClient, azVolumeAttachment, updateFunc, consts.NormalUpdateMaxNetRetry, azureutils.UpdateCRIStatus); err != nil {
1✔
408
                return err
×
409
        }
×
410
        return nil
1✔
411
}
412

413
func (r *ReconcileAttachDetach) demote(ctx context.Context, azVolumeAttachment *azdiskv1beta2.AzVolumeAttachment) error {
1✔
414
        var err error
1✔
415
        ctx, w := workflow.New(ctx)
1✔
416
        defer func() { w.Finish(err) }()
2✔
417

418
        w.Logger().V(5).Info("Demoting AzVolumeAttachment")
1✔
419
        // initialize metadata and update status block
1✔
420
        updateFunc := func(obj client.Object) error {
2✔
421
                azv := obj.(*azdiskv1beta2.AzVolumeAttachment)
1✔
422
                delete(azv.Status.Annotations, consts.VolumeAttachmentKey)
1✔
423
                _ = updateRole(azv, azdiskv1beta2.ReplicaRole)
1✔
424
                return nil
1✔
425
        }
1✔
426

427
        if _, err = azureutils.UpdateCRIWithRetry(ctx, nil, r.cachedClient, r.azClient, azVolumeAttachment, updateFunc, consts.NormalUpdateMaxNetRetry, azureutils.UpdateCRIStatus); err != nil {
1✔
428
                return err
×
429
        }
×
430
        return nil
1✔
431
}
432

433
func (r *ReconcileAttachDetach) updateVolumeAttachmentWithResult(ctx context.Context, azVolumeAttachment *azdiskv1beta2.AzVolumeAttachment) error {
2✔
434
        var vaName string
2✔
435
        vaName, err := r.waitForVolumeAttachmentName(ctx, azVolumeAttachment)
2✔
436
        if err != nil {
2✔
437
                return err
×
438
        }
×
439

440
        vaUpdateFunc := func(obj client.Object) error {
4✔
441
                va := obj.(*storagev1.VolumeAttachment)
2✔
442
                if azVolumeAttachment.Status.Detail != nil {
4✔
443
                        for key, value := range azVolumeAttachment.Status.Detail.PublishContext {
3✔
444
                                va.Status.AttachmentMetadata = azureutils.AddToMap(va.Status.AttachmentMetadata, key, value)
1✔
445
                        }
1✔
446
                }
447
                return nil
2✔
448
        }
449
        va := storagev1.VolumeAttachment{ObjectMeta: metav1.ObjectMeta{Name: vaName}}
2✔
450
        _, err = azureutils.UpdateCRIWithRetry(ctx, nil, r.cachedClient, r.azClient, &va, vaUpdateFunc, consts.ForcedUpdateMaxNetRetry, azureutils.UpdateCRIStatus)
2✔
451
        return err
2✔
452
}
453

454
func (r *ReconcileAttachDetach) deleteFinalizer(azVolumeAttachment *azdiskv1beta2.AzVolumeAttachment) *azdiskv1beta2.AzVolumeAttachment {
1✔
455
        if azVolumeAttachment == nil {
1✔
456
                return nil
×
457
        }
×
458

459
        if azVolumeAttachment.ObjectMeta.Finalizers == nil {
1✔
460
                return azVolumeAttachment
×
461
        }
×
462

463
        finalizers := []string{}
1✔
464
        for _, finalizer := range azVolumeAttachment.ObjectMeta.Finalizers {
2✔
465
                if finalizer == consts.AzVolumeAttachmentFinalizer {
2✔
466
                        continue
1✔
467
                }
468
                finalizers = append(finalizers, finalizer)
×
469
        }
470
        azVolumeAttachment.ObjectMeta.Finalizers = finalizers
1✔
471
        return azVolumeAttachment
1✔
472
}
473

474
func (r *ReconcileAttachDetach) attachVolume(ctx context.Context, volumeID, node string, volumeContext map[string]string) provisioner.CloudAttachResult {
1✔
475
        return r.cloudDiskAttacher.PublishVolume(ctx, volumeID, node, volumeContext)
1✔
476
}
1✔
477

478
func (r *ReconcileAttachDetach) detachVolume(ctx context.Context, volumeID, node string) error {
1✔
479
        return r.cloudDiskAttacher.UnpublishVolume(ctx, volumeID, node)
1✔
480
}
1✔
481

482
func (r *ReconcileAttachDetach) Recover(ctx context.Context) error {
3✔
483
        var err error
3✔
484
        ctx, w := workflow.New(ctx)
3✔
485
        defer func() { w.Finish(err) }()
6✔
486

487
        w.Logger().V(5).Info("Recovering AzVolumeAttachment CRIs...")
3✔
488
        // try to recreate missing AzVolumeAttachment CRI
3✔
489
        var syncedVolumeAttachments, volumesToSync map[string]bool
3✔
490

3✔
491
        for i := 0; i < maxRetry; i++ {
6✔
492
                if syncedVolumeAttachments, volumesToSync, err = r.recreateAzVolumeAttachment(ctx, syncedVolumeAttachments, volumesToSync); err == nil {
6✔
493
                        break
3✔
494
                }
495
                w.Logger().Error(err, "failed to recreate missing AzVolumeAttachment CRI")
×
496
        }
497
        // retrigger any aborted operation from possible previous controller crash
498
        recovered := &sync.Map{}
3✔
499
        for i := 0; i < maxRetry; i++ {
6✔
500
                if err = r.recoverAzVolumeAttachment(ctx, recovered); err == nil {
6✔
501
                        break
3✔
502
                }
503
                w.Logger().Error(err, "failed to recover AzVolumeAttachment state")
×
504
        }
505

506
        return err
3✔
507
}
508

509
func updateRole(azVolumeAttachment *azdiskv1beta2.AzVolumeAttachment, role azdiskv1beta2.Role) *azdiskv1beta2.AzVolumeAttachment {
4✔
510
        if azVolumeAttachment == nil {
4✔
511
                return nil
×
512
        }
×
513

514
        if azVolumeAttachment.Status.Detail == nil {
4✔
515
                return azVolumeAttachment
×
516
        }
×
517

518
        azVolumeAttachment.Status.Detail.PreviousRole = azVolumeAttachment.Status.Detail.Role
4✔
519
        azVolumeAttachment.Status.Detail.Role = role
4✔
520

4✔
521
        return azVolumeAttachment
4✔
522
}
523

524
func updateStatusDetail(azVolumeAttachment *azdiskv1beta2.AzVolumeAttachment, status map[string]string) *azdiskv1beta2.AzVolumeAttachment {
2✔
525
        if azVolumeAttachment == nil {
2✔
526
                return nil
×
527
        }
×
528

529
        if azVolumeAttachment.Status.Detail == nil {
3✔
530
                azVolumeAttachment.Status.Detail = &azdiskv1beta2.AzVolumeAttachmentStatusDetail{}
1✔
531
        }
1✔
532

533
        azVolumeAttachment.Status.Detail.PreviousRole = azVolumeAttachment.Status.Detail.Role
2✔
534
        azVolumeAttachment.Status.Detail.Role = azVolumeAttachment.Spec.RequestedRole
2✔
535
        azVolumeAttachment.Status.Detail.PublishContext = status
2✔
536

2✔
537
        return azVolumeAttachment
2✔
538
}
539

540
func updateError(azVolumeAttachment *azdiskv1beta2.AzVolumeAttachment, err error) *azdiskv1beta2.AzVolumeAttachment {
×
541
        if azVolumeAttachment == nil {
×
542
                return nil
×
543
        }
×
544

545
        if err != nil {
×
546
                azVolumeAttachment.Status.Error = util.NewAzError(err)
×
547
        }
×
548

549
        return azVolumeAttachment
×
550
}
551

552
func updateState(azVolumeAttachment *azdiskv1beta2.AzVolumeAttachment, state azdiskv1beta2.AzVolumeAttachmentAttachmentState, mode updateMode) (*azdiskv1beta2.AzVolumeAttachment, error) {
6✔
553
        var err error
6✔
554
        if azVolumeAttachment == nil {
6✔
555
                return nil, status.Errorf(codes.FailedPrecondition, "function `updateState` requires non-nil AzVolumeAttachment object.")
×
556
        }
×
557
        if mode == normalUpdate {
9✔
558
                if expectedStates, exists := allowedTargetAttachmentStates[string(azVolumeAttachment.Status.State)]; !exists || !containsString(string(state), expectedStates) {
3✔
559
                        err = status.Error(codes.FailedPrecondition, formatUpdateStateError("azVolume", string(azVolumeAttachment.Status.State), string(state), expectedStates...))
×
560
                }
×
561
        }
562
        if err == nil {
12✔
563
                azVolumeAttachment.Status.State = state
6✔
564
        }
6✔
565
        return azVolumeAttachment, err
6✔
566
}
567

568
func reportError(azVolumeAttachment *azdiskv1beta2.AzVolumeAttachment, state azdiskv1beta2.AzVolumeAttachmentAttachmentState, err error) (*azdiskv1beta2.AzVolumeAttachment, error) {
×
569
        if azVolumeAttachment == nil {
×
570
                return nil, status.Errorf(codes.FailedPrecondition, "function `reportError` requires non-nil AzVolumeAttachment object.")
×
571
        }
×
572
        azVolumeAttachment = updateError(azVolumeAttachment, err)
×
573
        return updateState(azVolumeAttachment, state, forceUpdate)
×
574
}
575

576
func (r *ReconcileAttachDetach) recreateAzVolumeAttachment(ctx context.Context, syncedVolumeAttachments map[string]bool, volumesToSync map[string]bool) (map[string]bool, map[string]bool, error) {
3✔
577
        w, _ := workflow.GetWorkflowFromContext(ctx)
3✔
578
        // Get all volumeAttachments
3✔
579
        volumeAttachments, err := r.kubeClient.StorageV1().VolumeAttachments().List(ctx, metav1.ListOptions{})
3✔
580
        if err != nil {
3✔
581
                return syncedVolumeAttachments, volumesToSync, err
×
582
        }
×
583

584
        if syncedVolumeAttachments == nil {
6✔
585
                syncedVolumeAttachments = map[string]bool{}
3✔
586
        }
3✔
587
        if volumesToSync == nil {
6✔
588
                volumesToSync = map[string]bool{}
3✔
589
        }
3✔
590

591
        // Loop through volumeAttachments and create Primary AzVolumeAttachments in correspondence
592
        for _, volumeAttachment := range volumeAttachments.Items {
5✔
593
                // skip if sync has been completed volumeAttachment
2✔
594
                if syncedVolumeAttachments[volumeAttachment.Name] {
2✔
595
                        continue
×
596
                }
597
                if volumeAttachment.Spec.Attacher == r.config.DriverName {
4✔
598
                        volumeName := volumeAttachment.Spec.Source.PersistentVolumeName
2✔
599
                        if volumeName == nil {
2✔
600
                                continue
×
601
                        }
602
                        // get PV and retrieve diskName
603
                        pv, err := r.kubeClient.CoreV1().PersistentVolumes().Get(ctx, *volumeName, metav1.GetOptions{})
2✔
604
                        if err != nil {
2✔
605
                                w.Logger().Errorf(err, "failed to get PV (%s)", *volumeName)
×
606
                                return syncedVolumeAttachments, volumesToSync, err
×
607
                        }
×
608

609
                        // if pv is migrated intree pv, convert it to csi pv for processing
610
                        // translate intree pv to csi pv to convert them into AzVolume resource
611
                        if utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) &&
2✔
612
                                utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAzureDisk) &&
2✔
613
                                pv.Spec.AzureDisk != nil {
2✔
614
                                // translate intree pv to csi pv to convert them into AzVolume resource
×
615
                                if utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) &&
×
616
                                        utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAzureDisk) &&
×
617
                                        pv.Spec.AzureDisk != nil {
×
618
                                        if pv, err = r.translateInTreePVToCSI(pv); err != nil {
×
619
                                                w.Logger().V(5).Errorf(err, "skipping azVolumeAttachment creation for volumeAttachment (%s)", volumeAttachment.Name)
×
620
                                        }
×
621
                                }
622
                        }
623

624
                        if pv.Spec.CSI == nil || pv.Spec.CSI.Driver != r.config.DriverName {
2✔
625
                                continue
×
626
                        }
627
                        volumesToSync[pv.Spec.CSI.VolumeHandle] = true
2✔
628

2✔
629
                        diskName, err := azureutils.GetDiskName(pv.Spec.CSI.VolumeHandle)
2✔
630
                        if err != nil {
2✔
631
                                w.Logger().Errorf(err, "failed to extract disk name from volumehandle (%s)", pv.Spec.CSI.VolumeHandle)
×
632
                                delete(volumesToSync, pv.Spec.CSI.VolumeHandle)
×
633
                                continue
×
634
                        }
635
                        nodeName := volumeAttachment.Spec.NodeName
2✔
636
                        azVolumeAttachmentName := azureutils.GetAzVolumeAttachmentName(diskName, nodeName)
2✔
637
                        r.azVolumeAttachmentToVaMap.Store(azVolumeAttachmentName, volumeAttachment.Name)
2✔
638

2✔
639
                        desiredAzVolumeAttachment := &azdiskv1beta2.AzVolumeAttachment{
2✔
640
                                ObjectMeta: metav1.ObjectMeta{
2✔
641
                                        Name: azVolumeAttachmentName,
2✔
642
                                        Labels: map[string]string{
2✔
643
                                                consts.NodeNameLabel:   nodeName,
2✔
644
                                                consts.VolumeNameLabel: *volumeName,
2✔
645
                                        },
2✔
646
                                        // if the volumeAttachment shows not yet attached, and VolumeAttachRequestAnnotation needs to be set from the controllerserver
2✔
647
                                        // if the volumeAttachment shows attached but the actual volume isn't due to controller restart, VolumeAttachRequestAnnotation needs to be set by the noderserver during NodeStageVolume
2✔
648
                                        Finalizers: []string{consts.AzVolumeAttachmentFinalizer},
2✔
649
                                },
2✔
650
                                Spec: azdiskv1beta2.AzVolumeAttachmentSpec{
2✔
651
                                        VolumeName:    *volumeName,
2✔
652
                                        VolumeID:      pv.Spec.CSI.VolumeHandle,
2✔
653
                                        NodeName:      nodeName,
2✔
654
                                        RequestedRole: azdiskv1beta2.PrimaryRole,
2✔
655
                                        VolumeContext: pv.Spec.CSI.VolumeAttributes,
2✔
656
                                },
2✔
657
                        }
2✔
658
                        azureutils.AnnotateAPIVersion(desiredAzVolumeAttachment)
2✔
659

2✔
660
                        statusUpdateRequired := true
2✔
661
                        // check if the CRI exists already
2✔
662
                        azVolumeAttachment, err := azureutils.GetAzVolumeAttachment(ctx, r.cachedClient, r.azClient, azVolumeAttachmentName, r.config.ObjectNamespace, false)
2✔
663
                        if err != nil {
3✔
664
                                if errors.IsNotFound(err) {
2✔
665
                                        w.Logger().Infof("Recreating AzVolumeAttachment(%s)", azVolumeAttachmentName)
1✔
666

1✔
667
                                        azVolumeAttachment, err = r.azClient.DiskV1beta2().AzVolumeAttachments(r.config.ObjectNamespace).Create(ctx, desiredAzVolumeAttachment, metav1.CreateOptions{})
1✔
668
                                        if err != nil {
1✔
669
                                                w.Logger().Errorf(err, "failed to create AzVolumeAttachment (%s) for volume (%s) and node (%s): %v", azVolumeAttachmentName, *volumeName, nodeName, err)
×
670
                                                return syncedVolumeAttachments, volumesToSync, err
×
671
                                        }
×
672
                                } else {
×
673
                                        w.Logger().Errorf(err, "failed to get AzVolumeAttachment (%s): %v", azVolumeAttachmentName, err)
×
674
                                        return syncedVolumeAttachments, volumesToSync, err
×
675
                                }
×
676
                        } else if apiVersion, ok := azureutils.GetFromMap(azVolumeAttachment.Annotations, consts.APIVersion); !ok || apiVersion != azdiskv1beta2.APIVersion {
2✔
677
                                w.Logger().Infof("Found AzVolumeAttachment (%s) with older api version. Converting to apiVersion(%s)", azVolumeAttachmentName, azdiskv1beta2.APIVersion)
1✔
678

1✔
679
                                for k, v := range desiredAzVolumeAttachment.Labels {
3✔
680
                                        azVolumeAttachment.Labels = azureutils.AddToMap(azVolumeAttachment.Labels, k, v)
2✔
681
                                }
2✔
682

683
                                for k, v := range azVolumeAttachment.Annotations {
2✔
684
                                        azVolumeAttachment.Status.Annotations = azureutils.AddToMap(azVolumeAttachment.Status.Annotations, k, v)
1✔
685
                                }
1✔
686

687
                                // for now, we don't empty the meta annotatinos after migrating them to status annotation for safety.
688
                                // note that this will leave some remnant garbage entries in meta annotations
689

690
                                for k, v := range desiredAzVolumeAttachment.Annotations {
2✔
691
                                        azVolumeAttachment.Annotations = azureutils.AddToMap(azVolumeAttachment.Status.Annotations, k, v)
1✔
692
                                }
1✔
693

694
                                azVolumeAttachment, err = r.azClient.DiskV1beta2().AzVolumeAttachments(r.config.ObjectNamespace).Update(ctx, azVolumeAttachment, metav1.UpdateOptions{})
1✔
695
                                if err != nil {
1✔
696
                                        w.Logger().Errorf(err, "failed to update AzVolumeAttachment (%s) for volume (%s) and node (%s): %v", azVolumeAttachmentName, *volumeName, nodeName, err)
×
697
                                        return syncedVolumeAttachments, volumesToSync, err
×
698
                                }
×
699
                        } else {
×
700
                                statusUpdateRequired = false
×
701
                        }
×
702

703
                        if statusUpdateRequired {
4✔
704
                                azVolumeAttachment.Status.Annotations = azureutils.AddToMap(azVolumeAttachment.Status.Annotations, consts.VolumeAttachmentKey, volumeAttachment.Name)
2✔
705

2✔
706
                                // update status
2✔
707
                                _, err = r.azClient.DiskV1beta2().AzVolumeAttachments(r.config.ObjectNamespace).UpdateStatus(ctx, azVolumeAttachment, metav1.UpdateOptions{})
2✔
708
                                if err != nil {
2✔
709
                                        w.Logger().Errorf(err, "failed to update status of AzVolumeAttachment (%s) for volume (%s) and node (%s): %v", azVolumeAttachmentName, *volumeName, nodeName, err)
×
710
                                        return syncedVolumeAttachments, volumesToSync, err
×
711
                                }
×
712
                        }
713

714
                        syncedVolumeAttachments[volumeAttachment.Name] = true
2✔
715
                }
716
        }
717
        return syncedVolumeAttachments, volumesToSync, nil
3✔
718
}
719

720
func (r *ReconcileAttachDetach) recoverAzVolumeAttachment(ctx context.Context, recoveredAzVolumeAttachments *sync.Map) error {
3✔
721
        w, _ := workflow.GetWorkflowFromContext(ctx)
3✔
722

3✔
723
        // list all AzVolumeAttachment
3✔
724
        azVolumeAttachments, err := r.azClient.DiskV1beta2().AzVolumeAttachments(r.config.ObjectNamespace).List(ctx, metav1.ListOptions{})
3✔
725
        if err != nil {
3✔
726
                w.Logger().Error(err, "failed to get list of existing AzVolumeAttachment CRI in controller recovery stage")
×
727
                return err
×
728
        }
×
729

730
        var wg sync.WaitGroup
3✔
731
        numRecovered := int32(0)
3✔
732

3✔
733
        for _, azVolumeAttachment := range azVolumeAttachments.Items {
7✔
734
                // skip if AzVolumeAttachment already recovered
4✔
735
                if _, ok := recoveredAzVolumeAttachments.Load(azVolumeAttachment.Name); ok {
4✔
736
                        numRecovered++
×
737
                        continue
×
738
                }
739

740
                wg.Add(1)
4✔
741
                go func(azv azdiskv1beta2.AzVolumeAttachment, azvMap *sync.Map) {
8✔
742
                        defer wg.Done()
4✔
743
                        var targetState azdiskv1beta2.AzVolumeAttachmentAttachmentState
4✔
744
                        updateMode := azureutils.UpdateCRIStatus
4✔
745
                        if azv.Spec.RequestedRole == azdiskv1beta2.ReplicaRole {
4✔
746
                                updateMode = azureutils.UpdateAll
×
747
                        }
×
748
                        updateFunc := func(obj client.Object) error {
8✔
749
                                var err error
4✔
750
                                azv := obj.(*azdiskv1beta2.AzVolumeAttachment)
4✔
751
                                if azv.Spec.RequestedRole == azdiskv1beta2.ReplicaRole {
4✔
752
                                        // conversion logic from v1beta1 to v1beta2 for replicas come here
×
753
                                        azv.Status.Annotations = azv.ObjectMeta.Annotations
×
754
                                        azv.ObjectMeta.Annotations = map[string]string{consts.VolumeAttachRequestAnnotation: "CRI recovery"}
×
755
                                }
×
756
                                // add a recover annotation to the CRI so that reconciliation can be triggered for the CRI even if CRI's current state == target state
757
                                azv.Status.Annotations = azureutils.AddToMap(azv.Status.Annotations, consts.RecoverAnnotation, "azVolumeAttachment")
4✔
758
                                if azv.Status.State != targetState {
6✔
759
                                        _, err = updateState(azv, targetState, forceUpdate)
2✔
760
                                }
2✔
761
                                return err
4✔
762
                        }
763
                        switch azv.Status.State {
4✔
764
                        case azdiskv1beta2.Attaching:
1✔
765
                                // reset state to Pending so Attach operation can be redone
1✔
766
                                targetState = azdiskv1beta2.AttachmentPending
1✔
767
                        case azdiskv1beta2.Detaching:
1✔
768
                                // reset state to Attached so Detach operation can be redone
1✔
769
                                targetState = azdiskv1beta2.Attached
1✔
770
                        default:
2✔
771
                                targetState = azv.Status.State
2✔
772
                        }
773

774
                        if _, err := azureutils.UpdateCRIWithRetry(ctx, nil, r.cachedClient, r.azClient, &azv, updateFunc, consts.ForcedUpdateMaxNetRetry, updateMode); err != nil {
4✔
775
                                w.Logger().Errorf(err, "failed to update AzVolumeAttachment (%s) for recovery", azv.Name)
×
776
                        } else {
4✔
777
                                // if update succeeded, add the CRI to the recoveryComplete list
4✔
778
                                azvMap.Store(azv.Name, struct{}{})
4✔
779
                                atomic.AddInt32(&numRecovered, 1)
4✔
780
                        }
4✔
781
                }(azVolumeAttachment, recoveredAzVolumeAttachments)
782
        }
783
        wg.Wait()
3✔
784

3✔
785
        // if recovery has not been completed for all CRIs, return error
3✔
786
        if numRecovered < int32(len(azVolumeAttachments.Items)) {
3✔
787
                return status.Errorf(codes.Internal, "failed to recover some AzVolumeAttachment states")
×
788
        }
×
789
        return nil
3✔
790
}
791

792
func NewAttachDetachController(mgr manager.Manager, cloudDiskAttacher CloudDiskAttachDetacher, crdDetacher CrdDetacher, controllerSharedState *SharedState) (*ReconcileAttachDetach, error) {
×
793
        logger := mgr.GetLogger().WithValues("controller", "azvolumeattachment")
×
794
        reconciler := ReconcileAttachDetach{
×
795
                crdDetacher:       crdDetacher,
×
796
                cloudDiskAttacher: cloudDiskAttacher,
×
797
                stateLock:         &sync.Map{},
×
798
                retryInfo:         newRetryInfo(),
×
799
                SharedState:       controllerSharedState,
×
800
                logger:            logger,
×
801
        }
×
802

×
803
        c, err := controller.New("azvolumeattachment-controller", mgr, controller.Options{
×
804
                MaxConcurrentReconciles: controllerSharedState.config.ControllerConfig.WorkerThreads,
×
805
                Reconciler:              &reconciler,
×
806
                LogConstructor:          func(req *reconcile.Request) logr.Logger { return logger },
×
807
        })
808

809
        if err != nil {
×
810
                c.GetLogger().Error(err, "failed to create controller")
×
811
                return nil, err
×
812
        }
×
813

814
        c.GetLogger().Info("Starting to watch AzVolumeAttachments.")
×
815

×
816
        // Watch for CRUD events on azVolumeAttachment objects
×
817
        err = c.Watch(&source.Kind{Type: &azdiskv1beta2.AzVolumeAttachment{}}, &handler.EnqueueRequestForObject{})
×
818
        if err != nil {
×
819
                c.GetLogger().Error(err, "failed to initialize watch for AzVolumeAttachment CRI")
×
820
                return nil, err
×
821
        }
×
822

823
        c.GetLogger().Info("Controller set-up successful.")
×
824
        return &reconciler, nil
×
825
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc