From a36c9857447aaf22628af1ef01406a916436133b Mon Sep 17 00:00:00 2001
From: jiangpengfei <jiangpengfei9@huawei.com>
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 <jiangpengfei9@huawei.com>
---
 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)

