Skip to content

Commit

Permalink
feat(region,host,climc): support container post image overlay (#21942)
Browse files Browse the repository at this point in the history
  • Loading branch information
zexi authored Jan 9, 2025
1 parent fb849ae commit f46c325
Show file tree
Hide file tree
Showing 21 changed files with 782 additions and 141 deletions.
46 changes: 33 additions & 13 deletions cmd/climc/shell/image/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ import (
"github.com/cheggaaa/pb/v3"

"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/printutils"

"yunion.io/x/onecloud/cmd/climc/shell"
imageapi "yunion.io/x/onecloud/pkg/apis/image"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modules/identity"
modules "yunion.io/x/onecloud/pkg/mcclient/modules/image"
Expand Down Expand Up @@ -62,6 +64,9 @@ type ImageOptionalOptions struct {
DisableUsbKbd bool `help:"Disable usb keyboard on this image(for hypervisor kvm)"`
BootMode string `help:"UEFI support" choices:"UEFI|BIOS"`
VdiProtocol string `help:"VDI protocol" choices:"vnc|spice"`
// for container usage
UsedByPostOverlay bool `help:"Used by container post-overlay"`
InternalPathMap []string `help:"Internal path map, e.g. 'com.taobao.taobao/10.42.12/data/data/com.taobao.taobao:/data/data/com.taobao.taobao'"`
}

func addImageOptionalOptions(s *mcclient.ClientSession, params *jsonutils.JSONDict, args ImageOptionalOptions) error {
Expand Down Expand Up @@ -106,50 +111,65 @@ func addImageOptionalOptions(s *mcclient.ClientSession, params *jsonutils.JSONDi
}
params.Add(jsonutils.NewString(projectId), "owner")
}
keyProperties := "properties"
if len(args.OsType) > 0 {
params.Add(jsonutils.NewString(args.OsType), "properties", "os_type")
params.Add(jsonutils.NewString(args.OsType), keyProperties, "os_type")
}
if len(args.OsDist) > 0 {
params.Add(jsonutils.NewString(args.OsDist), "properties", "os_distribution")
params.Add(jsonutils.NewString(args.OsDist), keyProperties, "os_distribution")
}
if len(args.OsVersion) > 0 {
params.Add(jsonutils.NewString(args.OsVersion), "properties", "os_version")
params.Add(jsonutils.NewString(args.OsVersion), keyProperties, "os_version")
}
if len(args.OsCodename) > 0 {
params.Add(jsonutils.NewString(args.OsCodename), "properties", "os_codename")
params.Add(jsonutils.NewString(args.OsCodename), keyProperties, "os_codename")
}
if len(args.OsArch) > 0 {
params.Add(jsonutils.NewString(args.OsArch), "properties", "os_arch")
params.Add(jsonutils.NewString(args.OsArch), keyProperties, "os_arch")
params.Add(jsonutils.NewString(args.OsArch), "os_arch")
}
if len(args.OsLang) > 0 {
params.Add(jsonutils.NewString(args.OsLang), "properties", "os_language")
params.Add(jsonutils.NewString(args.OsLang), keyProperties, "os_language")
}
if args.Preference > 0 {
params.Add(jsonutils.NewString(fmt.Sprintf("%d", args.Preference)), "properties", "preference")
}
if len(args.Notes) > 0 {
params.Add(jsonutils.NewString(args.Notes), "properties", "notes")
params.Add(jsonutils.NewString(args.Notes), keyProperties, "notes")
}
if len(args.DiskDriver) > 0 {
params.Add(jsonutils.NewString(args.DiskDriver), "properties", "disk_driver")
params.Add(jsonutils.NewString(args.DiskDriver), keyProperties, "disk_driver")
}
if len(args.NetDriver) > 0 {
params.Add(jsonutils.NewString(args.NetDriver), "properties", "net_driver")
params.Add(jsonutils.NewString(args.NetDriver), keyProperties, "net_driver")
}
if len(args.Hypervisor) > 0 {
params.Add(jsonutils.NewString(strings.Join(args.Hypervisor, ",")), "properties", "hypervisor")
}
if args.DisableUsbKbd {
params.Add(jsonutils.NewString("true"), "properties", "disable_usb_kbd")
params.Add(jsonutils.NewString("true"), keyProperties, "disable_usb_kbd")
}
if args.BootMode == "UEFI" {
params.Add(jsonutils.JSONTrue, "properties", "uefi_support")
params.Add(jsonutils.JSONTrue, keyProperties, "uefi_support")
} else if args.BootMode == "BIOS" {
params.Add(jsonutils.JSONFalse, "properties", "uefi_support")
params.Add(jsonutils.JSONFalse, keyProperties, "uefi_support")
}
if len(args.VdiProtocol) > 0 {
params.Add(jsonutils.NewString(args.VdiProtocol), "properties", "vdi_protocol")
params.Add(jsonutils.NewString(args.VdiProtocol), keyProperties, "vdi_protocol")
}
if args.UsedByPostOverlay {
params.Add(jsonutils.NewString("true"), keyProperties, imageapi.IMAGE_USED_BY_POST_OVERLAY)
}
if len(args.InternalPathMap) > 0 {
dirMap := make(map[string]string)
for _, dir := range args.InternalPathMap {
parts := strings.Split(dir, ":")
if len(parts) != 2 {
return errors.Errorf("internal dir format error: %s", dir)
}
dirMap[parts[0]] = parts[1]
}
params.Add(jsonutils.NewString(jsonutils.Marshal(dirMap).String()), keyProperties, imageapi.IMAGE_INTERNAL_PATH_MAP)
}
return nil
}
Expand Down
21 changes: 17 additions & 4 deletions pkg/apis/compute/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ const (
CONTAINER_STATUS_ADD_POST_OVERLY_FAILED = "add_post_overly_failed"
CONTAINER_STATUS_REMOVE_POST_OVERLY = "removing_post_overly"
CONTAINER_STATUS_REMOVE_POST_OVERLY_FAILED = "remove_post_overly_failed"
CONTAINER_STATUS_CACHE_IMAGE = "caching_image"
CONTAINER_STATUS_CACHE_IMAGE_FAILED = "caching_image_failed"
)

var (
Expand Down Expand Up @@ -177,10 +179,12 @@ type ContainerDevice struct {
}

type ContainerSaveVolumeMountToImageInput struct {
Name string `json:"name"`
GenerateName string `json:"generate_name"`
Notes string `json:"notes"`
Index int `json:"index"`
Name string `json:"name"`
GenerateName string `json:"generate_name"`
Notes string `json:"notes"`
Index int `json:"index"`
Dirs []string `json:"dirs"`
UsedByPostOverlay bool `json:"used_by_post_overlay"`
}

type ContainerExecInfoOutput struct {
Expand Down Expand Up @@ -277,3 +281,12 @@ type ContainerVolumeMountRemovePostOverlayInput struct {
UseLazy bool `json:"use_lazy"`
ClearLayers bool `json:"clear_layers"`
}

type ContainerCacheImageInput struct {
DiskId string `json:"disk_id"`
Image *CacheImageInput `json:"image"`
}

type ContainerCacheImagesInput struct {
Images []*ContainerCacheImageInput `json:"images"`
}
35 changes: 34 additions & 1 deletion pkg/apis/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,11 +228,44 @@ func (o ContainerVolumeMountDiskOverlay) IsValid() error {
return nil
}

type ContainerVolumeMountDiskPostImageOverlay struct {
Id string `json:"id"`
PathMap map[string]string `json:"path_map"`
}

type ContainerVolumeMountDiskPostOverlayType string

const (
CONTAINER_VOLUME_MOUNT_DISK_POST_OVERLAY_HOSTPATH ContainerVolumeMountDiskPostOverlayType = "host_path"
CONTAINER_VOLUME_MOUNT_DISK_POST_OVERLAY_IMAGE ContainerVolumeMountDiskPostOverlayType = "image"
)

type ContainerVolumeMountDiskPostOverlay struct {
// 宿主机底层目录
HostLowerDir []string `json:"host_lower_dir"`
// 合并后要挂载到容器的目录
ContainerTargetDir string `json:"container_target_dir"`
ContainerTargetDir string `json:"container_target_dir"`
Image *ContainerVolumeMountDiskPostImageOverlay `json:"image"`
}

func (o ContainerVolumeMountDiskPostOverlay) IsEqual(input ContainerVolumeMountDiskPostOverlay) bool {
if o.GetType() != input.GetType() {
return false
}
switch o.GetType() {
case CONTAINER_VOLUME_MOUNT_DISK_POST_OVERLAY_HOSTPATH:
return o.ContainerTargetDir == input.ContainerTargetDir
case CONTAINER_VOLUME_MOUNT_DISK_POST_OVERLAY_IMAGE:
return o.Image.Id == input.Image.Id
}
return false
}

func (o ContainerVolumeMountDiskPostOverlay) GetType() ContainerVolumeMountDiskPostOverlayType {
if o.Image != nil {
return CONTAINER_VOLUME_MOUNT_DISK_POST_OVERLAY_IMAGE
}
return CONTAINER_VOLUME_MOUNT_DISK_POST_OVERLAY_HOSTPATH
}

type ContainerVolumeMountDisk struct {
Expand Down
1 change: 1 addition & 0 deletions pkg/apis/host/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ type ContainerSaveVolumeMountToImageInput struct {

VolumeMountIndex int `json:"volume_mount_index"`
VolumeMount *ContainerVolumeMount `json:"volume_mount"`
VolumeMountDirs []string `json:"volume_mount_dirs"`
}

type ContainerCommitInput struct {
Expand Down
26 changes: 14 additions & 12 deletions pkg/apis/image/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,20 @@ const (
IMAGE_STORAGE_DRIVER_S3 = "s3"

// image properties
IMAGE_OS_ARCH = "os_arch"
IMAGE_OS_DISTRO = "os_distribution"
IMAGE_OS_TYPE = "os_type"
IMAGE_OS_VERSION = "os_version"
IMAGE_DISK_FORMAT = "disk_format"
IMAGE_UEFI_SUPPORT = "uefi_support"
IMAGE_IS_LVM_PARTITION = "is_lvm_partition"
IMAGE_IS_READONLY = "is_readonly"
IMAGE_PARTITION_TYPE = "partition_type"
IMAGE_INSTALLED_CLOUDINIT = "installed_cloud_init"
IMAGE_DISABLE_USB_KBD = "disable_usb_kbd"
IMAGE_VDI_PROTOCOL = "vdi_protocol"
IMAGE_OS_ARCH = "os_arch"
IMAGE_OS_DISTRO = "os_distribution"
IMAGE_OS_TYPE = "os_type"
IMAGE_OS_VERSION = "os_version"
IMAGE_DISK_FORMAT = "disk_format"
IMAGE_UEFI_SUPPORT = "uefi_support"
IMAGE_IS_LVM_PARTITION = "is_lvm_partition"
IMAGE_IS_READONLY = "is_readonly"
IMAGE_PARTITION_TYPE = "partition_type"
IMAGE_INSTALLED_CLOUDINIT = "installed_cloud_init"
IMAGE_DISABLE_USB_KBD = "disable_usb_kbd"
IMAGE_VDI_PROTOCOL = "vdi_protocol"
IMAGE_INTERNAL_PATH_MAP = "internal_path_map"
IMAGE_USED_BY_POST_OVERLAY = "used_by_post_overlay"

IMAGE_STATUS_UPDATING = "updating"
)
Expand Down
69 changes: 49 additions & 20 deletions pkg/compute/container_drivers/volume_mount/disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ package volume_mount
import (
"context"

"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/sets"

"yunion.io/x/onecloud/pkg/apis"
api "yunion.io/x/onecloud/pkg/apis/compute"
Expand All @@ -35,16 +37,26 @@ type iDiskOverlay interface {
validateCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *apis.ContainerVolumeMountDiskOverlay, obj *models.SDisk) error
}

type iDiskPostOverlay interface {
validateData(ctx context.Context, userCred mcclient.TokenCredential, pov *apis.ContainerVolumeMountDiskPostOverlay) error
getContainerTargetDirs(ov *apis.ContainerVolumeMountDiskPostOverlay) []string
}

type disk struct {
overlayDrivers map[apis.ContainerDiskOverlayType]iDiskOverlay
overlayDrivers map[apis.ContainerDiskOverlayType]iDiskOverlay
postOverlayDrivers map[apis.ContainerVolumeMountDiskPostOverlayType]iDiskPostOverlay
}

func newDisk() models.IContainerVolumeMountDriver {
func newDisk() models.IContainerVolumeMountDiskDriver {
return &disk{
overlayDrivers: map[apis.ContainerDiskOverlayType]iDiskOverlay{
apis.CONTAINER_DISK_OVERLAY_TYPE_DIRECTORY: newDiskOverlayDir(),
apis.CONTAINER_DISK_OVERLAY_TYPE_DISK_IMAGE: newDiskOverlayImage(),
},
postOverlayDrivers: map[apis.ContainerVolumeMountDiskPostOverlayType]iDiskPostOverlay{
apis.CONTAINER_VOLUME_MOUNT_DISK_POST_OVERLAY_HOSTPATH: newDiskPostOverlayHostPath(),
apis.CONTAINER_VOLUME_MOUNT_DISK_POST_OVERLAY_IMAGE: newDiskPostOverlayImage(),
},
}
}

Expand Down Expand Up @@ -135,7 +147,7 @@ func (d disk) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCre
if err := d.validateOverlay(ctx, userCred, vm, &diskObj); err != nil {
return nil, errors.Wrapf(err, "validate overlay")
}
if err := d.ValidatePostOverlay(vm); err != nil {
if err := d.ValidatePostOverlay(ctx, userCred, vm); err != nil {
return nil, errors.Wrap(err, "validate post overlay")
}
return vm, nil
Expand Down Expand Up @@ -173,6 +185,10 @@ func (d disk) getOverlayDriver(ov *apis.ContainerVolumeMountDiskOverlay) iDiskOv
return d.overlayDrivers[ov.GetType()]
}

func (d disk) getPostOverlayDriver(pov *apis.ContainerVolumeMountDiskPostOverlay) iDiskPostOverlay {
return d.postOverlayDrivers[pov.GetType()]
}

func (d disk) validateOverlay(ctx context.Context, userCred mcclient.TokenCredential, vm *apis.ContainerVolumeMount, diskObj *models.SDisk) error {
if vm.Disk.Overlay == nil {
return nil
Expand All @@ -187,28 +203,41 @@ func (d disk) validateOverlay(ctx context.Context, userCred mcclient.TokenCreden
return nil
}

func (d disk) ValidatePostOverlay(vm *apis.ContainerVolumeMount) error {
func (d disk) ValidatePostSingleOverlay(ctx context.Context, userCred mcclient.TokenCredential, ov *apis.ContainerVolumeMountDiskPostOverlay) error {
drv := d.getPostOverlayDriver(ov)
if err := drv.validateData(ctx, userCred, ov); err != nil {
return errors.Wrapf(err, "validate post overlay %s", ov.GetType())
}
return nil
}

func (d disk) ValidatePostOverlayTargetDirs(ovs []*apis.ContainerVolumeMountDiskPostOverlay) error {
ctrTargetDirs := sets.NewString()
for _, ov := range ovs {
drv := d.getPostOverlayDriver(ov)
ovCtrTargetDirs := drv.getContainerTargetDirs(ov)
if ctrTargetDirs.HasAny(ovCtrTargetDirs...) {
return httperrors.NewInputParameterError("duplicated container target dirs %v of ov %s", ctrTargetDirs, jsonutils.Marshal(ov))
} else {
ctrTargetDirs.Insert(ovCtrTargetDirs...)
}
}
return nil
}

func (d disk) ValidatePostOverlay(ctx context.Context, userCred mcclient.TokenCredential, vm *apis.ContainerVolumeMount) error {
if len(vm.Disk.PostOverlay) == 0 {
return nil
}
ovs := vm.Disk.PostOverlay
var duplicateCtrDir string
for _, ov := range ovs {
if len(ov.HostLowerDir) == 0 {
return httperrors.NewNotEmptyError("host_lower_dir is required")
for i, ov := range ovs {
if err := d.ValidatePostSingleOverlay(ctx, userCred, ov); err != nil {
return err
}
for i, hld := range ov.HostLowerDir {
if len(hld) == 0 {
return httperrors.NewNotEmptyError("host_lower_dir %d is empty", i)
}
}
if len(ov.ContainerTargetDir) == 0 {
return httperrors.NewNotEmptyError("container_target_dir is required")
}
if ov.ContainerTargetDir == duplicateCtrDir {
return httperrors.NewDuplicateNameError("container_target_dir", ov.ContainerTargetDir)
}
duplicateCtrDir = ov.ContainerTargetDir
vm.Disk.PostOverlay[i] = ov
}
if err := d.ValidatePostOverlayTargetDirs(vm.Disk.PostOverlay); err != nil {
return errors.Wrap(err, "validate post overlay target dirs")
}
if vm.Propagation == "" {
// 设置默认 propagation 为 rslave
Expand Down
35 changes: 35 additions & 0 deletions pkg/compute/container_drivers/volume_mount/disk_pov_host_path.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package volume_mount

import (
"context"

"yunion.io/x/onecloud/pkg/apis"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
)

type povHostPath struct {
}

func newDiskPostOverlayHostPath() iDiskPostOverlay {
return &povHostPath{}
}

func (p povHostPath) validateData(ctx context.Context, userCred mcclient.TokenCredential, ov *apis.ContainerVolumeMountDiskPostOverlay) error {
if len(ov.HostLowerDir) == 0 {
return httperrors.NewNotEmptyError("host_lower_dir is required")
}
for i, hld := range ov.HostLowerDir {
if len(hld) == 0 {
return httperrors.NewNotEmptyError("host_lower_dir %d is empty", i)
}
}
if len(ov.ContainerTargetDir) == 0 {
return httperrors.NewNotEmptyError("container_target_dir is required")
}
return nil
}

func (p povHostPath) getContainerTargetDirs(ov *apis.ContainerVolumeMountDiskPostOverlay) []string {
return []string{ov.ContainerTargetDir}
}
Loading

0 comments on commit f46c325

Please sign in to comment.