// 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 . package controller import ( "context" "fmt" "io/ioutil" "net/http" "os" "strconv" "github.com/go-chi/chi" "github.com/goodrain/rainbond/api/handler" api_model "github.com/goodrain/rainbond/api/model" ctxutil "github.com/goodrain/rainbond/api/util/ctx" "github.com/goodrain/rainbond/db" dbmodel "github.com/goodrain/rainbond/db/model" "github.com/goodrain/rainbond/event" validator "github.com/goodrain/rainbond/util/govalidator" httputil "github.com/goodrain/rainbond/util/http" "github.com/goodrain/rainbond/worker/discover/model" "github.com/jinzhu/gorm" "github.com/pquerna/ffjson/ffjson" "github.com/sirupsen/logrus" ) func vmOperationFailureMessage(action string, err error) string { if err == nil { return fmt.Sprintf("%s vm error", action) } return fmt.Sprintf("%s vm error, %v", action, err) } // StartService StartService // swagger:operation POST /v2/tenants/{tenant_name}/services/{service_alias}/start v2 startService // // 启动应用 // // start service // // --- // consumes: // - application/json // - application/x-protobuf // // produces: // - application/json // - application/xml // // responses: // // default: // schema: // "$ref": "#/responses/commandResponse" // description: 统一返回格式 func (t *TenantStruct) StartService(w http.ResponseWriter, r *http.Request) { tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string) serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string) tenant := r.Context().Value(ctxutil.ContextKey("tenant")).(*dbmodel.Tenants) service := r.Context().Value(ctxutil.ContextKey("service")).(*dbmodel.TenantServices) sEvent := r.Context().Value(ctxutil.ContextKey("event")).(*dbmodel.ServiceEvent) if service.Kind != "third_party" { var noMemory, noCPU, needStorage int if service.ContainerCPU == 0 { noCPU = service.Replicas } if service.ContainerMemory == 0 { noMemory = service.Replicas } storages, err := db.GetManager().TenantServiceVolumeDao().GetTenantServiceVolumesByServiceID(serviceID) if err != nil { httputil.ReturnResNotEnough(r, w, sEvent.EventID, err.Error()) return } if storages != nil && len(storages) > 0 { for _, storage := range storages { needStorage += int(storage.VolumeCapacity) } } if err := handler.CheckTenantResource(r.Context(), tenant, service.Replicas*service.ContainerMemory, service.Replicas*service.ContainerCPU, needStorage, noMemory, noCPU); err != nil { httputil.ReturnResNotEnough(r, w, sEvent.EventID, err.Error()) return } } startStopStruct := &api_model.StartStopStruct{ TenantID: tenantID, ServiceID: serviceID, EventID: sEvent.EventID, TaskType: "start", } if service.IsVM() { if err := handler.GetServiceManager().StartOrCreateVM(r.Context(), startStopStruct, service.DeployVersion); err != nil { logrus.Errorf("start vm service %s failure: %v", serviceID, err) httputil.ReturnError(r, w, 500, vmOperationFailureMessage("start", err)) return } httputil.ReturnSuccess(r, w, sEvent) return } if err := handler.GetServiceManager().StartStopService(startStopStruct); err != nil { httputil.ReturnError(r, w, 500, "get service info error.") return } httputil.ReturnSuccess(r, w, sEvent) } // StopService StopService // swagger:operation POST /v2/tenants/{tenant_name}/services/{service_alias}/stop v2 stopService // // 关闭应用 // // stop service // // --- // consumes: // - application/json // - application/x-protobuf // // produces: // - application/json // - application/xml // // responses: // // default: // schema: // "$ref": "#/responses/commandResponse" // description: 统一返回格式 func (t *TenantStruct) StopService(w http.ResponseWriter, r *http.Request) { tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string) serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string) service := r.Context().Value(ctxutil.ContextKey("service")).(*dbmodel.TenantServices) sEvent := r.Context().Value(ctxutil.ContextKey("event")).(*dbmodel.ServiceEvent) //save event defer event.CloseManager() if service.IsVM() { if err := handler.GetServiceManager().StopVM(r.Context(), serviceID); err != nil { logrus.Errorf("stop vm service %s failure: %v", serviceID, err) httputil.ReturnError(r, w, 500, vmOperationFailureMessage("stop", err)) return } httputil.ReturnSuccess(r, w, sEvent) return } startStopStruct := &api_model.StartStopStruct{ TenantID: tenantID, ServiceID: serviceID, EventID: sEvent.EventID, TaskType: "stop", } if err := handler.GetServiceManager().StartStopService(startStopStruct); err != nil { httputil.ReturnError(r, w, 500, "get service info error.") return } httputil.ReturnSuccess(r, w, sEvent) } // RestartService RestartService // swagger:operation POST /v2/tenants/{tenant_name}/services/{service_alias}/restart v2 restartService // // 重启应用 // // restart service // // --- // consumes: // - application/json // - application/x-protobuf // // produces: // - application/json // - application/xml // // responses: // // default: // schema: // "$ref": "#/responses/commandResponse" // description: 统一返回格式 func (t *TenantStruct) RestartService(w http.ResponseWriter, r *http.Request) { tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string) serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string) sEvent := r.Context().Value(ctxutil.ContextKey("event")).(*dbmodel.ServiceEvent) defer event.CloseManager() startStopStruct := &api_model.StartStopStruct{ TenantID: tenantID, ServiceID: serviceID, EventID: sEvent.EventID, TaskType: "restart", } curStatus := t.StatusCli.GetStatus(serviceID) if curStatus == "closed" { startStopStruct.TaskType = "start" } tenant := r.Context().Value(ctxutil.ContextKey("tenant")).(*dbmodel.Tenants) service := r.Context().Value(ctxutil.ContextKey("service")).(*dbmodel.TenantServices) var noMemory, noCPU, needStorage int if service.ContainerCPU == 0 { noCPU = service.Replicas } if service.ContainerMemory == 0 { noMemory = service.Replicas } storages, err := db.GetManager().TenantServiceVolumeDao().GetTenantServiceVolumesByServiceID(serviceID) if err != nil { httputil.ReturnResNotEnough(r, w, sEvent.EventID, err.Error()) return } if storages != nil && len(storages) > 0 { for _, storage := range storages { needStorage += int(storage.VolumeCapacity) } } if err := handler.CheckTenantResource(r.Context(), tenant, service.Replicas*service.ContainerMemory, needStorage, service.Replicas*service.ContainerCPU, noMemory, noCPU); err != nil { httputil.ReturnResNotEnough(r, w, sEvent.EventID, err.Error()) return } if service.IsVM() { if err := handler.GetServiceManager().RestartVM(r.Context(), startStopStruct, service.DeployVersion); err != nil { logrus.Errorf("restart vm service %s failure: %v", serviceID, err) httputil.ReturnError(r, w, 500, vmOperationFailureMessage("restart", err)) return } httputil.ReturnSuccess(r, w, sEvent) return } if err := handler.GetServiceManager().StartStopService(startStopStruct); err != nil { httputil.ReturnError(r, w, 500, "get service info error.") return } httputil.ReturnSuccess(r, w, sEvent) } // VerticalService VerticalService // swagger:operation PUT /v2/tenants/{tenant_name}/services/{service_alias}/vertical v2 verticalService // // 应用垂直伸缩 // // service vertical // // --- // consumes: // - application/json // - application/x-protobuf // // produces: // - application/json // - application/xml // // responses: // // default: // schema: // "$ref": "#/responses/commandResponse" // description: 统一返回格式 func (t *TenantStruct) VerticalService(w http.ResponseWriter, r *http.Request) { rules := validator.MapData{ "container_cpu": []string{"required"}, "container_memory": []string{"required"}, } data, ok := httputil.ValidatorRequestMapAndErrorResponse(r, w, rules, nil) if !ok { return } tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string) serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string) sEvent := r.Context().Value(ctxutil.ContextKey("event")).(*dbmodel.ServiceEvent) var cpuSet, gpuSet, memorySet *int if cpu, ok := data["container_cpu"].(float64); ok { cpuInt := int(cpu) cpuSet = &cpuInt } if memory, ok := data["container_memory"].(float64); ok { memoryInt := int(memory) memorySet = &memoryInt } if gpu, ok := data["container_gpu"].(float64); ok { gpuInt := int(gpu) gpuSet = &gpuInt } tenant := r.Context().Value(ctxutil.ContextKey("tenant")).(*dbmodel.Tenants) service := r.Context().Value(ctxutil.ContextKey("service")).(*dbmodel.TenantServices) if memorySet != nil && cpuSet != nil { var noMemory, noCPU int if *cpuSet == 0 { noCPU = service.Replicas } if *memorySet == 0 { noMemory = service.Replicas } if err := handler.CheckTenantResource(r.Context(), tenant, service.Replicas*(*memorySet), service.Replicas*(*cpuSet), 0, noMemory, noCPU); err != nil { httputil.ReturnResNotEnough(r, w, sEvent.EventID, err.Error()) return } } verticalTask := &model.VerticalScalingTaskBody{ TenantID: tenantID, ServiceID: serviceID, EventID: sEvent.EventID, ContainerCPU: cpuSet, ContainerMemory: memorySet, ContainerGPU: gpuSet, } if err := handler.GetServiceManager().ServiceVertical(r.Context(), verticalTask); err != nil { if coder, ok := err.(interface{ StatusCode() int }); ok { httputil.ReturnError(r, w, coder.StatusCode(), err.Error()) return } httputil.ReturnError(r, w, 500, fmt.Sprintf("service vertical error. %v", err)) return } httputil.ReturnSuccess(r, w, sEvent) } func (t *TenantStruct) VMLiveUpdateCapability(w http.ResponseWriter, r *http.Request) { serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string) capability := handler.GetServiceManager().GetVMLiveUpdateCapability(serviceID) httputil.ReturnSuccess(r, w, capability) } func (t *TenantStruct) SetVMFixedPodIP(w http.ResponseWriter, r *http.Request) { rules := validator.MapData{ "enabled": []string{"required"}, } data, ok := httputil.ValidatorRequestMapAndErrorResponse(r, w, rules, nil) if !ok { return } enabled, ok := data["enabled"].(bool) if !ok { httputil.ReturnError(r, w, 400, "enabled must be boolean") return } serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string) result, err := handler.GetServiceManager().SetVMFixedPodIP(r.Context(), serviceID, enabled) if err != nil { if coder, ok := err.(interface{ StatusCode() int }); ok { httputil.ReturnError(r, w, coder.StatusCode(), err.Error()) return } httputil.ReturnError(r, w, 500, fmt.Sprintf("set vm fixed pod ip error. %v", err)) return } httputil.ReturnSuccess(r, w, result) } // HorizontalService HorizontalService // swagger:operation PUT /v2/tenants/{tenant_name}/services/{service_alias}/horizontal v2 horizontalService // // 应用水平伸缩 // // service horizontal // // --- // consumes: // - application/json // - application/x-protobuf // // produces: // - application/json // - application/xml // // responses: // // default: // schema: // "$ref": "#/responses/commandResponse" // description: 统一返回格式 func (t *TenantStruct) HorizontalService(w http.ResponseWriter, r *http.Request) { rules := validator.MapData{ "node_num": []string{"required"}, } data, ok := httputil.ValidatorRequestMapAndErrorResponse(r, w, rules, nil) if !ok { return } tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string) serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string) sEvent := r.Context().Value(ctxutil.ContextKey("event")).(*dbmodel.ServiceEvent) replicas := int32(data["node_num"].(float64)) tenant := r.Context().Value(ctxutil.ContextKey("tenant")).(*dbmodel.Tenants) service := r.Context().Value(ctxutil.ContextKey("service")).(*dbmodel.TenantServices) var noMemory, noCPU int if service.ContainerCPU == 0 { noCPU = int(replicas) } if service.ContainerMemory == 0 { noMemory = int(replicas) } if int(replicas) > service.Replicas { if err := handler.CheckTenantResource(r.Context(), tenant, service.ContainerMemory*(int(replicas-int32(service.Replicas))), service.ContainerCPU*(int(replicas-int32(service.Replicas))), 0, noMemory, noCPU); err != nil { httputil.ReturnResNotEnough(r, w, sEvent.EventID, err.Error()) return } } horizontalTask := &model.HorizontalScalingTaskBody{ TenantID: tenantID, ServiceID: serviceID, EventID: sEvent.EventID, Username: sEvent.UserName, Replicas: replicas, } if err := handler.GetServiceManager().ServiceHorizontal(horizontalTask); err != nil { httputil.ReturnBcodeError(r, w, err) return } httputil.ReturnSuccess(r, w, sEvent) } // BuildService BuildService // swagger:operation POST /v2/tenants/{tenant_name}/services/{service_alias}/build v2 serviceBuild // // 应用构建 // // service build // // --- // consumes: // - application/json // - application/x-protobuf // // produces: // - application/json // - application/xml // // responses: // // default: // schema: // "$ref": "#/responses/commandResponse" // description: 统一返回格式 func (t *TenantStruct) BuildService(w http.ResponseWriter, r *http.Request) { var build api_model.ComponentBuildReq ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &build, nil) if !ok { return } serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string) tenantName := r.Context().Value(ctxutil.ContextKey("tenant_name")).(string) build.TenantName = tenantName build.EventID = r.Context().Value(ctxutil.ContextKey("event_id")).(string) if build.ServiceID != serviceID { httputil.ReturnError(r, w, 400, "build service id is failure") return } tenant := r.Context().Value(ctxutil.ContextKey("tenant")).(*dbmodel.Tenants) service := r.Context().Value(ctxutil.ContextKey("service")).(*dbmodel.TenantServices) var noMemory, noCPU, needStorage int if service.ContainerCPU == 0 { noCPU = service.Replicas } if service.ContainerMemory == 0 { noMemory = service.Replicas } storages, err := db.GetManager().TenantServiceVolumeDao().GetTenantServiceVolumesByServiceID(serviceID) if err != nil { httputil.ReturnResNotEnough(r, w, build.EventID, err.Error()) return } if storages != nil && len(storages) > 0 { for _, storage := range storages { needStorage += int(storage.VolumeCapacity) } } if err := handler.CheckTenantResource(r.Context(), tenant, service.Replicas*service.ContainerMemory, service.Replicas*service.ContainerCPU, needStorage, noMemory, noCPU); err != nil { httputil.ReturnResNotEnough(r, w, build.EventID, err.Error()) return } res, err := handler.GetOperationHandler().Build(&build) if err != nil { httputil.ReturnBcodeError(r, w, err) return } httputil.ReturnSuccess(r, w, res) } // PauseService virtual machine paused func (t *TenantStruct) PauseService(w http.ResponseWriter, r *http.Request) { serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string) sEvent := r.Context().Value(ctxutil.ContextKey("event")).(*dbmodel.ServiceEvent) if err := handler.GetServiceManager().PauseUNPauseService(serviceID, "pause"); err != nil { logrus.Errorf("pause vm service %s failure: %v", serviceID, err) httputil.ReturnError(r, w, 500, vmOperationFailureMessage("pause", err)) return } err := db.GetManager().ServiceEventDao().SetEventStatus(r.Context(), dbmodel.EventStatusSuccess) if err != nil { httputil.ReturnError(r, w, 500, fmt.Sprintf("pause update event failure: %v", err)) return } httputil.ReturnSuccess(r, w, sEvent) } // UNPauseService virtual machine unpaused func (t *TenantStruct) UNPauseService(w http.ResponseWriter, r *http.Request) { serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string) sEvent := r.Context().Value(ctxutil.ContextKey("event")).(*dbmodel.ServiceEvent) if err := handler.GetServiceManager().PauseUNPauseService(serviceID, "unpause"); err != nil { logrus.Errorf("unpause vm service %s failure: %v", serviceID, err) httputil.ReturnError(r, w, 500, vmOperationFailureMessage("unpause", err)) return } err := db.GetManager().ServiceEventDao().SetEventStatus(r.Context(), dbmodel.EventStatusSuccess) if err != nil { httputil.ReturnError(r, w, 500, fmt.Sprintf("unpause update event failure: %v", err)) return } httputil.ReturnSuccess(r, w, sEvent) } // BuildList BuildList func (t *TenantStruct) BuildList(w http.ResponseWriter, r *http.Request) { serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string) resp, err := handler.GetServiceManager().ListVersionInfo(serviceID) if err != nil { logrus.Error("get version info error", err.Error()) httputil.ReturnError(r, w, 500, fmt.Sprintf("get version info erro, %v", err)) return } httputil.ReturnSuccess(r, w, resp) } func (t *TenantStruct) FileManageService(w http.ResponseWriter, r *http.Request) { tarPath := r.FormValue("path") podName := r.FormValue("pod_name") containerName := r.FormValue("container_name") namespace := r.FormValue("namespace") serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string) fileInfos, err := handler.GetServiceManager().FileManageInfo(serviceID, podName, tarPath, containerName, namespace) if err != nil { logrus.Errorf("%v get file manage %v failure: %v", serviceID, tarPath, err) httputil.ReturnError(r, w, 500, fmt.Sprintf("%v get file manage %v failure: %v", serviceID, tarPath, err)) return } httputil.ReturnSuccess(r, w, fileInfos) } // BuildVersionIsExist - func (t *TenantStruct) BuildVersionIsExist(w http.ResponseWriter, r *http.Request) { statusMap := make(map[string]bool) serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string) buildVersion := chi.URLParam(r, "build_version") _, err := db.GetManager().VersionInfoDao().GetVersionByDeployVersion(buildVersion, serviceID) if err != nil && err != gorm.ErrRecordNotFound { httputil.ReturnError(r, w, 500, fmt.Sprintf("get build version status erro, %v", err)) return } if err == gorm.ErrRecordNotFound { statusMap["status"] = false } else { statusMap["status"] = true } httputil.ReturnSuccess(r, w, statusMap) } // DeleteBuildVersion - func (t *TenantStruct) DeleteBuildVersion(w http.ResponseWriter, r *http.Request) { serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string) buildVersion := chi.URLParam(r, "build_version") val, err := db.GetManager().VersionInfoDao().GetVersionByDeployVersion(buildVersion, serviceID) if err != nil && err != gorm.ErrRecordNotFound { httputil.ReturnError(r, w, 500, fmt.Sprintf("delete build version erro, %v", err)) return } if err == gorm.ErrRecordNotFound { } else { if val.DeliveredType == "slug" && val.FinalStatus == "success" { if err := os.Remove(val.DeliveredPath); err != nil { httputil.ReturnError(r, w, 500, fmt.Sprintf("delete build version erro, %v", err)) return } if err := db.GetManager().VersionInfoDao().DeleteVersionInfo(val); err != nil { httputil.ReturnError(r, w, 500, fmt.Sprintf("delete build version erro, %v", err)) return } } if val.FinalStatus == "failure" { if err := db.GetManager().VersionInfoDao().DeleteVersionInfo(val); err != nil { httputil.ReturnError(r, w, 500, fmt.Sprintf("delete build version erro, %v", err)) return } } if val.DeliveredType == "image" { if err := db.GetManager().VersionInfoDao().DeleteVersionInfo(val); err != nil { httputil.ReturnError(r, w, 500, fmt.Sprintf("delete build version erro, %v", err)) return } } } httputil.ReturnSuccess(r, w, nil) } // UpdateBuildVersion - func (t *TenantStruct) UpdateBuildVersion(w http.ResponseWriter, r *http.Request) { var build api_model.UpdateBuildVersionReq ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &build, nil) if !ok { return } serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string) buildVersion := chi.URLParam(r, "build_version") versionInfo, err := db.GetManager().VersionInfoDao().GetVersionByDeployVersion(buildVersion, serviceID) if err != nil { httputil.ReturnError(r, w, 500, fmt.Sprintf("update build version info error, %v", err)) return } versionInfo.PlanVersion = build.PlanVersion err = db.GetManager().VersionInfoDao().UpdateModel(versionInfo) if err != nil { httputil.ReturnError(r, w, 500, fmt.Sprintf("update build version info error, %v", err)) return } httputil.ReturnSuccess(r, w, nil) } // BuildVersionInfo - func (t *TenantStruct) BuildVersionInfo(w http.ResponseWriter, r *http.Request) { switch r.Method { case "DELETE": t.DeleteBuildVersion(w, r) case "GET": t.BuildVersionIsExist(w, r) case "PUT": t.UpdateBuildVersion(w, r) } } // GetDeployVersion GetDeployVersion by service func (t *TenantStruct) GetDeployVersion(w http.ResponseWriter, r *http.Request) { service := r.Context().Value(ctxutil.ContextKey("service")).(*dbmodel.TenantServices) version, err := db.GetManager().VersionInfoDao().GetVersionByDeployVersion(service.DeployVersion, service.ServiceID) if err != nil && err != gorm.ErrRecordNotFound { httputil.ReturnError(r, w, 500, fmt.Sprintf("get build version status erro, %v", err)) return } if err == gorm.ErrRecordNotFound { httputil.ReturnError(r, w, 404, "build version do not exist") return } httputil.ReturnSuccess(r, w, version) } // GetManyDeployVersion GetDeployVersion by some service id func (t *TenantStruct) GetManyDeployVersion(w http.ResponseWriter, r *http.Request) { rules := validator.MapData{ "service_ids": []string{"required"}, } data, ok := httputil.ValidatorRequestMapAndErrorResponse(r, w, rules, nil) if !ok { return } serviceIDs, ok := data["service_ids"].([]interface{}) if !ok { httputil.ReturnError(r, w, 400, "service ids must be a array") return } var list []string for _, s := range serviceIDs { list = append(list, s.(string)) } services, err := db.GetManager().TenantServiceDao().GetServiceByIDs(list) if err != nil { httputil.ReturnError(r, w, 500, err.Error()) return } var versionList []*dbmodel.VersionInfo for _, service := range services { version, err := db.GetManager().VersionInfoDao().GetVersionByDeployVersion(service.DeployVersion, service.ServiceID) if err != nil && err != gorm.ErrRecordNotFound { httputil.ReturnError(r, w, 500, fmt.Sprintf("get build version status erro, %v", err)) return } versionList = append(versionList, version) } httputil.ReturnSuccess(r, w, versionList) } // DeployService DeployService func (t *TenantStruct) DeployService(w http.ResponseWriter, r *http.Request) { logrus.Debugf("trans deploy service") w.Write([]byte("deploy service")) } // UpgradeService UpgradeService // swagger:operation POST /v2/tenants/{tenant_name}/services/{service_alias}/upgrade v2 upgradeService // // 升级应用 // // upgrade service // // --- // consumes: // - application/json // - application/x-protobuf // // produces: // - application/json // - application/xml // // responses: // // default: // schema: // "$ref": "#/responses/commandResponse" // description: 统一返回格式 func (t *TenantStruct) UpgradeService(w http.ResponseWriter, r *http.Request) { var upgradeRequest api_model.ComponentUpgradeReq ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &upgradeRequest, nil) if !ok { logrus.Errorf("start operation validate request body failure") return } upgradeRequest.EventID = r.Context().Value(ctxutil.ContextKey("event_id")).(string) serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string) if upgradeRequest.ServiceID != serviceID { httputil.ReturnError(r, w, 400, "upgrade service id failure") return } tenant := r.Context().Value(ctxutil.ContextKey("tenant")).(*dbmodel.Tenants) service := r.Context().Value(ctxutil.ContextKey("service")).(*dbmodel.TenantServices) if service.Kind != "third_party" { var noMemory, noCPU, needStorage int if service.ContainerCPU == 0 { noCPU = service.Replicas } if service.ContainerMemory == 0 { noMemory = service.Replicas } storages, err := db.GetManager().TenantServiceVolumeDao().GetTenantServiceVolumesByServiceID(serviceID) if err != nil { httputil.ReturnResNotEnough(r, w, upgradeRequest.EventID, err.Error()) return } if storages != nil && len(storages) > 0 { for _, storage := range storages { needStorage += int(storage.VolumeCapacity) } } if err := handler.CheckTenantResource(r.Context(), tenant, service.Replicas*service.ContainerMemory, service.Replicas*service.ContainerCPU, needStorage, noMemory, noCPU); err != nil { httputil.ReturnResNotEnough(r, w, upgradeRequest.EventID, err.Error()) return } } res, err := handler.GetOperationHandler().Upgrade(&upgradeRequest) if err != nil { httputil.ReturnBcodeError(r, w, err) return } httputil.ReturnSuccess(r, w, res) } // CheckCode CheckCode // swagger:operation POST /v2/tenants/{tenant_name}/code-check v2 checkCode // // 应用代码检测 // // check code // // --- // consumes: // - application/json // - application/x-protobuf // // produces: // - application/json // - application/xml // // responses: // // default: // schema: // "$ref": "#/responses/commandResponse" // description: 统一返回格式 func (t *TenantStruct) CheckCode(w http.ResponseWriter, r *http.Request) { var ccs api_model.CheckCodeStruct ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &ccs.Body, nil) if !ok { return } if ccs.Body.TenantID == "" { tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string) ccs.Body.TenantID = tenantID } ccs.Body.Action = "code_check" if err := handler.GetServiceManager().CodeCheck(&ccs); err != nil { httputil.ReturnError(r, w, 500, fmt.Sprintf("task code check error,%v", err)) return } httputil.ReturnSuccess(r, w, nil) } // RollBack RollBack // swagger:operation Post /v2/tenants/{tenant_name}/services/{service_alias}/rollback v2 rollback // // 应用版本回滚 // // service rollback // // --- // consumes: // - application/json // - application/x-protobuf // // produces: // - application/json // - application/xml // // responses: // // default: // schema: // "$ref": "#/responses/commandResponse" // description: 统一返回格式 func (t *TenantStruct) RollBack(w http.ResponseWriter, r *http.Request) { var rollbackRequest api_model.RollbackInfoRequestStruct ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &rollbackRequest, nil) if !ok { logrus.Errorf("start operation validate request body failure") return } serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string) if rollbackRequest.ServiceID != serviceID { httputil.ReturnError(r, w, 400, "rollback service id failure") return } rollbackRequest.EventID = r.Context().Value(ctxutil.ContextKey("event_id")).(string) tenant := r.Context().Value(ctxutil.ContextKey("tenant")).(*dbmodel.Tenants) service := r.Context().Value(ctxutil.ContextKey("service")).(*dbmodel.TenantServices) var noMemory, noCPU int if service.ContainerCPU == 0 { noCPU = service.Replicas } if service.ContainerMemory == 0 { noMemory = service.Replicas } if err := handler.CheckTenantResource(r.Context(), tenant, service.Replicas*service.ContainerMemory, service.Replicas*service.ContainerCPU, 0, noMemory, noCPU); err != nil { httputil.ReturnResNotEnough(r, w, rollbackRequest.EventID, err.Error()) return } re := handler.GetOperationHandler().RollBack(rollbackRequest) httputil.ReturnSuccess(r, w, re) } type TenantResourceQuota struct { LimitMemory int `json:"limit_memory"` LimitCPU int `json:"limit_cpu"` LimitStorage int `json:"limit_storage"` } // LimitTenantResource - func (t *TenantStruct) LimitTenantResource(w http.ResponseWriter, r *http.Request) { var trq TenantResourceQuota body, err := ioutil.ReadAll(r.Body) if err != nil { httputil.ReturnError(r, w, 500, err.Error()) return } err = ffjson.Unmarshal(body, &trq) if err != nil { httputil.ReturnError(r, w, 500, err.Error()) return } tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string) tenant, err := db.GetManager().TenantDao().GetTenantByUUID(tenantID) if err != nil { httputil.ReturnError(r, w, 400, err.Error()) return } tenant.LimitMemory = trq.LimitMemory tenant.LimitCPU = trq.LimitCPU tenant.LimitStorage = trq.LimitStorage err = handler.GetTenantManager().TenantResourceQuota(context.Background(), tenant.Namespace, trq.LimitCPU, trq.LimitMemory, trq.LimitStorage) if err != nil { httputil.ReturnError(r, w, 400, err.Error()) return } if err := db.GetManager().TenantDao().UpdateModel(tenant); err != nil { httputil.ReturnError(r, w, 500, err.Error()) } httputil.ReturnSuccess(r, w, "success!") } // SourcesInfo - type SourcesInfo struct { TenantID string `json:"tenant_id"` AvailableMemory int `json:"available_memory"` Status bool `json:"status"` MemTotal int `json:"mem_total"` MemUsed int `json:"mem_used"` CPUTotal int `json:"cpu_total"` CPUUsed int `json:"cpu_used"` } // TenantResourcesStatus tenant resources status func (t *TenantStruct) TenantResourcesStatus(w http.ResponseWriter, r *http.Request) { tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string) tenant, err := db.GetManager().TenantDao().GetTenantByUUID(tenantID) if err != nil { httputil.ReturnError(r, w, 400, err.Error()) return } //11ms services, err := handler.GetServiceManager().GetService(tenant.UUID) if err != nil { msg := httputil.ResponseBody{ Msg: fmt.Sprintf("get service error, %v", err), } httputil.Return(r, w, 500, msg) return } statsInfo, _ := handler.GetTenantManager().StatsMemCPU(services) if tenant.LimitMemory == 0 { sourcesInfo := SourcesInfo{ TenantID: tenantID, AvailableMemory: 0, Status: true, MemTotal: tenant.LimitMemory, MemUsed: statsInfo.MEM, CPUTotal: 0, CPUUsed: statsInfo.CPU, } httputil.ReturnSuccess(r, w, sourcesInfo) return } if statsInfo.MEM >= tenant.LimitMemory { sourcesInfo := SourcesInfo{ TenantID: tenantID, AvailableMemory: tenant.LimitMemory - statsInfo.MEM, Status: false, MemTotal: tenant.LimitMemory, MemUsed: statsInfo.MEM, CPUTotal: tenant.LimitMemory / 4, CPUUsed: statsInfo.CPU, } httputil.ReturnSuccess(r, w, sourcesInfo) } else { sourcesInfo := SourcesInfo{ TenantID: tenantID, AvailableMemory: tenant.LimitMemory - statsInfo.MEM, Status: true, MemTotal: tenant.LimitMemory, MemUsed: statsInfo.MEM, CPUTotal: tenant.LimitMemory / 4, CPUUsed: statsInfo.CPU, } httputil.ReturnSuccess(r, w, sourcesInfo) } } // GetServiceDeployInfo get service deploy info func GetServiceDeployInfo(w http.ResponseWriter, r *http.Request) { tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string) serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string) info, err := handler.GetServiceManager().GetServiceDeployInfo(tenantID, serviceID) if err != nil { err.Handle(r, w) return } httputil.ReturnSuccess(r, w, info) } // Log - func (t *TenantStruct) Log(w http.ResponseWriter, r *http.Request) { component := r.Context().Value(ctxutil.ContextKey("service")).(*dbmodel.TenantServices) podName := r.URL.Query().Get("podName") containerName := r.URL.Query().Get("containerName") follow, _ := strconv.ParseBool(r.URL.Query().Get("follow")) err := handler.GetServiceManager().Log(w, r, component, podName, containerName, follow) if err != nil { httputil.ReturnBcodeError(r, w, err) return } }