Skip to content

Commit

Permalink
Merge pull request #486 from igoihman/datavolume-controller-events
Browse files Browse the repository at this point in the history
add events to DataVolume controller
  • Loading branch information
awels committed Oct 17, 2018
2 parents d278698 + baac3e8 commit 8d7a20b
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 10 deletions.
126 changes: 120 additions & 6 deletions pkg/controller/datavolume-controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,68 @@ const (
SuccessSynced = "Synced"
// ErrResourceExists provides a const to indicate a resource exists error
ErrResourceExists = "ErrResourceExists"
// ErrResourceDoesntExist provides a const to indicate a resource doesn't exist error
ErrResourceDoesntExist = "ErrResourceDoesntExist"
// ErrClaimLost provides a const to indicate a claim is lost
ErrClaimLost = "ErrClaimLost"
// DataVolumeFailed provides a const to represent DataVolume failed status
DataVolumeFailed = "DataVolumeFailed"
// ImportScheduled provides a const to indicate import is scheduled
ImportScheduled = "ImportScheduled"
// ImportInProgress provides a const to indicate an import is in progress
ImportInProgress = "ImportInProgress"
// ImportFailed provides a const to indicate import has failed
ImportFailed = "ImportFailed"
// ImportSucceeded provides a const to indicate import has succeeded
ImportSucceeded = "ImportSucceded"
// CloneScheduled provides a const to indicate clone is scheduled
CloneScheduled = "CloneScheduled"
// CloneInProgress provides a const to indicate clone is in progress
CloneInProgress = "CloneInProgress"
// CloneFailed provides a const to indicate clone has failed
CloneFailed = "CloneFailed"
// CloneSucceeded provides a const to indicate clone has succeeded
CloneSucceeded = "CloneSucceeded"
// UploadScheduled provides a const to indicate upload is scheduled
UploadScheduled = "UploadScheduled"
// UploadReady provides a const to indicate upload is in progress
UploadReady = "UploadReady"
// UploadFailed provides a const to indicate upload has failed
UploadFailed = "UploadFailed"
// UploadSucceeded provides a const to indicate upload has succeeded
UploadSucceeded = "UploadSucceeded"
// MessageResourceExists provides a const to form a resource exists error message
MessageResourceExists = "Resource %q already exists and is not managed by DataVolume"
// MessageResourceDoesntExist provides a const to form a resource doesn't exist error message
MessageResourceDoesntExist = "Resource managed by %q doesn't exist"
// MessageResourceSynced provides a const to standardize a Resource Synced message
MessageResourceSynced = "DataVolume synced successfully"
// MessageErrClaimLost provides a const to form claim lost message
MessageErrClaimLost = "PVC %s lost"
// MessageImportScheduled provides a const to form import is scheduled message
MessageImportScheduled = "Import into %s scheduled"
// MessageImportInProgress provides a const to form import is in progress message
MessageImportInProgress = "Import into %s in progress"
// MessageImportFailed provides a const to form import has failed message
MessageImportFailed = "Failed to import into PVC %s"
// MessageImportSucceeded provides a const to form import has succeeded message
MessageImportSucceeded = "Successfully imported into PVC %s"
// MessageCloneScheduled provides a const to form clone is scheduled message
MessageCloneScheduled = "Cloning from %s/%s into %s/%s scheduled"
// MessageCloneInProgress provides a const to form clone is in progress message
MessageCloneInProgress = "Cloning from %s/%s into %s/%s in progress"
// MessageCloneFailed provides a const to form clone has failed message
MessageCloneFailed = "Cloning from %s/%s into %s/%s failed"
// MessageCloneSucceeded provides a const to form clone has succeeded message
MessageCloneSucceeded = "Successfully cloned from %s/%s into %s/%s"
// MessageUploadScheduled provides a const to form upload is scheduled message
MessageUploadScheduled = "Upload into %s scheduled"
// MessageUploadReady provides a const to form upload is ready message
MessageUploadReady = "Upload into %s ready"
// MessageUploadFailed provides a const to form upload has failed message
MessageUploadFailed = "Upload into %s failed"
// MessageUploadSucceeded provides a const to form upload has succeeded message
MessageUploadSucceeded = "Successfully uploaded into %s"
)

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

// DataVolumeEvent reoresents event
type DataVolumeEvent struct {
eventType string
reason string
message string
}

// NewDataVolumeController sets up a Data Volume Controller, and return a pointer to
// the newly created Controller
func NewDataVolumeController(
Expand Down Expand Up @@ -298,63 +363,102 @@ func (c *DataVolumeController) syncHandler(key string) error {
return nil
}

func (c *DataVolumeController) updateImportStatusPhase(pvc *corev1.PersistentVolumeClaim, dataVolumeCopy *cdiv1.DataVolume) {
func (c *DataVolumeController) updateImportStatusPhase(pvc *corev1.PersistentVolumeClaim, dataVolumeCopy *cdiv1.DataVolume, event *DataVolumeEvent) {
phase, ok := pvc.Annotations[AnnPodPhase]
if ok {
switch phase {
case string(corev1.PodPending):
// TODO: Use a more generic Scheduled, like maybe TransferScheduled.
dataVolumeCopy.Status.Phase = cdiv1.ImportScheduled
event.eventType = corev1.EventTypeNormal
event.reason = ImportScheduled
event.message = fmt.Sprintf(MessageImportScheduled, pvc.Name)
case string(corev1.PodRunning):
// TODO: Use a more generic In Progess, like maybe TransferInProgress.
dataVolumeCopy.Status.Phase = cdiv1.ImportInProgress
event.eventType = corev1.EventTypeNormal
event.reason = ImportInProgress
event.message = fmt.Sprintf(MessageImportInProgress, pvc.Name)
case string(corev1.PodFailed):
dataVolumeCopy.Status.Phase = cdiv1.Failed
event.eventType = corev1.EventTypeWarning
event.reason = ImportFailed
event.message = fmt.Sprintf(MessageImportFailed, pvc.Name)
case string(corev1.PodSucceeded):
dataVolumeCopy.Status.Phase = cdiv1.Succeeded
event.eventType = corev1.EventTypeNormal
event.reason = ImportSucceeded
event.message = fmt.Sprintf(MessageImportSucceeded, pvc.Name)
}
}
}

func (c *DataVolumeController) updateCloneStatusPhase(pvc *corev1.PersistentVolumeClaim, dataVolumeCopy *cdiv1.DataVolume) {
func (c *DataVolumeController) updateCloneStatusPhase(pvc *corev1.PersistentVolumeClaim, dataVolumeCopy *cdiv1.DataVolume, event *DataVolumeEvent) {
phase, ok := pvc.Annotations[AnnPodPhase]
if ok {
switch phase {
case string(corev1.PodPending):
// TODO: Use a more generic Scheduled, like maybe TransferScheduled.
dataVolumeCopy.Status.Phase = cdiv1.CloneScheduled
event.eventType = corev1.EventTypeNormal
event.reason = CloneScheduled
event.message = fmt.Sprintf(MessageCloneScheduled, dataVolumeCopy.Spec.Source.PVC.Namespace, dataVolumeCopy.Spec.Source.PVC.Name, pvc.Namespace, pvc.Name)
case string(corev1.PodRunning):
// TODO: Use a more generic In Progess, like maybe TransferInProgress.
dataVolumeCopy.Status.Phase = cdiv1.CloneInProgress
event.eventType = corev1.EventTypeNormal
event.reason = CloneInProgress
event.message = fmt.Sprintf(MessageCloneInProgress, dataVolumeCopy.Spec.Source.PVC.Namespace, dataVolumeCopy.Spec.Source.PVC.Name, pvc.Namespace, pvc.Name)
case string(corev1.PodFailed):
dataVolumeCopy.Status.Phase = cdiv1.Failed
event.eventType = corev1.EventTypeWarning
event.reason = CloneFailed
event.message = fmt.Sprintf(MessageCloneFailed, dataVolumeCopy.Spec.Source.PVC.Namespace, dataVolumeCopy.Spec.Source.PVC.Name, pvc.Namespace, pvc.Name)
case string(corev1.PodSucceeded):
dataVolumeCopy.Status.Phase = cdiv1.Succeeded
event.eventType = corev1.EventTypeNormal
event.reason = CloneSucceeded
event.message = fmt.Sprintf(MessageCloneSucceeded, dataVolumeCopy.Spec.Source.PVC.Namespace, dataVolumeCopy.Spec.Source.PVC.Name, pvc.Namespace, pvc.Name)
}

}
}

func (c *DataVolumeController) updateUploadStatusPhase(pvc *corev1.PersistentVolumeClaim, dataVolumeCopy *cdiv1.DataVolume) {
func (c *DataVolumeController) updateUploadStatusPhase(pvc *corev1.PersistentVolumeClaim, dataVolumeCopy *cdiv1.DataVolume, event *DataVolumeEvent) {
phase, ok := pvc.Annotations[AnnPodPhase]
if ok {
switch phase {
case string(corev1.PodPending):
// TODO: Use a more generic Scheduled, like maybe TransferScheduled.
dataVolumeCopy.Status.Phase = cdiv1.UploadScheduled
event.eventType = corev1.EventTypeNormal
event.reason = UploadScheduled
event.message = fmt.Sprintf(MessageUploadScheduled, pvc.Name)
case string(corev1.PodRunning):
// TODO: Use a more generic In Progess, like maybe TransferInProgress.
dataVolumeCopy.Status.Phase = cdiv1.UploadReady
event.eventType = corev1.EventTypeNormal
event.reason = UploadReady
event.message = fmt.Sprintf(MessageUploadReady, pvc.Name)
case string(corev1.PodFailed):
dataVolumeCopy.Status.Phase = cdiv1.Failed
event.eventType = corev1.EventTypeWarning
event.reason = UploadFailed
event.message = fmt.Sprintf(MessageUploadFailed, pvc.Name)
case string(corev1.PodSucceeded):
dataVolumeCopy.Status.Phase = cdiv1.Succeeded
event.eventType = corev1.EventTypeNormal
event.reason = UploadSucceeded
event.message = fmt.Sprintf(MessageUploadSucceeded, pvc.Name)

}
}
}

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

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

} else {
Expand All @@ -381,21 +488,24 @@ func (c *DataVolumeController) updateDataVolumeStatus(dataVolume *cdiv1.DataVolu
_, ok := pvc.Annotations[AnnImportPod]
if ok {
dataVolumeCopy.Status.Phase = cdiv1.ImportScheduled
c.updateImportStatusPhase(pvc, dataVolumeCopy)
c.updateImportStatusPhase(pvc, dataVolumeCopy, &event)
}
_, ok = pvc.Annotations[AnnCloneRequest]
if ok {
dataVolumeCopy.Status.Phase = cdiv1.CloneScheduled
c.updateCloneStatusPhase(pvc, dataVolumeCopy)
c.updateCloneStatusPhase(pvc, dataVolumeCopy, &event)
}
_, ok = pvc.Annotations[AnnUploadRequest]
if ok {
dataVolumeCopy.Status.Phase = cdiv1.UploadScheduled
c.updateUploadStatusPhase(pvc, dataVolumeCopy)
c.updateUploadStatusPhase(pvc, dataVolumeCopy, &event)
}

case corev1.ClaimLost:
dataVolumeCopy.Status.Phase = cdiv1.Failed
event.eventType = corev1.EventTypeWarning
event.reason = ErrClaimLost
event.message = fmt.Sprintf(MessageErrClaimLost, pvc.Name)
default:
if pvc.Status.Phase != "" {
dataVolumeCopy.Status.Phase = cdiv1.Unknown
Expand All @@ -406,6 +516,10 @@ func (c *DataVolumeController) updateDataVolumeStatus(dataVolume *cdiv1.DataVolu
// Only update the object if something actually changed in the status.
if !reflect.DeepEqual(dataVolume.Status, dataVolumeCopy.Status) {
_, err = c.cdiClientSet.CdiV1alpha1().DataVolumes(dataVolume.Namespace).Update(dataVolumeCopy)
// Emit the event only when the status change happens, not every time
if event.eventType != "" {
c.recorder.Event(dataVolume, event.eventType, event.reason, event.message)
}
}
return err
}
Expand Down
35 changes: 31 additions & 4 deletions tests/datavolume_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package tests_test

import (
"fmt"
"strings"
"time"

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

"kubevirt.io/containerized-data-importer/pkg/controller"
"kubevirt.io/containerized-data-importer/tests"
"kubevirt.io/containerized-data-importer/tests/framework"
"kubevirt.io/containerized-data-importer/tests/utils"

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

const (
pollingInterval = 2 * time.Second
timeout = 60 * time.Second
)

var _ = Describe("DataVolume tests", func() {

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

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

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

By(fmt.Sprint("Verifying events occured"))

for _, eventReason := range eventReasons {
Eventually(func() bool {
events, err := tests.RunKubectlCommand(f, "get", "events", "-n", dataVolume.Namespace)
Expect(err).NotTo(HaveOccurred())
return strings.Contains(events, eventReason)
}, timeout, pollingInterval).Should(BeTrue())

}

err = utils.DeleteDataVolume(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())

},
table.Entry("succeed when given valid url", utils.TinyCoreIsoURL, cdiv1.Succeeded, "dv-phase-test-1"),
table.Entry("fail due to invalid DNS entry", "http://i-made-this-up.kube-system/tinyCore.iso", cdiv1.Failed, "dv-phase-test-2"),
table.Entry("fail due to file not found", utils.TinyCoreIsoURL+"not.real.file", cdiv1.Failed, "dv-phase-test-3"),
table.Entry("succeed when given valid url", utils.TinyCoreIsoURL, cdiv1.Succeeded, "dv-phase-test-1", []string{controller.ImportScheduled, controller.ImportSucceeded}),
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}),
table.Entry("fail due to file not found", utils.TinyCoreIsoURL+"not.real.file", cdiv1.Failed, "dv-phase-test-3", []string{controller.ImportScheduled, controller.ImportInProgress}),
)

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

By(fmt.Sprintf("Verifying event %s occured", controller.CloneSucceeded))
Eventually(func() bool {
events, err := tests.RunKubectlCommand(f, "get", "events", "-n", dataVolume.Namespace)
Expect(err).NotTo(HaveOccurred())
return strings.Contains(events, controller.CloneSucceeded)
}, timeout, pollingInterval).Should(BeTrue())

err = utils.DeleteDataVolume(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())

Expand Down

0 comments on commit 8d7a20b

Please sign in to comment.