Skip to content

Commit

Permalink
Merge pull request #543 from codingben/CNV-40411
Browse files Browse the repository at this point in the history
feat(CNV-40411): set default instancetype and preference labels
  • Loading branch information
kubevirt-bot authored Nov 19, 2024
2 parents 4129a54 + d0999c6 commit cff603f
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 cff603f

Please sign in to comment.