package controller
import (
"encoding/json"
"fmt"
apigateway "github.com/goodrain/rainbond/api/controller/apigateway"
"github.com/goodrain/rainbond/pkg/component/k8s"
validation "github.com/goodrain/rainbond/util/endpoint"
"io/ioutil"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"github.com/go-chi/chi"
"github.com/goodrain/rainbond/api/handler"
"github.com/goodrain/rainbond/api/model"
apimodel "github.com/goodrain/rainbond/api/model"
"github.com/goodrain/rainbond/api/util/bcode"
ctxutil "github.com/goodrain/rainbond/api/util/ctx"
"github.com/goodrain/rainbond/cmd"
"github.com/goodrain/rainbond/db"
dberrors "github.com/goodrain/rainbond/db/errors"
dbmodel "github.com/goodrain/rainbond/db/model"
mqclient "github.com/goodrain/rainbond/mq/client"
"github.com/goodrain/rainbond/util/fuzzy"
validator "github.com/goodrain/rainbond/util/govalidator"
httputil "github.com/goodrain/rainbond/util/http"
"github.com/goodrain/rainbond/worker/client"
"github.com/jinzhu/gorm"
"github.com/sirupsen/logrus"
)
type V2Routes struct {
ClusterController
NodesController
TenantStruct
EventLogStruct
AppStruct
LongVersionStruct
GatewayStruct
ThirdPartyServiceController
LabelController
AppRestoreController
PodController
ApplicationController
RegistryAuthSecretStruct
K8sAttributeController
HelmStruct
Registry
apigateway.Struct
KubeBlocksController
}
func (v2 *V2Routes) Show(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(cmd.GetVersion()))
}
func (v2 *V2Routes) Health(w http.ResponseWriter, r *http.Request) {
httputil.ReturnSuccess(r, w, map[string]string{"status": "health", "info": "api service health"})
}
func (v2 *V2Routes) AlertManagerWebHook(w http.ResponseWriter, r *http.Request) {
fmt.Println("=======>webhook")
in, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Println(err)
httputil.ReturnError(r, w, 400, "")
return
}
fmt.Println("=====>body")
fmt.Println(string(in))
httputil.ReturnSuccess(r, w, "")
}
func (v2 *V2Routes) Version(w http.ResponseWriter, r *http.Request) {
httputil.ReturnSuccess(r, w, map[string]string{"version": os.Getenv("RELEASE_DESC")})
}
type TenantStruct struct {
StatusCli *client.AppRuntimeSyncClient
MQClient mqclient.MQClient
}
func (t *TenantStruct) TenantResources(w http.ResponseWriter, r *http.Request) {
var tr apimodel.TenantResources
ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &tr.Body, nil)
if !ok {
return
}
rep, err := handler.GetTenantManager().GetTenantsResources(r.Context(), &tr)
if err != nil {
httputil.ReturnError(r, w, 500, fmt.Sprintf("get resources error, %v", err))
return
}
var re []map[string]interface{}
for _, v := range rep {
if v != nil {
re = append(re, v)
}
}
httputil.ReturnSuccess(r, w, re)
return
}
func (t *TenantStruct) ServiceResources(w http.ResponseWriter, r *http.Request) {
var tr apimodel.ServicesResources
ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &tr.Body, nil)
if !ok {
return
}
rep, err := handler.GetTenantManager().GetServicesResources(&tr)
if err != nil {
httputil.ReturnError(r, w, 500, fmt.Sprintf("get resources error, %v", err))
return
}
httputil.ReturnSuccess(r, w, rep)
return
}
func (t *TenantStruct) TenantsQuery(w http.ResponseWriter, r *http.Request) {
tenantName := strings.TrimSpace(chi.URLParam(r, "tenant_name"))
rep, err := handler.GetTenantManager().GetTenantsName()
if err != nil {
httputil.ReturnError(r, w, 500, fmt.Sprintf("get tenants names error, %v", err))
return
}
result := fuzzy.Find(tenantName, rep)
httputil.ReturnSuccess(r, w, result)
return
}
func (t *TenantStruct) TenantsGetByName(w http.ResponseWriter, r *http.Request) {
tenantName := strings.TrimSpace(chi.URLParam(r, "tenant_name"))
v, err := handler.GetTenantManager().GetTenantsByName(tenantName)
if err != nil {
httputil.ReturnError(r, w, 404, fmt.Sprintf("get tenants names error, %v", err))
return
}
logrus.Infof("query tenant from db by name %s ,got %v", tenantName, v)
tenantServiceRes, err := handler.GetServiceManager().GetTenantRes(v.UUID)
if err != nil {
httputil.ReturnError(r, w, 500, fmt.Sprintf("get tenants service total resources error, %v", err))
return
}
tenantServiceRes.UUID = v.UUID
tenantServiceRes.Name = v.Name
tenantServiceRes.EID = v.EID
httputil.ReturnSuccess(r, w, tenantServiceRes)
return
}
func (t *TenantStruct) TenantsWithResource(w http.ResponseWriter, r *http.Request) {
pageLenStr := strings.TrimSpace(chi.URLParam(r, "pageLen"))
curPageStr := strings.TrimSpace(chi.URLParam(r, "curPage"))
pageLen, err := strconv.Atoi(pageLenStr)
if err != nil {
httputil.ReturnError(r, w, 400, fmt.Sprintf("bad request, %v", err))
return
}
curPage, err := strconv.Atoi(curPageStr)
if err != nil {
httputil.ReturnError(r, w, 400, fmt.Sprintf("bad request, %v", err))
return
}
resource, count, err := handler.GetServiceManager().GetPagedTenantRes((curPage-1)*pageLen, pageLen)
if err != nil {
httputil.ReturnError(r, w, 500, fmt.Sprintf("get tenants error, %v", err))
return
}
var ret apimodel.PagedTenantResList
ret.List = resource
ret.Length = count
httputil.ReturnSuccess(r, w, ret)
return
}
func (t *TenantStruct) SumTenants(w http.ResponseWriter, r *http.Request) {
s, err := handler.GetTenantManager().TenantsSum()
if err != nil {
httputil.ReturnError(r, w, 500, fmt.Sprintf("sum tenants error, %v", err))
return
}
rc := make(map[string]int)
rc["num"] = s
httputil.ReturnSuccess(r, w, rc)
}
func (t *TenantStruct) Tenant(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
t.GetTenant(w, r)
case "DELETE":
t.DeleteTenant(w, r)
case "PUT":
t.UpdateTenant(w, r)
}
}
func (t *TenantStruct) Tenants(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "POST":
t.AddTenant(w, r)
case "GET":
t.GetTenants(w, r)
}
}
func (t *TenantStruct) AddTenant(w http.ResponseWriter, r *http.Request) {
var ts apimodel.AddTenantStruct
ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &ts.Body, nil)
if !ok {
return
}
var dbts dbmodel.Tenants
if ts.Body.Eid != "" {
id, name, errN := handler.GetServiceManager().CreateTenandIDAndName(ts.Body.Eid)
if errN != nil {
httputil.ReturnError(r, w, 500, fmt.Sprintf("create tenant error, %v", errN))
return
}
dbts.EID = ts.Body.Eid
if ts.Body.TenantName == "" {
dbts.Name = name
} else {
dbts.Name = ts.Body.TenantName
name = ts.Body.TenantName
}
if ts.Body.TenantID == "" {
dbts.UUID = id
} else {
dbts.UUID = ts.Body.TenantID
id = ts.Body.TenantID
}
dbts.LimitMemory = ts.Body.LimitMemory
dbts.Namespace = dbts.UUID
if ts.Body.Namespace != "" {
dbts.Namespace = ts.Body.Namespace
}
if err := handler.GetServiceManager().CreateTenant(&dbts, ts.Body.BindExistingNamespace); err != nil {
if strings.HasSuffix(err.Error(), "is exist") {
httputil.ReturnError(r, w, 400, err.Error())
return
}
httputil.ReturnError(r, w, 500, fmt.Sprintf("create tenant error, %v", err))
return
}
rc := make(map[string]string)
rc["tenant_id"] = id
rc["tenang_name"] = name
rc["eid"] = ts.Body.Eid
rc["namespace"] = dbts.Namespace
httputil.ReturnSuccess(r, w, rc)
return
}
if ts.Body.TenantID != "" && ts.Body.TenantName != "" {
dbts.Name = ts.Body.TenantName
dbts.UUID = ts.Body.TenantID
dbts.Namespace = ts.Body.TenantID
if ts.Body.Namespace != "" {
dbts.Namespace = ts.Body.Namespace
}
if err := handler.GetServiceManager().CreateTenant(&dbts, ts.Body.BindExistingNamespace); err != nil {
if strings.HasSuffix(err.Error(), "is exist") {
httputil.ReturnError(r, w, 400, err.Error())
return
}
httputil.ReturnError(r, w, 500, fmt.Sprintf("create tenant error, %v", err))
return
}
httputil.ReturnSuccess(r, w, nil)
return
}
if ts.Body.Eid == "" && (ts.Body.TenantID == "" || ts.Body.TenantName == "") {
httputil.ReturnError(r, w, 400, "args error")
return
}
httputil.ReturnError(r, w, 400, "args error, need eid or tenatn_id / tenant_name")
return
}
func (t *TenantStruct) GetTenants(w http.ResponseWriter, r *http.Request) {
jsonTenantIDs := r.FormValue("tenant_ids")
var tenantIDs []string
err := json.Unmarshal([]byte(jsonTenantIDs), &tenantIDs)
if err != nil {
logrus.Debugf("json unmarshal failure: %v", err)
}
var tenants []*dbmodel.Tenants
tenants, err = handler.GetTenantManager().GetTenantsByTenantIDs(tenantIDs)
if err != nil {
httputil.ReturnError(r, w, 500, "get tenant error")
return
}
list := handler.GetTenantManager().BindTenantsResource(tenants)
httputil.ReturnSuccess(r, w, list)
}
func (t *TenantStruct) DeleteTenant(w http.ResponseWriter, r *http.Request) {
tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string)
if err := handler.GetTenantManager().DeleteTenant(r.Context(), tenantID); err != nil {
if err == handler.ErrTenantStillHasServices || err == handler.ErrTenantStillHasPlugins {
httputil.ReturnError(r, w, 400, err.Error())
return
}
if err == gorm.ErrRecordNotFound {
httputil.ReturnError(r, w, 404, err.Error())
return
}
httputil.ReturnError(r, w, 500, fmt.Sprintf("delete tenant: %v", err))
return
}
httputil.ReturnSuccess(r, w, nil)
}
func (t *TenantStruct) UpdateTenant(w http.ResponseWriter, r *http.Request) {
var ts apimodel.UpdateTenantStruct
ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &ts.Body, nil)
if !ok {
return
}
tenant := r.Context().Value(ctxutil.ContextKey("tenant")).(*dbmodel.Tenants)
tenant.LimitMemory = ts.Body.LimitMemory
if err := handler.GetTenantManager().UpdateTenant(tenant); err != nil {
httputil.ReturnError(r, w, 500, "update tenant error")
return
}
httputil.ReturnSuccess(r, w, tenant)
}
func (t *TenantStruct) GetTenant(w http.ResponseWriter, r *http.Request) {
tenant := r.Context().Value(ctxutil.ContextKey("tenant")).(*dbmodel.Tenants)
list := handler.GetTenantManager().BindTenantsResource([]*dbmodel.Tenants{tenant})
httputil.ReturnSuccess(r, w, list[0])
}
func (t *TenantStruct) ServicesCount(w http.ResponseWriter, r *http.Request) {
allStatus := t.StatusCli.GetAllStatus()
var closed int
var running int
var abnormal int
for _, v := range allStatus {
switch v {
case "closed":
closed++
case "running":
running++
case "abnormal":
abnormal++
}
}
serviceCount := map[string]int{"total": len(allStatus), "running": running, "closed": closed, "abnormal": abnormal}
httputil.ReturnSuccess(r, w, serviceCount)
}
func (t *TenantStruct) ServicesInfo(w http.ResponseWriter, r *http.Request) {
tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string)
services, err := handler.GetServiceManager().GetService(tenantID)
if err != nil {
httputil.ReturnError(r, w, 500, "get tenant services error")
return
}
httputil.ReturnSuccess(r, w, services)
return
}
func (t *TenantStruct) CreateService(w http.ResponseWriter, r *http.Request) {
var ss apimodel.ServiceStruct
if !httputil.ValidatorRequestStructAndErrorResponse(r, w, &ss, nil) {
return
}
if ss.AppID == "" {
httputil.ReturnBcodeError(r, w, bcode.ErrCreateNeedCorrectAppID)
return
}
_, err := handler.GetApplicationHandler().GetAppByID(ss.AppID)
if err != nil {
httputil.ReturnBcodeError(r, w, err)
return
}
handler.GetCleanDateBaseHandler().CleanServiceCheckData(ss.EtcdKey)
values := url.Values{}
if ss.Endpoints != nil {
for _, endpoint := range ss.Endpoints.Static {
if strings.Contains(endpoint, "127.0.0.1") {
values["ip"] = []string{"The ip field is can't contains '127.0.0.1'"}
}
}
if len(values) > 0 {
httputil.ReturnValidationError(r, w, values)
return
}
}
tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string)
ss.TenantID = tenantID
tenant := r.Context().Value(ctxutil.ContextKey("tenant")).(*dbmodel.Tenants)
var noMemory, noCPU, needStorage int
if ss.ContainerCPU == 0 {
noCPU = ss.Replicas
}
if ss.ContainerMemory == 0 {
noMemory = ss.Replicas
}
storages, err := db.GetManager().TenantServiceVolumeDao().GetTenantServiceVolumesByServiceID(ss.ServiceID)
if err != nil {
httputil.ReturnResNotEnough(r, w, ss.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, ss.Replicas*ss.ContainerMemory, ss.Replicas*ss.ContainerCPU, needStorage, noMemory, noCPU); err != nil {
httputil.ReturnResNotEnough(r, w, "", err.Error())
return
}
if err := handler.GetServiceManager().ServiceCreate(&ss); err != nil {
if strings.Contains(err.Error(), "is exist in tenant") {
httputil.ReturnSuccess(r, w, nil)
return
}
httputil.ReturnError(r, w, 500, fmt.Sprintf("create service error, %v", err))
return
}
httputil.ReturnSuccess(r, w, nil)
}
func (t *TenantStruct) UpdateService(w http.ResponseWriter, r *http.Request) {
rules := validator.MapData{
"container_cmd": []string{},
"image_name": []string{},
"container_memory": []string{},
"service_name": []string{},
"extend_method": []string{},
"app_id": []string{},
"k8s_component_name": []string{},
"job_strategy": []string{},
}
data, ok := httputil.ValidatorRequestMapAndErrorResponse(r, w, rules, nil)
if !ok {
return
}
serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
data["service_id"] = serviceID
var appID string
if data["app_id"] != nil && data["app_id"] != "" {
appID = data["app_id"].(string)
_, err := handler.GetApplicationHandler().GetAppByID(appID)
if err != nil {
httputil.ReturnBcodeError(r, w, err)
return
}
}
if err := handler.GetServiceManager().ServiceUpdate(data); err != nil {
httputil.ReturnBcodeError(r, w, err)
return
}
httputil.ReturnSuccess(r, w, nil)
}
func (t *TenantStruct) SetLanguage(w http.ResponseWriter, r *http.Request) {
rules := validator.MapData{
"language": []string{"required"},
}
langS := &apimodel.LanguageSet{}
data, ok := httputil.ValidatorRequestMapAndErrorResponse(r, w, rules, nil)
if !ok {
return
}
langS.Language = data["language"].(string)
langS.ServiceID = r.Context().Value(ctxutil.ContextKey("service_id")).(string)
if err := handler.GetServiceManager().LanguageSet(langS); err != nil {
httputil.ReturnError(r, w, 500, "set language error.")
return
}
httputil.ReturnSuccess(r, w, nil)
return
}
func (t *TenantStruct) StatusService(w http.ResponseWriter, r *http.Request) {
serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
statusList, err := handler.GetServiceManager().GetStatus(serviceID)
if err != nil {
httputil.ReturnError(r, w, 500, fmt.Sprintf("get service list error,%v", err))
return
}
httputil.ReturnSuccess(r, w, statusList)
return
}
func (t *TenantStruct) PostStatusService(w http.ResponseWriter, r *http.Request) {
logrus.Debugf("in status service serviceID")
}
func (t *TenantStruct) StatusServiceList(w http.ResponseWriter, r *http.Request) {
var services apimodel.StatusServiceListStruct
ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &services.Body, nil)
if !ok {
return
}
serviceList := services.Body.ServiceIDs
tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string)
info := handler.GetServiceManager().GetServicesStatus(tenantID, serviceList)
httputil.ReturnSuccess(r, w, info)
}
func (t *TenantStruct) Label(w http.ResponseWriter, r *http.Request) {
var req apimodel.LabelsStruct
ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &req, nil)
if !ok {
return
}
reqJSON, _ := json.Marshal(req)
logrus.Debugf("Request is : %s", string(reqJSON))
values := url.Values{}
if req.Labels == nil || len(req.Labels) == 0 {
values["labels"] = []string{"The labels field should have someting"}
}
for _, label := range req.Labels {
if label.LabelKey == "" {
values["label_key"] = []string{"The label_key field is required"}
}
if label.LabelValue == "" {
values["label_value"] = []string{"The label_value field is required"}
}
}
if len(values) != 0 {
httputil.ReturnValidationError(r, w, values)
return
}
switch r.Method {
case "DELETE":
t.DeleteLabel(w, r, &req)
case "POST":
t.AddLabel(w, r, &req)
case "PUT":
t.UpdateLabel(w, r, &req)
}
}
func (t *TenantStruct) AddLabel(w http.ResponseWriter, r *http.Request, labels *apimodel.LabelsStruct) {
logrus.Debugf("add label")
serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
if err := handler.GetServiceManager().AddLabel(labels, serviceID); err != nil {
httputil.ReturnError(r, w, 500, fmt.Sprintf("add label error, %v", err))
return
}
httputil.ReturnSuccess(r, w, nil)
}
func (t *TenantStruct) DeleteLabel(w http.ResponseWriter, r *http.Request, labels *apimodel.LabelsStruct) {
serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
if err := handler.GetServiceManager().DeleteLabel(labels, serviceID); err != nil {
httputil.ReturnError(r, w, 500, fmt.Sprintf("delete node label failure, %v", err))
return
}
httputil.ReturnSuccess(r, w, nil)
}
func (t *TenantStruct) UpdateLabel(w http.ResponseWriter, r *http.Request, labels *apimodel.LabelsStruct) {
serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
if err := handler.GetServiceManager().UpdateLabel(labels, serviceID); err != nil {
httputil.ReturnError(r, w, 500, fmt.Sprintf("error updating label: %v", err))
return
}
httputil.ReturnSuccess(r, w, nil)
}
func (t *TenantStruct) StatusContainerID(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("status container IDs list"))
}
func (t *TenantStruct) SingleServiceInfo(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "DELETE":
t.DeleteSingleServiceInfo(w, r)
case "GET":
t.GetSingleServiceInfo(w, r)
}
}
func (t *TenantStruct) GetSingleServiceInfo(w http.ResponseWriter, r *http.Request) {
tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string)
serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
tenantName := r.Context().Value(ctxutil.ContextKey("tenant_name")).(string)
serviceName := r.Context().Value(ctxutil.ContextKey("service_alias")).(string)
result := make(map[string]string)
result["tenantName"] = tenantName
result["serviceAlias"] = serviceName
result["tenantId"] = tenantID
result["serviceId"] = serviceID
httputil.ReturnSuccess(r, w, result)
}
func (t *TenantStruct) DeleteSingleServiceInfo(w http.ResponseWriter, r *http.Request) {
serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string)
tenant := r.Context().Value(ctxutil.ContextKey("tenant")).(*dbmodel.Tenants)
service := r.Context().Value(ctxutil.ContextKey("service")).(*dbmodel.TenantServices)
err := k8s.Default().ApiSixClient.ApisixV2().ApisixRoutes(tenant.Namespace).DeleteCollection(r.Context(), v1.DeleteOptions{}, v1.ListOptions{
LabelSelector: "component_sort=" + service.ServiceAlias,
})
if err != nil {
logrus.Errorf("delete apisix route error: %v", err)
}
var req apimodel.EtcdCleanReq
if httputil.ValidatorRequestStructAndErrorResponse(r, w, &req, nil) {
logrus.Debugf("delete service etcd keys : %+v", req.Keys)
handler.GetCleanDateBaseHandler().CleanAllServiceData(req.Keys)
}
if err := handler.GetServiceManager().TransServieToDelete(r.Context(), tenantID, serviceID); err != nil {
if err == handler.ErrServiceNotClosed {
httputil.ReturnError(r, w, 400, fmt.Sprintf("Service must be closed"))
return
}
httputil.ReturnError(r, w, 500, fmt.Sprintf("delete service error, %v", err))
return
}
httputil.ReturnSuccess(r, w, nil)
}
func (t *TenantStruct) Dependency(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "DELETE":
t.DeleteDependency(w, r)
case "POST":
t.AddDependency(w, r)
}
}
func (t *TenantStruct) Dependencys(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "POST":
t.AddDependencys(w, r)
}
}
func (t *TenantStruct) AddDependency(w http.ResponseWriter, r *http.Request) {
rules := validator.MapData{
"dep_service_id": []string{"required"},
"dep_service_type": []string{"required"},
"namespace": []string{"required"},
"dep_sa_name": []string{},
"dep_order": []string{},
}
data, ok := httputil.ValidatorRequestMapAndErrorResponse(r, w, rules, nil)
if !ok {
return
}
ds := &apimodel.DependService{
TenantID: r.Context().Value(ctxutil.ContextKey("tenant_id")).(string),
ServiceID: r.Context().Value(ctxutil.ContextKey("service_id")).(string),
DepServiceID: data["dep_service_id"].(string),
DepServiceType: data["dep_service_type"].(string),
DepSAName: data["dep_sa_name"].(string),
Namespace: data["namespace"].(string),
}
if err := handler.GetServiceManager().ServiceDepend("add", ds); err != nil {
httputil.ReturnError(r, w, 500, fmt.Sprintf("add dependency error, %v", err))
return
}
httputil.ReturnSuccess(r, w, nil)
}
func (t *TenantStruct) AddDependencys(w http.ResponseWriter, r *http.Request) {
rules := validator.MapData{
"be_dep_service_ids": []string{"required"},
"dep_service_type": []string{"required"},
"dep_order": []string{},
}
data, ok := httputil.ValidatorRequestMapAndErrorResponse(r, w, rules, nil)
if !ok {
httputil.ReturnError(r, w, 500, "add dependency error")
return
}
var relations []*dbmodel.TenantServiceRelation
for _, beDepServiceID := range strings.Split(data["be_dep_service_ids"].(string), ",") {
relations = append(relations, &dbmodel.TenantServiceRelation{
TenantID: r.Context().Value(ctxutil.ContextKey("tenant_id")).(string),
ServiceID: beDepServiceID,
DependServiceID: r.Context().Value(ctxutil.ContextKey("service_id")).(string),
DependServiceType: data["dep_service_type"].(string),
DependOrder: 1,
})
}
err := db.GetManager().TenantServiceRelationDao().CreateOrUpdateRelationsInBatch(relations)
if err != nil {
httputil.ReturnError(r, w, 500, fmt.Sprintf("add dependency error, %v", err))
return
}
httputil.ReturnSuccess(r, w, nil)
}
func (t *TenantStruct) DeleteDependency(w http.ResponseWriter, r *http.Request) {
logrus.Debugf("trans delete depend service ")
rules := validator.MapData{
"dep_service_id": []string{"required"},
"dep_service_type": []string{},
"dep_order": []string{},
"namespace": []string{"required"},
"dep_sa_name": []string{},
}
data, ok := httputil.ValidatorRequestMapAndErrorResponse(r, w, rules, nil)
if !ok {
return
}
ds := &apimodel.DependService{
TenantID: r.Context().Value(ctxutil.ContextKey("tenant_id")).(string),
ServiceID: r.Context().Value(ctxutil.ContextKey("service_id")).(string),
DepServiceID: data["dep_service_id"].(string),
DepSAName: data["dep_sa_name"].(string),
Namespace: data["namespace"].(string),
}
if err := handler.GetServiceManager().ServiceDepend("delete", ds); err != nil {
httputil.ReturnError(r, w, 500, fmt.Sprintf("delete dependency error, %v", err))
return
}
httputil.ReturnSuccess(r, w, nil)
}
func (t *TenantStruct) Env(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "DELETE":
t.DeleteEnv(w, r)
case "POST":
t.AddEnv(w, r)
case "PUT":
t.UpdateEnv(w, r)
}
}
func (t *TenantStruct) AddEnv(w http.ResponseWriter, r *http.Request) {
var envM apimodel.AddTenantServiceEnvVar
if !httputil.ValidatorRequestStructAndErrorResponse(r, w, &envM, nil) {
return
}
tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string)
serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
var envD dbmodel.TenantServiceEnvVar
envD.AttrName = envM.AttrName
envD.AttrValue = envM.AttrValue
envD.TenantID = tenantID
envD.ServiceID = serviceID
envD.ContainerPort = envM.ContainerPort
envD.IsChange = envM.IsChange
envD.Name = envM.Name
envD.Scope = envM.Scope
if err := handler.GetServiceManager().EnvAttr("add", &envD); err != nil {
if err == dberrors.ErrRecordAlreadyExist {
httputil.ReturnError(r, w, 400, fmt.Sprintf("%v", err))
return
}
logrus.Errorf("Add env error, %v", err)
httputil.ReturnError(r, w, 500, fmt.Sprintf("Add env error, %v", err))
return
}
httputil.ReturnSuccess(r, w, nil)
}
func (t *TenantStruct) UpdateEnv(w http.ResponseWriter, r *http.Request) {
var envM apimodel.AddTenantServiceEnvVar
if !httputil.ValidatorRequestStructAndErrorResponse(r, w, &envM, nil) {
return
}
tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string)
serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
var envD dbmodel.TenantServiceEnvVar
envD.AttrName = envM.AttrName
envD.AttrValue = envM.AttrValue
envD.TenantID = tenantID
envD.ServiceID = serviceID
envD.ContainerPort = envM.ContainerPort
envD.IsChange = envM.IsChange
envD.Name = envM.Name
envD.Scope = envM.Scope
if err := handler.GetServiceManager().EnvAttr("update", &envD, envM.OldAttrName); err != nil {
logrus.Errorf("update env error, %v", err)
httputil.ReturnError(r, w, 500, fmt.Sprintf("update env error, %v", err))
return
}
httputil.ReturnSuccess(r, w, nil)
}
func (t *TenantStruct) DeleteEnv(w http.ResponseWriter, r *http.Request) {
var envM apimodel.DelTenantServiceEnvVar
if !httputil.ValidatorRequestStructAndErrorResponse(r, w, &envM, nil) {
return
}
tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string)
serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
envM.TenantID = tenantID
envM.ServiceID = serviceID
var envD dbmodel.TenantServiceEnvVar
envD.AttrName = envM.AttrName
envD.AttrValue = envM.AttrValue
envD.TenantID = tenantID
envD.ServiceID = serviceID
envD.ContainerPort = envM.ContainerPort
envD.IsChange = envM.IsChange
envD.Name = envM.Name
envD.Scope = envM.Scope
if err := handler.GetServiceManager().EnvAttr("delete", &envD); err != nil {
logrus.Errorf("delete env error, %v", err)
if err.Error() == gorm.ErrRecordNotFound.Error() {
httputil.ReturnError(r, w, 404, "service port "+err.Error())
return
}
httputil.ReturnError(r, w, 500, fmt.Sprintf("Delete env error, %v", err))
return
}
httputil.ReturnSuccess(r, w, nil)
}
func (t *TenantStruct) Ports(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "DELETE":
t.deletePortController(w, r)
case "POST":
t.addPortController(w, r)
case "PUT":
t.updatePortController(w, r)
}
}
func (t *TenantStruct) PutPorts(w http.ResponseWriter, r *http.Request) {
tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string)
serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
var ports apimodel.ServicePorts
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &ports, nil); !ok {
return
}
if err := handler.GetServiceManager().PortVar("update", tenantID, serviceID, &ports, 0); err != nil {
httputil.ReturnBcodeError(r, w, err)
return
}
httputil.ReturnSuccess(r, w, nil)
}
func (t *TenantStruct) addPortController(w http.ResponseWriter, r *http.Request) {
tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string)
serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
var ports apimodel.ServicePorts
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &ports, nil); !ok {
return
}
if err := handler.GetServiceManager().CreatePorts(tenantID, serviceID, &ports); err != nil {
logrus.Errorf("add port error. %v", err)
httputil.ReturnError(r, w, 500, err.Error())
return
}
httputil.ReturnSuccess(r, w, ports.Port)
}
func (t *TenantStruct) updatePortController(w http.ResponseWriter, r *http.Request) {
tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string)
serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
portStr := chi.URLParam(r, "port")
oldPort, err := strconv.Atoi(portStr)
if err != nil {
httputil.ReturnError(r, w, 400, "port must be a number")
return
}
var ports apimodel.ServicePorts
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &ports, nil); !ok {
return
}
if err := handler.GetServiceManager().PortVar("update", tenantID, serviceID, &ports, oldPort); err != nil {
logrus.Errorf("update port error. %v", err)
httputil.ReturnError(r, w, 500, err.Error())
return
}
httputil.ReturnSuccess(r, w, nil)
}
func (t *TenantStruct) deletePortController(w http.ResponseWriter, r *http.Request) {
tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string)
serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
portStr := chi.URLParam(r, "port")
oldPort, err := strconv.Atoi(portStr)
if err != nil {
httputil.ReturnError(r, w, 400, "port must be a number")
return
}
var port = &apimodel.TenantServicesPort{
TenantID: tenantID,
ServiceID: serviceID,
ContainerPort: oldPort,
}
var ports apimodel.ServicePorts
ports.Port = append(ports.Port, port)
if err := handler.GetServiceManager().PortVar("delete", tenantID, serviceID, &ports, oldPort); err != nil {
if err.Error() == gorm.ErrRecordNotFound.Error() {
httputil.ReturnError(r, w, 404, "port can not found")
return
}
httputil.ReturnError(r, w, 500, err.Error())
return
}
httputil.ReturnSuccess(r, w, nil)
}
func (t *TenantStruct) PortOuterController(w http.ResponseWriter, r *http.Request) {
var data apimodel.ServicePortInnerOrOuter
if !httputil.ValidatorRequestStructAndErrorResponse(r, w, &(data.Body), nil) {
return
}
serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
service := r.Context().Value(ctxutil.ContextKey("service")).(*dbmodel.TenantServices)
if dbmodel.ServiceKind(service.Kind) == dbmodel.ServiceKindThirdParty {
endpoints, err := db.GetManager().EndpointsDao().List(serviceID)
if err != nil {
logrus.Errorf("find endpoints by sid[%s], error: %s", serviceID, err.Error())
httputil.ReturnError(r, w, 500, "fund endpoints failure")
return
}
for _, ep := range endpoints {
address := validation.SplitEndpointAddress(ep.IP)
if validation.IsDomainNotIP(address) {
httputil.ReturnError(r, w, 400, "do not allow operate outer port for thirdpart domain endpoints")
return
}
}
}
tenantName := r.Context().Value(ctxutil.ContextKey("tenant_name")).(string)
portStr := chi.URLParam(r, "port")
containerPort, err := strconv.Atoi(portStr)
if err != nil {
httputil.ReturnError(r, w, 400, "port must be a number")
return
}
vsPort, _, errV := handler.GetServiceManager().PortOuter(tenantName, serviceID, containerPort, &data)
if errV != nil {
if strings.HasSuffix(errV.Error(), gorm.ErrRecordNotFound.Error()) {
httputil.ReturnError(r, w, 404, errV.Error())
return
}
httputil.ReturnError(r, w, 500, errV.Error())
return
}
rc := make(map[string]string)
rc["domain"] = ""
rc["port"] = fmt.Sprintf("%v", vsPort.Port)
if err := handler.GetGatewayHandler().SendTaskDeprecated(map[string]interface{}{
"service_id": serviceID,
"action": "port-" + data.Body.Operation,
"port": containerPort,
"is_inner": false,
}); err != nil {
logrus.Errorf("send runtime message about gateway failure %s", err.Error())
}
httputil.ReturnSuccess(r, w, rc)
}
func (t *TenantStruct) PortInnerController(w http.ResponseWriter, r *http.Request) {
var data apimodel.ServicePortInnerOrOuter
if !httputil.ValidatorRequestStructAndErrorResponse(r, w, &(data.Body), nil) {
return
}
serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
tenantName := r.Context().Value(ctxutil.ContextKey("tenant_name")).(string)
portStr := chi.URLParam(r, "port")
containerPort, err := strconv.Atoi(portStr)
if err != nil {
httputil.ReturnError(r, w, 400, "port must be a number")
return
}
if err := handler.GetServiceManager().PortInner(tenantName, serviceID, data.Body.Operation, containerPort); err != nil {
if err.Error() == gorm.ErrRecordNotFound.Error() {
httputil.ReturnError(r, w, 404, "service port "+err.Error())
return
} else if err.Error() == "already open" || err.Error() == "already close" {
httputil.Return(r, w, 200, httputil.ResponseBody{Msg: err.Error()})
return
} else {
httputil.ReturnError(r, w, 500, err.Error())
return
}
}
if err := handler.GetGatewayHandler().SendTaskDeprecated(map[string]interface{}{
"service_id": serviceID,
"action": "port-" + data.Body.Operation,
"port": containerPort,
"is_inner": true,
}); err != nil {
logrus.Errorf("send runtime message about gateway failure %s", err.Error())
}
httputil.ReturnSuccess(r, w, nil)
}
func (t *TenantStruct) Pods(w http.ResponseWriter, r *http.Request) {
serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
pods, err := handler.GetServiceManager().GetPods(serviceID)
if err != nil {
if err.Error() == gorm.ErrRecordNotFound.Error() {
logrus.Error("record not found:", err)
httputil.ReturnError(r, w, 404, fmt.Sprintf("get pods error, %v", err))
return
}
logrus.Error("get pods error:", err)
httputil.ReturnError(r, w, 500, fmt.Sprintf("get pods error, %v", err))
return
}
httputil.ReturnSuccess(r, w, pods)
}
func (t *TenantStruct) Probe(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "PUT":
t.UpdateProbe(w, r)
case "DELETE":
t.DeleteProbe(w, r)
case "POST":
t.AddProbe(w, r)
}
}
func (t *TenantStruct) AddProbe(w http.ResponseWriter, r *http.Request) {
serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
var tsp apimodel.ServiceProbe
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &tsp, nil); !ok {
return
}
var tspD dbmodel.TenantServiceProbe
tspD.ServiceID = serviceID
tspD.Cmd = tsp.Cmd
tspD.FailureThreshold = tsp.FailureThreshold
tspD.HTTPHeader = tsp.HTTPHeader
tspD.InitialDelaySecond = tsp.InitialDelaySecond
tspD.IsUsed = &tsp.IsUsed
tspD.Mode = tsp.Mode
tspD.Path = tsp.Path
tspD.PeriodSecond = tsp.PeriodSecond
tspD.Port = tsp.Port
tspD.ProbeID = tsp.ProbeID
tspD.Scheme = tsp.Scheme
tspD.SuccessThreshold = tsp.SuccessThreshold
tspD.TimeoutSecond = tsp.TimeoutSecond
tspD.FailureAction = tsp.FailureAction
if err := handler.GetServiceManager().ServiceProbe(&tspD, "add"); err != nil {
httputil.ReturnError(r, w, 500, fmt.Sprintf("add service probe error, %v", err))
return
}
httputil.ReturnSuccess(r, w, nil)
}
func (t *TenantStruct) UpdateProbe(w http.ResponseWriter, r *http.Request) {
serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
var tsp apimodel.ServiceProbe
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &tsp, nil); !ok {
return
}
var tspD dbmodel.TenantServiceProbe
tspD.ServiceID = serviceID
tspD.Cmd = tsp.Cmd
tspD.FailureThreshold = tsp.FailureThreshold
tspD.HTTPHeader = tsp.HTTPHeader
tspD.InitialDelaySecond = tsp.InitialDelaySecond
tspD.IsUsed = &tsp.IsUsed
tspD.Mode = tsp.Mode
tspD.Path = tsp.Path
tspD.PeriodSecond = tsp.PeriodSecond
tspD.Port = tsp.Port
tspD.ProbeID = tsp.ProbeID
tspD.Scheme = tsp.Scheme
tspD.SuccessThreshold = tsp.SuccessThreshold
tspD.TimeoutSecond = tsp.TimeoutSecond
if err := handler.GetServiceManager().ServiceProbe(&tspD, "update"); err != nil {
if err.Error() == gorm.ErrRecordNotFound.Error() {
httputil.ReturnError(r, w, 404, fmt.Sprintf("update prob error, %v", err))
return
}
httputil.ReturnError(r, w, 500, fmt.Sprintf("update service probe error, %v", err))
return
}
httputil.ReturnSuccess(r, w, nil)
}
func (t *TenantStruct) DeleteProbe(w http.ResponseWriter, r *http.Request) {
serviceID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
var tsp apimodel.ServiceProbe
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &tsp, nil); !ok {
return
}
var tspD dbmodel.TenantServiceProbe
tspD.ServiceID = serviceID
tspD.ProbeID = tsp.ProbeID
if err := handler.GetServiceManager().ServiceProbe(&tspD, "delete"); err != nil {
if err.Error() == gorm.ErrRecordNotFound.Error() {
httputil.ReturnError(r, w, 404, fmt.Sprintf("delete prob error, %v", err))
return
}
httputil.ReturnError(r, w, 500, fmt.Sprintf("delete service probe error, %v", err))
return
}
httputil.ReturnSuccess(r, w, nil)
}
func (t *TenantStruct) Port(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "PUT":
t.UpdatePort(w, r)
case "DELETE":
t.DeletePort(w, r)
case "POST":
t.AddPort(w, r)
}
}
func (t *TenantStruct) AddPort(w http.ResponseWriter, r *http.Request) {
}
func (t *TenantStruct) DeletePort(w http.ResponseWriter, r *http.Request) {
}
func (t *TenantStruct) UpdatePort(w http.ResponseWriter, r *http.Request) {
}
func (t *TenantStruct) SingleTenantResources(w http.ResponseWriter, r *http.Request) {
tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string)
services, err := handler.GetServiceManager().GetService(tenantID)
if err != nil {
msg := httputil.ResponseBody{
Msg: fmt.Sprintf("get service error, %v", err),
}
httputil.Return(r, w, 500, msg)
}
statsInfo, _ := handler.GetTenantManager().StatsMemCPU(services)
statsInfo.UUID = tenantID
httputil.ReturnSuccess(r, w, statsInfo)
return
}
func (t *TenantStruct) GetSupportProtocols(w http.ResponseWriter, r *http.Request) {
rps, err := handler.GetTenantManager().GetProtocols()
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, rps)
return
}
func (t *TenantStruct) TransPlugins(w http.ResponseWriter, r *http.Request) {
var tps apimodel.TransPlugins
ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &tps.Body, nil)
if !ok {
return
}
tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string)
tenantName := r.Context().Value(ctxutil.ContextKey("tenant_name")).(string)
rc := make(map[string]string)
err := handler.GetTenantManager().TransPlugins(tenantID, tenantName, tps.Body.FromTenantName, tps.Body.PluginsID)
if err != nil {
err.Handle(r, w)
return
}
rc["result"] = "success"
httputil.ReturnSuccess(r, w, rc)
}
func (t *TenantStruct) CheckResourceName(w http.ResponseWriter, r *http.Request) {
var req model.CheckResourceNameReq
if !httputil.ValidatorRequestStructAndErrorResponse(r, w, &req, nil) {
return
}
tenant := r.Context().Value(ctxutil.ContextKey("tenant")).(*dbmodel.Tenants)
res, err := handler.GetTenantManager().CheckResourceName(r.Context(), tenant.UUID, &req)
if err != nil {
httputil.ReturnBcodeError(r, w, err)
return
}
httputil.ReturnSuccess(r, w, res)
}
func (t *TenantStruct) TenantStartService(w http.ResponseWriter, r *http.Request) {
tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string)
services, err := db.GetManager().TenantServiceDao().GetStartServicesAllInfoByTenantID(tenantID)
if err != nil {
logrus.Errorf("pass tenantid %v because get services failure: %v", tenantID, err)
httputil.ReturnSuccess(r, w, "")
}
tenant, err := db.GetManager().TenantDao().GetTenantByUUID(tenantID)
if err != nil {
logrus.Errorf("pass tenantid %v because get tenant failure: %v", tenantID, err)
httputil.ReturnSuccess(r, w, "")
}
for _, service := range services {
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(service.ServiceID)
if err != nil {
break
}
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 {
break
}
}
startStopStruct := &apimodel.StartStopStruct{
TenantID: tenantID,
ServiceID: service.ServiceID,
EventID: "",
TaskType: "start",
}
if err := handler.GetServiceManager().StartStopService(startStopStruct); err != nil {
break
}
}
if err != nil {
logrus.Errorf("start services failure: %v", err)
httputil.ReturnSuccess(r, w, "")
}
httputil.ReturnSuccess(r, w, "")
}