Skip to content

Commit 8d7a20b

Browse files
authored
Merge pull request #486 from igoihman/datavolume-controller-events
add events to DataVolume controller
2 parents d278698 + baac3e8 commit 8d7a20b

File tree

2 files changed

+151
-10
lines changed

2 files changed

+151
-10
lines changed

pkg/controller/datavolume-controller.go

+120-6
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,68 @@ const (
5353
SuccessSynced = "Synced"
5454
// ErrResourceExists provides a const to indicate a resource exists error
5555
ErrResourceExists = "ErrResourceExists"
56+
// ErrResourceDoesntExist provides a const to indicate a resource doesn't exist error
57+
ErrResourceDoesntExist = "ErrResourceDoesntExist"
58+
// ErrClaimLost provides a const to indicate a claim is lost
59+
ErrClaimLost = "ErrClaimLost"
60+
// DataVolumeFailed provides a const to represent DataVolume failed status
61+
DataVolumeFailed = "DataVolumeFailed"
62+
// ImportScheduled provides a const to indicate import is scheduled
63+
ImportScheduled = "ImportScheduled"
64+
// ImportInProgress provides a const to indicate an import is in progress
65+
ImportInProgress = "ImportInProgress"
66+
// ImportFailed provides a const to indicate import has failed
67+
ImportFailed = "ImportFailed"
68+
// ImportSucceeded provides a const to indicate import has succeeded
69+
ImportSucceeded = "ImportSucceded"
70+
// CloneScheduled provides a const to indicate clone is scheduled
71+
CloneScheduled = "CloneScheduled"
72+
// CloneInProgress provides a const to indicate clone is in progress
73+
CloneInProgress = "CloneInProgress"
74+
// CloneFailed provides a const to indicate clone has failed
75+
CloneFailed = "CloneFailed"
76+
// CloneSucceeded provides a const to indicate clone has succeeded
77+
CloneSucceeded = "CloneSucceeded"
78+
// UploadScheduled provides a const to indicate upload is scheduled
79+
UploadScheduled = "UploadScheduled"
80+
// UploadReady provides a const to indicate upload is in progress
81+
UploadReady = "UploadReady"
82+
// UploadFailed provides a const to indicate upload has failed
83+
UploadFailed = "UploadFailed"
84+
// UploadSucceeded provides a const to indicate upload has succeeded
85+
UploadSucceeded = "UploadSucceeded"
5686
// MessageResourceExists provides a const to form a resource exists error message
5787
MessageResourceExists = "Resource %q already exists and is not managed by DataVolume"
88+
// MessageResourceDoesntExist provides a const to form a resource doesn't exist error message
89+
MessageResourceDoesntExist = "Resource managed by %q doesn't exist"
5890
// MessageResourceSynced provides a const to standardize a Resource Synced message
5991
MessageResourceSynced = "DataVolume synced successfully"
92+
// MessageErrClaimLost provides a const to form claim lost message
93+
MessageErrClaimLost = "PVC %s lost"
94+
// MessageImportScheduled provides a const to form import is scheduled message
95+
MessageImportScheduled = "Import into %s scheduled"
96+
// MessageImportInProgress provides a const to form import is in progress message
97+
MessageImportInProgress = "Import into %s in progress"
98+
// MessageImportFailed provides a const to form import has failed message
99+
MessageImportFailed = "Failed to import into PVC %s"
100+
// MessageImportSucceeded provides a const to form import has succeeded message
101+
MessageImportSucceeded = "Successfully imported into PVC %s"
102+
// MessageCloneScheduled provides a const to form clone is scheduled message
103+
MessageCloneScheduled = "Cloning from %s/%s into %s/%s scheduled"
104+
// MessageCloneInProgress provides a const to form clone is in progress message
105+
MessageCloneInProgress = "Cloning from %s/%s into %s/%s in progress"
106+
// MessageCloneFailed provides a const to form clone has failed message
107+
MessageCloneFailed = "Cloning from %s/%s into %s/%s failed"
108+
// MessageCloneSucceeded provides a const to form clone has succeeded message
109+
MessageCloneSucceeded = "Successfully cloned from %s/%s into %s/%s"
110+
// MessageUploadScheduled provides a const to form upload is scheduled message
111+
MessageUploadScheduled = "Upload into %s scheduled"
112+
// MessageUploadReady provides a const to form upload is ready message
113+
MessageUploadReady = "Upload into %s ready"
114+
// MessageUploadFailed provides a const to form upload has failed message
115+
MessageUploadFailed = "Upload into %s failed"
116+
// MessageUploadSucceeded provides a const to form upload has succeeded message
117+
MessageUploadSucceeded = "Successfully uploaded into %s"
60118
)
61119

62120
// DataVolumeController represents the CDI Data Volume Controller
@@ -78,6 +136,13 @@ type DataVolumeController struct {
78136
pvcExpectations *expectations.UIDTrackingControllerExpectations
79137
}
80138

139+
// DataVolumeEvent reoresents event
140+
type DataVolumeEvent struct {
141+
eventType string
142+
reason string
143+
message string
144+
}
145+
81146
// NewDataVolumeController sets up a Data Volume Controller, and return a pointer to
82147
// the newly created Controller
83148
func NewDataVolumeController(
@@ -298,63 +363,102 @@ func (c *DataVolumeController) syncHandler(key string) error {
298363
return nil
299364
}
300365

301-
func (c *DataVolumeController) updateImportStatusPhase(pvc *corev1.PersistentVolumeClaim, dataVolumeCopy *cdiv1.DataVolume) {
366+
func (c *DataVolumeController) updateImportStatusPhase(pvc *corev1.PersistentVolumeClaim, dataVolumeCopy *cdiv1.DataVolume, event *DataVolumeEvent) {
302367
phase, ok := pvc.Annotations[AnnPodPhase]
303368
if ok {
304369
switch phase {
305370
case string(corev1.PodPending):
306371
// TODO: Use a more generic Scheduled, like maybe TransferScheduled.
307372
dataVolumeCopy.Status.Phase = cdiv1.ImportScheduled
373+
event.eventType = corev1.EventTypeNormal
374+
event.reason = ImportScheduled
375+
event.message = fmt.Sprintf(MessageImportScheduled, pvc.Name)
308376
case string(corev1.PodRunning):
309377
// TODO: Use a more generic In Progess, like maybe TransferInProgress.
310378
dataVolumeCopy.Status.Phase = cdiv1.ImportInProgress
379+
event.eventType = corev1.EventTypeNormal
380+
event.reason = ImportInProgress
381+
event.message = fmt.Sprintf(MessageImportInProgress, pvc.Name)
311382
case string(corev1.PodFailed):
312383
dataVolumeCopy.Status.Phase = cdiv1.Failed
384+
event.eventType = corev1.EventTypeWarning
385+
event.reason = ImportFailed
386+
event.message = fmt.Sprintf(MessageImportFailed, pvc.Name)
313387
case string(corev1.PodSucceeded):
314388
dataVolumeCopy.Status.Phase = cdiv1.Succeeded
389+
event.eventType = corev1.EventTypeNormal
390+
event.reason = ImportSucceeded
391+
event.message = fmt.Sprintf(MessageImportSucceeded, pvc.Name)
315392
}
316393
}
317394
}
318395

319-
func (c *DataVolumeController) updateCloneStatusPhase(pvc *corev1.PersistentVolumeClaim, dataVolumeCopy *cdiv1.DataVolume) {
396+
func (c *DataVolumeController) updateCloneStatusPhase(pvc *corev1.PersistentVolumeClaim, dataVolumeCopy *cdiv1.DataVolume, event *DataVolumeEvent) {
320397
phase, ok := pvc.Annotations[AnnPodPhase]
321398
if ok {
322399
switch phase {
323400
case string(corev1.PodPending):
324401
// TODO: Use a more generic Scheduled, like maybe TransferScheduled.
325402
dataVolumeCopy.Status.Phase = cdiv1.CloneScheduled
403+
event.eventType = corev1.EventTypeNormal
404+
event.reason = CloneScheduled
405+
event.message = fmt.Sprintf(MessageCloneScheduled, dataVolumeCopy.Spec.Source.PVC.Namespace, dataVolumeCopy.Spec.Source.PVC.Name, pvc.Namespace, pvc.Name)
326406
case string(corev1.PodRunning):
327407
// TODO: Use a more generic In Progess, like maybe TransferInProgress.
328408
dataVolumeCopy.Status.Phase = cdiv1.CloneInProgress
409+
event.eventType = corev1.EventTypeNormal
410+
event.reason = CloneInProgress
411+
event.message = fmt.Sprintf(MessageCloneInProgress, dataVolumeCopy.Spec.Source.PVC.Namespace, dataVolumeCopy.Spec.Source.PVC.Name, pvc.Namespace, pvc.Name)
329412
case string(corev1.PodFailed):
330413
dataVolumeCopy.Status.Phase = cdiv1.Failed
414+
event.eventType = corev1.EventTypeWarning
415+
event.reason = CloneFailed
416+
event.message = fmt.Sprintf(MessageCloneFailed, dataVolumeCopy.Spec.Source.PVC.Namespace, dataVolumeCopy.Spec.Source.PVC.Name, pvc.Namespace, pvc.Name)
331417
case string(corev1.PodSucceeded):
332418
dataVolumeCopy.Status.Phase = cdiv1.Succeeded
419+
event.eventType = corev1.EventTypeNormal
420+
event.reason = CloneSucceeded
421+
event.message = fmt.Sprintf(MessageCloneSucceeded, dataVolumeCopy.Spec.Source.PVC.Namespace, dataVolumeCopy.Spec.Source.PVC.Name, pvc.Namespace, pvc.Name)
333422
}
423+
334424
}
335425
}
336426

337-
func (c *DataVolumeController) updateUploadStatusPhase(pvc *corev1.PersistentVolumeClaim, dataVolumeCopy *cdiv1.DataVolume) {
427+
func (c *DataVolumeController) updateUploadStatusPhase(pvc *corev1.PersistentVolumeClaim, dataVolumeCopy *cdiv1.DataVolume, event *DataVolumeEvent) {
338428
phase, ok := pvc.Annotations[AnnPodPhase]
339429
if ok {
340430
switch phase {
341431
case string(corev1.PodPending):
342432
// TODO: Use a more generic Scheduled, like maybe TransferScheduled.
343433
dataVolumeCopy.Status.Phase = cdiv1.UploadScheduled
434+
event.eventType = corev1.EventTypeNormal
435+
event.reason = UploadScheduled
436+
event.message = fmt.Sprintf(MessageUploadScheduled, pvc.Name)
344437
case string(corev1.PodRunning):
345438
// TODO: Use a more generic In Progess, like maybe TransferInProgress.
346439
dataVolumeCopy.Status.Phase = cdiv1.UploadReady
440+
event.eventType = corev1.EventTypeNormal
441+
event.reason = UploadReady
442+
event.message = fmt.Sprintf(MessageUploadReady, pvc.Name)
347443
case string(corev1.PodFailed):
348444
dataVolumeCopy.Status.Phase = cdiv1.Failed
445+
event.eventType = corev1.EventTypeWarning
446+
event.reason = UploadFailed
447+
event.message = fmt.Sprintf(MessageUploadFailed, pvc.Name)
349448
case string(corev1.PodSucceeded):
350449
dataVolumeCopy.Status.Phase = cdiv1.Succeeded
450+
event.eventType = corev1.EventTypeNormal
451+
event.reason = UploadSucceeded
452+
event.message = fmt.Sprintf(MessageUploadSucceeded, pvc.Name)
453+
351454
}
352455
}
353456
}
354457

355458
func (c *DataVolumeController) updateDataVolumeStatus(dataVolume *cdiv1.DataVolume, pvc *corev1.PersistentVolumeClaim) error {
356459
dataVolumeCopy := dataVolume.DeepCopy()
357460
var err error
461+
var event DataVolumeEvent
358462

359463
curPhase := dataVolumeCopy.Status.Phase
360464
if pvc == nil {
@@ -364,6 +468,9 @@ func (c *DataVolumeController) updateDataVolumeStatus(dataVolume *cdiv1.DataVolu
364468
// something has gone wrong. Perhaps the PVC was deleted out from
365469
// underneath the DataVolume
366470
dataVolumeCopy.Status.Phase = cdiv1.Failed
471+
event.eventType = corev1.EventTypeWarning
472+
event.reason = DataVolumeFailed
473+
event.message = fmt.Sprintf(MessageResourceDoesntExist, dataVolume.Name)
367474
}
368475

369476
} else {
@@ -381,21 +488,24 @@ func (c *DataVolumeController) updateDataVolumeStatus(dataVolume *cdiv1.DataVolu
381488
_, ok := pvc.Annotations[AnnImportPod]
382489
if ok {
383490
dataVolumeCopy.Status.Phase = cdiv1.ImportScheduled
384-
c.updateImportStatusPhase(pvc, dataVolumeCopy)
491+
c.updateImportStatusPhase(pvc, dataVolumeCopy, &event)
385492
}
386493
_, ok = pvc.Annotations[AnnCloneRequest]
387494
if ok {
388495
dataVolumeCopy.Status.Phase = cdiv1.CloneScheduled
389-
c.updateCloneStatusPhase(pvc, dataVolumeCopy)
496+
c.updateCloneStatusPhase(pvc, dataVolumeCopy, &event)
390497
}
391498
_, ok = pvc.Annotations[AnnUploadRequest]
392499
if ok {
393500
dataVolumeCopy.Status.Phase = cdiv1.UploadScheduled
394-
c.updateUploadStatusPhase(pvc, dataVolumeCopy)
501+
c.updateUploadStatusPhase(pvc, dataVolumeCopy, &event)
395502
}
396503

397504
case corev1.ClaimLost:
398505
dataVolumeCopy.Status.Phase = cdiv1.Failed
506+
event.eventType = corev1.EventTypeWarning
507+
event.reason = ErrClaimLost
508+
event.message = fmt.Sprintf(MessageErrClaimLost, pvc.Name)
399509
default:
400510
if pvc.Status.Phase != "" {
401511
dataVolumeCopy.Status.Phase = cdiv1.Unknown
@@ -406,6 +516,10 @@ func (c *DataVolumeController) updateDataVolumeStatus(dataVolume *cdiv1.DataVolu
406516
// Only update the object if something actually changed in the status.
407517
if !reflect.DeepEqual(dataVolume.Status, dataVolumeCopy.Status) {
408518
_, err = c.cdiClientSet.CdiV1alpha1().DataVolumes(dataVolume.Namespace).Update(dataVolumeCopy)
519+
// Emit the event only when the status change happens, not every time
520+
if event.eventType != "" {
521+
c.recorder.Event(dataVolume, event.eventType, event.reason, event.message)
522+
}
409523
}
410524
return err
411525
}

tests/datavolume_test.go

+31-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package tests_test
22

33
import (
44
"fmt"
5+
"strings"
6+
"time"
57

68
. "github.com/onsi/ginkgo"
79
. "github.com/onsi/gomega"
@@ -11,12 +13,19 @@ import (
1113
"k8s.io/api/core/v1"
1214
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1315

16+
"kubevirt.io/containerized-data-importer/pkg/controller"
17+
"kubevirt.io/containerized-data-importer/tests"
1418
"kubevirt.io/containerized-data-importer/tests/framework"
1519
"kubevirt.io/containerized-data-importer/tests/utils"
1620

1721
cdiv1 "kubevirt.io/containerized-data-importer/pkg/apis/datavolumecontroller/v1alpha1"
1822
)
1923

24+
const (
25+
pollingInterval = 2 * time.Second
26+
timeout = 60 * time.Second
27+
)
28+
2029
var _ = Describe("DataVolume tests", func() {
2130

2231
var sourcePvc *v1.PersistentVolumeClaim
@@ -37,7 +46,7 @@ var _ = Describe("DataVolume tests", func() {
3746
})
3847

3948
Describe("Verify DataVolume", func() {
40-
table.DescribeTable("with http import source should", func(url string, phase cdiv1.DataVolumePhase, dataVolumeName string) {
49+
table.DescribeTable("with http import source should", func(url string, phase cdiv1.DataVolumePhase, dataVolumeName string, eventReasons []string) {
4150
dataVolume := utils.NewDataVolumeWithHTTPImport(dataVolumeName, "1Gi", url)
4251

4352
By(fmt.Sprintf("creating new datavolume %s", dataVolume.Name))
@@ -52,13 +61,24 @@ var _ = Describe("DataVolume tests", func() {
5261
_, err = f.K8sClient.CoreV1().PersistentVolumeClaims(dataVolume.Namespace).Get(dataVolume.Name, metav1.GetOptions{})
5362
Expect(err).ToNot(HaveOccurred())
5463

64+
By(fmt.Sprint("Verifying events occured"))
65+
66+
for _, eventReason := range eventReasons {
67+
Eventually(func() bool {
68+
events, err := tests.RunKubectlCommand(f, "get", "events", "-n", dataVolume.Namespace)
69+
Expect(err).NotTo(HaveOccurred())
70+
return strings.Contains(events, eventReason)
71+
}, timeout, pollingInterval).Should(BeTrue())
72+
73+
}
74+
5575
err = utils.DeleteDataVolume(f.CdiClient, f.Namespace.Name, dataVolume)
5676
Expect(err).ToNot(HaveOccurred())
5777

5878
},
59-
table.Entry("succeed when given valid url", utils.TinyCoreIsoURL, cdiv1.Succeeded, "dv-phase-test-1"),
60-
table.Entry("fail due to invalid DNS entry", "http://i-made-this-up.kube-system/tinyCore.iso", cdiv1.Failed, "dv-phase-test-2"),
61-
table.Entry("fail due to file not found", utils.TinyCoreIsoURL+"not.real.file", cdiv1.Failed, "dv-phase-test-3"),
79+
table.Entry("succeed when given valid url", utils.TinyCoreIsoURL, cdiv1.Succeeded, "dv-phase-test-1", []string{controller.ImportScheduled, controller.ImportSucceeded}),
80+
table.Entry("fail due to invalid DNS entry", "http://i-made-this-up.kube-system/tinyCore.iso", cdiv1.Failed, "dv-phase-test-2", []string{controller.ImportScheduled, controller.ImportInProgress}),
81+
table.Entry("fail due to file not found", utils.TinyCoreIsoURL+"not.real.file", cdiv1.Failed, "dv-phase-test-3", []string{controller.ImportScheduled, controller.ImportInProgress}),
6282
)
6383

6484
table.DescribeTable("with clone source should", func(command string, phase cdiv1.DataVolumePhase, dataVolumeName string) {
@@ -89,6 +109,13 @@ var _ = Describe("DataVolume tests", func() {
89109
Expect(f.VerifyTargetPVCContent(f.Namespace, targetPvc, testFile, fillData)).To(BeTrue())
90110
}
91111

112+
By(fmt.Sprintf("Verifying event %s occured", controller.CloneSucceeded))
113+
Eventually(func() bool {
114+
events, err := tests.RunKubectlCommand(f, "get", "events", "-n", dataVolume.Namespace)
115+
Expect(err).NotTo(HaveOccurred())
116+
return strings.Contains(events, controller.CloneSucceeded)
117+
}, timeout, pollingInterval).Should(BeTrue())
118+
92119
err = utils.DeleteDataVolume(f.CdiClient, f.Namespace.Name, dataVolume)
93120
Expect(err).ToNot(HaveOccurred())
94121

0 commit comments

Comments
 (0)