mirror of
https://gitee.com/rainbond/Rainbond.git
synced 2024-11-29 18:27:58 +08:00
add volume type model and update create volume
This commit is contained in:
parent
4055d01946
commit
e6387584e5
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
83
api/handler/VolumeTypeHandler.go
Normal file
83
api/handler/VolumeTypeHandler.go
Normal 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
|
||||
}
|
@ -73,6 +73,8 @@ func InitHandle(conf option.Config, statusCli *client.AppRuntimeSyncClient) erro
|
||||
defaultAppRestoreHandler = NewAppRestoreHandler()
|
||||
defPodHandler = NewPodHandler(statusCli)
|
||||
|
||||
defaultVolumeTypeHandler = CreateVolumeTypeManger(statusCli)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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"}
|
||||
}
|
||||
|
@ -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
|
||||
|
1
db/db.go
1
db/db.go
@ -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
|
||||
|
129
db/db_test.go
129
db/db_test.go
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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
53
db/model/volumetype.go
Normal 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"
|
||||
}
|
69
db/mysql/dao/volumetype.go
Normal file
69
db/mysql/dao/volumetype.go
Normal 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
|
||||
}
|
@ -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{
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
101
worker/appm/volume/config-file.go
Normal file
101
worker/appm/volume/config-file.go
Normal 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
|
||||
}
|
65
worker/appm/volume/local.go
Normal file
65
worker/appm/volume/local.go
Normal 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
|
||||
}
|
67
worker/appm/volume/memoryfs.go
Normal file
67
worker/appm/volume/memoryfs.go
Normal 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
|
||||
}
|
80
worker/appm/volume/other.go
Normal file
80
worker/appm/volume/other.go
Normal 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
|
||||
}
|
126
worker/appm/volume/share-file.go
Normal file
126
worker/appm/volume/share-file.go
Normal 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
|
||||
}
|
@ -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
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user