Skip to content

Commit

Permalink
tests for cdi datasource
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Henriksen <mhenriks@redhat.com>
  • Loading branch information
mhenriks committed Jul 15, 2021
1 parent 4e3e60d commit d20b8a4
Show file tree
Hide file tree
Showing 8 changed files with 294 additions and 12 deletions.
1 change: 0 additions & 1 deletion go.mod
Expand Up @@ -80,7 +80,6 @@ require (
)

replace (

github.com/go-kit/kit => github.com/go-kit/kit v0.3.0
github.com/golang/glog => ./staging/src/github.com/golang/glog
github.com/onsi/ginkgo => github.com/onsi/ginkgo v1.12.1
Expand Down
6 changes: 6 additions & 0 deletions pkg/util/types/BUILD.bazel
Expand Up @@ -23,19 +23,25 @@ go_library(
go_test(
name = "go_default_test",
srcs = [
"dv_test.go",
"pvc_test.go",
"types_suite_test.go",
],
embed = [":go_default_library"],
deps = [
"//staging/src/kubevirt.io/client-go/api/v1:go_default_library",
"//staging/src/kubevirt.io/client-go/generated/containerized-data-importer/clientset/versioned/fake:go_default_library",
"//staging/src/kubevirt.io/client-go/kubecli:go_default_library",
"//staging/src/kubevirt.io/client-go/testutils:go_default_library",
"//vendor/github.com/golang/mock/gomock:go_default_library",
"//vendor/github.com/onsi/ginkgo:go_default_library",
"//vendor/github.com/onsi/ginkgo/extensions/table:go_default_library",
"//vendor/github.com/onsi/gomega:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
"//vendor/kubevirt.io/containerized-data-importer/pkg/apis/core/v1beta1:go_default_library",
],
)
2 changes: 1 addition & 1 deletion pkg/util/types/dv.go
Expand Up @@ -45,7 +45,7 @@ func GetCloneSource(ctx context.Context, client kubecli.KubevirtClient, vm *virt
if cloneSource.Namespace == "" {
cloneSource.Namespace = vm.Namespace
}
} else if dvSpec.SourceRef != nil {
} else if dvSpec.SourceRef != nil && dvSpec.SourceRef.Kind == "DataSource" {
ns := vm.Namespace
if dvSpec.SourceRef.Namespace != nil {
ns = *dvSpec.SourceRef.Namespace
Expand Down
147 changes: 147 additions & 0 deletions pkg/util/types/dv_test.go
@@ -0,0 +1,147 @@
/*
* This file is part of the KubeVirt project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2018 Red Hat, Inc.
*
*/

package types

import (
"context"

. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega"

"github.com/golang/mock/gomock"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"

virtv1 "kubevirt.io/client-go/api/v1"
cdifake "kubevirt.io/client-go/generated/containerized-data-importer/clientset/versioned/fake"
"kubevirt.io/client-go/kubecli"
cdiv1 "kubevirt.io/containerized-data-importer/pkg/apis/core/v1beta1"
)

var _ = Describe("DataVolume utils test", func() {
Context("with VM", func() {
vm := &virtv1.VirtualMachine{
ObjectMeta: metav1.ObjectMeta{
Namespace: "vmnamespace",
Name: "vm",
},
}

createClient := func(cdiObjects ...runtime.Object) kubecli.KubevirtClient {
ctrl := gomock.NewController(GinkgoT())
virtClient := kubecli.NewMockKubevirtClient(ctrl)
cdiClient := cdifake.NewSimpleClientset(cdiObjects...)
virtClient.EXPECT().CdiClient().Return(cdiClient).AnyTimes()
return virtClient
}

It("should ignore DataVolume with no clone operation", func() {
dv := &cdiv1.DataVolumeSpec{
Source: &cdiv1.DataVolumeSource{
Blank: &cdiv1.DataVolumeBlankImage{},
},
}

cs, err := GetCloneSource(context.TODO(), createClient(), vm, dv)
Expect(err).ToNot(HaveOccurred())
Expect(cs).To(BeNil())
})

DescribeTable("should properly handle DataVolume clone source", func(sourceNamespace, expectedNamespace string) {
sourceName := "name"
dv := &cdiv1.DataVolumeSpec{
Source: &cdiv1.DataVolumeSource{
PVC: &cdiv1.DataVolumeSourcePVC{
Namespace: sourceNamespace,
Name: sourceName,
},
},
}

cs, err := GetCloneSource(context.TODO(), createClient(), vm, dv)
Expect(err).ToNot(HaveOccurred())
Expect(cs).ToNot(BeNil())
Expect(cs.Namespace).To(Equal(expectedNamespace))
Expect(cs.Name).To(Equal(sourceName))
},
Entry("source namespace not specified", "", vm.Namespace),
Entry("source namespace is specified", "ns2", "ns2"),
)

It("should error if DataSource does not exist", func() {
ns := "foo"
dv := &cdiv1.DataVolumeSpec{
SourceRef: &cdiv1.DataVolumeSourceRef{
Kind: "DataSource",
Namespace: &ns,
Name: "bar",
},
}

cs, err := GetCloneSource(context.TODO(), createClient(), vm, dv)
Expect(err).To(HaveOccurred())
Expect(cs).To(BeNil())
})

DescribeTable("should properly handle DataVolume clone sourceRef", func(sourceRefNamespace, sourceNamespace, expectedNamespace string) {
sourceRefName := "sourceRef"
sourceName := "name"

ref := &cdiv1.DataSource{
ObjectMeta: metav1.ObjectMeta{
Namespace: vm.Namespace,
Name: sourceRefName,
},
Spec: cdiv1.DataSourceSpec{
Source: cdiv1.DataSourceSource{
PVC: &cdiv1.DataVolumeSourcePVC{
Namespace: sourceNamespace,
Name: sourceName,
},
},
},
}

dv := &cdiv1.DataVolumeSpec{
SourceRef: &cdiv1.DataVolumeSourceRef{
Kind: "DataSource",
Name: sourceRefName,
},
}

if sourceRefNamespace != "" {
ref.Namespace = sourceRefNamespace
dv.SourceRef.Namespace = &sourceRefNamespace
}

cs, err := GetCloneSource(context.TODO(), createClient(ref), vm, dv)
Expect(err).ToNot(HaveOccurred())
Expect(cs).ToNot(BeNil())
Expect(cs.Namespace).To(Equal(expectedNamespace))
Expect(cs.Name).To(Equal(sourceName))
},
Entry("sourceRef namespace and source namespace not specified", "", "", vm.Namespace),
Entry("source namespace not specified", "foo", "", "foo"),
Entry("sourceRef namespace not specified", "", "bar", "bar"),
Entry("everything specified", "foo", "bar", "bar"),
)
})
})
Expand Up @@ -72,6 +72,7 @@ go_test(
"//pkg/virt-operator/resource/generate/rbac:go_default_library",
"//staging/src/kubevirt.io/client-go/api/v1:go_default_library",
"//staging/src/kubevirt.io/client-go/apis/snapshot/v1alpha1:go_default_library",
"//staging/src/kubevirt.io/client-go/generated/containerized-data-importer/clientset/versioned/fake:go_default_library",
"//staging/src/kubevirt.io/client-go/generated/kubevirt/clientset/versioned/fake:go_default_library",
"//staging/src/kubevirt.io/client-go/kubecli:go_default_library",
"//staging/src/kubevirt.io/client-go/testutils:go_default_library",
Expand Down
Expand Up @@ -38,6 +38,7 @@ import (
k8sfield "k8s.io/apimachinery/pkg/util/validation/field"

v1 "kubevirt.io/client-go/api/v1"
cdifake "kubevirt.io/client-go/generated/containerized-data-importer/clientset/versioned/fake"
"kubevirt.io/client-go/kubecli"
cdiv1 "kubevirt.io/containerized-data-importer/pkg/apis/core/v1beta1"
"kubevirt.io/kubevirt/pkg/testutils"
Expand Down Expand Up @@ -1117,6 +1118,91 @@ var _ = Describe("Validating VM Admitter", func() {
table.Entry("when everything suppied with 'sa' service account", "ns1", "ns2", "ns3", "sa", "ns3", "ns2", "sa"),
)

table.DescribeTable("should successfully authorize clone from sourceRef", func(arNamespace, vmNamespace, sourceRefNamespace,
sourceNamespace, serviceAccount, expectedSourceNamespace, expectedTargetNamespace, expectedServiceAccount string) {
sourceRefName := "sourceRef"
ds := &cdiv1.DataSource{
ObjectMeta: metav1.ObjectMeta{
Namespace: vmNamespace,
Name: sourceRefName,
},
Spec: cdiv1.DataSourceSpec{
Source: cdiv1.DataSourceSource{
PVC: &cdiv1.DataVolumeSourcePVC{
Name: "whocares",
},
},
},
}

vm := &v1.VirtualMachine{
ObjectMeta: metav1.ObjectMeta{
Namespace: vmNamespace,
},
Spec: v1.VirtualMachineSpec{
Template: &v1.VirtualMachineInstanceTemplateSpec{},
DataVolumeTemplates: []v1.DataVolumeTemplateSpec{
{
ObjectMeta: metav1.ObjectMeta{
Name: "whatever",
},
Spec: cdiv1.DataVolumeSpec{
SourceRef: &cdiv1.DataVolumeSourceRef{
Kind: "DataSource",
Name: sourceRefName,
},
},
},
},
},
}

if sourceRefNamespace != "" {
ds.Namespace = sourceRefNamespace
vm.Spec.DataVolumeTemplates[0].Spec.SourceRef.Namespace = &sourceRefNamespace
}

if sourceNamespace != "" {
ds.Spec.Source.PVC.Namespace = sourceNamespace
}

if serviceAccount != "" {
vm.Spec.Template.Spec.Volumes = []v1.Volume{
{
VolumeSource: v1.VolumeSource{
ServiceAccount: &v1.ServiceAccountVolumeSource{
ServiceAccountName: serviceAccount,
},
},
},
}
}

ar := &admissionv1.AdmissionRequest{
Namespace: arNamespace,
}

cdiClient := cdifake.NewSimpleClientset(ds)
virtClient.EXPECT().CdiClient().Return(cdiClient).AnyTimes()

vmsAdmitter.cloneAuthFunc = makeCloneAdmitFunc(expectedSourceNamespace, "whocares",
expectedTargetNamespace, expectedServiceAccount)
causes, err := vmsAdmitter.authorizeVirtualMachineSpec(ar, vm)
Expect(err).ToNot(HaveOccurred())
Expect(causes).To(BeEmpty())
},
table.Entry("when source namespace suppied", "ns1", "", "", "ns3", "", "ns3", "ns1", "default"),
table.Entry("when vm namespace suppied and source not", "ns1", "ns2", "", "", "", "ns2", "ns2", "default"),
table.Entry("when ar namespace suppied and vm/source not", "ns1", "", "", "", "", "ns1", "ns1", "default"),
table.Entry("when everything suppied with default service account", "ns1", "ns2", "", "ns3", "", "ns3", "ns2", "default"),
table.Entry("when everything suppied with 'sa' service account", "ns1", "ns2", "", "ns3", "sa", "ns3", "ns2", "sa"),
table.Entry("when source namespace and sourceRef namespace suppied", "ns1", "", "foo", "ns3", "", "ns3", "ns1", "default"),
table.Entry("when vm namespace and sourceRef namespace suppied and source not", "ns1", "ns2", "foo", "", "", "foo", "ns2", "default"),
table.Entry("when ar namespace and sourceRef namespace suppied and vm/source not", "ns1", "", "foo", "", "", "foo", "ns1", "default"),
table.Entry("when everything and sourceRef suppied with default service account", "ns1", "ns2", "foo", "ns3", "", "ns3", "ns2", "default"),
table.Entry("when everything and sourceRef suppied with 'sa' service account", "ns1", "ns2", "foo", "ns3", "sa", "ns3", "ns2", "sa"),
)

table.DescribeTable("should deny clone", func(sourceNamespace, sourceName, failMessage string, failErr error, expectedMessage string) {
vm := &v1.VirtualMachine{
Spec: v1.VirtualMachineSpec{
Expand Down
62 changes: 53 additions & 9 deletions pkg/virt-controller/watch/vm_test.go
Expand Up @@ -701,6 +701,34 @@ var _ = Describe("VirtualMachine", func() {
},
}

ds := &cdiv1.DataSource{
ObjectMeta: metav1.ObjectMeta{
Namespace: "ns2",
Name: "source-ref",
},
Spec: cdiv1.DataSourceSpec{
Source: cdiv1.DataSourceSource{
PVC: &cdiv1.DataVolumeSourcePVC{
Namespace: "ns1",
Name: "source-pvc",
},
},
},
}

dv3 := &v1.DataVolumeTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Name: "dv3",
},
Spec: cdiv1.DataVolumeSpec{
SourceRef: &cdiv1.DataVolumeSourceRef{
Kind: "DataSource",
Namespace: &ds.Namespace,
Name: ds.Name,
},
},
}

serviceAccountVol := &v1.Volume{
Name: "sa",
VolumeSource: v1.VolumeSource{
Expand All @@ -710,7 +738,7 @@ var _ = Describe("VirtualMachine", func() {
},
}

table.DescribeTable("create clone DataVolume for VirtualMachineInstance", func(dv *v1.DataVolumeTemplateSpec, saVol *v1.Volume, fail bool) {
table.DescribeTable("create clone DataVolume for VirtualMachineInstance", func(dv *v1.DataVolumeTemplateSpec, saVol *v1.Volume, ds *cdiv1.DataSource, fail bool) {
vm, _ := DefaultVirtualMachine(true)
vm.Spec.Template.Spec.Volumes = append(vm.Spec.Template.Spec.Volumes,
v1.Volume{
Expand Down Expand Up @@ -738,14 +766,29 @@ var _ = Describe("VirtualMachine", func() {
vmInterface.EXPECT().UpdateStatus(gomock.Any()).Times(1).Return(vm, nil)
}

if ds != nil {
cdiClient.PrependReactor("get", "datasources", func(action testing.Action) (handled bool, obj runtime.Object, err error) {
ga := action.(testing.GetAction)
Expect(ga.GetNamespace()).To(Equal(ds.Namespace))
Expect(ga.GetName()).To(Equal(ds.Name))
return true, ds, nil
})
}

controller.cloneAuthFunc = func(pvcNamespace, pvcName, saNamespace, saName string) (bool, string, error) {
if dv.Spec.Source.PVC.Namespace != "" {
Expect(pvcNamespace).Should(Equal(dv.Spec.Source.PVC.Namespace))
if dv.Spec.Source != nil {
if dv.Spec.Source.PVC.Namespace != "" {
Expect(pvcNamespace).Should(Equal(dv.Spec.Source.PVC.Namespace))
} else {
Expect(pvcNamespace).Should(Equal(vm.Namespace))
}

Expect(pvcName).Should(Equal(dv.Spec.Source.PVC.Name))
} else {
Expect(pvcNamespace).Should(Equal(vm.Namespace))
Expect(pvcNamespace).Should(Equal(ds.Spec.Source.PVC.Namespace))
Expect(pvcName).Should(Equal(ds.Spec.Source.PVC.Name))
}

Expect(pvcName).Should(Equal(dv.Spec.Source.PVC.Name))
Expect(saNamespace).Should(Equal(vm.Namespace))

if saVol != nil {
Expand All @@ -769,10 +812,11 @@ var _ = Describe("VirtualMachine", func() {
testutils.ExpectEvent(recorder, SuccessfulDataVolumeCreateReason)
}
},
table.Entry("with auth and source namespace defined", dv1, serviceAccountVol, false),
table.Entry("with auth and no source namespace defined", dv2, serviceAccountVol, false),
table.Entry("with auth and source namespace no serviceaccount defined", dv1, nil, false),
table.Entry("with no auth and source namespace defined", dv1, serviceAccountVol, true),
table.Entry("with auth and source namespace defined", dv1, serviceAccountVol, nil, false),
table.Entry("with auth and no source namespace defined", dv2, serviceAccountVol, nil, false),
table.Entry("with auth and source namespace no serviceaccount defined", dv1, nil, nil, false),
table.Entry("with no auth and source namespace defined", dv1, serviceAccountVol, nil, true),
table.Entry("with auth, datasource and source namespace defined", dv3, serviceAccountVol, ds, false),
)
})

Expand Down

0 comments on commit d20b8a4

Please sign in to comment.