Skip to content
This repository was archived by the owner on Nov 4, 2024. It is now read-only.

Commit 4c54a4b

Browse files
committed
feat(CNV-48772): export from multiple sources
Currently kubevirt-disk-uploader is hardcoded to export only from a single source "VirtualMachine" (kind). Extend API to allow export from multiple sources: - VirtualMachine - VirtualMachineSnapshot - PersistentVolumeClaim Signed-off-by: Ben Oukhanov <[email protected]>
1 parent 9f03527 commit 4c54a4b

File tree

7 files changed

+157
-69
lines changed

7 files changed

+157
-69
lines changed

README.md

+21-9
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,39 @@ Extracts disk and uploads it to a container registry.
44

55
## About
66

7-
A tool designed to automate the extraction of disks from KubeVirt Virtual Machines, package them into [Container Disks](https://kubevirt.io/user-guide/virtual_machines/disks_and_volumes/#containerdisk), and upload them to the Container Registry.
7+
A tool designed to automate the extraction of disk, rebuild as [Container Disk](https://kubevirt.io/user-guide/virtual_machines/disks_and_volumes/#containerdisk) and upload to the Container Registry.
88

9-
## Workflow
9+
## Usage
1010

11-
KubeVirt Disk Uploader -> Download VM Disk -> Build New Container Disk -> Push To Container Registry
11+
These are the supported export sources:
1212

13-
## Installation
13+
- VirtualMachine (VM)
14+
- VirtualMachineSnapshot (VM Snapshot)
15+
- PersistentVolumeClaim (PVC)
16+
17+
Data from the source can be exported only when it is not used.
1418

1519
**Prerequisites**
1620

17-
1. Ensure Virtual Machine (VM) is powered off. Data from VM can be exported only when it is not used.
18-
2. Modify [kubevirt-disk-uploader](https://github.com/codingben/kubevirt-disk-uploader/blob/main/kubevirt-disk-uploader.yaml#L58) arguments (VM Namespace, VM Name, Volume Name, Image Destination, Enable or Disable System Preparation and Push Timeout).
19-
3. Modify [kubevirt-disk-uploader-credentials](https://github.com/codingben/kubevirt-disk-uploader/blob/main/kubevirt-disk-uploader.yaml#L65-L74) of the external container registry (Username, Password and Hostname).
21+
- Modify [kubevirt-disk-uploader](https://github.com/codingben/kubevirt-disk-uploader/blob/main/kubevirt-disk-uploader.yaml#L58) arguments.
22+
- Modify [kubevirt-disk-uploader-credentials](https://github.com/codingben/kubevirt-disk-uploader/blob/main/kubevirt-disk-uploader.yaml#L65-L74) of the external container registry.
23+
24+
**Parameters**
25+
26+
- **Export Source Kind**: Specify the export source kind (`vm`, `vmsnapshot`, `pvc`).
27+
- **Export Source Namespace**: The namespace of the export source.
28+
- **Export Source Name**: The name of the export source.
29+
- **Volume Name**: The name of the volume to export data.
30+
- **Image Destination**: Destination of the image in container registry (`$HOST/$OWNER/$REPO:$TAG`).
31+
- **Push Timeout**: The push timeout of container disk to registry.
2032

21-
Deploy `kubevirt-disk-uploader` within the same namespace as the Virtual Machine (VM):
33+
Deploy `kubevirt-disk-uploader` within the same namespace of Export Source (VM, VM Snapshot, PVC):
2234

2335
```
2436
kubectl apply -f kubevirt-disk-uploader.yaml -n $POD_NAMESPACE
2537
```
2638

27-
**Note**: If both `POD_NAMESPACE` and `--vmnamespace` argument are set, `POD_NAMESPACE` will be used.
39+
Setting of environment variable `POD_NAMESPACE` overrides the value in `--export-source-namespace` if passed.
2840

2941
## KubeVirt Documentation
3042

cmd/main.go

+40-24
Original file line numberDiff line numberDiff line change
@@ -20,35 +20,53 @@ const (
2020
kvExportTokenHeader string = "x-kubevirt-export-token"
2121
)
2222

23-
func run(client kubecli.KubevirtClient, vmNamespace, vmName, volumeName, imageDestination string, pushTimeout int) error {
24-
log.Printf("Creating a new Secret '%s/%s' object...", vmNamespace, vmName)
23+
type RunOptions struct {
24+
client kubecli.KubevirtClient
25+
exportSourceKind string
26+
exportSourceNamespace string
27+
exportSourceName string
28+
volumeName string
29+
imageDestination string
30+
pushTimeout int
31+
}
32+
33+
func run(opts RunOptions) error {
34+
client := opts.client
35+
kind := opts.exportSourceKind
36+
name := opts.exportSourceName
37+
namespace := opts.exportSourceNamespace
38+
volumeName := opts.volumeName
39+
imageDestination := opts.imageDestination
40+
imagePushTimeout := opts.pushTimeout
2541

26-
if err := secrets.CreateVirtualMachineExportSecret(client, vmNamespace, vmName); err != nil {
42+
log.Printf("Creating a new Secret '%s/%s' object...", namespace, name)
43+
44+
if err := secrets.CreateVirtualMachineExportSecret(client, namespace, name); err != nil {
2745
return err
2846
}
2947

30-
log.Printf("Creating a new VirtualMachineExport '%s/%s' object...", vmNamespace, vmName)
48+
log.Printf("Creating a new VirtualMachineExport '%s/%s' object...", namespace, name)
3149

32-
if err := vmexport.CreateVirtualMachineExport(client, vmNamespace, vmName); err != nil {
50+
if err := vmexport.CreateVirtualMachineExport(client, kind, namespace, name); err != nil {
3351
return err
3452
}
3553

3654
log.Println("Waiting for VirtualMachineExport status to be ready...")
3755

38-
if err := vmexport.WaitUntilVirtualMachineExportReady(client, vmNamespace, vmName); err != nil {
56+
if err := vmexport.WaitUntilVirtualMachineExportReady(client, namespace, name); err != nil {
3957
return err
4058
}
4159

4260
log.Println("Getting raw disk URL from the VirtualMachineExport object status...")
4361

44-
rawDiskUrl, err := vmexport.GetRawDiskUrlFromVolumes(client, vmNamespace, vmName, volumeName)
62+
rawDiskUrl, err := vmexport.GetRawDiskUrlFromVolumes(client, namespace, name, volumeName)
4563
if err != nil {
4664
return err
4765
}
4866

4967
log.Println("Creating TLS certificate file from the VirtualMachineExport object status...")
5068

51-
certificateData, err := certificate.GetCertificateFromVirtualMachineExport(client, vmNamespace, vmName)
69+
certificateData, err := certificate.GetCertificateFromVirtualMachineExport(client, namespace, name)
5270
if err != nil {
5371
return err
5472
}
@@ -59,7 +77,7 @@ func run(client kubecli.KubevirtClient, vmNamespace, vmName, volumeName, imageDe
5977

6078
log.Println("Getting export token from the Secret object...")
6179

62-
kvExportToken, err := secrets.GetTokenFromVirtualMachineExportSecret(client, vmNamespace, vmName)
80+
kvExportToken, err := secrets.GetTokenFromVirtualMachineExportSecret(client, namespace, name)
6381
if err != nil {
6482
return err
6583
}
@@ -79,7 +97,7 @@ func run(client kubecli.KubevirtClient, vmNamespace, vmName, volumeName, imageDe
7997

8098
log.Println("Pushing new container image to the container registry...")
8199

82-
if err := image.Push(containerImage, imageDestination, pushTimeout); err != nil {
100+
if err := image.Push(containerImage, imageDestination, imagePushTimeout); err != nil {
83101
return err
84102
}
85103

@@ -88,12 +106,7 @@ func run(client kubecli.KubevirtClient, vmNamespace, vmName, volumeName, imageDe
88106
}
89107

90108
func main() {
91-
var vmNamespace string
92-
var vmName string
93-
var volumeName string
94-
var imageDestination string
95-
var pushTimeout int
96-
109+
var opts RunOptions
97110
var command = &cobra.Command{
98111
Use: "kubevirt-disk-uploader",
99112
Short: "Extracts disk and uploads it to a container registry",
@@ -102,24 +115,27 @@ func main() {
102115
if err != nil {
103116
log.Panicln(err)
104117
}
118+
opts.client = client
105119

106120
namespace := os.Getenv("POD_NAMESPACE")
107121
if namespace != "" {
108-
vmNamespace = namespace
122+
opts.exportSourceNamespace = namespace
109123
}
110124

111-
if err := run(client, namespace, vmName, volumeName, imageDestination, pushTimeout); err != nil {
125+
if err := run(opts); err != nil {
112126
log.Panicln(err)
113127
}
114128
},
115129
}
116130

117-
command.Flags().StringVar(&vmNamespace, "vmnamespace", "", "namespace of the virtual machine")
118-
command.Flags().StringVar(&vmName, "vmname", "", "name of the virtual machine")
119-
command.Flags().StringVar(&volumeName, "volumename", "", "volume name of the virtual machine")
120-
command.Flags().StringVar(&imageDestination, "imagedestination", "", "destination of the image in container registry")
121-
command.Flags().IntVar(&pushTimeout, "pushtimeout", 60, "containerdisk push timeout in minutes")
122-
command.MarkFlagRequired("vmname")
131+
command.Flags().StringVar(&opts.exportSourceKind, "export-source-kind", "", "specify the export source kind (vm, vmsnapshot, pvc)")
132+
command.Flags().StringVar(&opts.exportSourceNamespace, "export-source-namespace", "", "namespace of the export source")
133+
command.Flags().StringVar(&opts.exportSourceName, "export-source-name", "", "name of the export source")
134+
command.Flags().StringVar(&opts.volumeName, "volumename", "", "name of the volume (if source kind is 'pvc', then volume name is equal to source name)")
135+
command.Flags().StringVar(&opts.imageDestination, "imagedestination", "", "destination of the image in container registry")
136+
command.Flags().IntVar(&opts.pushTimeout, "pushtimeout", 60, "push timeout of container disk to registry")
137+
command.MarkFlagRequired("export-source-kind")
138+
command.MarkFlagRequired("export-source-name")
123139
command.MarkFlagRequired("volumename")
124140
command.MarkFlagRequired("imagedestination")
125141

examples/kubevirt-disk-uploader-tekton.yaml

+24-12
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,14 @@ metadata:
156156
name: kubevirt-disk-uploader-task
157157
spec:
158158
params:
159-
- name: POD_NAME
160-
description: The name of the virtual machine
159+
- name: EXPORT_SOURCE_KIND
160+
description: The name of the export source kind
161+
type: string
162+
- name: EXPORT_SOURCE_NAME
163+
description: The name of the export source
161164
type: string
162165
- name: VOLUME_NAME
163-
description: The volume name of the virtual machine
166+
description: The volume name (If source kind is PVC, then volume name is equal to source name)
164167
type: string
165168
- name: IMAGE_DESTINATION
166169
description: Destination of the image in container registry
@@ -192,8 +195,10 @@ spec:
192195
fieldPath: metadata.name
193196
command: ["/usr/local/bin/kubevirt-disk-uploader"]
194197
args:
195-
- "--vmname"
196-
- $(params.POD_NAME)
198+
- "--export-source-kind"
199+
- $(params.EXPORT_SOURCE_KIND)
200+
- "--export-source-name"
201+
- $(params.EXPORT_SOURCE_NAME)
197202
- "--volumename"
198203
- $(params.VOLUME_NAME)
199204
- "--imagedestination"
@@ -212,17 +217,20 @@ metadata:
212217
name: kubevirt-disk-uploader-pipeline
213218
spec:
214219
params:
215-
- name: POD_NAME
216-
description: "Name of the virtual machine"
220+
- name: EXPORT_SOURCE_KIND
221+
description: "Kind of the export source"
222+
type: string
223+
- name: EXPORT_SOURCE_NAME
224+
description: "Name of the export source"
217225
type: string
218226
- name: VOLUME_NAME
219-
description: "Volume name of the virtual machine"
227+
description: "Volume name (If source kind is PVC, then volume name is equal to source name)"
220228
type: string
221229
- name: IMAGE_DESTINATION
222230
description: "Destination of the image in container registry"
223231
type: string
224232
- name: PUSH_TIMEOUT
225-
description: "ContainerDisk push timeout in minutes"
233+
description: "Push timeout of container disk to registry"
226234
type: string
227235
tasks:
228236
- name: deploy-example-vm
@@ -234,8 +242,10 @@ spec:
234242
runAfter:
235243
- deploy-example-vm
236244
params:
237-
- name: POD_NAME
238-
value: "$(params.POD_NAME)"
245+
- name: EXPORT_SOURCE_KIND
246+
value: "$(params.EXPORT_SOURCE_KIND)"
247+
- name: EXPORT_SOURCE_NAME
248+
value: "$(params.EXPORT_SOURCE_NAME)"
239249
- name: VOLUME_NAME
240250
value: "$(params.VOLUME_NAME)"
241251
- name: IMAGE_DESTINATION
@@ -256,7 +266,9 @@ spec:
256266
pipelineRef:
257267
name: kubevirt-disk-uploader-pipeline
258268
params:
259-
- name: POD_NAME
269+
- name: EXPORT_SOURCE_KIND
270+
value: vm
271+
- name: EXPORT_SOURCE_NAME
260272
value: example-vm-tekton
261273
- name: VOLUME_NAME
262274
value: example-dv-tekton

kubevirt-disk-uploader.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ spec:
6161
fieldRef:
6262
fieldPath: metadata.name
6363
command: ["/usr/local/bin/kubevirt-disk-uploader"]
64-
# args: ["--vmname", "example-vm", "--volumename", "example-dv", "--imagedestination", "quay.io/boukhano/example-vm-exported:latest", "--pushtimeout", "120"]
64+
# args: ["--export-source-kind", "vm", "--export-source-name", "example-vm", "--volumename", "example-dv", "--imagedestination", "quay.io/boukhano/example-vm-exported:latest", "--pushtimeout", "120"]
6565
resources:
6666
requests:
6767
memory: 3Gi

pkg/certificate/certificate.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import (
99
kubecli "kubevirt.io/client-go/kubecli"
1010
)
1111

12-
func GetCertificateFromVirtualMachineExport(client kubecli.KubevirtClient, vmNamespace, vmName string) (string, error) {
13-
vmExport, err := client.VirtualMachineExport(vmNamespace).Get(context.Background(), vmName, metav1.GetOptions{})
12+
func GetCertificateFromVirtualMachineExport(client kubecli.KubevirtClient, namespace, name string) (string, error) {
13+
vmExport, err := client.VirtualMachineExport(namespace).Get(context.Background(), name, metav1.GetOptions{})
1414
if err != nil {
1515
return "", err
1616
}

pkg/secrets/secrets.go

+7-7
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
kubecli "kubevirt.io/client-go/kubecli"
1515
)
1616

17-
func CreateVirtualMachineExportSecret(client kubecli.KubevirtClient, vmNamespace, vmName string) error {
17+
func CreateVirtualMachineExportSecret(client kubecli.KubevirtClient, namespace, name string) error {
1818
length := 20
1919
token, err := GenerateSecureRandomString(length)
2020
if err != nil {
@@ -23,8 +23,8 @@ func CreateVirtualMachineExportSecret(client kubecli.KubevirtClient, vmNamespace
2323

2424
v1Secret := &corev1.Secret{
2525
ObjectMeta: metav1.ObjectMeta{
26-
Name: vmName,
27-
Namespace: vmNamespace,
26+
Name: name,
27+
Namespace: namespace,
2828
},
2929
StringData: map[string]string{
3030
"token": token,
@@ -35,19 +35,19 @@ func CreateVirtualMachineExportSecret(client kubecli.KubevirtClient, vmNamespace
3535
return err
3636
}
3737

38-
_, err = client.CoreV1().Secrets(vmNamespace).Create(context.Background(), v1Secret, metav1.CreateOptions{})
38+
_, err = client.CoreV1().Secrets(namespace).Create(context.Background(), v1Secret, metav1.CreateOptions{})
3939
return err
4040
}
4141

42-
func GetTokenFromVirtualMachineExportSecret(client kubecli.KubevirtClient, vmNamespace, vmName string) (string, error) {
43-
secret, err := client.CoreV1().Secrets(vmNamespace).Get(context.Background(), vmName, metav1.GetOptions{})
42+
func GetTokenFromVirtualMachineExportSecret(client kubecli.KubevirtClient, namespace, name string) (string, error) {
43+
secret, err := client.CoreV1().Secrets(namespace).Get(context.Background(), name, metav1.GetOptions{})
4444
if err != nil {
4545
return "", err
4646
}
4747

4848
data := secret.Data["token"]
4949
if len(data) == 0 {
50-
return "", fmt.Errorf("failed to get export token from '%s/%s'", vmNamespace, vmName)
50+
return "", fmt.Errorf("failed to get export token from '%s/%s'", namespace, name)
5151
}
5252
return string(data), nil
5353
}

0 commit comments

Comments
 (0)