diff --git a/pkg/apis/container.go b/pkg/apis/container.go index 88dad4a73ba..520a236c5dc 100644 --- a/pkg/apis/container.go +++ b/pkg/apis/container.go @@ -131,18 +131,23 @@ type ContainerOverlayDiskImage struct { type ContainerDiskOverlayType string const ( - CONTAINER_DISK_OVERLAY_TYPE_DIRECTORY ContainerDiskOverlayType = "directory" - CONTAINER_DISK_OVERLAY_TYPE_UNKNOWN ContainerDiskOverlayType = "unknown" + CONTAINER_DISK_OVERLAY_TYPE_DIRECTORY ContainerDiskOverlayType = "directory" + CONTAINER_DISK_OVERLAY_TYPE_DISK_IMAGE ContainerDiskOverlayType = "disk_image" + CONTAINER_DISK_OVERLAY_TYPE_UNKNOWN ContainerDiskOverlayType = "unknown" ) type ContainerVolumeMountDiskOverlay struct { - LowerDir []string `json:"lower_dir"` + LowerDir []string `json:"lower_dir"` + UseDiskImage bool `json:"use_disk_image"` } func (o ContainerVolumeMountDiskOverlay) GetType() ContainerDiskOverlayType { if len(o.LowerDir) != 0 { return CONTAINER_DISK_OVERLAY_TYPE_DIRECTORY } + if o.UseDiskImage { + return CONTAINER_DISK_OVERLAY_TYPE_DISK_IMAGE + } return CONTAINER_DISK_OVERLAY_TYPE_UNKNOWN } diff --git a/pkg/compute/container_drivers/volume_mount/disk.go b/pkg/compute/container_drivers/volume_mount/disk.go index aff6e29d1db..762782c7258 100644 --- a/pkg/compute/container_drivers/volume_mount/disk.go +++ b/pkg/compute/container_drivers/volume_mount/disk.go @@ -17,7 +17,8 @@ func init() { } type iDiskOverlay interface { - validateCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *apis.ContainerVolumeMountDiskOverlay) error + validatePodCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *apis.ContainerVolumeMountDiskOverlay, disk *api.DiskConfig) error + validateCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *apis.ContainerVolumeMountDiskOverlay, obj *models.SDisk) error } type disk struct { @@ -27,7 +28,8 @@ type disk struct { func newDisk() models.IContainerVolumeMountDriver { return &disk{ overlayDrivers: map[apis.ContainerDiskOverlayType]iDiskOverlay{ - apis.CONTAINER_DISK_OVERLAY_TYPE_DIRECTORY: newDiskOverlayDir(), + apis.CONTAINER_DISK_OVERLAY_TYPE_DIRECTORY: newDiskOverlayDir(), + apis.CONTAINER_DISK_OVERLAY_TYPE_DISK_IMAGE: newDiskOverlayImage(), }, } } @@ -61,20 +63,16 @@ func (d disk) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCre return nil, errors.Wrap(err, "get pod disks") } disk := vm.Disk + var diskObj models.SDisk if disk.Index != nil { diskIndex := *disk.Index if diskIndex >= len(disks) { return nil, httperrors.NewInputParameterError("disk.index %d is large than disk size %d", diskIndex, len(disks)) } - diskObj := disks[diskIndex] + diskObj = disks[diskIndex] vm.Disk.Id = diskObj.GetId() // remove index vm.Disk.Index = nil - if diskObj.TemplateId != "" { - if vm.Disk.SubDirectory == "" { - return nil, httperrors.NewInputParameterError("sub_directory is required when disk has template_id %s", diskObj.TemplateId) - } - } } else { if disk.Id == "" { return nil, httperrors.NewNotEmptyError("disk.id is empty") @@ -83,11 +81,7 @@ func (d disk) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCre for _, d := range disks { if d.GetId() == disk.Id || d.GetName() == disk.Id { disk.Id = d.GetId() - if d.TemplateId != "" { - if vm.Disk.SubDirectory == "" { - return nil, httperrors.NewInputParameterError("sub_directory is required when disk has template_id %s", d.TemplateId) - } - } + diskObj = d foundDisk = true break } @@ -96,7 +90,7 @@ func (d disk) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCre return nil, httperrors.NewNotFoundError("not found pod disk by %s", disk.Id) } } - if err := d.validateOverlay(ctx, userCred, vm); err != nil { + if err := d.validateOverlay(ctx, userCred, vm, &diskObj); err != nil { return nil, errors.Wrapf(err, "validate overlay") } return vm, nil @@ -122,9 +116,9 @@ func (d disk) ValidatePodCreateData(ctx context.Context, userCred mcclient.Token return httperrors.NewInputParameterError("disk.index %d is large than disk size %d", diskIndex, len(disks)) } inputDisk := disks[diskIndex] - if inputDisk.ImageId != "" { - if disk.SubDirectory == "" { - return httperrors.NewInputParameterError("sub_directory is required when disk has image_id %s", inputDisk.ImageId) + if vm.Disk.Overlay != nil { + if err := d.getOverlayDriver(vm.Disk.Overlay).validatePodCreateData(ctx, userCred, vm.Disk.Overlay, inputDisk); err != nil { + return httperrors.NewInputParameterError("valid overlay %v", err) } } return nil @@ -134,7 +128,7 @@ func (d disk) getOverlayDriver(ov *apis.ContainerVolumeMountDiskOverlay) iDiskOv return d.overlayDrivers[ov.GetType()] } -func (d disk) validateOverlay(ctx context.Context, userCred mcclient.TokenCredential, vm *apis.ContainerVolumeMount) error { +func (d disk) validateOverlay(ctx context.Context, userCred mcclient.TokenCredential, vm *apis.ContainerVolumeMount, diskObj *models.SDisk) error { if vm.Disk.Overlay == nil { return nil } @@ -142,7 +136,7 @@ func (d disk) validateOverlay(ctx context.Context, userCred mcclient.TokenCreden if err := ov.IsValid(); err != nil { return httperrors.NewInputParameterError("invalid overlay input: %v", err) } - if err := d.getOverlayDriver(ov).validateCreateData(ctx, userCred, ov); err != nil { + if err := d.getOverlayDriver(ov).validateCreateData(ctx, userCred, ov, diskObj); err != nil { return errors.Wrapf(err, "validate overlay %s", ov.GetType()) } return nil @@ -154,7 +148,7 @@ func newDiskOverlayDir() iDiskOverlay { return &diskOverlayDir{} } -func (d diskOverlayDir) validateCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *apis.ContainerVolumeMountDiskOverlay) error { +func (d diskOverlayDir) validateCommonCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *apis.ContainerVolumeMountDiskOverlay) error { if len(input.LowerDir) == 0 { return httperrors.NewNotEmptyError("lower_dir is required") } @@ -168,3 +162,37 @@ func (d diskOverlayDir) validateCreateData(ctx context.Context, userCred mcclien } return nil } + +func (d diskOverlayDir) validateCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *apis.ContainerVolumeMountDiskOverlay, _ *models.SDisk) error { + return d.validateCommonCreateData(ctx, userCred, input) +} + +func (d diskOverlayDir) validatePodCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *apis.ContainerVolumeMountDiskOverlay, disk *api.DiskConfig) error { + return d.validateCommonCreateData(ctx, userCred, input) +} + +type diskOverlayImage struct{} + +func newDiskOverlayImage() iDiskOverlay { + return &diskOverlayImage{} +} + +func (d diskOverlayImage) validateCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *apis.ContainerVolumeMountDiskOverlay, diskObj *models.SDisk) error { + if !input.UseDiskImage { + return nil + } + if diskObj.TemplateId == "" { + return httperrors.NewInputParameterError("disk %s must have template_id", diskObj.GetId()) + } + return nil +} + +func (d diskOverlayImage) validatePodCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *apis.ContainerVolumeMountDiskOverlay, disk *api.DiskConfig) error { + if !input.UseDiskImage { + return nil + } + if disk.ImageId == "" { + return httperrors.NewInputParameterError("disk %#v must have image_id", disk) + } + return nil +} diff --git a/pkg/compute/models/containers.go b/pkg/compute/models/containers.go index ebdb9fbef40..40181e37b75 100644 --- a/pkg/compute/models/containers.go +++ b/pkg/compute/models/containers.go @@ -305,19 +305,23 @@ func (vm *ContainerVolumeMountRelation) toHostDiskMount(disk *apis.ContainerVolu } func (vm *ContainerVolumeMountRelation) ToHostMount() (*hostapi.ContainerVolumeMount, error) { - disk, err := vm.toHostDiskMount(vm.VolumeMount.Disk) - if err != nil { - return nil, errors.Wrap(err, "toHostDiskMount") - } - return &hostapi.ContainerVolumeMount{ + ret := &hostapi.ContainerVolumeMount{ Type: vm.VolumeMount.Type, - Disk: disk, + Disk: nil, HostPath: vm.VolumeMount.HostPath, ReadOnly: vm.VolumeMount.ReadOnly, MountPath: vm.VolumeMount.MountPath, SelinuxRelabel: vm.VolumeMount.SelinuxRelabel, Propagation: vm.VolumeMount.Propagation, - }, nil + } + if vm.VolumeMount.Disk != nil { + disk, err := vm.toHostDiskMount(vm.VolumeMount.Disk) + if err != nil { + return nil, errors.Wrap(err, "toHostDiskMount") + } + ret.Disk = disk + } + return ret, nil } func (m *SContainerManager) GetVolumeMountRelations(pod *SGuest, spec *api.ContainerSpec) ([]*ContainerVolumeMountRelation, error) { diff --git a/pkg/compute/models/disks.go b/pkg/compute/models/disks.go index ba9f86051d9..cf24ff8fd5d 100644 --- a/pkg/compute/models/disks.go +++ b/pkg/compute/models/disks.go @@ -1377,11 +1377,24 @@ func (self *SDisk) GetFsFormat() string { return self.FsFormat } -func (self *SDisk) GetCacheImageFormat() string { +func (self *SDisk) GetCacheImageFormat(ctx context.Context) (string, error) { + if self.TemplateId != "" { + s := auth.GetAdminSession(ctx, options.Options.Region) + img, err := image.Images.Get(s, self.TemplateId, nil) + if err == nil { + diskFmt, err := img.GetString("disk_format") + if err != nil { + return "", errors.Wrapf(err, "not found disk_format of image %s", self.TemplateId) + } + if diskFmt == imageapi.IMAGE_DISK_FORMAT_TGZ { + return imageapi.IMAGE_DISK_FORMAT_TGZ, nil + } + } + } if self.DiskFormat == "raw" { - return "qcow2" + return "qcow2", nil } - return self.DiskFormat + return self.DiskFormat, nil } func (manager *SDiskManager) getDisksByStorage(storage *SStorage) ([]SDisk, error) { diff --git a/pkg/compute/tasks/disk_create_task.go b/pkg/compute/tasks/disk_create_task.go index fc4338f6938..ee6c4b49884 100644 --- a/pkg/compute/tasks/disk_create_task.go +++ b/pkg/compute/tasks/disk_create_task.go @@ -44,9 +44,14 @@ func (self *DiskCreateTask) OnInit(ctx context.Context, obj db.IStandaloneModel, // use image only if disk not created from snapshot or backup if len(imageId) > 0 && len(disk.SnapshotId) == 0 && len(disk.BackupId) == 0 { self.SetStage("OnStorageCacheImageComplete", nil) + cacheImageFmt, err := disk.GetCacheImageFormat(ctx) + if err != nil { + self.OnStartAllocateFailed(ctx, disk, jsonutils.NewString(err.Error())) + return + } input := api.CacheImageInput{ ImageId: imageId, - Format: disk.GetCacheImageFormat(), + Format: cacheImageFmt, ParentTaskId: self.GetTaskId(), } guest := disk.GetGuest() diff --git a/pkg/hostman/container/volume_mount/disk.go b/pkg/hostman/container/volume_mount/disk.go index 0fd8faa3868..a669304d165 100644 --- a/pkg/hostman/container/volume_mount/disk.go +++ b/pkg/hostman/container/volume_mount/disk.go @@ -35,7 +35,8 @@ type disk struct { func newDisk() IVolumeMount { return &disk{ overlayDrivers: map[apis.ContainerDiskOverlayType]iDiskOverlay{ - apis.CONTAINER_DISK_OVERLAY_TYPE_DIRECTORY: newDiskOverlayDir(), + apis.CONTAINER_DISK_OVERLAY_TYPE_DIRECTORY: newDiskOverlayDir(), + apis.CONTAINER_DISK_OVERLAY_TYPE_DISK_IMAGE: newDiskOverlayImage(), }, } } @@ -160,11 +161,6 @@ func (d disk) Mount(pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount return errors.Wrapf(err, "mount container %s overlay dir: %#v", ctrId, vmDisk.Overlay) } } - if vmDisk.TemplateId != "" { - if err := d.mountTemplateOverlay(context.Background(), pod, ctrId, vm); err != nil { - return errors.Wrapf(err, "mount container %s template overlay: %#v", ctrId, vmDisk.TemplateId) - } - } return nil } @@ -197,11 +193,6 @@ func (d disk) Unmount(pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMou return errors.Wrapf(err, "umount overlay") } } - if vm.Disk.TemplateId != "" { - if err := d.unmountTemplateOverlay(context.Background(), pod, ctrId, vm); err != nil { - return errors.Wrapf(err, "unmount container %s template overlay: %#v", ctrId, vm.Disk.TemplateId) - } - } mntPoint := pod.GetDiskMountPoint(iDisk) if err := container_storage.Unmount(mntPoint); err != nil { return errors.Wrapf(err, "unmount %s", mntPoint) @@ -258,24 +249,6 @@ func (d disk) doTemplateOverlayAction( return nil } -func (d disk) mountTemplateOverlay( - ctx context.Context, - pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) error { - if err := d.doTemplateOverlayAction(ctx, pod, ctrId, vm, newDiskOverlayDir().mount); err != nil { - return errors.Wrapf(err, "mount template overlay") - } - return nil -} - -func (d disk) unmountTemplateOverlay( - ctx context.Context, - pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) error { - if err := d.doTemplateOverlayAction(ctx, pod, ctrId, vm, newDiskOverlayDir().unmount); err != nil { - return errors.Wrapf(err, "unmount template overlay") - } - return nil -} - type diskOverlayDir struct{} func newDiskOverlayDir() iDiskOverlay { @@ -314,3 +287,23 @@ func (dod diskOverlayDir) unmount(d disk, pod IPodInfo, ctrId string, vm *hostap overlayDir := d.getOverlayMergedDir(pod, ctrId, vm, upperDir) return container_storage.Unmount(overlayDir) } + +type diskOverlayImage struct{} + +func newDiskOverlayImage() iDiskOverlay { + return &diskOverlayImage{} +} + +func (di diskOverlayImage) mount(d disk, pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) error { + if err := d.doTemplateOverlayAction(context.Background(), pod, ctrId, vm, newDiskOverlayDir().mount); err != nil { + return errors.Wrapf(err, "mount template overlay") + } + return nil +} + +func (di diskOverlayImage) unmount(d disk, pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) error { + if err := d.doTemplateOverlayAction(context.Background(), pod, ctrId, vm, newDiskOverlayDir().unmount); err != nil { + return errors.Wrapf(err, "unmount template overlay") + } + return nil +} diff --git a/pkg/mcclient/options/compute/containers.go b/pkg/mcclient/options/compute/containers.go index 65f0b2661d7..252c47da375 100644 --- a/pkg/mcclient/options/compute/containers.go +++ b/pkg/mcclient/options/compute/containers.go @@ -208,6 +208,15 @@ func parseContainerVolumeMount(vmStr string) (*apis.ContainerVolumeMount, error) vm.Disk.Overlay = &apis.ContainerVolumeMountDiskOverlay{ LowerDir: strings.Split(val, ":"), } + case "overlay_disk_image": + if strings.ToLower(val) == "true" { + if vm.Disk == nil { + vm.Disk = &apis.ContainerVolumeMountDisk{} + } + vm.Disk.Overlay = &apis.ContainerVolumeMountDiskOverlay{ + UseDiskImage: true, + } + } } } return vm, nil diff --git a/pkg/mcclient/options/compute/containers_test.go b/pkg/mcclient/options/compute/containers_test.go index 9b9680a65f3..5e6eb7ee900 100644 --- a/pkg/mcclient/options/compute/containers_test.go +++ b/pkg/mcclient/options/compute/containers_test.go @@ -51,6 +51,21 @@ func Test_parseContainerVolumeMount(t *testing.T) { Type: apis.CONTAINER_VOLUME_MOUNT_TYPE_DISK, }, }, + { + args: "readonly=true,mount_path=/data,disk_index=0,disk_subdir=data,overlay_disk_image=true", + want: &apis.ContainerVolumeMount{ + ReadOnly: true, + MountPath: "/data", + Disk: &apis.ContainerVolumeMountDisk{ + Index: &index0, + SubDirectory: "data", + Overlay: &apis.ContainerVolumeMountDiskOverlay{ + UseDiskImage: true, + }, + }, + Type: apis.CONTAINER_VOLUME_MOUNT_TYPE_DISK, + }, + }, { args: "readonly=true,mount_path=/storage_size,disk_index=0,disk_ssf=storage_size", want: &apis.ContainerVolumeMount{