add volume type model and update create volume

This commit is contained in:
凡羊羊 2019-12-12 11:59:02 +08:00
parent 4055d01946
commit e6387584e5
25 changed files with 1020 additions and 570 deletions

View File

@ -42,8 +42,6 @@ type TenantInterface interface {
GetManyDeployVersion(w http.ResponseWriter, r *http.Request)
LimitTenantMemory(w http.ResponseWriter, r *http.Request)
TenantResourcesStatus(w http.ResponseWriter, r *http.Request)
VolumeProvider(w http.ResponseWriter, r *http.Request)
VolumeBestSelector(w http.ResponseWriter, r *http.Request)
GetVolumesStatus(w http.ResponseWriter, r *http.Request)
}

View File

@ -56,6 +56,7 @@ func (v2 *V2) Routes() chi.Router {
r.Mount("/events", v2.eventsRouter())
r.Get("/gateway/ips", controller.GetGatewayIPs)
r.Get("/gateway/ports", controller.GetManager().GetAvailablePort)
r.Get("/volume-options", controller.VolumeOptions)
return r
}
@ -132,9 +133,6 @@ func (v2 *V2) tenantNameRouter() chi.Router {
//batch operation
r.Post("/batchoperation", controller.BatchOperation)
r.Get("/volume-providers", controller.GetManager().VolumeProvider)
r.Post("/volume-best", controller.GetManager().VolumeBestSelector)
return r
}

View File

@ -34,6 +34,66 @@ import (
"github.com/jinzhu/gorm"
)
// VolumeOptions list volume option
func VolumeOptions(w http.ResponseWriter, r *http.Request) {
// swagger:operation POST /v2/volume-options v2 volumeOptions
//
// 查询可用存储驱动模型列表
//
// get volume-options
//
// ---
// consumes:
// - application/json
// - application/x-protobuf
//
// produces:
// - application/json
// - application/xml
//
// responses:
// default:
// schema:
// description: 统一返回格式
storageClasses, err := handler.GetVolumeTypeHandler().GetAllStorageClasses()
if err != nil {
httputil.ReturnError(r, w, 500, err.Error())
return
}
var optionList []api_model.VolumeOptionsStruct
volumeTypeMap := make(map[string]*dbmodel.TenantServiceVolumeType)
volumeTypes, err := handler.GetVolumeTypeHandler().GetAllVolumeTypes()
if err != nil {
logrus.Errorf("get all volumeTypes error: %s", err.Error())
httputil.ReturnError(r, w, 500, err.Error())
return
}
for _, vt := range volumeTypes {
volumeTypeMap[vt.VolumeType] = vt
}
for _, sc := range storageClasses {
vt := util.ParseVolumeOptionType(sc)
opt := api_model.VolumeOptionsStruct{}
opt.VolumeType = vt
if dbvt, ok := volumeTypeMap[opt.VolumeType]; ok {
util.HackVolumeOptionDetailFromDB(&opt, dbvt)
} else {
util.HackVolumeOptionDetail(vt, &opt, sc.GetName(), sc.GetReclaimPolicy(), sc.VolumeBindingMode, sc.AllowVolumeExpansion)
}
optionList = append(optionList, opt)
}
// TODO 管理后台支持自定义StorageClass则内容与db中的数据进行融合进行更多的业务逻辑
memoryVolumeType := api_model.VolumeOptionsStruct{VolumeType: dbmodel.MemoryFSVolumeType.String(), NameShow: "内存文件存储"}
util.HackVolumeOptionDetailFromDB(&memoryVolumeType, volumeTypeMap["memoryfs"])
optionList = append(optionList, memoryVolumeType)
httputil.ReturnSuccess(r, w, optionList)
}
// GetVolumesStatus getvolume status
func (t *TenantStruct) GetVolumesStatus(w http.ResponseWriter, r *http.Request) {
// swagger:operation POST /v2/tenants/{tenant_name}/volumes-status v2 GetVolumesStatus
@ -80,147 +140,6 @@ func (t *TenantStruct) GetVolumesStatus(w http.ResponseWriter, r *http.Request)
httputil.ReturnSuccess(r, w, ret)
}
// VolumeBestSelector best volume by volume filter
func (t *TenantStruct) VolumeBestSelector(w http.ResponseWriter, r *http.Request) {
// swagger:operation POST /v2/tenants/{tenant_name}/volume-best v2 VolumeBest
//
// 查询可用存储驱动模型列表
//
// post volume-best
//
// ---
// consumes:
// - application/json
// - application/x-protobuf
//
// produces:
// - application/json
// - application/xml
//
// responses:
// default:
// schema:
// description: 统一返回格式
var oldVolumeSelector api_model.VolumeBestReqStruct
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &oldVolumeSelector, nil); !ok {
return
}
if oldVolumeSelector.VolumeType == dbmodel.ShareFileVolumeType.String() || oldVolumeSelector.VolumeType == dbmodel.LocalVolumeType.String() {
ret := api_model.VolumeBestRespStruct{Changed: false}
httputil.ReturnSuccess(r, w, ret)
return
}
storageClasses, err := t.StatusCli.GetStorageClasses()
if err != nil {
httputil.ReturnError(r, w, 500, err.Error())
return
}
var providerMap = make(map[string][]api_model.VolumeProviderDetail)
kindFilter := oldVolumeSelector.VolumeType
for _, storageClass := range storageClasses.GetList() {
kind := util.ParseVolumeProviderKind(storageClass)
if kind == "" {
logrus.Debugf("unknown storageclass: %+v", storageClass)
continue
}
if kindFilter != "" && kind != kindFilter {
continue
}
detail := api_model.VolumeProviderDetail{
Name: storageClass.Name,
Provisioner: storageClass.Provisioner,
ReclaimPolicy: storageClass.ReclaimPolicy,
VolumeBindingMode: storageClass.VolumeBindingMode,
AllowVolumeExpansion: &storageClass.AllowVolumeExpansion,
}
util.HackVolumeProviderDetail(kind, &detail)
exists := false
for _, accessMode := range detail.AccessMode {
if strings.ToUpper(oldVolumeSelector.AccessMode) == accessMode {
exists = true
break
}
}
if !exists {
logrus.Warnf("not suitable select for volume[volumeType:%s, accessMode:%s] of kind(%s)", oldVolumeSelector.VolumeType, oldVolumeSelector.AccessMode, kind)
continue
} else {
providerMap[kind] = []api_model.VolumeProviderDetail{detail}
break
}
}
ret := api_model.VolumeBestRespStruct{}
if len(providerMap) > 0 {
ret.Changed = false
} else {
ret.Changed = true
ret.VolumeType = dbmodel.ShareFileVolumeType.String()
}
httputil.ReturnSuccess(r, w, ret)
}
// VolumeProvider list volume provider
func (t *TenantStruct) VolumeProvider(w http.ResponseWriter, r *http.Request) {
// swagger:operation POST /v2/tenants/{tenant_name}/volume-providers v2 volumeProvider
//
// 查询可用存储驱动模型列表
//
// get volume-providers
//
// ---
// consumes:
// - application/json
// - application/x-protobuf
//
// produces:
// - application/json
// - application/xml
//
// responses:
// default:
// schema:
// description: 统一返回格式
kindFilter := r.FormValue("kind")
storageClasses, err := t.StatusCli.GetStorageClasses()
if err != nil {
httputil.ReturnError(r, w, 500, err.Error())
return
}
var providerList []api_model.VolumeProviderStruct
var providerMap = make(map[string][]api_model.VolumeProviderDetail)
for _, storageClass := range storageClasses.GetList() {
kind := util.ParseVolumeProviderKind(storageClass)
if kind == "" {
logrus.Debugf("not support storageclass: %+v", storageClass)
continue
}
if kindFilter != "" && kind != kindFilter {
continue
}
detail := api_model.VolumeProviderDetail{
Name: storageClass.Name,
Provisioner: storageClass.Provisioner,
ReclaimPolicy: storageClass.ReclaimPolicy,
VolumeBindingMode: storageClass.VolumeBindingMode,
AllowVolumeExpansion: &storageClass.AllowVolumeExpansion,
}
util.HackVolumeProviderDetail(kind, &detail)
if _, ok := providerMap[kind]; ok {
providerMap[kind] = append(providerMap[kind], detail)
} else {
providerMap[kind] = []api_model.VolumeProviderDetail{detail}
}
}
for key, value := range providerMap {
providerList = append(providerList, api_model.VolumeProviderStruct{Kind: key, Provisioner: value})
}
httputil.ReturnSuccess(r, w, providerList)
}
//VolumeDependency VolumeDependency
func (t *TenantStruct) VolumeDependency(w http.ResponseWriter, r *http.Request) {
switch r.Method {

View File

@ -0,0 +1,83 @@
// Copyright (C) 2014-2018 Goodrain Co., Ltd.
// RAINBOND, Application Management Platform
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package handler
import (
"github.com/Sirupsen/logrus"
"github.com/goodrain/rainbond/db"
dbmodel "github.com/goodrain/rainbond/db/model"
"github.com/goodrain/rainbond/worker/client"
"github.com/goodrain/rainbond/worker/server/pb"
// pb "github.com/goodrain/rainibond/worker/server/pb"
)
//VolumeTypeHandler LicenseAction
type VolumeTypeHandler interface {
VolumeTypeVar(action string, vtm *dbmodel.TenantServiceVolumeType) error
GetAllVolumeTypes() ([]*dbmodel.TenantServiceVolumeType, error)
GetVolumeTypeByType(volumeType string) (*dbmodel.TenantServiceVolumeType, error)
GetAllStorageClasses() ([]*pb.StorageClassDetail, error)
}
var defaultVolumeTypeHandler VolumeTypeHandler
//CreateVolumeTypeManger create VolumeType manager
func CreateVolumeTypeManger(statusCli *client.AppRuntimeSyncClient) *VolumeTypeAction {
return &VolumeTypeAction{statusCli: statusCli}
}
//GetVolumeTypeHandler get volumeType handler
func GetVolumeTypeHandler() VolumeTypeHandler {
return defaultVolumeTypeHandler
}
// VolumeTypeAction action
type VolumeTypeAction struct {
statusCli *client.AppRuntimeSyncClient
}
// VolumeTypeVar volume type crud
func (vta *VolumeTypeAction) VolumeTypeVar(action string, vtm *dbmodel.TenantServiceVolumeType) error {
switch action {
case "add":
logrus.Debug("add volumeType")
case "update":
logrus.Debug("update volumeType")
}
return nil
}
// GetAllVolumeTypes get all volume types
func (vta *VolumeTypeAction) GetAllVolumeTypes() ([]*dbmodel.TenantServiceVolumeType, error) {
return db.GetManager().VolumeTypeDao().GetAllVolumeTypes()
}
// GetVolumeTypeByType get volume type by type
func (vta *VolumeTypeAction) GetVolumeTypeByType(volumtType string) (*dbmodel.TenantServiceVolumeType, error) {
return db.GetManager().VolumeTypeDao().GetVolumeTypeByType(volumtType)
}
// GetAllStorageClasses get all storage class
func (vta *VolumeTypeAction) GetAllStorageClasses() ([]*pb.StorageClassDetail, error) {
sces, err := vta.statusCli.GetStorageClasses()
if err != nil {
return nil, err
}
return sces.List, nil
}

View File

@ -73,6 +73,8 @@ func InitHandle(conf option.Config, statusCli *client.AppRuntimeSyncClient) erro
defaultAppRestoreHandler = NewAppRestoreHandler()
defPodHandler = NewPodHandler(statusCli)
defaultVolumeTypeHandler = CreateVolumeTypeManger(statusCli)
return nil
}

View File

@ -32,10 +32,20 @@ type VolumeBestRespStruct struct {
VolumeType string `json:"volume_type"`
}
// VolumeProviderStruct volume provider struct
type VolumeProviderStruct struct {
Kind string `json:"kind"`
Provisioner []VolumeProviderDetail `json:"provisioner"`
// VolumeOptionsStruct volume option struct
type VolumeOptionsStruct struct {
VolumeType string `json:"volume_type"`
NameShow string `json:"name_show"`
VolumeProviderName string `json:"volume_provider_name"`
CapacityValidation map[string]interface{} `json:"capacity_validation"`
Description string `json:"description"`
AccessMode []string `json:"access_mode"`
SharePolicy []string `json:"share_policy"` //共享模式
BackupPolicy []string `json:"backup_policy"` // 备份策略
ReclaimPolicy string `json:"reclaim_policy"` // 回收策略,delete, retain, recyle
VolumeBindingMode string `json:"volume_binding_mode"` // 绑定模式,Immediate,WaitForFirstConsumer
AllowVolumeExpansion *bool `json:"allow_volume_expansion"` // 是否支持扩展
Sort int `json:"sort"` // 排序
}
// VolumeProviderDetail volume provider detail
@ -74,7 +84,7 @@ type AddVolumeStruct struct {
//存储类型share,local,tmpfs
// in: body
// required: true
VolumeType string `json:"volume_type" validate:"volume_type|required|in:share-file,local,memoryfs,config-file,ceph-rbd,alicloud-disk"`
VolumeType string `json:"volume_type" validate:"volume_type|required"`
// 存储名称(同一个应用唯一)
// in: body
// required: true

View File

@ -1,8 +1,10 @@
package util
import (
"github.com/Sirupsen/logrus"
"strings"
"encoding/json"
api_model "github.com/goodrain/rainbond/api/model"
dbmodel "github.com/goodrain/rainbond/db/model"
v1 "github.com/goodrain/rainbond/worker/appm/types/v1"
@ -35,31 +37,30 @@ func SetVolumeDefaultValue(info *dbmodel.TenantServiceVolume) {
}
}
// ParseVolumeProviderKind parse volume provider kind
func ParseVolumeProviderKind(detail *pb.StorageClassDetail) string {
volumeType := transferVolumeProviderName2Kind(detail.Name, detail.Provisioner, detail.Parameters)
// ParseVolumeOptionType parse volume Option name show and volume type
func ParseVolumeOptionType(detail *pb.StorageClassDetail) string {
volumeType := transferVolumeOptionType(detail.Name, detail.Provisioner, detail.Parameters)
if volumeType != nil {
return volumeType.String()
}
return ""
return "unknown"
}
func transferVolumeProviderName2Kind(name string, opts ...interface{}) *dbmodel.VolumeType {
func transferVolumeOptionType(name string, opts ...interface{}) *dbmodel.VolumeType {
if name == v1.RainbondStatefuleShareStorageClass {
return &dbmodel.ShareFileVolumeType
}
if name == v1.RainbondStatefuleLocalStorageClass {
return &dbmodel.LocalVolumeType
}
if len(opts) > 0 {
return transferCustomVolumeProviderName2Kind(opts...)
}
return nil
vt := dbmodel.MakeNewVolume(name)
return &vt
}
// opts[0]: kind is storageClass's provisioner
// opts[1]: parameters is storageClass's parameter
func transferCustomVolumeProviderName2Kind(opts ...interface{}) *dbmodel.VolumeType {
func transferCustomVolumeOptionName2Kind(opts ...interface{}) *dbmodel.VolumeType {
if len(opts) != 2 {
return nil
}
@ -77,16 +78,98 @@ func transferCustomVolumeProviderName2Kind(opts ...interface{}) *dbmodel.VolumeT
return nil
}
// HackVolumeProviderDetail hack volume provider detail, like accessMode, sharePolicy, backupPolicy
func HackVolumeProviderDetail(kind string, detail *api_model.VolumeProviderDetail) {
// HackVolumeOptionDetailFromDB hack volumeOptionDetail from db
func HackVolumeOptionDetailFromDB(detail *api_model.VolumeOptionsStruct, data *dbmodel.TenantServiceVolumeType) {
if data != nil {
detail.Description = data.Description
detail.NameShow = data.NameShow
detail.VolumeProviderName = data.VolumeProviderName
if err := json.Unmarshal([]byte(data.CapacityValidation), &detail.CapacityValidation); err != nil {
logrus.Warnf("unmarshal volumetype's capacityValidation error: %s, set capacityValidation to default", err.Error())
detail.CapacityValidation = defaultcapacityValidation
}
detail.AccessMode = strings.Split(data.AccessMode, ",")
detail.SharePolicy = strings.Split(data.SharePolicy, ",")
detail.BackupPolicy = strings.Split(data.BackupPolicy, ",")
detail.ReclaimPolicy = data.ReclaimPolicy
detail.VolumeBindingMode = data.VolumeBindingMode
detail.AllowVolumeExpansion = &data.AllowVolumeExpansion
detail.Sort = data.Sort
}
}
var defaultcapacityValidation map[string]interface{}
func init() {
capacityValidation := make(map[string]interface{})
capacityValidation["min"] = 1
capacityValidation["required"] = false
capacityValidation["max"] = 999999999
}
// HackVolumeOptionDetail hack volume Option detail, like accessMode, sharePolicy, backupPolicy
func HackVolumeOptionDetail(volumeType string, detail *api_model.VolumeOptionsStruct, more ...interface{}) {
/*
RWO - ReadWriteOnce
ROX - ReadOnlyMany
RWX - ReadWriteMany
*/
detail.AccessMode = append(detail.AccessMode, hackVolumeProviderAccessMode(kind)...)
detail.SharePolicy = append(detail.SharePolicy, hackVolumeProviderSharePolicy(kind)...)
detail.BackupPolicy = append(detail.BackupPolicy, hackVolumeProviderBackupPolicy(kind)...)
detail.AccessMode = append(detail.AccessMode, hackVolumeOptionAccessMode(volumeType)...)
detail.SharePolicy = append(detail.SharePolicy, hackVolumeOptionSharePolicy(volumeType)...)
detail.BackupPolicy = append(detail.BackupPolicy, hackVolumeOptionBackupPolicy(volumeType)...)
detail.CapacityValidation = hackVolumeOptionCapacityValidation(volumeType)
detail.Description = hackVolumeOptionDesc(volumeType)
detail.NameShow = hackVolumeOptionNameShow(volumeType)
if len(more) == 4 {
detail.VolumeProviderName = more[0].(string)
detail.ReclaimPolicy = more[1].(string)
detail.VolumeBindingMode = more[2].(string)
AllowVolumeExpansion := more[3].(bool)
detail.AllowVolumeExpansion = &AllowVolumeExpansion
}
}
func hackVolumeOptionNameShow(volumeType string) string {
nameShow := volumeType
if volumeType == "alicloud-disk-available" {
nameShow = "阿里云盘(智能选择)"
} else if volumeType == "alicloud-disk-common" {
nameShow = "阿里云盘(基础)"
} else if volumeType == "alicloud-disk-efficiency" {
nameShow = "阿里云盘(高效)"
} else if volumeType == "alicloud-disk-ssd" {
nameShow = "阿里云盘SSD"
}
return nameShow
}
func hackVolumeOptionDesc(vt string) string {
volumeType := dbmodel.VolumeType(vt)
switch volumeType {
case dbmodel.ShareFileVolumeType:
return "default分布式文件存储可租户内共享挂载适用于所有类型应用"
case dbmodel.LocalVolumeType:
return "default本地存储设备适用于有状态数据库服务"
case dbmodel.MemoryFSVolumeType:
return "default基于内存的存储设备容量由内存量限制。应用重启数据即丢失适用于高速暂存数据"
default:
return ""
}
}
func hackVolumeOptionCapacityValidation(volumeType string) map[string]interface{} {
data := make(map[string]interface{})
data["required"] = true
data["default"] = 1
if strings.HasPrefix(volumeType, "alicloud-disk") {
data["min"] = 20
data["default"] = 20
data["max"] = 32768 // [ali-cloud-disk usage limit](https://help.aliyun.com/document_detail/25412.html?spm=5176.2020520101.0.0.41d84df5faliP4)
} else {
data["min"] = 1
data["max"] = 999999999
}
return data
}
/*
@ -118,32 +201,28 @@ ScaleIO | ✓ | ✓ | -
StorageOS | | - | -
*/
func hackVolumeProviderAccessMode(kind string) []string {
volumeType := dbmodel.VolumeType(kind)
func hackVolumeOptionAccessMode(vt string) []string {
volumeType := dbmodel.VolumeType(vt)
switch volumeType {
case dbmodel.ShareFileVolumeType:
return []string{"RWO", "ROX", "RWX"}
case dbmodel.LocalVolumeType:
return []string{"RWO", "ROX", "RWX"}
case dbmodel.CephRBDVolumeType:
return []string{"RWO", "ROX"}
case dbmodel.ConfigFileVolumeType:
return []string{"ROX"}
case dbmodel.MemoryFSVolumeType:
return []string{"ROX"}
case dbmodel.AliCloudVolumeType:
return []string{"RWO"}
default:
return []string{"RWO"}
}
}
// TODO finish volume share policy
func hackVolumeProviderSharePolicy(kind string) []string {
func hackVolumeOptionSharePolicy(volumeType string) []string {
return []string{"exclusive"}
}
// TODO finish vollume backup policy
func hackVolumeProviderBackupPolicy(kind string) []string {
func hackVolumeOptionBackupPolicy(volumeType string) []string {
return []string{"exclusive"}
}

View File

@ -62,6 +62,13 @@ type AppDao interface {
DeleteModelByEventId(eventID string) error
}
// VolumeTypeDao volume type dao
type VolumeTypeDao interface {
Dao
GetAllVolumeTypes() ([]*model.TenantServiceVolumeType, error)
GetVolumeTypeByType(vt string) (*model.TenantServiceVolumeType, error)
}
//LicenseDao LicenseDao
type LicenseDao interface {
Dao

View File

@ -33,6 +33,7 @@ type Manager interface {
CloseManager() error
Begin() *gorm.DB
EnsureEndTransactionFunc() func(tx *gorm.DB)
VolumeTypeDao() dao.VolumeTypeDao
LicenseDao() dao.LicenseDao
AppDao() dao.AppDao
TenantDao() dao.TenantDao

View File

@ -20,11 +20,13 @@ package db
import (
"context"
"encoding/json"
"fmt"
"github.com/testcontainers/testcontainers-go"
"testing"
"time"
"github.com/testcontainers/testcontainers-go"
dbconfig "github.com/goodrain/rainbond/db/config"
"github.com/goodrain/rainbond/db/model"
"github.com/goodrain/rainbond/util"
@ -296,3 +298,128 @@ func TestGetCertificateByID(t *testing.T) {
}
fmt.Println(cert)
}
var shareFileVolumeType = &model.TenantServiceVolumeType{
VolumeType: "share-file",
NameShow: "共享存储(文件)",
Description: "分布式文件存储,可租户内共享挂载,适用于所有类型应用",
// CapacityValidation: string(bs),
ReclaimPolicy: "Retain",
VolumeProviderName: "rainbondsssc",
VolumeBindingMode: "Immediate",
AllowVolumeExpansion: false,
BackupPolicy: "exclusive",
SharePolicy: "exclusive",
AccessMode: "RWO,ROX,RWX",
}
var localVolumeType = &model.TenantServiceVolumeType{
VolumeType: "local",
NameShow: "本地存储",
Description: "本地存储设备,适用于有状态数据库服务",
// CapacityValidation: string(bs),
ReclaimPolicy: "Retain",
VolumeProviderName: "rainbondslsc",
VolumeBindingMode: "Immediate",
AllowVolumeExpansion: false,
BackupPolicy: "exclusive",
SharePolicy: "exclusive",
AccessMode: "RWO,ROX,RWX",
}
var memoryFSVolumeType = &model.TenantServiceVolumeType{
VolumeType: "memoryfs",
NameShow: "内存文件存储",
Description: "基于内存的存储设备,容量由内存量限制。应用重启数据即丢失,适用于高速暂存数据",
// CapacityValidation: string(bs),
ReclaimPolicy: "Retain",
VolumeProviderName: "rainbondslsc",
VolumeBindingMode: "Immediate",
AllowVolumeExpansion: false,
BackupPolicy: "exclusive",
SharePolicy: "exclusive",
AccessMode: "RWO,ROX,RWX",
}
var alicloudDiskAvailableVolumeType = &model.TenantServiceVolumeType{
VolumeType: "alicloud-disk-available",
NameShow: "阿里云盘(智能选择)",
Description: "阿里云智能选择云盘。会通过高效云盘、SSD、基础云盘的顺序依次尝试创建当前阿里云区域支持的云盘类型",
// CapacityValidation: string(bs),
ReclaimPolicy: "Delete",
VolumeProviderName: "alicloud-disk-available",
VolumeBindingMode: "Immediate",
AllowVolumeExpansion: false,
BackupPolicy: "exclusive",
SharePolicy: "exclusive",
AccessMode: "RWO",
Sort: 10,
}
var alicloudDiskcommonVolumeType = &model.TenantServiceVolumeType{
VolumeType: "alicloud-disk-common",
NameShow: "阿里云盘(基础)",
Description: "阿里云普通基础云盘。最小限额5G",
// CapacityValidation: string(bs),
ReclaimPolicy: "Delete",
VolumeProviderName: "alicloud-disk-common",
VolumeBindingMode: "Immediate",
AllowVolumeExpansion: false,
BackupPolicy: "exclusive",
SharePolicy: "exclusive",
AccessMode: "RWO",
Sort: 13,
}
var alicloudDiskEfficiencyVolumeType = &model.TenantServiceVolumeType{
VolumeType: "alicloud-disk-efficiency",
NameShow: "阿里云盘(高效)",
Description: "阿里云高效云盘。最小限额20G",
// CapacityValidation: string(bs),
ReclaimPolicy: "Delete",
VolumeProviderName: "alicloud-disk-efficiency",
VolumeBindingMode: "Immediate",
AllowVolumeExpansion: false,
BackupPolicy: "exclusive",
SharePolicy: "exclusive",
AccessMode: "RWO",
Sort: 11,
}
var alicloudDiskeSSDVolumeType = &model.TenantServiceVolumeType{
VolumeType: "alicloud-disk-ssd",
NameShow: "阿里云盘SSD",
Description: "阿里云SSD类型云盘。最小限额20G",
// CapacityValidation: string(bs),
ReclaimPolicy: "Delete",
VolumeProviderName: "alicloud-disk-ssd",
VolumeBindingMode: "Immediate",
AllowVolumeExpansion: false,
BackupPolicy: "exclusive",
SharePolicy: "exclusive",
AccessMode: "RWO",
Sort: 12,
}
func TestVolumeType(t *testing.T) {
capacityValidation := make(map[string]interface{})
capacityValidation["required"] = true
capacityValidation["default"] = 20
capacityValidation["min"] = 20
capacityValidation["max"] = 32768 // [ali-cloud-disk usage limit](https://help.aliyun.com/document_detail/25412.html?spm=5176.2020520101.0.0.41d84df5faliP4)
bs, _ := json.Marshal(capacityValidation)
shareFileVolumeType.CapacityValidation = string(bs)
localVolumeType.CapacityValidation = string(bs)
memoryFSVolumeType.CapacityValidation = string(bs)
alicloudDiskeSSDVolumeType.CapacityValidation = string(bs)
if err := CreateManager(dbconfig.Config{
MysqlConnectionInfo: "oc6Poh:noot6Mea@tcp(192.168.2.203:3306)/region",
DBType: "mysql",
}); err != nil {
t.Fatal(err)
}
if err := GetManager().VolumeTypeDao().AddModel(alicloudDiskeSSDVolumeType); err != nil {
t.Error(err)
} else {
t.Log("yes")
}
}

View File

@ -345,6 +345,11 @@ var CephRBDVolumeType VolumeType = "ceph-rbd"
// AliCloudVolumeType alicloud volume type
var AliCloudVolumeType VolumeType = "alicloud-disk"
// MakeNewVolume make volumeType
func MakeNewVolume(name string) VolumeType {
return VolumeType(name)
}
func (vt VolumeType) String() string {
return string(vt)
}
@ -356,7 +361,7 @@ type TenantServiceVolume struct {
//服务类型
Category string `gorm:"column:category;size:50" json:"category"`
//存储类型share,local,tmpfs
VolumeType string `gorm:"column:volume_type;size:20" json:"volume_type"`
VolumeType string `gorm:"column:volume_type;size:64" json:"volume_type"`
//存储名称
VolumeName string `gorm:"column:volume_name;size:40" json:"volume_name"`
//主机地址

53
db/model/volumetype.go Normal file
View File

@ -0,0 +1,53 @@
// Copyright (C) 2014-2018 Goodrain Co., Ltd.
// RAINBOND, Application Management Platform
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package model
// TenantServiceVolumeType tenant service volume type
type TenantServiceVolumeType struct {
Model
// 存储类型
VolumeType string `gorm:"column:volume_type; size:64" json:"volume_type"`
// 别名
NameShow string `gorm:"column:name_show; size:64" json:"name_show"`
// 存储大小校验条件
CapacityValidation string `gorm:"column:capacity_validation; size:1024" json:"capacity_validation"`
// 描述
Description string `gorm:"column:description; size:1024" json:"description"`
// 回收策略
ReclaimPolicy string `gorm:"column:reclaim_policy; size:20" json:"reclaim_policy"`
// 提供者名称
VolumeProviderName string `gorm:"column:volume_provider_name; size:64" json:"volume_provider_name"`
// 绑定模式
VolumeBindingMode string `gorm:"column:volume_binding_mode; size:20" json:"volume_binding_mode"`
// 是否可扩容
AllowVolumeExpansion bool `gorm:"column:allow_volume_expansion" json:"allow_volume_expansion"`
// 备份策略
BackupPolicy string `gorm:"column:backup_policy; size:128" json:"backup_policy"`
//读写模式
AccessMode string `gorm:"column:access_mode; size:128" json:"access_mode"`
// 分享策略
SharePolicy string `gorm:"share_policy; size:128" json:"share_policy"`
// 排序
Sort int `gorm:"sort; default:9999" json:"sort"`
}
// TableName 表名
func (t *TenantServiceVolumeType) TableName() string {
return "tenant_services_volume_type"
}

View File

@ -0,0 +1,69 @@
// Copyright (C) 2014-2018 Goodrain Co., Ltd.
// RAINBOND, Application Management Platform
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package dao
import (
"fmt"
"github.com/goodrain/rainbond/db/model"
"github.com/jinzhu/gorm"
)
//VolumeTypeDaoImpl license model 管理
type VolumeTypeDaoImpl struct {
DB *gorm.DB
}
//AddModel AddModel
func (vtd *VolumeTypeDaoImpl) AddModel(mo model.Interface) error {
volumeType := mo.(*model.TenantServiceVolumeType)
var oldVolumeType model.TenantServiceVolumeType
if ok := vtd.DB.Where("volume_type=?", volumeType.VolumeType).Find(&oldVolumeType).RecordNotFound(); ok {
if err := vtd.DB.Create(volumeType).Error; err != nil {
return err
}
} else {
return fmt.Errorf("volumeType is exist")
}
return nil
}
// UpdateModel update model
func (vtd *VolumeTypeDaoImpl) UpdateModel(mo model.Interface) error {
return nil
}
// GetAllVolumeTypes get all volumeTypes
func (vtd *VolumeTypeDaoImpl) GetAllVolumeTypes() ([]*model.TenantServiceVolumeType, error) {
var volumeTypes []*model.TenantServiceVolumeType
if err := vtd.DB.Find(&volumeTypes).Error; err != nil {
return nil, err
}
return volumeTypes, nil
}
// GetVolumeTypeByType get volume type by type
func (vtd *VolumeTypeDaoImpl) GetVolumeTypeByType(vt string) (*model.TenantServiceVolumeType, error) {
var volumeType *model.TenantServiceVolumeType
if err := vtd.DB.Where("volume_type=?", vt).Find(&volumeType).Error; err != nil {
return nil, err
}
return volumeType, nil
}

View File

@ -24,6 +24,13 @@ import (
"github.com/jinzhu/gorm"
)
// VolumeTypeDao volumeTypeDao
func (m *Manager) VolumeTypeDao() dao.VolumeTypeDao {
return &mysqldao.VolumeTypeDaoImpl{
DB: m.db,
}
}
//LicenseDao LicenseDao
func (m *Manager) LicenseDao() dao.LicenseDao {
return &mysqldao.LicenseDaoImpl{

View File

@ -132,6 +132,9 @@ func (m *Manager) RegisterTableModel() {
m.models = append(m.models, &model.Endpoint{})
m.models = append(m.models, &model.ThirdPartySvcDiscoveryCfg{})
m.models = append(m.models, &model.GwRuleConfig{})
// volumeType
m.models = append(m.models, &model.TenantServiceVolumeType{})
}
//CheckTable check and create tables

View File

@ -231,3 +231,15 @@ func (m *MockStorer) GetStorageClasses() []v1.StorageClass {
func (mr *MockStorerMockRecorder) GetStorageClasses() *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageClasses", reflect.TypeOf((*MockStorer)(nil).GetStorageClasses))
}
// GetServiceClaims mocks base method
func (m *MockStorer) GetServiceClaims(tenantID, serviceID string) []v10.PersistentVolumeClaim {
ret := m.ctrl.Call(m, "GetServiceClaims", tenantID, serviceID)
ret0, _ := ret[0].([]v10.PersistentVolumeClaim)
return ret0
}
// GetServiceClaims indicates an expected call of GetServiceClaims
func (mr *MockStorerMockRecorder) GetServiceClaims(tenantID, serviceID interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceClaims", reflect.TypeOf((*MockStorer)(nil).GetServiceClaims), tenantID, serviceID)
}

View File

@ -67,6 +67,7 @@ type Storer interface {
UnRegistPodUpdateListener(string)
InitOneThirdPartService(service *model.TenantServices) error
GetStorageClasses() []v1.StorageClass
GetServiceClaims(tenantID, serviceID string) []corev1.PersistentVolumeClaim
}
// EventType type of event associated with an informer
@ -1126,3 +1127,13 @@ func (a *appRuntimeStore) GetStorageClasses() []v1.StorageClass {
}
return sclist
}
func (a *appRuntimeStore) GetServiceClaims(tenantID, serviceID string) []corev1.PersistentVolumeClaim {
// claims := as.GetClaims()// TODO 临时使用client直接获取PVC后续换成store中获取
claimList, err := a.clientset.CoreV1().PersistentVolumeClaims(tenantID).List(metav1.ListOptions{LabelSelector: fmt.Sprintf("service_id=%s", serviceID)})
if err != nil {
logrus.Errorf("get claims error: %s", err.Error())
return nil
}
return claimList.Items
}

View File

@ -0,0 +1,101 @@
// RAINBOND, Application Management Platform
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package volume
import (
"fmt"
"path"
"github.com/Sirupsen/logrus"
"github.com/goodrain/rainbond/util"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ConfigFileVolume config file volume struct
type ConfigFileVolume struct {
Base
}
// CreateVolume config file volume create volume
func (v *ConfigFileVolume) CreateVolume(define *Define) error {
// environment variables
configs := make(map[string]string)
envs, err := createEnv(v.as, v.dbmanager)
if err != nil {
logrus.Warningf("error creating environment variables: %v", err)
} else {
for _, env := range *envs {
configs[env.Name] = env.Value
}
}
cf, err := v.dbmanager.TenantServiceConfigFileDao().GetByVolumeName(v.as.ServiceID, v.svm.VolumeName)
if err != nil {
logrus.Errorf("error getting config file by volume name(%s): %v", v.svm.VolumeName, err)
return fmt.Errorf("error getting config file by volume name(%s): %v", v.svm.VolumeName, err)
}
cmap := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: util.NewUUID(),
Namespace: v.as.TenantID,
Labels: v.as.GetCommonLabels(),
},
Data: make(map[string]string),
}
cmap.Data[path.Base(v.svm.VolumePath)] = util.ParseVariable(cf.FileContent, configs)
v.as.SetConfigMap(cmap)
define.SetVolumeCMap(cmap, path.Base(v.svm.VolumePath), v.svm.VolumePath, false)
return nil
}
// CreateDependVolume config file volume create depend volume
func (v *ConfigFileVolume) CreateDependVolume(define *Define) error {
configs := make(map[string]string)
envs, err := createEnv(v.as, v.dbmanager)
if err != nil {
logrus.Warningf("error creating environment variables: %v", err)
} else {
for _, env := range *envs {
configs[env.Name] = env.Value
}
}
_, err = v.dbmanager.TenantServiceVolumeDao().GetVolumeByServiceIDAndName(v.smr.DependServiceID, v.smr.VolumeName)
if err != nil {
return fmt.Errorf("error getting TenantServiceVolume according to serviceID(%s) and volumeName(%s): %v",
v.smr.DependServiceID, v.smr.VolumeName, err)
}
cf, err := v.dbmanager.TenantServiceConfigFileDao().GetByVolumeName(v.smr.DependServiceID, v.smr.VolumeName)
if err != nil {
return fmt.Errorf("error getting TenantServiceConfigFile according to volumeName(%s): %v", v.smr.VolumeName, err)
}
cmap := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: util.NewUUID(),
Namespace: v.as.TenantID,
Labels: v.as.GetCommonLabels(),
},
Data: make(map[string]string),
}
cmap.Data[path.Base(v.smr.VolumePath)] = util.ParseVariable(cf.FileContent, configs)
v.as.SetConfigMap(cmap)
define.SetVolumeCMap(cmap, path.Base(v.smr.VolumePath), v.smr.VolumePath, false)
return nil
}

View File

@ -0,0 +1,65 @@
// RAINBOND, Application Management Platform
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package volume
import (
"fmt"
"github.com/goodrain/rainbond/node/nodem/client"
v1 "github.com/goodrain/rainbond/worker/appm/types/v1"
corev1 "k8s.io/api/core/v1"
)
// LocalVolume local volume struct
type LocalVolume struct {
Base
}
// CreateVolume local volume create volume
func (v *LocalVolume) CreateVolume(define *Define) error {
volumeMountName := fmt.Sprintf("manual%d", v.svm.ID)
volumeMountPath := v.svm.VolumePath
volumeReadOnly := v.svm.IsReadOnly
statefulset := v.as.GetStatefulSet()
labels := v.as.GetCommonLabels(map[string]string{"volume_name": v.svm.VolumeName, "volume_path": volumeMountPath, "version": v.as.DeployVersion})
annotations := map[string]string{"volume_name": v.svm.VolumeName}
claim := newVolumeClaim(volumeMountName, volumeMountPath, v.svm.AccessMode, v1.RainbondStatefuleLocalStorageClass, v.svm.VolumeCapacity, labels, annotations)
claim.Annotations = map[string]string{
client.LabelOS: func() string {
if v.as.IsWindowsService {
return "windows"
}
return "linux"
}(),
}
statefulset.Spec.VolumeClaimTemplates = append(statefulset.Spec.VolumeClaimTemplates, *claim)
vm := corev1.VolumeMount{
Name: volumeMountName,
MountPath: volumeMountPath,
ReadOnly: volumeReadOnly,
}
define.volumeMounts = append(define.volumeMounts, vm)
return nil
}
// CreateDependVolume empty func
func (v *LocalVolume) CreateDependVolume(define *Define) error {
return nil
}

View File

@ -0,0 +1,67 @@
// RAINBOND, Application Management Platform
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package volume
import (
"fmt"
"github.com/Sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
)
// MemoryFSVolume memory fs volume struct
type MemoryFSVolume struct {
Base
}
// CreateVolume memory fs volume create volume
func (v *MemoryFSVolume) CreateVolume(define *Define) error {
volumeMountName := fmt.Sprintf("mnt%d", v.svm.ID)
volumeMountPath := v.svm.VolumePath
volumeReadOnly := false
if volumeMountPath != "" {
logrus.Warningf("service[%s]'s mount path is empty, skip it", v.version.ServiceID)
return nil
}
for _, m := range define.volumeMounts {
if m.MountPath == volumeMountPath {
logrus.Warningf("found the same mount path: %s, skip it", volumeMountPath)
return nil
}
}
name := fmt.Sprintf("manual%d", v.svm.ID)
vo := corev1.Volume{Name: name}
vo.EmptyDir = &corev1.EmptyDirVolumeSource{
Medium: corev1.StorageMediumMemory,
}
define.volumes = append(define.volumes, vo)
vm := corev1.VolumeMount{
MountPath: volumeMountPath,
Name: volumeMountName,
ReadOnly: volumeReadOnly,
SubPath: "",
}
define.volumeMounts = append(define.volumeMounts, vm)
return nil
}
// CreateDependVolume empty func
func (v *MemoryFSVolume) CreateDependVolume(define *Define) error {
return nil
}

View File

@ -0,0 +1,80 @@
// RAINBOND, Application Management Platform
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package volume
import (
"fmt"
"github.com/Sirupsen/logrus"
"github.com/goodrain/rainbond/node/nodem/client"
corev1 "k8s.io/api/core/v1"
)
// OtherVolume ali cloud volume struct
type OtherVolume struct {
Base
}
// CreateVolume ceph rbd volume create volume
func (v *OtherVolume) CreateVolume(define *Define) error {
if v.svm.VolumeCapacity <= 0 {
return fmt.Errorf("volume capcacity is %d, must be greater than zero", v.svm.VolumeCapacity)
}
volumeMountName := fmt.Sprintf("manual%d", v.svm.ID)
volumeMountPath := v.svm.VolumePath
volumeReadOnly := v.svm.IsReadOnly
labels := v.as.GetCommonLabels(map[string]string{"volume_name": v.svm.VolumeName, "version": v.as.DeployVersion})
annotations := map[string]string{"volume_name": v.svm.VolumeName}
annotations["reclaim_policy"] = v.svm.ReclaimPolicy
annotations["volume_path"] = volumeMountPath
claim := newVolumeClaim(volumeMountName, volumeMountPath, v.svm.AccessMode, v.svm.VolumeProviderName, v.svm.VolumeCapacity, labels, annotations)
logrus.Debugf("storage class is : %s, claim value is : %s", v.svm.VolumeProviderName, claim.GetName())
v.as.SetClaim(claim) // store claim to appService
claim.Annotations = map[string]string{
client.LabelOS: func() string {
if v.as.IsWindowsService {
return "windows"
}
return "linux"
}(),
}
statefulset := v.as.GetStatefulSet() //有状态组件
if statefulset != nil {
statefulset.Spec.VolumeClaimTemplates = append(statefulset.Spec.VolumeClaimTemplates, *claim)
} else {
vo := corev1.Volume{Name: volumeMountName}
vo.PersistentVolumeClaim = &corev1.PersistentVolumeClaimVolumeSource{ClaimName: claim.GetName(), ReadOnly: volumeReadOnly}
define.volumes = append(define.volumes, vo)
logrus.Warnf("service[%s] is not stateful, mount volume by k8s volume.PersistenVolumeClaim[%s]", v.svm.ServiceID, claim.GetName())
}
vm := corev1.VolumeMount{
Name: volumeMountName,
MountPath: volumeMountPath,
ReadOnly: volumeReadOnly,
}
define.volumeMounts = append(define.volumeMounts, vm)
return nil
}
// CreateDependVolume create depend volume
func (v *OtherVolume) CreateDependVolume(define *Define) error {
return nil
}

View File

@ -0,0 +1,126 @@
// RAINBOND, Application Management Platform
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package volume
import (
"fmt"
"os"
"github.com/Sirupsen/logrus"
"github.com/goodrain/rainbond/util"
v1 "github.com/goodrain/rainbond/worker/appm/types/v1"
corev1 "k8s.io/api/core/v1"
)
// ShareFileVolume nfs volume struct
type ShareFileVolume struct {
Base
}
// CreateVolume share file volume create volume
func (v *ShareFileVolume) CreateVolume(define *Define) error {
err := util.CheckAndCreateDir(v.svm.HostPath)
if err != nil {
return fmt.Errorf("create host path %s error,%s", v.svm.HostPath, err.Error())
}
os.Chmod(v.svm.HostPath, 0777)
volumeMountName := fmt.Sprintf("manual%d", v.svm.ID)
volumeMountPath := v.svm.VolumePath
volumeReadOnly := v.svm.IsReadOnly
var vm *corev1.VolumeMount
if v.as.GetStatefulSet() != nil {
statefulset := v.as.GetStatefulSet()
labels := v.as.GetCommonLabels(map[string]string{"volume_name": volumeMountName, "volume_path": volumeMountPath})
annotations := map[string]string{"volume_name": v.svm.VolumeName}
claim := newVolumeClaim(volumeMountName, volumeMountPath, v.svm.AccessMode, v1.RainbondStatefuleShareStorageClass, v.svm.VolumeCapacity, labels, annotations)
statefulset.Spec.VolumeClaimTemplates = append(statefulset.Spec.VolumeClaimTemplates, *claim)
vm = &corev1.VolumeMount{
Name: volumeMountName,
MountPath: volumeMountPath,
ReadOnly: volumeReadOnly,
}
} else {
for _, m := range define.volumeMounts {
if m.MountPath == volumeMountPath { // TODO move to prepare
logrus.Warningf("found the same mount path: %s, skip it", volumeMountPath)
return nil
}
}
hostPath := v.svm.HostPath
if v.as.IsWindowsService {
hostPath = RewriteHostPathInWindows(hostPath)
}
vo := corev1.Volume{Name: volumeMountName}
hostPathType := corev1.HostPathDirectoryOrCreate
vo.HostPath = &corev1.HostPathVolumeSource{
Path: hostPath,
Type: &hostPathType,
}
define.volumes = append(define.volumes, vo)
vm = &corev1.VolumeMount{
Name: volumeMountName,
MountPath: volumeMountPath,
ReadOnly: volumeReadOnly,
}
}
if vm != nil {
define.volumeMounts = append(define.volumeMounts, *vm)
}
return nil
}
// CreateDependVolume create depend volume
func (v *ShareFileVolume) CreateDependVolume(define *Define) error {
volumeMountName := fmt.Sprintf("mnt%d", v.smr.ID)
volumeMountPath := v.smr.VolumePath
volumeReadOnly := false
for _, m := range define.volumeMounts {
if m.MountPath == volumeMountPath {
logrus.Warningf("found the same mount path: %s, skip it", volumeMountPath)
return nil
}
}
err := util.CheckAndCreateDir(v.smr.HostPath)
if err != nil {
return fmt.Errorf("create host path %s error,%s", v.smr.HostPath, err.Error())
}
hostPath := v.smr.HostPath
if v.as.IsWindowsService {
hostPath = RewriteHostPathInWindows(hostPath)
}
vo := corev1.Volume{Name: volumeMountName}
hostPathType := corev1.HostPathDirectoryOrCreate
vo.HostPath = &corev1.HostPathVolumeSource{
Path: hostPath,
Type: &hostPathType,
}
define.volumes = append(define.volumes, vo)
vm := corev1.VolumeMount{
Name: volumeMountName,
MountPath: volumeMountPath,
ReadOnly: volumeReadOnly,
}
define.volumeMounts = append(define.volumeMounts, vm)
return nil
}

View File

@ -30,7 +30,6 @@ import (
"github.com/goodrain/rainbond/db"
"github.com/goodrain/rainbond/db/model"
dbmodel "github.com/goodrain/rainbond/db/model"
"github.com/goodrain/rainbond/node/nodem/client"
"github.com/goodrain/rainbond/util"
v1 "github.com/goodrain/rainbond/worker/appm/types/v1"
corev1 "k8s.io/api/core/v1"
@ -68,13 +67,9 @@ func NewVolumeManager(as *v1.AppService, serviceVolume *model.TenantServiceVolum
v = new(MemoryFSVolume)
case dbmodel.LocalVolumeType.String():
v = new(LocalVolume)
case dbmodel.CephRBDVolumeType.String():
v = new(CephRBDVolume)
case dbmodel.AliCloudVolumeType.String():
v = new(AliCloudVolume)
default:
logrus.Warnf("unknown service volume type: serviceID : %s", as.ServiceID)
return nil
logrus.Warnf("other volume type[%s]", volumeType)
v = new(OtherVolume)
}
v.setBaseInfo(as, serviceVolume, serviceMountR, version, dbmanager)
return v
@ -101,70 +96,9 @@ func prepare() {
// TODO prepare volume info, create volume just create volume and return volumeMount, do not process anything else
}
// ShareFileVolume nfs volume struct
type ShareFileVolume struct {
Base
}
// CreateVolume share file volume create volume
func (v *ShareFileVolume) CreateVolume(define *Define) error {
err := util.CheckAndCreateDir(v.svm.HostPath)
if err != nil {
return fmt.Errorf("create host path %s error,%s", v.svm.HostPath, err.Error())
}
os.Chmod(v.svm.HostPath, 0777)
volumeMountName := fmt.Sprintf("manual%d", v.svm.ID)
volumeMountPath := v.svm.VolumePath
volumeReadOnly := v.svm.IsReadOnly
var vm *corev1.VolumeMount
if v.as.GetStatefulSet() != nil {
statefulset := v.as.GetStatefulSet()
labels := v.as.GetCommonLabels(map[string]string{"volume_name": volumeMountName, "volume_path": volumeMountPath})
annotations := map[string]string{"volume_name": v.svm.VolumeName}
claim := newVolumeClaim(volumeMountName, volumeMountPath, v.svm.AccessMode, v.svm.VolumeCapacity, labels, annotations)
statefulset.Spec.VolumeClaimTemplates = append(statefulset.Spec.VolumeClaimTemplates, *claim)
vm = &corev1.VolumeMount{
Name: volumeMountName,
MountPath: volumeMountPath,
ReadOnly: volumeReadOnly,
}
} else {
for _, m := range define.volumeMounts {
if m.MountPath == volumeMountPath { // TODO move to prepare
logrus.Warningf("found the same mount path: %s, skip it", volumeMountPath)
return nil
}
}
hostPath := v.svm.HostPath
if v.as.IsWindowsService {
hostPath = RewriteHostPathInWindows(hostPath)
}
vo := corev1.Volume{Name: volumeMountName}
hostPathType := corev1.HostPathDirectoryOrCreate
vo.HostPath = &corev1.HostPathVolumeSource{
Path: hostPath,
Type: &hostPathType,
}
define.volumes = append(define.volumes, vo)
vm = &corev1.VolumeMount{
Name: volumeMountName,
MountPath: volumeMountPath,
ReadOnly: volumeReadOnly,
}
}
if vm != nil {
define.volumeMounts = append(define.volumeMounts, *vm)
}
return nil
}
func newVolumeClaim(name, volumePath, accessMode string, capacity int64, labels, annotations map[string]string) *corev1.PersistentVolumeClaim {
// TODO use capacity as resroouceStorage
resourceStorage, _ := resource.ParseQuantity("500Gi")
func newVolumeClaim(name, volumePath, accessMode, storageClassName string, capacity int64, labels, annotations map[string]string) *corev1.PersistentVolumeClaim {
logrus.Debugf("volume annotaion is %+v", annotations)
resourceStorage, _ := resource.ParseQuantity(fmt.Sprintf("%dGi", capacity)) // 统一单位使用G
return &corev1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: name,
@ -173,7 +107,7 @@ func newVolumeClaim(name, volumePath, accessMode string, capacity int64, labels,
},
Spec: corev1.PersistentVolumeClaimSpec{
AccessModes: []corev1.PersistentVolumeAccessMode{parseAccessMode(accessMode)},
StorageClassName: &v1.RainbondStatefuleShareStorageClass,
StorageClassName: &storageClassName,
Resources: corev1.ResourceRequirements{
Requests: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceStorage: resourceStorage,
@ -202,319 +136,6 @@ func parseAccessMode(accessMode string) corev1.PersistentVolumeAccessMode {
}
}
func newVolumeClaim4RBD(name, volumePath, accessMode, storageClassName string, capacity int64, labels, annotations map[string]string) *corev1.PersistentVolumeClaim {
resourceStorage, _ := resource.ParseQuantity(fmt.Sprintf("%dGi", capacity)) // TODO 统一单位使用G
return &corev1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: labels,
Annotations: annotations,
},
Spec: corev1.PersistentVolumeClaimSpec{
AccessModes: []corev1.PersistentVolumeAccessMode{parseAccessMode(accessMode)},
StorageClassName: &storageClassName,
Resources: corev1.ResourceRequirements{
Requests: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceStorage: resourceStorage,
},
},
},
}
}
// CreateDependVolume create depend volume
func (v *ShareFileVolume) CreateDependVolume(define *Define) error {
volumeMountName := fmt.Sprintf("mnt%d", v.smr.ID)
volumeMountPath := v.smr.VolumePath
volumeReadOnly := false
for _, m := range define.volumeMounts {
if m.MountPath == volumeMountPath {
logrus.Warningf("found the same mount path: %s, skip it", volumeMountPath)
return nil
}
}
err := util.CheckAndCreateDir(v.smr.HostPath)
if err != nil {
return fmt.Errorf("create host path %s error,%s", v.smr.HostPath, err.Error())
}
hostPath := v.smr.HostPath
if v.as.IsWindowsService {
hostPath = RewriteHostPathInWindows(hostPath)
}
vo := corev1.Volume{Name: volumeMountName}
hostPathType := corev1.HostPathDirectoryOrCreate
vo.HostPath = &corev1.HostPathVolumeSource{
Path: hostPath,
Type: &hostPathType,
}
define.volumes = append(define.volumes, vo)
vm := corev1.VolumeMount{
Name: volumeMountName,
MountPath: volumeMountPath,
ReadOnly: volumeReadOnly,
}
define.volumeMounts = append(define.volumeMounts, vm)
return nil
}
// LocalVolume local volume struct
type LocalVolume struct {
Base
}
// CreateVolume local volume create volume
func (v *LocalVolume) CreateVolume(define *Define) error {
volumeMountName := fmt.Sprintf("manual%d", v.svm.ID)
volumeMountPath := v.svm.VolumePath
volumeReadOnly := v.svm.IsReadOnly
statefulset := v.as.GetStatefulSet()
labels := v.as.GetCommonLabels(map[string]string{"volume_name": v.svm.VolumeName, "volume_path": volumeMountPath, "version": v.as.DeployVersion})
annotations := map[string]string{"volume_name": v.svm.VolumeName}
claim := newVolumeClaim(volumeMountName, volumeMountPath, v.svm.AccessMode, v.svm.VolumeCapacity, labels, annotations)
claim.Annotations = map[string]string{
client.LabelOS: func() string {
if v.as.IsWindowsService {
return "windows"
}
return "linux"
}(),
}
statefulset.Spec.VolumeClaimTemplates = append(statefulset.Spec.VolumeClaimTemplates, *claim)
vm := corev1.VolumeMount{
Name: volumeMountName,
MountPath: volumeMountPath,
ReadOnly: volumeReadOnly,
}
define.volumeMounts = append(define.volumeMounts, vm)
return nil
}
// CreateDependVolume empty func
func (v *LocalVolume) CreateDependVolume(define *Define) error {
return nil
}
// MemoryFSVolume memory fs volume struct
type MemoryFSVolume struct {
Base
}
// CreateVolume memory fs volume create volume
func (v *MemoryFSVolume) CreateVolume(define *Define) error {
volumeMountName := fmt.Sprintf("mnt%d", v.svm.ID)
volumeMountPath := v.svm.VolumePath
volumeReadOnly := false
if volumeMountPath != "" {
logrus.Warningf("service[%s]'s mount path is empty, skip it", v.version.ServiceID)
return nil
}
for _, m := range define.volumeMounts {
if m.MountPath == volumeMountPath {
logrus.Warningf("found the same mount path: %s, skip it", volumeMountPath)
return nil
}
}
name := fmt.Sprintf("manual%d", v.svm.ID)
vo := corev1.Volume{Name: name}
vo.EmptyDir = &corev1.EmptyDirVolumeSource{
Medium: corev1.StorageMediumMemory,
}
define.volumes = append(define.volumes, vo)
vm := corev1.VolumeMount{
MountPath: volumeMountPath,
Name: volumeMountName,
ReadOnly: volumeReadOnly,
SubPath: "",
}
define.volumeMounts = append(define.volumeMounts, vm)
return nil
}
// CreateDependVolume empty func
func (v *MemoryFSVolume) CreateDependVolume(define *Define) error {
return nil
}
// ConfigFileVolume config file volume struct
type ConfigFileVolume struct {
Base
}
// CreateVolume config file volume create volume
func (v *ConfigFileVolume) CreateVolume(define *Define) error {
// environment variables
configs := make(map[string]string)
envs, err := createEnv(v.as, v.dbmanager)
if err != nil {
logrus.Warningf("error creating environment variables: %v", err)
} else {
for _, env := range *envs {
configs[env.Name] = env.Value
}
}
cf, err := v.dbmanager.TenantServiceConfigFileDao().GetByVolumeName(v.as.ServiceID, v.svm.VolumeName)
if err != nil {
logrus.Errorf("error getting config file by volume name(%s): %v", v.svm.VolumeName, err)
return fmt.Errorf("error getting config file by volume name(%s): %v", v.svm.VolumeName, err)
}
cmap := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: util.NewUUID(),
Namespace: v.as.TenantID,
Labels: v.as.GetCommonLabels(),
},
Data: make(map[string]string),
}
cmap.Data[path.Base(v.svm.VolumePath)] = util.ParseVariable(cf.FileContent, configs)
v.as.SetConfigMap(cmap)
define.SetVolumeCMap(cmap, path.Base(v.svm.VolumePath), v.svm.VolumePath, false)
return nil
}
// CreateDependVolume config file volume create depend volume
func (v *ConfigFileVolume) CreateDependVolume(define *Define) error {
configs := make(map[string]string)
envs, err := createEnv(v.as, v.dbmanager)
if err != nil {
logrus.Warningf("error creating environment variables: %v", err)
} else {
for _, env := range *envs {
configs[env.Name] = env.Value
}
}
_, err = v.dbmanager.TenantServiceVolumeDao().GetVolumeByServiceIDAndName(v.smr.DependServiceID, v.smr.VolumeName)
if err != nil {
return fmt.Errorf("error getting TenantServiceVolume according to serviceID(%s) and volumeName(%s): %v",
v.smr.DependServiceID, v.smr.VolumeName, err)
}
cf, err := v.dbmanager.TenantServiceConfigFileDao().GetByVolumeName(v.smr.DependServiceID, v.smr.VolumeName)
if err != nil {
return fmt.Errorf("error getting TenantServiceConfigFile according to volumeName(%s): %v", v.smr.VolumeName, err)
}
cmap := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: util.NewUUID(),
Namespace: v.as.TenantID,
Labels: v.as.GetCommonLabels(),
},
Data: make(map[string]string),
}
cmap.Data[path.Base(v.smr.VolumePath)] = util.ParseVariable(cf.FileContent, configs)
v.as.SetConfigMap(cmap)
define.SetVolumeCMap(cmap, path.Base(v.smr.VolumePath), v.smr.VolumePath, false)
return nil
}
// CephRBDVolume ceph rbd volume struct
type CephRBDVolume struct {
Base
}
// CreateVolume ceph rbd volume create volume
func (v *CephRBDVolume) CreateVolume(define *Define) error {
if v.svm.VolumeCapacity <= 0 {
return fmt.Errorf("volume capcacity is %d, must be greater than zero", v.svm.VolumeCapacity)
}
volumeMountName := fmt.Sprintf("manual%d", v.svm.ID)
volumeMountPath := v.svm.VolumePath
volumeReadOnly := v.svm.IsReadOnly
labels := v.as.GetCommonLabels(map[string]string{"volume_name": v.svm.VolumeName, "version": v.as.DeployVersion})
annotations := map[string]string{"volume_name": v.svm.VolumeName}
annotations["reclaim_policy"] = v.svm.ReclaimPolicy
annotations["volume_path"] = volumeMountPath
claim := newVolumeClaim4RBD(volumeMountName, volumeMountPath, v.svm.AccessMode, v.svm.VolumeProviderName, v.svm.VolumeCapacity, labels, annotations)
logrus.Debugf("storage class is : %s, claim value is : %s", v.svm.VolumeProviderName, claim.GetName())
v.as.SetClaim(claim) // store claim to appService
claim.Annotations = map[string]string{
client.LabelOS: func() string {
if v.as.IsWindowsService {
return "windows"
}
return "linux"
}(),
}
statefulset := v.as.GetStatefulSet() //有状态组件
if statefulset != nil {
statefulset.Spec.VolumeClaimTemplates = append(statefulset.Spec.VolumeClaimTemplates, *claim)
} else {
vo := corev1.Volume{Name: volumeMountName}
vo.PersistentVolumeClaim = &corev1.PersistentVolumeClaimVolumeSource{ClaimName: claim.GetName(), ReadOnly: volumeReadOnly}
define.volumes = append(define.volumes, vo)
logrus.Warnf("service[%s] is not stateful, mount volume by k8s volume.PersistenVolumeClaim[%s]", v.svm.ServiceID, claim.GetName())
}
vm := corev1.VolumeMount{
Name: volumeMountName,
MountPath: volumeMountPath,
ReadOnly: volumeReadOnly,
}
define.volumeMounts = append(define.volumeMounts, vm)
return nil
}
// CreateDependVolume create depend volume
func (v *CephRBDVolume) CreateDependVolume(define *Define) error {
return nil
}
// AliCloudVolume ali cloud volume struct
type AliCloudVolume struct {
Base
}
// CreateVolume ceph rbd volume create volume
func (v *AliCloudVolume) CreateVolume(define *Define) error {
if v.svm.VolumeCapacity <= 0 {
return fmt.Errorf("volume capcacity is %d, must be greater than zero", v.svm.VolumeCapacity)
}
volumeMountName := fmt.Sprintf("manual%d", v.svm.ID)
volumeMountPath := v.svm.VolumePath
volumeReadOnly := v.svm.IsReadOnly
labels := v.as.GetCommonLabels(map[string]string{"volume_name": v.svm.VolumeName, "version": v.as.DeployVersion})
annotations := map[string]string{"volume_name": v.svm.VolumeName}
annotations["reclaim_policy"] = v.svm.ReclaimPolicy
annotations["volume_path"] = volumeMountPath
claim := newVolumeClaim4RBD(volumeMountName, volumeMountPath, v.svm.AccessMode, v.svm.VolumeProviderName, v.svm.VolumeCapacity, labels, annotations)
logrus.Debugf("storage class is : %s, claim value is : %s", v.svm.VolumeProviderName, claim.GetName())
v.as.SetClaim(claim) // store claim to appService
claim.Annotations = map[string]string{
client.LabelOS: func() string {
if v.as.IsWindowsService {
return "windows"
}
return "linux"
}(),
}
statefulset := v.as.GetStatefulSet() //有状态组件
if statefulset != nil {
statefulset.Spec.VolumeClaimTemplates = append(statefulset.Spec.VolumeClaimTemplates, *claim)
} else {
vo := corev1.Volume{Name: volumeMountName}
vo.PersistentVolumeClaim = &corev1.PersistentVolumeClaimVolumeSource{ClaimName: claim.GetName(), ReadOnly: volumeReadOnly}
define.volumes = append(define.volumes, vo)
logrus.Warnf("service[%s] is not stateful, mount volume by k8s volume.PersistenVolumeClaim[%s]", v.svm.ServiceID, claim.GetName())
}
vm := corev1.VolumeMount{
Name: volumeMountName,
MountPath: volumeMountPath,
ReadOnly: volumeReadOnly,
}
define.volumeMounts = append(define.volumeMounts, vm)
return nil
}
// CreateDependVolume create depend volume
func (v *AliCloudVolume) CreateDependVolume(define *Define) error {
return nil
}
// Define define volume
type Define struct {
as *v1.AppService

View File

@ -471,19 +471,24 @@ func (r *RuntimeServer) GetAppVolumeStatus(ctx context.Context, re *pb.ServiceRe
if as == nil {
return ret, nil
}
// claims := as.GetClaims()// TODO 临时使用client直接获取PVC后续换成store中获取
claimList, err := r.clientset.CoreV1().PersistentVolumeClaims(as.TenantID).List(metav1.ListOptions{LabelSelector: "service_id=69123df08744e36800c29c91574370d5"})
if err != nil {
return ret, err
}
if claimList != nil {
for _, claim := range claimList.Items {
if claim.Annotations != nil {
claims := r.store.GetServiceClaims(as.TenantID, as.ServiceID)
if claims != nil {
for _, claim := range claims {
if claim.Annotations != nil && claim.Annotations["volume_name"] != "" {
if claim.Status.Phase != corev1.ClaimBound {
ret.Status[claim.Annotations["volume_name"]] = pb.ServiceVolumeStatus_NOT_READY
} else {
ret.Status[claim.Annotations["volume_name"]] = pb.ServiceVolumeStatus_READY
}
} else if claim.Labels != nil && claim.Labels["volume_name"] != "" {
logrus.Debug("can't get volume name from claim's annotation, test get volume from claim.label ")
if claim.Status.Phase != corev1.ClaimBound {
ret.Status[claim.Labels["volume_name"]] = pb.ServiceVolumeStatus_NOT_READY
} else {
ret.Status[claim.Labels["volume_name"]] = pb.ServiceVolumeStatus_READY
}
} else {
logrus.Warn("can't get volume status")
}
}
}

View File

@ -240,10 +240,11 @@ func TestGetAppVolumeStatus(t *testing.T) {
store: storer,
clientset: clientset,
}
statusList, err := server.GetAppVolumeStatus(context.Background(), &pb.ServiceRequest{ServiceId: "69123df08744e36800c29c91574370d5"})
statusList, err := server.GetAppVolumeStatus(context.Background(), &pb.ServiceRequest{ServiceId: "c5802cd0276018209ff1f9b52bc04ec1"})
if err != nil {
t.Fatal(err)
}
t.Log(statusList.GetStatus())
t.Log("end")
time.Sleep(20 * time.Second) // db woulld close
}