Skip to content

Commit

Permalink
feat(CNV-40411): set default instancetype and preference labels
Browse files Browse the repository at this point in the history
During execution of the disk-uploader, it should
be able to get the default-instancetype and
default-preference labels from the DV or PVC,
and set them in the newly created containerDisk
metadata afterwards.

Signed-off-by: Ben Oukhanov <[email protected]>
  • Loading branch information
codingben committed Nov 18, 2024
1 parent 4129a54 commit d0999c6
Show file tree
Hide file tree
Showing 23 changed files with 1,999 additions and 2 deletions.
8 changes: 7 additions & 1 deletion cmd/disk-uploader/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,13 @@ func run(opts parse.CLIOptions, k8sClient kubernetes.Interface, virtClient kubec

log.Logger().Info("Building a new container image...")

containerImage, err := image.Build(diskPath)
labels, err := vmexport.GetLabelsFromExportSource(virtClient, kind, namespace, name, volumeName)
if err != nil {
return err
}

config := image.DefaultConfig(labels)
containerImage, err := image.Build(diskPath, config)
if err != nil {
return err
}
Expand Down
31 changes: 30 additions & 1 deletion modules/disk-uploader/pkg/image/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package image

import (
"context"
"fmt"
"log"
"os"
"time"
Expand All @@ -16,7 +17,24 @@ import (
tar "kubevirt.io/containerdisks/pkg/build"
)

func Build(diskPath string) (v1.Image, error) {
var labelEnvPairs = map[string]string{
"instancetype.kubevirt.io/default-instancetype": "INSTANCETYPE_KUBEVIRT_IO_DEFAULT_INSTANCETYPE",
"instancetype.kubevirt.io/default-instancetype-kind": "INSTANCETYPE_KUBEVIRT_IO_DEFAULT_INSTANCETYPE_KIND",
"instancetype.kubevirt.io/default-preference": "INSTANCETYPE_KUBEVIRT_IO_DEFAULT_PREFERENCE",
"instancetype.kubevirt.io/default-preference-kind": "INSTANCETYPE_KUBEVIRT_IO_DEFAULT_PREFERENCE_KIND",
}

func DefaultConfig(labels map[string]string) v1.Config {
var env []string
for label, envVar := range labelEnvPairs {
if value, exists := labels[label]; exists {
env = append(env, fmt.Sprintf("%s=%s", envVar, value))
}
}
return v1.Config{Env: env}
}

func Build(diskPath string, config v1.Config) (v1.Image, error) {
layer, err := tarball.LayerFromOpener(tar.StreamLayerOpener(diskPath))
if err != nil {
log.Fatalf("Error creating layer from file: %v", err)
Expand All @@ -28,6 +46,17 @@ func Build(diskPath string) (v1.Image, error) {
log.Fatalf("Error appending layer: %v", err)
return nil, err
}

configFile, err := image.ConfigFile()
if err != nil {
return nil, fmt.Errorf("error getting the image config file: %v", err)
}
configFile.Config = config

image, err = mutate.ConfigFile(image, configFile)
if err != nil {
return nil, fmt.Errorf("error setting the image config file: %v", err)
}
return image, nil
}

Expand Down
31 changes: 31 additions & 0 deletions modules/disk-uploader/pkg/vmexport/vmexport.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/kubevirt/kubevirt-tekton-tasks/modules/shared/pkg/ownerreference"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"

Expand Down Expand Up @@ -90,6 +91,36 @@ func GetRawDiskUrlFromVolumes(client kubecli.KubevirtClient, namespace, name, vo
return "", fmt.Errorf("volume %s is not found in VirtualMachineExport internal volumes", volumeName)
}

func GetLabelsFromExportSource(virtClient kubecli.KubevirtClient, exportSourceKind, exportSourceNamespace, exportSourceName, volumeName string) (map[string]string, error) {
switch exportSourceKind {
case "VirtualMachine", "VirtualMachineSnapshot":
return getLabelsFromVirtualMachineOrSnapshot(virtClient, exportSourceNamespace, volumeName)
case "PersistentVolumeClaim":
return getLabelsFromPVC(virtClient, exportSourceNamespace, exportSourceName)
default:
return nil, fmt.Errorf("unsupported source kind: %s", exportSourceKind)
}
}

func getLabelsFromVirtualMachineOrSnapshot(virtClient kubecli.KubevirtClient, namespace, volumeName string) (map[string]string, error) {
dv, err := virtClient.CdiClient().CdiV1beta1().DataVolumes(namespace).Get(context.Background(), volumeName, metav1.GetOptions{})
if err == nil {
return dv.GetLabels(), nil
}
if !errors.IsNotFound(err) {
return nil, err
}
return getLabelsFromPVC(virtClient, namespace, volumeName)
}

func getLabelsFromPVC(virtClient kubecli.KubevirtClient, namespace, name string) (map[string]string, error) {
pvc, err := virtClient.CoreV1().PersistentVolumeClaims(namespace).Get(context.Background(), name, metav1.GetOptions{})
if err != nil {
return nil, err
}
return pvc.GetLabels(), nil
}

func getExportSource(exportSourceKind, exportSourceName string) (corev1.TypedLocalObjectReference, error) {
switch exportSourceKind {
case sourceVM:
Expand Down
91 changes: 91 additions & 0 deletions modules/disk-uploader/pkg/vmexport/vmexport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ import (
fakek8sclient "k8s.io/client-go/kubernetes/fake"

v1beta1 "kubevirt.io/api/export/v1beta1"
fakecdiclient "kubevirt.io/client-go/generated/containerized-data-importer/clientset/versioned/fake"
kubevirtfake "kubevirt.io/client-go/generated/kubevirt/clientset/versioned/fake"
"kubevirt.io/client-go/kubecli"
cdiv1beta1 "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1"

"github.com/kubevirt/kubevirt-tekton-tasks/modules/disk-uploader/pkg/vmexport"
)
Expand All @@ -29,6 +31,7 @@ var _ = Describe("VMExport", func() {

var (
kubeClient *fakek8sclient.Clientset
cdiClient *fakecdiclient.Clientset
vmExportClient *kubevirtfake.Clientset
virtClient kubecli.KubevirtClient
)
Expand All @@ -37,11 +40,13 @@ var _ = Describe("VMExport", func() {
ctrl := gomock.NewController(GinkgoT())
kubeClient = fakek8sclient.NewSimpleClientset()
vmExportClient = kubevirtfake.NewSimpleClientset()
cdiClient = fakecdiclient.NewSimpleClientset()

kubecli.GetKubevirtClientFromClientConfig = kubecli.GetMockKubevirtClientFromClientConfig
kubecli.MockKubevirtClientInstance = kubecli.NewMockKubevirtClient(ctrl)
kubecli.MockKubevirtClientInstance.EXPECT().CoreV1().Return(kubeClient.CoreV1()).AnyTimes()
kubecli.MockKubevirtClientInstance.EXPECT().VirtualMachineExport(namespace).Return(vmExportClient.ExportV1beta1().VirtualMachineExports(namespace)).AnyTimes()
kubecli.MockKubevirtClientInstance.EXPECT().CdiClient().Return(cdiClient).AnyTimes()

virtClient, _ = kubecli.GetKubevirtClientFromClientConfig(nil)

Expand Down Expand Up @@ -186,4 +191,90 @@ var _ = Describe("VMExport", func() {
Expect(err).To(MatchError("no links found in VirtualMachineExport status"))
})
})

Describe("GetLabelsFromExportSource", func() {
var (
defaultInstanceType = "instancetype.kubevirt.io/default-instancetype"
defaultPreference = "instancetype.kubevirt.io/default-preference"
)

DescribeTable("should return labels from datavolume", func(resourceType string) {
_, err := virtClient.CdiClient().CdiV1beta1().DataVolumes(namespace).Create(context.Background(), &cdiv1beta1.DataVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "example-volume",
Namespace: namespace,
Labels: map[string]string{
defaultInstanceType: "cx1.2xlarge",
defaultPreference: "fedora",
},
},
}, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())

labels, err := vmexport.GetLabelsFromExportSource(virtClient, resourceType, namespace, name, "example-volume")
Expect(err).NotTo(HaveOccurred())
Expect(labels).To(HaveKeyWithValue(defaultInstanceType, "cx1.2xlarge"))
Expect(labels).To(HaveKeyWithValue(defaultPreference, "fedora"))
},
Entry("for VirtualMachine", "VirtualMachine"),
Entry("for VirtualMachineSnapshot", "VirtualMachineSnapshot"),
)

DescribeTable("should return labels from pvc if there is no datavolume", func(resourceType string) {
_, err := kubeClient.CoreV1().PersistentVolumeClaims(namespace).Create(context.Background(), &corev1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "example-pvc",
Namespace: namespace,
Labels: map[string]string{
defaultInstanceType: "cx1.2xlarge",
defaultPreference: "fedora",
},
},
}, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())

labels, err := vmexport.GetLabelsFromExportSource(virtClient, resourceType, namespace, name, "example-pvc")
Expect(err).NotTo(HaveOccurred())
Expect(labels).To(HaveKeyWithValue(defaultInstanceType, "cx1.2xlarge"))
Expect(labels).To(HaveKeyWithValue(defaultPreference, "fedora"))
},
Entry("for VirtualMachine", "VirtualMachine"),
Entry("for VirtualMachineSnapshot", "VirtualMachineSnapshot"),
)

It("should return labels directly from pvc", func() {
_, err := kubeClient.CoreV1().PersistentVolumeClaims(namespace).Create(context.Background(), &corev1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "example-pvc",
Namespace: namespace,
Labels: map[string]string{
defaultInstanceType: "cx1.2xlarge",
defaultPreference: "fedora",
},
},
}, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())

labels, err := vmexport.GetLabelsFromExportSource(virtClient, "PersistentVolumeClaim", namespace, "example-pvc", "")
Expect(err).NotTo(HaveOccurred())
Expect(labels).To(HaveKeyWithValue(defaultInstanceType, "cx1.2xlarge"))
Expect(labels).To(HaveKeyWithValue(defaultPreference, "fedora"))
})

It("should return unsupported source kind error", func() {
labels, err := vmexport.GetLabelsFromExportSource(virtClient, "VirtualMachineInstance", namespace, name, "disk-volume-1")
Expect(err).To(MatchError("unsupported source kind: VirtualMachineInstance"))
Expect(labels).To(BeNil())
})

DescribeTable("should return an error when DV or PVC is not found", func(exportSourceKind string, volumeName string) {
labels, err := vmexport.GetLabelsFromExportSource(virtClient, exportSourceKind, namespace, name, volumeName)
Expect(err).To(MatchError(errors.IsNotFound, "errors.IsNotFound"))
Expect(labels).To(BeNil())
},
Entry("for VirtualMachine with missing DV and PVC", "VirtualMachine", "example-volume"),
Entry("for VirtualMachineSnapshot with missing DV and PVC", "VirtualMachineSnapshot", "example-volume"),
Entry("for PersistentVolumeClaim with missing PVC", "PersistentVolumeClaim", ""),
)
})
})

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit d0999c6

Please sign in to comment.