diff --git a/operator/config/crd/bases/forklift.konveyor.io_migrations.yaml b/operator/config/crd/bases/forklift.konveyor.io_migrations.yaml index 427070175..31308e480 100644 --- a/operator/config/crd/bases/forklift.konveyor.io_migrations.yaml +++ b/operator/config/crd/bases/forklift.konveyor.io_migrations.yaml @@ -318,6 +318,50 @@ spec: vsphere: The managed object ID. type: string + luks: + description: Disk decryption LUKS keys + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. + TODO: this design is not final and this field is subject to change in the future. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + namespace: + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + type: string + resourceVersion: + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids + type: string + type: object + x-kubernetes-map-type: atomic name: description: |- An object Name. diff --git a/operator/config/crd/bases/forklift.konveyor.io_plans.yaml b/operator/config/crd/bases/forklift.konveyor.io_plans.yaml index 09369dd41..9a1ee0a7e 100644 --- a/operator/config/crd/bases/forklift.konveyor.io_plans.yaml +++ b/operator/config/crd/bases/forklift.konveyor.io_plans.yaml @@ -373,6 +373,50 @@ spec: vsphere: The managed object ID. type: string + luks: + description: Disk decryption LUKS keys + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. + TODO: this design is not final and this field is subject to change in the future. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + namespace: + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + type: string + resourceVersion: + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids + type: string + type: object + x-kubernetes-map-type: atomic name: description: |- An object Name. @@ -778,6 +822,50 @@ spec: vsphere: The managed object ID. type: string + luks: + description: Disk decryption LUKS keys + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. + TODO: this design is not final and this field is subject to change in the future. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + namespace: + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + type: string + resourceVersion: + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids + type: string + type: object + x-kubernetes-map-type: atomic name: description: |- An object Name. diff --git a/pkg/apis/forklift/v1beta1/plan/vm.go b/pkg/apis/forklift/v1beta1/plan/vm.go index d2d2444bd..084a4c5f1 100644 --- a/pkg/apis/forklift/v1beta1/plan/vm.go +++ b/pkg/apis/forklift/v1beta1/plan/vm.go @@ -30,6 +30,9 @@ type VM struct { ref.Ref `json:",inline"` // Enable hooks. Hooks []HookRef `json:"hooks,omitempty"` + // Disk decryption LUKS keys + // +optional + LUKS core.ObjectReference `json:"luks" ref:"Secret"` } // Find a Hook for the specified step. diff --git a/pkg/apis/forklift/v1beta1/plan/zz_generated.deepcopy.go b/pkg/apis/forklift/v1beta1/plan/zz_generated.deepcopy.go index 61fb184d7..32618a990 100644 --- a/pkg/apis/forklift/v1beta1/plan/zz_generated.deepcopy.go +++ b/pkg/apis/forklift/v1beta1/plan/zz_generated.deepcopy.go @@ -289,6 +289,7 @@ func (in *VM) DeepCopyInto(out *VM) { *out = make([]HookRef, len(*in)) copy(*out, *in) } + out.LUKS = in.LUKS } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VM. diff --git a/pkg/controller/plan/kubevirt.go b/pkg/controller/plan/kubevirt.go index 50f9ccd26..aac21eaae 100644 --- a/pkg/controller/plan/kubevirt.go +++ b/pkg/controller/plan/kubevirt.go @@ -14,7 +14,6 @@ import ( "strings" "time" - api "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1" planbase "github.com/konveyor/forklift-controller/pkg/controller/plan/adapter/base" template "github.com/openshift/api/template/v1" "github.com/openshift/library-go/pkg/template/generator" @@ -29,7 +28,7 @@ import ( instancetype "kubevirt.io/api/instancetype/v1beta1" libvirtxml "libvirt.org/libvirt-go-xml" - "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1" + api "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1" "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1/plan" "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1/ref" "github.com/konveyor/forklift-controller/pkg/controller/plan/adapter" @@ -40,7 +39,7 @@ import ( core "k8s.io/api/core/v1" k8serr "k8s.io/apimachinery/pkg/api/errors" meta "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" + k8slabels "k8s.io/apimachinery/pkg/labels" cdi "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1" "sigs.k8s.io/controller-runtime/pkg/client" k8sutil "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -76,6 +75,8 @@ const ( kVM = "vmID" // App label kApp = "forklift.app" + // LUKS + kLUKS = "isLUKS" ) // User @@ -126,7 +127,7 @@ func (r *KubeVirt) ListVMs() ([]VirtualMachine, error) { context.TODO(), vList, &client.ListOptions{ - LabelSelector: labels.SelectorFromSet(planLabels), + LabelSelector: k8slabels.SelectorFromSet(planLabels), Namespace: r.Plan.Spec.TargetNamespace, }, ) @@ -230,7 +231,7 @@ func (r *KubeVirt) getImporterPods(pvc *core.PersistentVolumeClaim) (pods []core podList, &client.ListOptions{ Namespace: r.Plan.Spec.TargetNamespace, - LabelSelector: labels.SelectorFromSet(map[string]string{"app": "containerized-data-importer"}), + LabelSelector: k8slabels.SelectorFromSet(map[string]string{"app": "containerized-data-importer"}), }, ) if err != nil { @@ -302,7 +303,7 @@ func (r *KubeVirt) DeleteJobs(vm *plan.VMStatus) (err error) { context.TODO(), list, &client.ListOptions{ - LabelSelector: labels.SelectorFromSet(vmLabels), + LabelSelector: k8slabels.SelectorFromSet(vmLabels), Namespace: r.Plan.Spec.TargetNamespace, }, ) @@ -336,7 +337,7 @@ func (r *KubeVirt) DeleteJobs(vm *plan.VMStatus) (err error) { context.TODO(), podList, &client.ListOptions{ - LabelSelector: labels.SelectorFromSet(map[string]string{"job-name": job}), + LabelSelector: k8slabels.SelectorFromSet(map[string]string{"job-name": job}), Namespace: r.Plan.Spec.TargetNamespace, }, ) @@ -371,7 +372,7 @@ func (r *KubeVirt) EnsureVM(vm *plan.VMStatus) error { context.TODO(), vms, &client.ListOptions{ - LabelSelector: labels.SelectorFromSet(r.vmLabels(vm.Ref)), + LabelSelector: k8slabels.SelectorFromSet(r.vmLabels(vm.Ref)), Namespace: r.Plan.Spec.TargetNamespace, }, ) @@ -406,7 +407,7 @@ func (r *KubeVirt) EnsureVM(vm *plan.VMStatus) error { context.TODO(), dvs, &client.ListOptions{ - LabelSelector: labels.SelectorFromSet(r.vmLabels(vm.Ref)), + LabelSelector: k8slabels.SelectorFromSet(r.vmLabels(vm.Ref)), Namespace: r.Plan.Spec.TargetNamespace, }) if err != nil { @@ -439,7 +440,7 @@ func (r *KubeVirt) DeleteSecret(vm *plan.VMStatus) (err error) { context.TODO(), list, &client.ListOptions{ - LabelSelector: labels.SelectorFromSet(vmLabels), + LabelSelector: k8slabels.SelectorFromSet(vmLabels), Namespace: r.Plan.Spec.TargetNamespace, }, ) @@ -464,7 +465,7 @@ func (r *KubeVirt) DeleteConfigMap(vm *plan.VMStatus) (err error) { context.TODO(), list, &client.ListOptions{ - LabelSelector: labels.SelectorFromSet(vmLabels), + LabelSelector: k8slabels.SelectorFromSet(vmLabels), Namespace: r.Plan.Spec.TargetNamespace, }, ) @@ -489,7 +490,7 @@ func (r *KubeVirt) DeleteVM(vm *plan.VMStatus) (err error) { context.TODO(), list, &client.ListOptions{ - LabelSelector: labels.SelectorFromSet(vmLabels), + LabelSelector: k8slabels.SelectorFromSet(vmLabels), Namespace: r.Plan.Spec.TargetNamespace, }, ) @@ -522,7 +523,8 @@ func (r *KubeVirt) DeleteVM(vm *plan.VMStatus) (err error) { } func (r *KubeVirt) DataVolumes(vm *plan.VMStatus) (dataVolumes []cdi.DataVolume, err error) { - secret, err := r.ensureSecret(vm.Ref, r.secretDataSetterForCDI(vm.Ref)) + labels := r.vmLabels(vm.Ref) + secret, err := r.ensureSecret(vm.Ref, r.secretDataSetterForCDI(vm.Ref), labels) if err != nil { return } @@ -539,7 +541,8 @@ func (r *KubeVirt) DataVolumes(vm *plan.VMStatus) (dataVolumes []cdi.DataVolume, } func (r *KubeVirt) PopulatorVolumes(vmRef ref.Ref) (pvcs []*core.PersistentVolumeClaim, err error) { - secret, err := r.ensureSecret(vmRef, r.copyDataFromProviderSecret) + labels := r.vmLabels(vmRef) + secret, err := r.ensureSecret(vmRef, r.copyDataFromProviderSecret, labels) if err != nil { err = liberr.Wrap(err) return @@ -560,7 +563,7 @@ func (r *KubeVirt) EnsureDataVolumes(vm *plan.VMStatus, dataVolumes []cdi.DataVo context.TODO(), dataVolumeList, &client.ListOptions{ - LabelSelector: labels.SelectorFromSet(r.vmLabels(vm.Ref)), + LabelSelector: k8slabels.SelectorFromSet(r.vmLabels(vm.Ref)), Namespace: r.Plan.Spec.TargetNamespace, }) if err != nil { @@ -617,7 +620,7 @@ func (r *KubeVirt) getDVs(vm *plan.VMStatus) (edvs []ExtendedDataVolume, err err context.TODO(), dvsList, &client.ListOptions{ - LabelSelector: labels.SelectorFromSet(r.vmLabels(vm.Ref)), + LabelSelector: k8slabels.SelectorFromSet(r.vmLabels(vm.Ref)), Namespace: r.Plan.Spec.TargetNamespace, }) @@ -643,7 +646,7 @@ func (r *KubeVirt) getPVCs(vmRef ref.Ref) (pvcs []*core.PersistentVolumeClaim, e context.TODO(), pvcsList, &client.ListOptions{ - LabelSelector: labels.SelectorFromSet(map[string]string{ + LabelSelector: k8slabels.SelectorFromSet(map[string]string{ "migration": string(r.Migration.UID), kVM: vmRef.ID, }), @@ -754,7 +757,7 @@ func (r *KubeVirt) setKvmOnPodSpec(podSpec *core.PodSpec) { return } switch *r.Plan.Provider.Source.Spec.Type { - case v1beta1.VSphere, v1beta1.Ova: + case api.VSphere, api.Ova: if podSpec.NodeSelector == nil { podSpec.NodeSelector = make(map[string]string) } @@ -780,7 +783,8 @@ func (r *KubeVirt) getListOptionsNamespaced() (listOptions *client.ListOptions) // Ensure the guest conversion (virt-v2v) pod exists on the destination. func (r *KubeVirt) EnsureGuestConversionPod(vm *plan.VMStatus, vmCr *VirtualMachine, pvcs []*core.PersistentVolumeClaim) (err error) { - v2vSecret, err := r.ensureSecret(vm.Ref, r.secretDataSetterForCDI(vm.Ref)) + labels := r.vmLabels(vm.Ref) + v2vSecret, err := r.ensureSecret(vm.Ref, r.secretDataSetterForCDI(vm.Ref), labels) if err != nil { return } @@ -833,7 +837,7 @@ func (r *KubeVirt) EnsureOVAVirtV2VPVCStatus(vmID string) (ready bool, err error context.TODO(), pvcs, &client.ListOptions{ - LabelSelector: labels.SelectorFromSet(pvcLabels), + LabelSelector: k8slabels.SelectorFromSet(pvcLabels), Namespace: r.Plan.Spec.TargetNamespace, }, ) @@ -879,7 +883,7 @@ func (r *KubeVirt) GetGuestConversionPod(vm *plan.VMStatus) (pod *core.Pod, err context.TODO(), list, &client.ListOptions{ - LabelSelector: labels.SelectorFromSet(r.conversionLabels(vm.Ref, false)), + LabelSelector: k8slabels.SelectorFromSet(r.conversionLabels(vm.Ref, false)), Namespace: r.Plan.Spec.TargetNamespace, }) if err != nil { @@ -985,7 +989,7 @@ func (r *KubeVirt) GetPodsWithLabels(podLabels map[string]string) (pods *core.Po context.TODO(), pods, &client.ListOptions{ - LabelSelector: labels.SelectorFromSet(podLabels), + LabelSelector: k8slabels.SelectorFromSet(podLabels), Namespace: r.Plan.Spec.TargetNamespace, }, ) @@ -1026,7 +1030,7 @@ func (r *KubeVirt) DeleteHookJobs(vm *plan.VMStatus) (err error) { context.TODO(), list, &client.ListOptions{ - LabelSelector: labels.SelectorFromSet(vmLabels), + LabelSelector: k8slabels.SelectorFromSet(vmLabels), Namespace: r.Plan.Spec.TargetNamespace, }, ) @@ -1288,7 +1292,7 @@ func (r *KubeVirt) vmPreference(vm *plan.VMStatus) (virtualMachine *cnv.VirtualM return } -func (r *KubeVirt) getOsMapConfig(providerType v1beta1.ProviderType) (configMap *core.ConfigMap, err error) { +func (r *KubeVirt) getOsMapConfig(providerType api.ProviderType) (configMap *core.ConfigMap, err error) { configMap = &core.ConfigMap{} var configMapName string switch providerType { @@ -1506,7 +1510,7 @@ func (r *KubeVirt) findTemplate(vm *plan.VMStatus) (tmpl *template.Template, err context.TODO(), templateList, &client.ListOptions{ - LabelSelector: labels.SelectorFromSet(templateLabels), + LabelSelector: k8slabels.SelectorFromSet(templateLabels), Namespace: "openshift", }) if err != nil { @@ -1529,7 +1533,7 @@ func (r *KubeVirt) findTemplate(vm *plan.VMStatus) (tmpl *template.Template, err } func (r *KubeVirt) guestConversionPod(vm *plan.VMStatus, vmVolumes []cnv.Volume, configMap *core.ConfigMap, pvcs []*core.PersistentVolumeClaim, v2vSecret *core.Secret) (pod *core.Pod, err error) { - volumes, volumeMounts, volumeDevices, err := r.podVolumeMounts(vmVolumes, configMap, pvcs, vm.ID) + volumes, volumeMounts, volumeDevices, err := r.podVolumeMounts(vmVolumes, configMap, pvcs, vm) if err != nil { return } @@ -1659,7 +1663,7 @@ func (r *KubeVirt) guestConversionPod(vm *plan.VMStatus, vmVolumes []cnv.Volume, return } -func (r *KubeVirt) podVolumeMounts(vmVolumes []cnv.Volume, configMap *core.ConfigMap, pvcs []*core.PersistentVolumeClaim, vmID string) (volumes []core.Volume, mounts []core.VolumeMount, devices []core.VolumeDevice, err error) { +func (r *KubeVirt) podVolumeMounts(vmVolumes []cnv.Volume, configMap *core.ConfigMap, pvcs []*core.PersistentVolumeClaim, vm *plan.VMStatus) (volumes []core.Volume, mounts []core.VolumeMount, devices []core.VolumeDevice, err error) { pvcsByName := make(map[string]*core.PersistentVolumeClaim) for _, pvc := range pvcs { pvcsByName[pvc.Name] = pvc @@ -1712,7 +1716,7 @@ func (r *KubeVirt) podVolumeMounts(vmVolumes []cnv.Volume, configMap *core.Confi } pvcNamePrefix := getEntityPrefixName("pvc", r.Source.Provider.Name, r.Plan.Name) var pvcName string - pvcName, err = r.CreatePvcForNfs(pvcNamePrefix, pvName, vmID) + pvcName, err = r.CreatePvcForNfs(pvcNamePrefix, pvName, vm.ID) if err != nil { return } @@ -1760,7 +1764,29 @@ func (r *KubeVirt) podVolumeMounts(vmVolumes []cnv.Volume, configMap *core.Confi EmptyDir: &core.EmptyDirVolumeSource{}, }, }) - + if vm.LUKS.Name != "" { + labels := r.vmLabels(vm.Ref) + labels[kLUKS] = "true" + var secret *core.Secret + if secret, err = r.ensureSecret(vm.Ref, r.secretLUKS(vm.LUKS.Name, r.Plan.Namespace), labels); err != nil { + err = liberr.Wrap(err) + return + } + volumes = append(volumes, core.Volume{ + Name: "luks", + VolumeSource: core.VolumeSource{ + Secret: &core.SecretVolumeSource{ + SecretName: secret.Name, + }, + }, + }) + mounts = append(mounts, + core.VolumeMount{ + Name: "luks", + MountPath: "/etc/luks", + ReadOnly: true, + }) + } return } @@ -1846,7 +1872,7 @@ func (r *KubeVirt) ensureConfigMap(vmRef ref.Ref) (configMap *core.ConfigMap, er context.TODO(), list, &client.ListOptions{ - LabelSelector: labels.SelectorFromSet(r.vmLabels(vmRef)), + LabelSelector: k8slabels.SelectorFromSet(r.vmLabels(vmRef)), Namespace: r.Plan.Spec.TargetNamespace, }, ) @@ -1942,14 +1968,26 @@ func (r *KubeVirt) secretDataSetterForCDI(vmRef ref.Ref) func(*core.Secret) erro } } +func (r *KubeVirt) secretLUKS(name, namespace string) func(*core.Secret) error { + return func(secret *core.Secret) error { + sourceSecret := &core.Secret{} + err := r.Client.Get(context.TODO(), client.ObjectKey{Name: name, Namespace: namespace}, sourceSecret) + if err != nil { + return err + } + secret.Data = sourceSecret.Data + return nil + } +} + // Ensure the credential secret for the data transfer exists on the destination. -func (r *KubeVirt) ensureSecret(vmRef ref.Ref, setSecretData func(*core.Secret) error) (secret *core.Secret, err error) { +func (r *KubeVirt) ensureSecret(vmRef ref.Ref, setSecretData func(*core.Secret) error, labels map[string]string) (secret *core.Secret, err error) { _, err = r.Source.Inventory.VM(&vmRef) if err != nil { return } - newSecret, err := r.secret(vmRef, setSecretData) + newSecret, err := r.secret(vmRef, setSecretData, labels) if err != nil { return } @@ -1959,7 +1997,7 @@ func (r *KubeVirt) ensureSecret(vmRef ref.Ref, setSecretData func(*core.Secret) context.TODO(), list, &client.ListOptions{ - LabelSelector: labels.SelectorFromSet(r.vmLabels(vmRef)), + LabelSelector: k8slabels.SelectorFromSet(labels), Namespace: r.Plan.Spec.TargetNamespace, }, ) @@ -2004,10 +2042,10 @@ func (r *KubeVirt) ensureSecret(vmRef ref.Ref, setSecretData func(*core.Secret) } // Build the credential secret for the data transfer (CDI importer / popoulator pod). -func (r *KubeVirt) secret(vmRef ref.Ref, setSecretData func(*core.Secret) error) (secret *core.Secret, err error) { +func (r *KubeVirt) secret(vmRef ref.Ref, setSecretData func(*core.Secret) error, labels map[string]string) (secret *core.Secret, err error) { secret = &core.Secret{ ObjectMeta: meta.ObjectMeta{ - Labels: r.vmLabels(vmRef), + Labels: labels, Namespace: r.Plan.Spec.TargetNamespace, GenerateName: strings.Join( []string{ @@ -2169,7 +2207,7 @@ func (r *KubeVirt) EnsurePersistentVolume(vmRef ref.Ref, persistentVolumes []cor context.TODO(), list, &client.ListOptions{ - LabelSelector: labels.SelectorFromSet(r.vmLabels(vmRef)), + LabelSelector: k8slabels.SelectorFromSet(r.vmLabels(vmRef)), Namespace: r.Plan.Spec.TargetNamespace, }) if err != nil { @@ -2216,7 +2254,7 @@ func GetOvaPvListNfs(dClient client.Client, planID string) (pvs *core.Persistent context.TODO(), pvs, &client.ListOptions{ - LabelSelector: labels.SelectorFromSet(pvLabels), + LabelSelector: k8slabels.SelectorFromSet(pvLabels), }, ) if err != nil { @@ -2240,7 +2278,7 @@ func GetOvaPvcListNfs(dClient client.Client, planID string, planNamespace string context.TODO(), pvcs, &client.ListOptions{ - LabelSelector: labels.SelectorFromSet(pvcLabels), + LabelSelector: k8slabels.SelectorFromSet(pvcLabels), Namespace: planNamespace, }, ) diff --git a/pkg/forklift-api/webhooks/validating-webhook/admitters/plan-admitter.go b/pkg/forklift-api/webhooks/validating-webhook/admitters/plan-admitter.go index eb3075d99..cd8b4dc02 100644 --- a/pkg/forklift-api/webhooks/validating-webhook/admitters/plan-admitter.go +++ b/pkg/forklift-api/webhooks/validating-webhook/admitters/plan-admitter.go @@ -116,6 +116,38 @@ func (admitter *PlanAdmitter) validateWarmMigrations() error { return nil } +func (admitter *PlanAdmitter) validateLUKS() error { + hasLUKS := false + for _, vm := range admitter.plan.Spec.VMs { + if vm.LUKS.Name != "" { + hasLUKS = true + break + } + } + if !hasLUKS { + return nil + } + + providerType := admitter.sourceProvider.Type() + if providerType != api.VSphere && providerType != api.Ova { + err := liberr.New(fmt.Sprintf("migration of encrypted disks from source provider of type %s is not supported", providerType)) + log.Error(err, "Provider type (non-VSphere & non-OVA) does not support LUKS") + return err + } + + el9, el9Err := admitter.plan.VSphereUsesEl9VirtV2v() + if el9Err != nil { + log.Error(el9Err, "Could not analyze plan, failing") + return el9Err + } + if !el9 { + err := liberr.New("migration of encrypted disks is not supported for warm migrations or migrations to remote providers") + log.Error(err, "Warm migration does not support LUKS") + return err + } + return nil +} + func (admitter *PlanAdmitter) Admit(ar *admissionv1.AdmissionReview) *admissionv1.AdmissionResponse { log.Info("Plan admitter was called") raw := ar.Request.Object.Raw @@ -167,5 +199,10 @@ func (admitter *PlanAdmitter) Admit(ar *admissionv1.AdmissionReview) *admissionv return util.ToAdmissionResponseError(err) } + err = admitter.validateLUKS() + if err != nil { + return util.ToAdmissionResponseError(err) + } + return util.ToAdmissionResponseAllow() } diff --git a/virt-v2v/cold/entrypoint.go b/virt-v2v/cold/entrypoint.go index 1544b9922..734b13cfe 100644 --- a/virt-v2v/cold/entrypoint.go +++ b/virt-v2v/cold/entrypoint.go @@ -20,6 +20,7 @@ const ( FS = "/mnt/disks/disk[0-9]*" Block = "/dev/block[0-9]*" VDDK = "/opt/vmware-vix-disklib-distrib" + LUKSDIR = "/etc/luks" ) var ( @@ -76,7 +77,7 @@ func main() { } func buildCommand() []string { - virtV2vArgs := []string{"virt-v2v", "-v", "-x"} + virtV2vArgs := []string{"-v", "-x"} source := os.Getenv("V2V_source") if !isValidSource(source) { @@ -127,6 +128,20 @@ func buildCommand() []string { virtV2vArgs = append(virtV2vArgs, "--mac", macToIp) } } + // Adds LUKS keys, if exist. + if _, err := os.Stat(LUKSDIR); err == nil { + files, err := getFilesInPath(LUKSDIR) + if err != nil { + fmt.Println("Error reading files in LUKS directory ", err) + os.Exit(1) + } + for _, file := range files { + virtV2vArgs = append(virtV2vArgs, "--key", fmt.Sprintf("all:file:%s", file)) + } + } else if !os.IsNotExist(err) { + fmt.Println("Error accessing the LUKS directory ", err) + os.Exit(1) + } if info, err := os.Stat(VDDK); err == nil && info.IsDir() { virtV2vArgs = append(virtV2vArgs, @@ -149,6 +164,20 @@ func buildCommand() []string { return virtV2vArgs } +func getFilesInPath(rootPath string) (paths []string, err error) { + files, err := os.ReadDir(rootPath) + if err != nil { + fmt.Println("Error reading the files in the directory ", err) + return + } + for _, file := range files { + if !file.IsDir() && !strings.HasPrefix(file.Name(), "..") { + paths = append(paths, fmt.Sprintf("%s/%s", rootPath, file.Name())) + } + } + return +} + func checkEnvVariablesSet(envVars ...string) bool { for _, v := range envVars { if os.Getenv(v) == "" { @@ -196,7 +225,7 @@ func LinkDisks(diskKind string, num int) (err error) { } func executeVirtV2v(args []string) error { - v2vCmd := exec.Command(args[0], args[1:]...) + v2vCmd := exec.Command("virt-v2v", args...) monitorCmd := exec.Command("/usr/local/bin/virt-v2v-monitor") monitorCmd.Stdout = os.Stdout monitorCmd.Stderr = os.Stderr