Skip to content
This repository has been archived by the owner on Mar 29, 2022. It is now read-only.

Commit

Permalink
Add to import image from local
Browse files Browse the repository at this point in the history
- add hostpath source to vmim
- user can use hostpath source to import local image
  • Loading branch information
hyoung-90 committed Sep 22, 2020
1 parent 55813ab commit 70a2e00
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 69 deletions.
10 changes: 6 additions & 4 deletions dbox
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@ g)
operator-sdk generate k8s
;;
da)
kubectl delete -f deploy/crds/hypercloud.tmaxanc.com_v1alpha1_virtualmachineimage_cr.yaml --ignore-not-found=true
kubectl delete -f deploy/crds/hypercloud.tmaxanc.com_v1alpha1_virtualmachineimage_http_cr.yaml --ignore-not-found=true
kubectl delete -f deploy/crds/hypercloud.tmaxanc.com_v1alpha1_virtualmachineimage_hostpath_cr.yaml --ignore-not-found=true
kubectl delete -f deploy/crds/hypercloud.tmaxanc.com_virtualmachineimages_crd.yaml --ignore-not-found=true
kubectl delete -f deploy/crds/hypercloud.tmaxanc.com_v1alpha1_virtualmachinevolume_cr.yaml --ignore-not-found=true
kubectl delete -f deploy/crds/hypercloud.tmaxanc.com_virtualmachinevolumes_crd.yaml --ignore-not-found=true
kubectl delete -f deploy/crds/hypercloud.tmaxanc.com_v1alpha1_virtualmachinevolumeexport_cr.yaml --ignore-not-found=true
kubectl delete -f deploy/crds/hypercloud.tmaxanc.com_virtualmachinevolumeexports_crd.yaml --ignore-not-found=true
;;
dcr)
kubectl delete -f deploy/crds/hypercloud.tmaxanc.com_v1alpha1_virtualmachineimage_cr.yaml --ignore-not-found=true
kubectl delete -f deploy/crds/hypercloud.tmaxanc.com_v1alpha1_virtualmachineimage_http_cr.yaml --ignore-not-found=true
kubectl delete -f deploy/crds/hypercloud.tmaxanc.com_v1alpha1_virtualmachineimage_hostpath_cr.yaml --ignore-not-found=true
kubectl delete -f deploy/crds/hypercloud.tmaxanc.com_v1alpha1_virtualmachinevolume_cr.yaml --ignore-not-found=true
kubectl delete -f deploy/crds/hypercloud.tmaxanc.com_v1alpha1_virtualmachinevolumeexport_cr.yaml --ignore-not-found=true
;;
Expand All @@ -31,14 +33,14 @@ do)
;;
aa)
kubectl apply -f deploy/crds/hypercloud.tmaxanc.com_virtualmachineimages_crd.yaml
kubectl apply -f deploy/crds/hypercloud.tmaxanc.com_v1alpha1_virtualmachineimage_cr.yaml
kubectl apply -f deploy/crds/hypercloud.tmaxanc.com_v1alpha1_virtualmachineimage_http_cr.yaml
kubectl apply -f deploy/crds/hypercloud.tmaxanc.com_virtualmachinevolumes_crd.yaml
kubectl apply -f deploy/crds/hypercloud.tmaxanc.com_v1alpha1_virtualmachinevolume_cr.yaml
kubectl apply -f deploy/crds/hypercloud.tmaxanc.com_virtualmachinevolumeexports_crd.yaml
kubectl apply -f deploy/crds/hypercloud.tmaxanc.com_v1alpha1_virtualmachinevolumeexport_cr.yaml
;;
acr)
kubectl apply -f deploy/crds/hypercloud.tmaxanc.com_v1alpha1_virtualmachineimage_cr.yaml
kubectl apply -f deploy/crds/hypercloud.tmaxanc.com_v1alpha1_virtualmachineimage_http_cr.yaml
kubectl apply -f deploy/crds/hypercloud.tmaxanc.com_v1alpha1_virtualmachinevolume_cr.yaml
kubectl apply -f deploy/crds/hypercloud.tmaxanc.com_v1alpha1_virtualmachinevolumeexport_cr.yaml
;;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apiVersion: hypercloud.tmaxanc.com/v1alpha1
kind: VirtualMachineImage
metadata:
name: localvmim
spec:
source:
hostPath:
path: /mnt/hy
nodeName: young
snapshotClassName: csi-rbdplugin-snapclass
pvc:
volumeMode: Block
accessModes:
- ReadWriteMany
resources:
requests:
storage: "3Gi"
storageClassName: rook-ceph-block
18 changes: 14 additions & 4 deletions deploy/crds/hypercloud.tmaxanc.com_virtualmachineimages_crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,23 @@ spec:
snapshotClassName:
type: string
source:
description: VirtualMachineImageSource provides parameters to create
a VirtualMachineImage from an HTTP source
description: VirtualMachineImageSource represents the source for our
VirtualMachineImage, this can be HTTP or host path
properties:
hostPath:
description: VirtualMachineImageSourceHostPath provides the parameters
to create a virtual machine image from a host path
properties:
nodeName:
type: string
path:
type: string
required:
- nodeName
- path
type: object
http:
type: string
required:
- http
type: object
required:
- pvc
Expand Down
22 changes: 20 additions & 2 deletions docs/USERGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,38 @@ kubevirt-image-service 3/3 3 3 23s

# Use Kubevirt-Image-Service

## Import image from HTTP source
## Import image

### 1. Import image from HTTP source

vmim is the shortname for `VirtualMachineImage`.

``` shell
# Deploy image CR. See the following yaml file for more information about each CR fields
$ kubectl apply -f deploy/crds/hypercloud.tmaxanc.com_v1alpha1_virtualmachineimage_cr.yaml
$ kubectl apply -f deploy/crds/hypercloud.tmaxanc.com_v1alpha1_virtualmachineimage_http_cr.yaml

# Wait until image state is ready to use
$ kubectl get vmim
NAME STATE
myubuntu Available
```

### 2. Import image from hostpath source

```shell
# Create a qcow2 image file with the name `disk.img` in the desired path (/mnt/data).

# Deploy host path image CR using the path that created the qcow2 file.
$ kubectl apply -f deploy/crds/hypercloud.tmaxanc.com_v1alpha1_virtualmachineimage_hostpath_cr.yaml

# Wait until image state is ready to use
$ kubectl get vmim
NAME STATE
localvmim Available

# When the status of vmim becomes Availalbe, user can delete the qcow2 file.
```

## Create volume from image

vmv is the shortname for `VirtualMachineVolume`.
Expand Down
11 changes: 9 additions & 2 deletions pkg/apis/hypercloud/v1alpha1/virtualmachineimage_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,16 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// VirtualMachineImageSource provides parameters to create a VirtualMachineImage from an HTTP source
// VirtualMachineImageSource represents the source for our VirtualMachineImage, this can be HTTP or host path
type VirtualMachineImageSource struct {
HTTP string `json:"http"`
HTTP string `json:"http,omitempty"`
HostPath *VirtualMachineImageSourceHostPath `json:"hostPath,omitempty"`
}

// VirtualMachineImageSourceHostPath provides the parameters to create a virtual machine image from a host path
type VirtualMachineImageSourceHostPath struct {
Path string `json:"path"`
NodeName string `json:"nodeName"`
}

// VirtualMachineImageSpec defines the desired state of VirtualMachineImage
Expand Down
23 changes: 22 additions & 1 deletion pkg/apis/hypercloud/v1alpha1/zz_generated.deepcopy.go

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

87 changes: 38 additions & 49 deletions pkg/controller/virtualmachineimage/importer_pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,14 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/klog"
hc "kubevirt-image-service/pkg/apis/hypercloud/v1alpha1"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

const (
// DataVolName provides a const to use for creating volumes in pod specs
DataVolName = "data-vol"
// ScratchVolName provides a const to use for creating scratch pvc volumes in pod specs
ScratchVolName = "scratch-vol"
// ScratchDataDir provides a constant for the controller pkg to use as a hardcoded path to where scratch space is located.
ScratchDataDir = "/scratch"
// WriteBlockPath provides a constant for the path where the PV is mounted.
WriteBlockPath = "/dev/cdi-block-volume"
// ImporterSource provides a constant to capture our env variable "IMPORTER_SOURCE"
Expand All @@ -32,15 +26,21 @@ const (
ImporterImageSize = "IMPORTER_IMAGE_SIZE"
// InsecureTLSVar provides a constant to capture our env variable "INSECURE_TLS"
InsecureTLSVar = "INSECURE_TLS"
// SourceHTTP is the source type HTTP, if unspecified or invalid, it defaults to SourceHTTP
// SourceHTTP is the source type HTTP
SourceHTTP = "http"
// SourceHostPath is the source type host path
SourceHostPath = "hostPath"
// ImageContentType is the content-type of the imported file
ImageContentType = "kubevirt"
// ImportPodImage and ImportPodVerbose should be modified to get value from vmi env
// ImportPodImage indicates image name of the import pod
ImportPodImage = "kubevirt/cdi-importer:v1.13.0"
// ImportPodVerbose indicates log level of the import pod
ImportPodVerbose = "1"
// SourceVolumeName is used for creating source volume in pod specs
SourceVolumeName = "source-vol"
// SourceVolumeMountPath is a path where the source volume is mounted
SourceVolumeMountPath = "/data/source"
)

func (r *ReconcileVirtualMachineImage) syncImporterPod() error {
Expand Down Expand Up @@ -71,7 +71,7 @@ func (r *ReconcileVirtualMachineImage) syncImporterPod() error {
} else if !imported && !existsImporterPod {
// 임포팅을 해야 하므로 임포터파드를 만든다
klog.Infof("syncImporterPod create new importerPod for vmi %s", r.vmi.Name)
newPod, err := newImporterPod(r.vmi, r.scheme)
newPod, err := r.newImporterPod()
if err != nil {
return err
}
Expand All @@ -93,33 +93,18 @@ func GetImporterPodNameFromVmiName(vmiName string) string {
return vmiName + "-image-importer"
}

func newImporterPod(vmi *hc.VirtualMachineImage, scheme *runtime.Scheme) (*corev1.Pod, error) {
source, endpoint, err := getSourceAndEndpoint(vmi)
if err != nil {
return nil, err
}

// virtual machine image spec의 pvc storage는 필수 값이다.
pvcSize := vmi.Spec.PVC.Resources.Requests[corev1.ResourceStorage]
func (r *ReconcileVirtualMachineImage) newImporterPod() (*corev1.Pod, error) {
ip := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: GetImporterPodNameFromVmiName(vmi.Name),
Namespace: vmi.Namespace,
Name: GetImporterPodNameFromVmiName(r.vmi.Name),
Namespace: r.vmi.Namespace,
},
Spec: corev1.PodSpec{
RestartPolicy: corev1.RestartPolicyOnFailure,
Containers: []corev1.Container{
{
Name: "importer",
Image: ImportPodImage,
Args: []string{"-v=" + ImportPodVerbose},
Env: []corev1.EnvVar{
{Name: ImporterSource, Value: source},
{Name: ImporterEndpoint, Value: endpoint},
{Name: ImporterContentType, Value: ImageContentType},
{Name: ImporterImageSize, Value: pvcSize.String()},
{Name: InsecureTLSVar, Value: "true"},
},
Resources: corev1.ResourceRequirements{
Limits: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceCPU: resource.MustParse("0"),
Expand All @@ -128,12 +113,6 @@ func newImporterPod(vmi *hc.VirtualMachineImage, scheme *runtime.Scheme) (*corev
corev1.ResourceCPU: resource.MustParse("0"),
corev1.ResourceMemory: resource.MustParse("0")},
},
VolumeMounts: []corev1.VolumeMount{
{
Name: ScratchVolName,
MountPath: ScratchDataDir,
},
},
VolumeDevices: []corev1.VolumeDevice{
{Name: DataVolName, DevicePath: WriteBlockPath},
},
Expand All @@ -144,15 +123,7 @@ func newImporterPod(vmi *hc.VirtualMachineImage, scheme *runtime.Scheme) (*corev
Name: DataVolName,
VolumeSource: corev1.VolumeSource{
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
ClaimName: GetPvcNameFromVmiName(vmi.Name),
},
},
},
{
Name: ScratchVolName,
VolumeSource: corev1.VolumeSource{
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
ClaimName: getScratchPvcNameFromVmiName(vmi.Name),
ClaimName: GetPvcNameFromVmiName(r.vmi.Name),
},
},
},
Expand All @@ -162,15 +133,33 @@ func newImporterPod(vmi *hc.VirtualMachineImage, scheme *runtime.Scheme) (*corev
},
},
}
if err := controllerutil.SetControllerReference(vmi, ip, scheme); err != nil {

if r.getSource() == SourceHTTP {
pvcSize := r.vmi.Spec.PVC.Resources.Requests[corev1.ResourceStorage]

ip.Spec.Containers[0].Args = []string{"-v=" + ImportPodVerbose}
ip.Spec.Containers[0].Env = []corev1.EnvVar{
{Name: ImporterSource, Value: SourceHTTP},
{Name: ImporterEndpoint, Value: r.vmi.Spec.Source.HTTP},
{Name: ImporterContentType, Value: ImageContentType},
{Name: ImporterImageSize, Value: pvcSize.String()},
{Name: InsecureTLSVar, Value: "true"},
}
} else if r.getSource() == SourceHostPath {
ip.Spec.NodeName = r.vmi.Spec.Source.HostPath.NodeName
ip.Spec.Containers[0].Command = []string{"qemu-img", "convert", "-f", "qcow2", "-O", "raw", SourceVolumeMountPath + "/disk.img", WriteBlockPath}
ip.Spec.Volumes = append(ip.Spec.Volumes, corev1.Volume{
Name: SourceVolumeName,
VolumeSource: corev1.VolumeSource{
HostPath: &corev1.HostPathVolumeSource{
Path: r.vmi.Spec.Source.HostPath.Path,
}},
})
ip.Spec.Containers[0].VolumeMounts = []corev1.VolumeMount{
{Name: SourceVolumeName, MountPath: SourceVolumeMountPath}}
}
if err := controllerutil.SetControllerReference(r.vmi, ip, r.scheme); err != nil {
return nil, err
}
return ip, nil
}

func getSourceAndEndpoint(vmi *hc.VirtualMachineImage) (source, endpoint string, err error) {
if vmi.Spec.Source.HTTP == "" {
return "", "", errors.NewBadRequest("Invalid spec.source. Must provide http source.")
}
return SourceHTTP, vmi.Spec.Source.HTTP, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,19 +82,16 @@ func (r *ReconcileVirtualMachineImage) Reconcile(request reconcile.Request) (rec
if err := r.validateVirtualMachineImageSpec(); err != nil {
return err
}
// pvc가 없으면 상태를 업데이트하고 pvc를 생성한다.
// If the pvc doesn't exist, create a pvc and update vmim's status to creating
if err := r.syncPvc(); err != nil {
return err
}
// imported=false인 경우 스크래치 pvc를 생성한다.
if err := r.syncScratchPvc(); err != nil {
return err
}
// imported=false인 경우 임포터파드가 없으면 만든다. 있으면 컴플리트인지 확인해서 imported=true로 변경한다.
// If the pvc import is not complete, create a importer pod
// If the pvc import is complete, delete the importer pod and update imported value to true
if err := r.syncImporterPod(); err != nil {
return err
}
// imported=true인 경우 스냅샷이 없으면 만들고, readyToUse를 true로 변경한다.
// If the pvc import is complete, create a snapshot and update vmim's status to available
if err := r.syncSnapshot(); err != nil {
return err
}
Expand Down Expand Up @@ -126,5 +123,18 @@ func (r *ReconcileVirtualMachineImage) validateVirtualMachineImageSpec() error {
if !found {
return goerrors.New("storage request in pvc is missing")
}
if src := r.getSource(); src == "" {
return goerrors.New("vmim source is not set")
}
return nil
}

func (r *ReconcileVirtualMachineImage) getSource() string {
if r.vmi.Spec.Source.HTTP != "" {
return SourceHTTP
} else if r.vmi.Spec.Source.HostPath != nil {
return SourceHostPath
} else {
return ""
}
}

0 comments on commit 70a2e00

Please sign in to comment.