mirror of
https://github.com/kubevirt/containerized-data-importer.git
synced 2025-06-03 06:30:22 +00:00

* make deps-update Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * ReourceRequirements -> VolumeResourceRequirements Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * fix calls to controller.Watch() controller-runtime changed the API! Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * Fix errors with actual openshift/library-go lib Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * make all works now and everything compiles Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * fix "make update-codegen" because generate_groups.sh deprecated Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * run "make generate" Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * fix transfer unittest because of change to controller-runtime Signed-off-by: Michael Henriksen <mhenriks@redhat.com> --------- Signed-off-by: Michael Henriksen <mhenriks@redhat.com>
302 lines
9.3 KiB
Go
302 lines
9.3 KiB
Go
package datavolume
|
|
|
|
import (
|
|
"context"
|
|
"strconv"
|
|
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
|
|
ocpconfigv1 "github.com/openshift/api/config/v1"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/api/errors"
|
|
"k8s.io/apimachinery/pkg/api/resource"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/client-go/kubernetes/scheme"
|
|
"k8s.io/utils/ptr"
|
|
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
|
|
|
cdiv1 "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1"
|
|
. "kubevirt.io/containerized-data-importer/pkg/controller/common"
|
|
)
|
|
|
|
var _ = Describe("renderPvcSpecVolumeSize", func() {
|
|
client := createClient()
|
|
volumeSize := resource.MustParse("1G")
|
|
scName := "test"
|
|
|
|
It("Should return empty volume size on clone PVC with empty storage size", func() {
|
|
pvcSpec := &corev1.PersistentVolumeClaimSpec{}
|
|
err := renderPvcSpecVolumeSize(client, pvcSpec, true)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
requestedVolumeSize, found := pvcSpec.Resources.Requests[corev1.ResourceStorage]
|
|
Expect(found).To(BeTrue())
|
|
Expect(requestedVolumeSize.IsZero()).To(BeTrue())
|
|
})
|
|
|
|
It("Should return error on non-clone PVC with empty storage size", func() {
|
|
pvcSpec := &corev1.PersistentVolumeClaimSpec{}
|
|
err := renderPvcSpecVolumeSize(client, pvcSpec, false)
|
|
Expect(err).To(HaveOccurred())
|
|
Expect(err.Error()).To(ContainSubstring("PVC Spec is not valid - missing storage size"))
|
|
_, found := pvcSpec.Resources.Requests[corev1.ResourceStorage]
|
|
Expect(found).To(BeFalse())
|
|
})
|
|
|
|
It("Should return error on PVC with storage size smaller than 1MiB", func() {
|
|
volumeMode := corev1.PersistentVolumeBlock
|
|
pvcSpec := &corev1.PersistentVolumeClaimSpec{
|
|
StorageClassName: &scName,
|
|
VolumeMode: &volumeMode,
|
|
Resources: corev1.VolumeResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceStorage: resource.MustParse("9"),
|
|
},
|
|
},
|
|
}
|
|
err := renderPvcSpecVolumeSize(client, pvcSpec, false)
|
|
Expect(err).To(HaveOccurred())
|
|
Expect(err.Error()).To(ContainSubstring("PVC Spec is not valid - storage size should be at least 1MiB"))
|
|
})
|
|
|
|
It("Should return the same volume size (block volume mode)", func() {
|
|
volumeMode := corev1.PersistentVolumeBlock
|
|
pvcSpec := &corev1.PersistentVolumeClaimSpec{
|
|
StorageClassName: &scName,
|
|
VolumeMode: &volumeMode,
|
|
Resources: corev1.VolumeResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceStorage: volumeSize,
|
|
},
|
|
},
|
|
}
|
|
err := renderPvcSpecVolumeSize(client, pvcSpec, false)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
requestedVolumeSize, found := pvcSpec.Resources.Requests[corev1.ResourceStorage]
|
|
Expect(found).To(BeTrue())
|
|
Expect(requestedVolumeSize.Value()).To(Equal(volumeSize.Value()))
|
|
})
|
|
|
|
It("Should return the inflated volume size (filesystem volume mode)", func() {
|
|
volumeMode := corev1.PersistentVolumeFilesystem
|
|
pvcSpec := &corev1.PersistentVolumeClaimSpec{
|
|
StorageClassName: &scName,
|
|
VolumeMode: &volumeMode,
|
|
Resources: corev1.VolumeResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceStorage: volumeSize,
|
|
},
|
|
},
|
|
}
|
|
err := renderPvcSpecVolumeSize(client, pvcSpec, false)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
requestedVolumeSize, found := pvcSpec.Resources.Requests[corev1.ResourceStorage]
|
|
Expect(found).To(BeTrue())
|
|
|
|
// Inflate expected size with overhead
|
|
fsOverhead, err := GetFilesystemOverheadForStorageClass(context.TODO(), client, pvcSpec.StorageClassName)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
fsOverheadFloat, _ := strconv.ParseFloat(string(fsOverhead), 64)
|
|
requiredSpace := GetRequiredSpace(fsOverheadFloat, volumeSize.Value())
|
|
expectedResult := resource.NewScaledQuantity(requiredSpace, 0)
|
|
|
|
Expect(requestedVolumeSize.Value()).To(BeNumerically(">", volumeSize.Value()))
|
|
Expect(requestedVolumeSize.Value()).To(Equal(expectedResult.Value()))
|
|
})
|
|
})
|
|
|
|
var _ = Describe("updateDataVolumeDefaultInstancetypeLabels", func() {
|
|
|
|
const (
|
|
namespace = "namespace"
|
|
sourcePVCName = "sourcePVC"
|
|
sourceDataSourceName = "sourceDataSource"
|
|
)
|
|
|
|
var (
|
|
fakeClient client.Client
|
|
dataVolumeWithSourcePVC cdiv1.DataVolume
|
|
dataVolumeWithSourceDataSource cdiv1.DataVolume
|
|
)
|
|
|
|
defaultInstancetypeLabelMap := map[string]string{
|
|
LabelDefaultInstancetype: LabelDefaultInstancetype,
|
|
LabelDefaultInstancetypeKind: LabelDefaultInstancetypeKind,
|
|
LabelDefaultPreference: LabelDefaultPreference,
|
|
LabelDefaultPreferenceKind: LabelDefaultPreferenceKind,
|
|
}
|
|
|
|
BeforeEach(func() {
|
|
dataVolumeWithSourcePVC = cdiv1.DataVolume{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "pvc-datavolume",
|
|
Namespace: namespace,
|
|
},
|
|
Spec: cdiv1.DataVolumeSpec{
|
|
Source: &cdiv1.DataVolumeSource{
|
|
PVC: &cdiv1.DataVolumeSourcePVC{
|
|
Name: sourcePVCName,
|
|
Namespace: namespace,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
dataVolumeWithSourceDataSource = cdiv1.DataVolume{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "datasource-datavolume",
|
|
Namespace: namespace,
|
|
},
|
|
Spec: cdiv1.DataVolumeSpec{
|
|
SourceRef: &cdiv1.DataVolumeSourceRef{
|
|
Kind: cdiv1.DataVolumeDataSource,
|
|
Name: sourceDataSourceName,
|
|
Namespace: ptr.To[string](namespace),
|
|
},
|
|
},
|
|
}
|
|
fakeClient = createClient(
|
|
&corev1.PersistentVolumeClaim{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: sourcePVCName,
|
|
Namespace: namespace,
|
|
Labels: defaultInstancetypeLabelMap,
|
|
},
|
|
},
|
|
&cdiv1.DataSource{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: sourceDataSourceName,
|
|
Namespace: namespace,
|
|
Labels: defaultInstancetypeLabelMap,
|
|
},
|
|
Spec: cdiv1.DataSourceSpec{
|
|
Source: cdiv1.DataSourceSource{
|
|
PVC: &cdiv1.DataVolumeSourcePVC{
|
|
Name: sourcePVCName,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
)
|
|
})
|
|
|
|
DescribeTable("should update DataVolume with labels from source using", func(dataVolume *cdiv1.DataVolume) {
|
|
syncState := &dvSyncState{
|
|
dvMutated: dataVolume,
|
|
}
|
|
Expect(syncState.dvMutated.Labels).To(BeEmpty())
|
|
Expect(updateDataVolumeDefaultInstancetypeLabels(fakeClient, syncState)).To(Succeed())
|
|
Expect(syncState.dvMutated.Labels).ToNot(BeEmpty())
|
|
for k, v := range defaultInstancetypeLabelMap {
|
|
Expect(syncState.dvMutated.Labels).To(HaveKeyWithValue(k, v))
|
|
}
|
|
},
|
|
Entry("PVC", &dataVolumeWithSourcePVC),
|
|
Entry("dataSource", &dataVolumeWithSourceDataSource),
|
|
)
|
|
|
|
DescribeTable("should not update DataVolume with labels from source if already present using", func(dataVolume *cdiv1.DataVolume) {
|
|
const customDefaultInstancetype = "customDefaultInstancetype"
|
|
dv := dataVolume
|
|
dv.Labels = map[string]string{
|
|
LabelDefaultInstancetype: customDefaultInstancetype,
|
|
}
|
|
syncState := &dvSyncState{
|
|
dvMutated: dv,
|
|
}
|
|
|
|
Expect(updateDataVolumeDefaultInstancetypeLabels(fakeClient, syncState)).To(Succeed())
|
|
Expect(syncState.dvMutated.Labels).To(HaveLen(1))
|
|
Expect(syncState.dvMutated.Labels).To(HaveKeyWithValue(LabelDefaultInstancetype, customDefaultInstancetype))
|
|
},
|
|
Entry("PVC", &dataVolumeWithSourcePVC),
|
|
Entry("DataSource", &dataVolumeWithSourceDataSource),
|
|
)
|
|
|
|
DescribeTable("should ignore IsNotFound errors", func(dataVolume *cdiv1.DataVolume) {
|
|
syncState := &dvSyncState{
|
|
dvMutated: dataVolume,
|
|
}
|
|
Expect(updateDataVolumeDefaultInstancetypeLabels(fakeClient, syncState)).To(Succeed())
|
|
},
|
|
Entry("PVC", &cdiv1.DataVolume{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "pvc-datavolume",
|
|
Namespace: namespace,
|
|
},
|
|
Spec: cdiv1.DataVolumeSpec{
|
|
Source: &cdiv1.DataVolumeSource{
|
|
PVC: &cdiv1.DataVolumeSourcePVC{
|
|
Name: "unknown",
|
|
Namespace: namespace,
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
Entry("DataSource", &cdiv1.DataVolume{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "datasource-datavolume",
|
|
Namespace: namespace,
|
|
},
|
|
Spec: cdiv1.DataVolumeSpec{
|
|
SourceRef: &cdiv1.DataVolumeSourceRef{
|
|
Kind: cdiv1.DataVolumeDataSource,
|
|
Name: "unknown",
|
|
Namespace: ptr.To[string](namespace),
|
|
},
|
|
},
|
|
}),
|
|
)
|
|
|
|
DescribeTable("should return all non IsNotFound errors", func(dataVolume *cdiv1.DataVolume) {
|
|
err := updateDataVolumeDefaultInstancetypeLabels(
|
|
fakeClientWithGetServiceUnavailableErr{
|
|
fakeClient,
|
|
},
|
|
&dvSyncState{
|
|
dvMutated: dataVolume,
|
|
},
|
|
)
|
|
Expect(errors.IsServiceUnavailable(err)).To(BeTrue())
|
|
},
|
|
Entry("PVC", &dataVolumeWithSourcePVC),
|
|
Entry("DataSource", &dataVolumeWithSourceDataSource),
|
|
)
|
|
})
|
|
|
|
func createDataVolumeWithStorageAPI(name, ns string, source *cdiv1.DataVolumeSource, storageSpec *cdiv1.StorageSpec) *cdiv1.DataVolume {
|
|
return &cdiv1.DataVolume{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: name,
|
|
Namespace: ns,
|
|
},
|
|
Spec: cdiv1.DataVolumeSpec{
|
|
Source: source,
|
|
Storage: storageSpec,
|
|
},
|
|
}
|
|
}
|
|
|
|
func createClient(objs ...client.Object) client.Client {
|
|
// Register cdi types with the runtime scheme.
|
|
s := scheme.Scheme
|
|
_ = cdiv1.AddToScheme(s)
|
|
// Register other types with the runtime scheme.
|
|
_ = ocpconfigv1.Install(s)
|
|
// Create a fake client to mock API calls.
|
|
return fake.NewClientBuilder().WithScheme(s).WithObjects(objs...).Build()
|
|
}
|
|
|
|
type fakeClientWithGetServiceUnavailableErr struct {
|
|
client.Client
|
|
}
|
|
|
|
func (c fakeClientWithGetServiceUnavailableErr) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
|
|
return errors.NewServiceUnavailable("error")
|
|
}
|