From a36c9857447aaf22628af1ef01406a916436133b Mon Sep 17 00:00:00 2001 From: jiangpengfei Date: Tue, 18 Aug 2020 22:00:04 +0800 Subject: [PATCH 39/50] storage: add storage common functions and structs reason: 1. add storage.go and storage_spec.go 2. provide funcs for mount nfs and gpath Signed-off-by: jiangpengfei --- virtcontainers/storage/storage.go | 115 +++++++++++++++++++++++++++++++++ virtcontainers/storage/storage_spec.go | 98 ++++++++++++++++++++++++++++ 2 files changed, 213 insertions(+) create mode 100644 virtcontainers/storage/storage.go create mode 100644 virtcontainers/storage/storage_spec.go diff --git a/virtcontainers/storage/storage.go b/virtcontainers/storage/storage.go new file mode 100644 index 00000000..77ef994f --- /dev/null +++ b/virtcontainers/storage/storage.go @@ -0,0 +1,115 @@ +// Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +// Description: common functions +// Author: licuifang +// Create: 2019-07-14 + +// Package storage provides functions for mounting +package storage + +import ( + "fmt" + "path/filepath" + "regexp" + "strings" + + "github.com/kata-containers/agent/protocols/grpc" + "github.com/opencontainers/runtime-spec/specs-go" +) + +var ( + // simple regular expressions for "nfs-server.com:/aaa/bbb/ccc/" or "192.168.1.1:/remote/path" + regUrl = regexp.MustCompile(`^((([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6})|((?:(?:25[0-5]|2[0-4]\d|[01]?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d?\d))):/[A-Za-z0-9_./-]{0,1000}$`) + + defaultMountOption = "nolock" + storageMountOptionKey = "mount_op" + storageCustomOptionKey = "custom_op" +) + +type RemoteStorage struct { + Source string + Dest string + Options map[string][]string +} + +func (n *RemoteStorage) Validate(volumeType string) error { + if n.Source == "" || n.Dest == "" { + return fmt.Errorf("the source and dest of storage cannot be empty") + } + switch volumeType { + case NFS: + chk := regUrl.FindAllString(n.Source, -1) + if chk == nil { + return fmt.Errorf("invalid url for nfs") + } + case GPATHFS: + if !filepath.IsAbs(n.Source) { + return fmt.Errorf("invalid gpath") + } + } + + return nil +} + +func (n *RemoteStorage) GetGrpcStorageAndAppendMount(volumeType string, vmBasePath string, spec *specs.Spec, sandboxId string) *grpc.Storage { + var ( + grpcStorag *grpc.Storage + vmPath string + ) + switch volumeType { + case NFS: + // source of sfs like remote-nfs.com:/share-53ee51a1 + // source of sfs with subpath like remote-nfs.com:/share-53ee51a1/aaa/bbb + // source of sfs-turbo like ip:/ + // source of sfs-turbo with subpath like ip://aaa or ip://aaa/bbb + // here we only get the origin source such as remote-nfs.com:/share-53ee51a1 or ip:/ as the Source of grpcStorage + item := strings.Split(n.Source, ":") + if len(item) != 2 { + return nil + } + vmPath = filepath.Join(vmBasePath, sandboxId, item[0], item[1]) + parts := strings.SplitAfter(item[1], "/") + if len(parts) > 2 { + n.Source = item[0] + ":" + parts[0] + parts[1] + } + + grpcStorag = &grpc.Storage{ + Driver: NFS, + Source: n.Source, + MountPoint: vmPath, + } + grpcStorag.Options = append(grpcStorag.Options, defaultMountOption) + case GPATHFS: + vmPath = n.Source + grpcStorag = &grpc.Storage{ + Driver: GPATHFS, + Source: vmPath, + MountPoint: n.Dest, + } + } + if custumOpts, ok := n.Options[storageCustomOptionKey]; ok { + for _, custumOpt := range custumOpts { + grpcStorag.Options = append(grpcStorag.Options, custumOpt) + } + } + + mnt := specs.Mount{ + Source: vmPath, + Destination: n.Dest, + } + if mountOpts, ok := n.Options[storageMountOptionKey]; ok { + for _, mountOpt := range mountOpts { + if mountOpt == "shared" { + // in order to support "shared" propagation for guestpath mounting to container directory + // we should set rootfspropagation shared + spec.Linux.RootfsPropagation = "shared" + } + mnt.Options = append(mnt.Options, mountOpt) + } + } + mnt.Options = append(mnt.Options, "rbind") + mnt.Type = "bind" + spec.Mounts = append(spec.Mounts, mnt) + + return grpcStorag +} diff --git a/virtcontainers/storage/storage_spec.go b/virtcontainers/storage/storage_spec.go new file mode 100644 index 00000000..8e866b8d --- /dev/null +++ b/virtcontainers/storage/storage_spec.go @@ -0,0 +1,98 @@ +// Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. +// Description: common functions +// Author: caihaomin c00416947 +// Create: 2019-05-05 + +package storage + +import ( + "encoding/json" + "fmt" + + "github.com/kata-containers/agent/protocols/grpc" + "github.com/opencontainers/runtime-spec/specs-go" +) + +const ( + NFS = "nfs" + GPATHFS = "gpath" +) + +// STORAGES specifies the storage type and is supported +var STORAGES = map[string]bool{ + NFS: true, + GPATHFS: true, +} + +type StorageSpec struct { + // StorageType specifies the type of storage passing down + StorageType string `json:"storage_type,omitempty"` + + Source string `json:"source,omitempty"` + Destination string `json:"dest,omitempty"` + + // Options specifies the options of storage type + Options map[string][]string `json:"options,omitempty"` +} + +func (s *StorageSpec) generateInstances() StorageOperation { + var storageOpt StorageOperation + storageOpt = &RemoteStorage{ + Source: s.Source, + Dest: s.Destination, + Options: s.Options, + } + + return storageOpt +} + +func ValidateStorageValue(value string) error { + var storageSpecs []StorageSpec + if err := json.Unmarshal([]byte(value), &storageSpecs); err != nil { + return err + } + + var storageOpt []StorageOperation + for _, item := range storageSpecs { + if supported, ok := STORAGES[item.StorageType]; !ok || !supported { + return fmt.Errorf("type %s of storage is not supported", item.StorageType) + } + storageItem := item.generateInstances() + if err := storageItem.Validate(item.StorageType); err != nil { + return err + } + storageOpt = append(storageOpt, storageItem) + } + + return nil +} + +func GetGrpcStorageAndAppendMount(vmBasePath, value string, spec *specs.Spec, sandboxId string) []*grpc.Storage { + var ( + grpcStorages []*grpc.Storage + storageSpec []StorageSpec + ) + + // the value has been validated before, and there is no need to judge the return value here + json.Unmarshal([]byte(value), &storageSpec) + for _, item := range storageSpec { + storageItem := item.generateInstances() + grpcStorages = append(grpcStorages, storageItem.GetGrpcStorageAndAppendMount(item.StorageType, vmBasePath, spec, sandboxId)) + } + return grpcStorages +} + +type StorageOperation interface { + Validate(volumeType string) error + GetGrpcStorageAndAppendMount(volumeType string, vmBasePath string, spec *specs.Spec, sandboxId string) *grpc.Storage +} + +type FakeStorage struct{} + +func (f *FakeStorage) Validate() error { + return nil +} + +func (f *FakeStorage) GetGrpcStorageAndAppendMount(vmBasePath string, spec *specs.Spec) *grpc.Storage { + return &grpc.Storage{} +} -- 2.14.3 (Apple Git-98)