diff --git a/pkg/internal/checkup/checkup.go b/pkg/internal/checkup/checkup.go index 4888db58..bfdd1dd6 100644 --- a/pkg/internal/checkup/checkup.go +++ b/pkg/internal/checkup/checkup.go @@ -67,6 +67,7 @@ type kubeVirtStorageClient interface { ListDataImportCrons(ctx context.Context, namespace string) (*cdiv1.DataImportCronList, error) ListVirtualMachinesInstances(ctx context.Context, namespace string) (*kvcorev1.VirtualMachineInstanceList, error) ListCDIs(ctx context.Context) (*cdiv1.CDIList, error) + GetNamespace(ctx context.Context, name string) (*corev1.Namespace, error) GetPersistentVolumeClaim(ctx context.Context, namespace, name string) (*corev1.PersistentVolumeClaim, error) GetPersistentVolume(ctx context.Context, name string) (*corev1.PersistentVolume, error) GetVolumeSnapshot(ctx context.Context, namespace, name string) (*snapshotv1.VolumeSnapshot, error) @@ -79,8 +80,11 @@ const ( VMIUnderTestNamePrefix = "vmi-under-test" hotplugVolumeName = "hotplug-volume" pvcName = "checkup-pvc" + StrTrue = "true" + StrFalse = "false" - AnnDefaultStorageClass = "storageclass.kubernetes.io/is-default-class" + AnnDefaultVirtStorageClass = "storageclass.kubevirt.io/is-default-virt-class" + AnnDefaultStorageClass = "storageclass.kubernetes.io/is-default-class" ErrNoDefaultStorageClass = "no default storage class" ErrPvcNotBound = "pvc failed to bound" @@ -89,14 +93,15 @@ const ( // FIXME: need to decide of we want to return errors in this cases // errMissingVolumeSnapshotClass = "there are StorageProfiles missing VolumeSnapshotClass" // errVMsWithNonVirtRbdStorageClass = "there are VMs using the plain RBD storageclass when the virtualization storageclass exists" - ErrVMsWithUnsetEfsStorageClass = "there are VMs using an EFS storageclass where the gid and uid are not set in the storageclass" - ErrGoldenImagesNotUpToDate = "there are golden images whose DataImportCron is not up to date or DataSource is not ready" - ErrGoldenImageNoDataSource = "dataSource has no PVC or Snapshot source" - ErrBootFailedOnSomeVMs = "some of the VMs failed to complete boot on time" - MessageBootCompletedOnAllVMs = "Boot completed on all VMs on time" - MessageSkipNoGoldenImage = "Skip check - no golden image PVC or Snapshot" - MessageSkipNoVMI = "Skip check - no VMI" - MessageSkipSingleNode = "Skip check - single node" + ErrVMsWithUnsetEfsStorageClass = "there are VMs using an EFS storageclass where the gid and uid are not set in the storageclass" + ErrGoldenImagesNotUpToDate = "there are golden images whose DataImportCron is not up to date or DataSource is not ready" + ErrGoldenImageNoDataSource = "dataSource has no PVC or Snapshot source" + ErrBootFailedOnSomeVMs = "some of the VMs failed to complete boot on time" + MessageBootCompletedOnAllVMs = "Boot completed on all VMs on time" + MessageSkipNoDefaultStorageClass = "Skip check - no default storage class" + MessageSkipNoGoldenImage = "Skip check - no golden image PVC or Snapshot" + MessageSkipNoVMI = "Skip check - no VMI" + MessageSkipSingleNode = "Skip check - single node" pollInterval = 5 * time.Second ) @@ -111,14 +116,15 @@ var UnsupportedProvisioners = map[string]struct{}{ } type Checkup struct { - client kubeVirtStorageClient - namespace string - checkupConfig config.Config - goldenImageScs []string - goldenImagePvc *corev1.PersistentVolumeClaim - goldenImageSnap *snapshotv1.VolumeSnapshot - vmUnderTest *kvcorev1.VirtualMachine - results status.Results + client kubeVirtStorageClient + namespace string + checkupConfig config.Config + defaultStorageClass string + goldenImageScs []string + goldenImagePvc *corev1.PersistentVolumeClaim + goldenImageSnap *snapshotv1.VolumeSnapshot + vmUnderTest *kvcorev1.VirtualMachine + results status.Results } type goldenImagesCheckState struct { @@ -243,14 +249,23 @@ func (c *Checkup) checkVersions(ctx context.Context) error { func (c *Checkup) checkGoldenImages(ctx context.Context, namespaces *corev1.NamespaceList, errStr *string) error { log.Print("checkGoldenImages") + const defaultGoldenImagesNamespace = "openshift-virtualization-os-images" var cs goldenImagesCheckState - for i := range namespaces.Items { - ns := namespaces.Items[i].Name - if err := c.checkDataImportCrons(ctx, ns, &cs); err != nil { + + if ns, err := c.client.GetNamespace(ctx, defaultGoldenImagesNamespace); err == nil { + if err := c.checkDataImportCrons(ctx, ns.Name, &cs); err != nil { return err } } + for i := range namespaces.Items { + if ns := namespaces.Items[i].Name; ns != defaultGoldenImagesNamespace { + if err := c.checkDataImportCrons(ctx, ns, &cs); err != nil { + return err + } + } + } + if c.goldenImagePvc == nil { if cs.fallbackPvcDefaultSC != nil { c.goldenImagePvc = cs.fallbackPvcDefaultSC @@ -392,18 +407,35 @@ func (c *Checkup) updateGoldenImageSnapshot(snap *snapshotv1.VolumeSnapshot) { func (c *Checkup) checkDefaultStorageClass(scs *storagev1.StorageClassList, errStr *string) { log.Print("checkDefaultStorageClass") + var multipleDefaultStorageClasses, hasDefaultVirtStorageClass, hasDefaultStorageClass bool for i := range scs.Items { sc := scs.Items[i] - if sc.Annotations[AnnDefaultStorageClass] == "true" { - if c.results.DefaultStorageClass != "" { - c.results.DefaultStorageClass = ErrMultipleDefaultStorageClasses - appendSep(errStr, ErrMultipleDefaultStorageClasses) - break + if sc.Annotations[AnnDefaultVirtStorageClass] == StrTrue { + if !hasDefaultVirtStorageClass { + hasDefaultVirtStorageClass = true + c.defaultStorageClass = sc.Name + } else { + multipleDefaultStorageClasses = true + } + } + if sc.Annotations[AnnDefaultStorageClass] == StrTrue { + if !hasDefaultStorageClass { + hasDefaultStorageClass = true + if !hasDefaultVirtStorageClass { + c.defaultStorageClass = sc.Name + } + } else { + multipleDefaultStorageClasses = true } - c.results.DefaultStorageClass = sc.Name } } - if c.results.DefaultStorageClass == "" { + + if multipleDefaultStorageClasses { + c.results.DefaultStorageClass = ErrMultipleDefaultStorageClasses + appendSep(errStr, ErrMultipleDefaultStorageClasses) + } else if c.defaultStorageClass != "" { + c.results.DefaultStorageClass = c.defaultStorageClass + } else { c.results.DefaultStorageClass = ErrNoDefaultStorageClass appendSep(errStr, ErrNoDefaultStorageClass) } @@ -412,6 +444,12 @@ func (c *Checkup) checkDefaultStorageClass(scs *storagev1.StorageClassList, errS func (c *Checkup) checkPVCCreationAndBinding(ctx context.Context, errStr *string) error { log.Print("checkPVCCreationAndBinding") + if c.defaultStorageClass == "" && c.checkupConfig.StorageClass == "" { + log.Print(MessageSkipNoDefaultStorageClass) + c.results.PVCBound = MessageSkipNoDefaultStorageClass + return nil + } + dv := &cdiv1.DataVolume{ ObjectMeta: metav1.ObjectMeta{ Name: pvcName, @@ -422,7 +460,7 @@ func (c *Checkup) checkPVCCreationAndBinding(ctx context.Context, errStr *string UID: types.UID(c.checkupConfig.PodUID), }}, Annotations: map[string]string{ - "cdi.kubevirt.io/storage.bind.immediate.requested": "true", + "cdi.kubevirt.io/storage.bind.immediate.requested": StrTrue, }, }, Spec: cdiv1.DataVolumeSpec{ @@ -786,6 +824,13 @@ func (c *Checkup) Config() config.Config { func (c *Checkup) checkVMIBoot(ctx context.Context, errStr *string) error { log.Print("checkVMIBoot") + + if c.defaultStorageClass == "" && c.checkupConfig.StorageClass == "" { + log.Print(MessageSkipNoDefaultStorageClass) + c.results.VMBootFromGoldenImage = MessageSkipNoDefaultStorageClass + return nil + } + if c.goldenImagePvc == nil && c.goldenImageSnap == nil { log.Print(MessageSkipNoGoldenImage) c.results.VMBootFromGoldenImage = MessageSkipNoGoldenImage @@ -803,6 +848,11 @@ func (c *Checkup) checkVMIBoot(ctx context.Context, errStr *string) error { return err } + if c.goldenImageSnap != nil { + c.results.VMVolumeClone = "DV cloneType: snapshot" + return nil + } + pvc, err := c.client.GetPersistentVolumeClaim(ctx, c.namespace, getVMDvName(vmName)) if err != nil { return err @@ -980,6 +1030,12 @@ func (c *Checkup) checkConcurrentVMIBoot(ctx context.Context, errStr *string) er numOfVMs := c.checkupConfig.NumOfVMs log.Printf("checkConcurrentVMIBoot numOfVMs:%d", numOfVMs) + if c.defaultStorageClass == "" && c.checkupConfig.StorageClass == "" { + log.Print(MessageSkipNoDefaultStorageClass) + c.results.ConcurrentVMBoot = MessageSkipNoDefaultStorageClass + return nil + } + if c.goldenImagePvc == nil && c.goldenImageSnap == nil { log.Print(MessageSkipNoGoldenImage) c.results.ConcurrentVMBoot = MessageSkipNoGoldenImage diff --git a/pkg/internal/checkup/checkup_test.go b/pkg/internal/checkup/checkup_test.go index 7d54af0d..5426d696 100644 --- a/pkg/internal/checkup/checkup_test.go +++ b/pkg/internal/checkup/checkup_test.go @@ -89,20 +89,50 @@ var tests = map[string]struct { expectedErr string }{ "noStorageClasses": { - clientConfig: clientConfig{noStorageClasses: true}, - expectedResults: map[string]string{reporter.DefaultStorageClassKey: checkup.ErrNoDefaultStorageClass}, - expectedErr: checkup.ErrNoDefaultStorageClass, + clientConfig: clientConfig{noStorageClasses: true, expectNoVMI: true}, + expectedResults: map[string]string{ + reporter.DefaultStorageClassKey: checkup.ErrNoDefaultStorageClass, + reporter.PVCBoundKey: checkup.MessageSkipNoDefaultStorageClass, + reporter.VMBootFromGoldenImageKey: checkup.MessageSkipNoDefaultStorageClass, + reporter.ConcurrentVMBootKey: checkup.MessageSkipNoDefaultStorageClass, + }, + expectedErr: checkup.ErrNoDefaultStorageClass, }, "noDefaultStorageClass": { - clientConfig: clientConfig{noDefaultStorageClass: true}, - expectedResults: map[string]string{reporter.DefaultStorageClassKey: checkup.ErrNoDefaultStorageClass}, - expectedErr: checkup.ErrNoDefaultStorageClass, + clientConfig: clientConfig{noDefaultStorageClass: true, expectNoVMI: true}, + expectedResults: map[string]string{ + reporter.DefaultStorageClassKey: checkup.ErrNoDefaultStorageClass, + reporter.PVCBoundKey: checkup.MessageSkipNoDefaultStorageClass, + reporter.VMBootFromGoldenImageKey: checkup.MessageSkipNoDefaultStorageClass, + reporter.ConcurrentVMBootKey: checkup.MessageSkipNoDefaultStorageClass, + }, + expectedErr: checkup.ErrNoDefaultStorageClass, + }, + "onlyDefaultStorageClass": { + clientConfig: clientConfig{onlyDefaultStorageClass: true}, + expectedResults: map[string]string{reporter.DefaultStorageClassKey: testScName2}, + expectedErr: "", + }, + "onlyDefaultVirtStorageClass": { + clientConfig: clientConfig{onlyDefaultVirtStorageClass: true}, + expectedResults: map[string]string{reporter.DefaultStorageClassKey: testScName}, + expectedErr: "", + }, + "bothDefaultStorageClasses": { + clientConfig: clientConfig{bothDefaultStorageClasses: true}, + expectedResults: map[string]string{reporter.DefaultStorageClassKey: testScName}, + expectedErr: "", }, "multipleDefaultStorageClasses": { clientConfig: clientConfig{multipleDefaultStorageClasses: true}, expectedResults: map[string]string{reporter.DefaultStorageClassKey: checkup.ErrMultipleDefaultStorageClasses}, expectedErr: checkup.ErrMultipleDefaultStorageClasses, }, + "multipleDefaultVirtStorageClasses": { + clientConfig: clientConfig{multipleDefaultVirtStorageClasses: true}, + expectedResults: map[string]string{reporter.DefaultStorageClassKey: checkup.ErrMultipleDefaultStorageClasses}, + expectedErr: checkup.ErrMultipleDefaultStorageClasses, + }, "failPvcBound": { clientConfig: clientConfig{failPvcBound: true}, expectedResults: map[string]string{reporter.PVCBoundKey: checkup.ErrPvcNotBound}, @@ -247,20 +277,24 @@ func successfulRunResults(vmiUnderTestName string) map[string]string { } type clientConfig struct { - skipDeletion bool - noStorageClasses bool - noDefaultStorageClass bool - multipleDefaultStorageClasses bool - failPvcBound bool - unsetEfsStorageClass bool - spIncomplete bool - noVolumeSnapshotClasses bool - dicNoDataSource bool - dataSourceNotReady bool - expectNoVMI bool - cloneFallback bool - failMigration bool - singleNode bool + skipDeletion bool + noStorageClasses bool + noDefaultStorageClass bool + onlyDefaultStorageClass bool + onlyDefaultVirtStorageClass bool + bothDefaultStorageClasses bool + multipleDefaultStorageClasses bool + multipleDefaultVirtStorageClasses bool + failPvcBound bool + unsetEfsStorageClass bool + spIncomplete bool + noVolumeSnapshotClasses bool + dicNoDataSource bool + dataSourceNotReady bool + expectNoVMI bool + cloneFallback bool + failMigration bool + singleNode bool } type clientStub struct { @@ -455,29 +489,41 @@ func (cs *clientStub) ListStorageClasses(ctx context.Context) (*storagev1.Storag Items: []storagev1.StorageClass{ { ObjectMeta: metav1.ObjectMeta{ - Name: testScName, - Annotations: map[string]string{checkup.AnnDefaultStorageClass: "true"}, + Name: testScName, + Annotations: map[string]string{ + checkup.AnnDefaultVirtStorageClass: checkup.StrTrue, + checkup.AnnDefaultStorageClass: checkup.StrFalse, + }, }, }, { ObjectMeta: metav1.ObjectMeta{ - Name: testScName2, - Annotations: map[string]string{checkup.AnnDefaultStorageClass: "false"}, + Name: testScName2, + Annotations: map[string]string{ + checkup.AnnDefaultVirtStorageClass: checkup.StrFalse, + checkup.AnnDefaultStorageClass: checkup.StrTrue, + }, }, }, }, } - if cs.noDefaultStorageClass { - scList.Items[0].Annotations[checkup.AnnDefaultStorageClass] = "false" + if cs.onlyDefaultStorageClass || cs.noDefaultStorageClass { + scList.Items[0].Annotations[checkup.AnnDefaultVirtStorageClass] = checkup.StrFalse + } + if cs.onlyDefaultVirtStorageClass || cs.noDefaultStorageClass { + scList.Items[1].Annotations[checkup.AnnDefaultStorageClass] = checkup.StrFalse } if cs.multipleDefaultStorageClasses { - scList.Items[1].Annotations[checkup.AnnDefaultStorageClass] = "true" + scList.Items[0].Annotations[checkup.AnnDefaultStorageClass] = checkup.StrTrue + } + if cs.multipleDefaultVirtStorageClasses { + scList.Items[1].Annotations[checkup.AnnDefaultVirtStorageClass] = checkup.StrTrue } if cs.unsetEfsStorageClass { scList.Items = append(scList.Items, storagev1.StorageClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test-sc-unset-efs", - Annotations: map[string]string{checkup.AnnDefaultStorageClass: "false"}, + Annotations: map[string]string{checkup.AnnDefaultStorageClass: checkup.StrFalse}, }, Provisioner: efsSc, Parameters: map[string]string{"uid": ""}, @@ -527,6 +573,10 @@ func (cs *clientStub) ListVolumeSnapshotClasses(ctx context.Context) (*snapshotv } func (cs *clientStub) ListDataImportCrons(ctx context.Context, namespace string) (*cdiv1.DataImportCronList, error) { + if namespace != testNamespace { + return &cdiv1.DataImportCronList{}, nil + } + dicList := &cdiv1.DataImportCronList{ Items: []cdiv1.DataImportCron{ { @@ -592,6 +642,15 @@ func (cs *clientStub) ListVirtualMachinesInstances(ctx context.Context, namespac return vmiList, nil } +func (cs *clientStub) GetNamespace(ctx context.Context, name string) (*corev1.Namespace, error) { + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + } + return ns, nil +} + func (cs *clientStub) GetPersistentVolumeClaim(ctx context.Context, namespace, name string) (*corev1.PersistentVolumeClaim, error) { blockMode := corev1.PersistentVolumeBlock pvc := &corev1.PersistentVolumeClaim{ diff --git a/pkg/internal/client/client.go b/pkg/internal/client/client.go index dc433749..7e8d9c20 100644 --- a/pkg/internal/client/client.go +++ b/pkg/internal/client/client.go @@ -132,6 +132,10 @@ func (c *Client) ListCDIs(ctx context.Context) (*cdiv1.CDIList, error) { return c.CdiClient().CdiV1beta1().CDIs().List(ctx, metav1.ListOptions{}) } +func (c *Client) GetNamespace(ctx context.Context, name string) (*corev1.Namespace, error) { + return c.CoreV1().Namespaces().Get(ctx, name, metav1.GetOptions{}) +} + func (c *Client) GetPersistentVolumeClaim(ctx context.Context, namespace, name string) (*corev1.PersistentVolumeClaim, error) { return c.CoreV1().PersistentVolumeClaims(namespace).Get(ctx, name, metav1.GetOptions{}) }