| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- 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"
- hostapi "yunion.io/x/onecloud/pkg/apis/host"
- "yunion.io/x/onecloud/pkg/compute/models"
- "yunion.io/x/onecloud/pkg/httperrors"
- "yunion.io/x/onecloud/pkg/mcclient"
- )
- func init() {
- models.RegisterContainerVolumeMountDriver(newDisk())
- }
- type iDiskOverlay interface {
- 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 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
- postOverlayDrivers map[apis.ContainerVolumeMountDiskPostOverlayType]iDiskPostOverlay
- }
- 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(),
- },
- }
- }
- func (d disk) GetType() apis.ContainerVolumeMountType {
- return apis.CONTAINER_VOLUME_MOUNT_TYPE_DISK
- }
- func (d disk) validateDiskCreateData(ctx context.Context, userCred mcclient.TokenCredential, disk *apis.ContainerVolumeMountDisk) (*apis.ContainerVolumeMountDisk, error) {
- if disk == nil {
- return nil, httperrors.NewNotEmptyError("disk is nil")
- }
- if disk.Index == nil && disk.Id == "" {
- return nil, httperrors.NewNotEmptyError("one of index or id is required")
- }
- if disk.Index != nil {
- if *disk.Index < 0 {
- return nil, httperrors.NewInputParameterError("index is less than 0")
- }
- }
- return disk, nil
- }
- func (d disk) validateCreateData(ctx context.Context, userCred mcclient.TokenCredential, vm *apis.ContainerVolumeMount) (*apis.ContainerVolumeMount, error) {
- disk, err := d.validateDiskCreateData(ctx, userCred, vm.Disk)
- if err != nil {
- return nil, err
- }
- vm.Disk = disk
- return vm, nil
- }
- func (d disk) validateCaseInsensitive(disk *models.SDisk, vm *apis.ContainerVolumeMountDisk) error {
- if len(vm.CaseInsensitivePaths) == 0 {
- return nil
- }
- if disk.FsFeatures == nil {
- return httperrors.NewInputParameterError("disk(%s) fs_features is not set", disk.GetId())
- }
- if disk.FsFeatures.Ext4 == nil {
- return httperrors.NewInputParameterError("disk(%s) fs_features.ext4 is not set", disk.GetId())
- }
- if !disk.FsFeatures.Ext4.CaseInsensitive {
- return httperrors.NewInputParameterError("disk(%s) fs_features.ext4.case_insensitive is not set", disk.GetId())
- }
- if disk.FsFeatures.F2fs == nil {
- return httperrors.NewInputParameterError("disk(%s) fs_features.f2fs is not set", disk.GetId())
- }
- if !disk.FsFeatures.F2fs.CaseInsensitive {
- return httperrors.NewInputParameterError("disk(%s) fs_features.f2fs.case_insensitive is not set", disk.GetId())
- }
- if vm.Overlay != nil {
- return httperrors.NewInputParameterError("can't use case_insensitive and overlay at the same time")
- }
- if vm.SubDirectory == "" {
- return httperrors.NewInputParameterError("sub_directory must set to use case_insensitive")
- }
- return nil
- }
- func (d disk) ValidateRootFsCreateData(ctx context.Context, userCred mcclient.TokenCredential, pod *models.SGuest, rootFs *apis.ContainerRootfs) error {
- disk, err := d.validateDiskCreateData(ctx, userCred, rootFs.Disk)
- if err != nil {
- return err
- }
- disk, _, err = d.validateDiskIndex(ctx, userCred, pod, disk)
- if err != nil {
- return err
- }
- rootFs.Disk = disk
- if rootFs.Disk.Overlay != nil {
- return httperrors.NewInputParameterError("can't use overlay and root_fs at the same time")
- }
- if len(rootFs.Disk.PostOverlay) > 0 {
- return httperrors.NewInputParameterError("can't use post_overlay and root_fs at the same time")
- }
- return nil
- }
- func (d disk) ToHostRootFs(rootFs *apis.ContainerRootfs) (*hostapi.ContainerRootfs, error) {
- diskObj := models.DiskManager.FetchDiskById(rootFs.Disk.Id)
- if diskObj == nil {
- return nil, errors.Wrapf(httperrors.ErrNotFound, "fetch disk %s", rootFs.Disk.Id)
- }
- disk := rootFs.Disk
- return &hostapi.ContainerRootfs{
- Type: rootFs.Type,
- Disk: &hostapi.ContainerVolumeMountDisk{
- Index: disk.Index,
- Id: disk.Id,
- SubDirectory: disk.SubDirectory,
- },
- Persistent: rootFs.Persistent,
- }, nil
- }
- func (d disk) validateDiskIndex(ctx context.Context, userCred mcclient.TokenCredential, pod *models.SGuest, disk *apis.ContainerVolumeMountDisk) (*apis.ContainerVolumeMountDisk, *models.SDisk, error) {
- disks, err := pod.GetDisks()
- if err != nil {
- return nil, nil, errors.Wrap(err, "get pod disks")
- }
- var diskObj models.SDisk
- if disk.Index != nil {
- diskIndex := *disk.Index
- if diskIndex >= len(disks) {
- return nil, nil, httperrors.NewInputParameterError("disk.index %d is large than disk size %d", diskIndex, len(disks))
- }
- diskObj = disks[diskIndex]
- disk.Id = diskObj.GetId()
- // remove index
- disk.Index = nil
- if err := d.validateCaseInsensitive(&diskObj, disk); err != nil {
- return nil, nil, err
- }
- } else {
- if disk.Id == "" {
- return nil, nil, httperrors.NewNotEmptyError("disk.id is empty")
- }
- foundDisk := false
- for _, d := range disks {
- if d.GetId() == disk.Id || d.GetName() == disk.Id {
- disk.Id = d.GetId()
- diskObj = d
- foundDisk = true
- break
- }
- }
- if !foundDisk {
- return nil, nil, httperrors.NewNotFoundError("not found pod disk by %s", disk.Id)
- }
- if err := d.validateCaseInsensitive(&diskObj, disk); err != nil {
- return nil, nil, err
- }
- }
- return disk, &diskObj, nil
- }
- func (d disk) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, pod *models.SGuest, vm *apis.ContainerVolumeMount) (*apis.ContainerVolumeMount, error) {
- if _, err := d.validateCreateData(ctx, userCred, vm); err != nil {
- return nil, err
- }
- disk, diskObj, err := d.validateDiskIndex(ctx, userCred, pod, vm.Disk)
- if err != nil {
- return nil, err
- }
- vm.Disk = disk
- if err := d.validateOverlay(ctx, userCred, vm, diskObj); err != nil {
- return nil, errors.Wrapf(err, "validate overlay")
- }
- if err := d.ValidatePostOverlay(ctx, userCred, vm); err != nil {
- return nil, errors.Wrap(err, "validate post overlay")
- }
- return vm, nil
- }
- func (d disk) ValidatePodCreateData(ctx context.Context, userCred mcclient.TokenCredential, vm *apis.ContainerVolumeMount, input *api.ServerCreateInput) error {
- if _, err := d.validateCreateData(ctx, userCred, vm); err != nil {
- return err
- }
- disk := vm.Disk
- if disk.Id != "" {
- return httperrors.NewInputParameterError("can't specify disk_id %s when creating pod", disk.Id)
- }
- if disk.Index == nil {
- return httperrors.NewNotEmptyError("disk.index is required")
- }
- diskIndex := *disk.Index
- disks := input.Disks
- if diskIndex < 0 {
- return httperrors.NewInputParameterError("disk.index %d is less than 0", diskIndex)
- }
- if diskIndex >= len(disks) {
- return httperrors.NewInputParameterError("disk.index %d is large than disk size %d", diskIndex, len(disks))
- }
- inputDisk := disks[diskIndex]
- 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
- }
- func (d disk) getOverlayDriver(ov *apis.ContainerVolumeMountDiskOverlay) iDiskOverlay {
- 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
- }
- ov := vm.Disk.Overlay
- if err := ov.IsValid(); err != nil {
- return httperrors.NewInputParameterError("invalid overlay input: %v", err)
- }
- if err := d.getOverlayDriver(ov).validateCreateData(ctx, userCred, ov, diskObj); err != nil {
- return errors.Wrapf(err, "validate overlay %s", ov.GetType())
- }
- return nil
- }
- 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 i := range ovs {
- ov := ovs[i]
- 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
- for i := range ovs {
- ov := ovs[i]
- if err := d.ValidatePostSingleOverlay(ctx, userCred, ov); err != nil {
- return errors.Wrap(err, "validate post single overlay")
- }
- 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
- vm.Propagation = apis.MOUNTPROPAGATION_PROPAGATION_HOST_TO_CONTAINER
- }
- return nil
- }
- type diskOverlayDir struct{}
- func newDiskOverlayDir() iDiskOverlay {
- return &diskOverlayDir{}
- }
- 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")
- }
- for idx, ld := range input.LowerDir {
- if ld == "" {
- return httperrors.NewNotEmptyError("empty %d dir", idx)
- }
- if ld == "/" {
- return httperrors.NewInputParameterError("can't use '/' as lower_dir")
- }
- }
- 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
- }
|