api/controller/cluster.go
Rama main · réplica de lectura del repositorio original.
// 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 controller
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"github.com/go-chi/chi"
"github.com/goodrain/rainbond-operator/api/v1alpha1"
"github.com/goodrain/rainbond-operator/util/constants"
"github.com/goodrain/rainbond/api/handler"
"github.com/goodrain/rainbond/api/model"
"github.com/goodrain/rainbond/builder/parser/code"
"github.com/goodrain/rainbond/db"
dbmodel "github.com/goodrain/rainbond/db/model"
"github.com/goodrain/rainbond/pkg/component/k8s"
utils "github.com/goodrain/rainbond/util"
"github.com/jinzhu/gorm"
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/types"
k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
httputil "github.com/goodrain/rainbond/util/http"
)
// ClusterController -
type ClusterController struct {
}
// GetClusterInfo -
func (c *ClusterController) GetClusterInfo(w http.ResponseWriter, r *http.Request) {
nodes, err := handler.GetClusterHandler().GetClusterInfo(r.Context())
if err != nil {
logrus.Errorf("get cluster info: %v", err)
httputil.ReturnError(r, w, 500, err.Error())
return
}
httputil.ReturnSuccess(r, w, nodes)
}
// MavenSettingList maven setting list
func (c *ClusterController) MavenSettingList(w http.ResponseWriter, r *http.Request) {
httputil.ReturnSuccess(r, w, handler.GetClusterHandler().MavenSettingList(r.Context()))
}
// MavenSettingAdd maven setting add
func (c *ClusterController) MavenSettingAdd(w http.ResponseWriter, r *http.Request) {
var set handler.MavenSetting
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &set, nil); !ok {
return
}
if err := handler.GetClusterHandler().MavenSettingAdd(r.Context(), &set); err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, &set)
}
// MavenSettingUpdate maven setting file update
func (c *ClusterController) MavenSettingUpdate(w http.ResponseWriter, r *http.Request) {
type SettingUpdate struct {
Content string `json:"content" validate:"required"`
}
var su SettingUpdate
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &su, nil); !ok {
return
}
set := &handler.MavenSetting{
Name: chi.URLParam(r, "name"),
Content: su.Content,
}
if err := handler.GetClusterHandler().MavenSettingUpdate(r.Context(), set); err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, set)
}
// MavenSettingDelete maven setting file delete
func (c *ClusterController) MavenSettingDelete(w http.ResponseWriter, r *http.Request) {
err := handler.GetClusterHandler().MavenSettingDelete(r.Context(), chi.URLParam(r, "name"))
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, nil)
}
// MavenSettingDetail maven setting file delete
func (c *ClusterController) MavenSettingDetail(w http.ResponseWriter, r *http.Request) {
setting, err := handler.GetClusterHandler().MavenSettingDetail(r.Context(), chi.URLParam(r, "name"))
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, setting)
}
// BatchGetGateway batch get resource gateway
func (c *ClusterController) BatchGetGateway(w http.ResponseWriter, r *http.Request) {
ns, err := handler.GetClusterHandler().BatchGetGateway(r.Context())
if err != nil {
logrus.Error(err)
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, ns)
}
// GetNamespace Get the unconnected namespaces under the current cluster
func (c *ClusterController) GetNamespace(w http.ResponseWriter, r *http.Request) {
content := r.FormValue("content")
format := r.FormValue("format")
if format == "detail" {
ns, err := handler.GetClusterHandler().GetNamespaceDetail(r.Context(), content)
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, ns)
return
}
ns, err := handler.GetClusterHandler().GetNamespace(r.Context(), content)
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, ns)
}
// GetNamespaceResource Get all resources in the current namespace
func (c *ClusterController) GetNamespaceResource(w http.ResponseWriter, r *http.Request) {
content := r.FormValue("content")
namespace := r.FormValue("namespace")
rs, err := handler.GetClusterHandler().GetNamespaceSource(r.Context(), content, namespace)
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, rs)
}
// ConvertResource Get the resources under the current namespace to the rainbond platform
func (c *ClusterController) ConvertResource(w http.ResponseWriter, r *http.Request) {
content := r.FormValue("content")
namespace := r.FormValue("namespace")
rs, err := handler.GetClusterHandler().GetNamespaceSource(r.Context(), content, namespace)
if err != nil {
err.Handle(r, w)
return
}
appsServices, err := handler.GetClusterHandler().ConvertResource(r.Context(), namespace, rs)
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, appsServices)
}
// ResourceImport Import the converted k8s resources into recognition
func (c *ClusterController) ResourceImport(w http.ResponseWriter, r *http.Request) {
content := r.FormValue("content")
namespace := r.FormValue("namespace")
eid := r.FormValue("eid")
rs, err := handler.GetClusterHandler().GetNamespaceSource(r.Context(), content, namespace)
if err != nil {
err.Handle(r, w)
return
}
appsServices, err := handler.GetClusterHandler().ConvertResource(r.Context(), namespace, rs)
if err != nil {
err.Handle(r, w)
return
}
rri, err := handler.GetClusterHandler().ResourceImport(namespace, appsServices, eid)
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, rri)
}
// GetResource -
func (c *ClusterController) GetResource(w http.ResponseWriter, r *http.Request) {
var hr model.HandleResource
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &hr, nil); !ok {
return
}
rri, err := handler.GetClusterHandler().GetAppK8SResource(r.Context(), hr.Namespace, hr.AppID, hr.Name, hr.ResourceYaml, hr.Kind)
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, rri)
}
// AddResource -
func (c *ClusterController) AddResource(w http.ResponseWriter, r *http.Request) {
var hr model.AddHandleResource
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &hr, nil); !ok {
return
}
rri, err := handler.GetClusterHandler().AddAppK8SResource(r.Context(), hr.Namespace, hr.AppID, hr.ResourceYaml)
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, rri)
}
// UpdateResource -
func (c *ClusterController) UpdateResource(w http.ResponseWriter, r *http.Request) {
var hr model.HandleResource
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &hr, nil); !ok {
return
}
rri, err := handler.GetClusterHandler().UpdateAppK8SResource(r.Context(), hr.Namespace, hr.AppID, hr.Name, hr.ResourceYaml, hr.Kind)
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, rri)
}
// DeleteResource -
func (c *ClusterController) DeleteResource(w http.ResponseWriter, r *http.Request) {
var hr model.HandleResource
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &hr, nil); !ok {
return
}
result, err := handler.GetClusterHandler().DeleteK8SResources(r.Context(), &model.K8sResourceDeletionRequest{
AppID: hr.AppID,
CascadeCRD: hr.CascadeCRD,
K8sResources: []model.HandleResource{hr},
})
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, result)
}
// PreviewDeleteResources returns the cluster-wide effect of deleting application resources.
func (c *ClusterController) PreviewDeleteResources(w http.ResponseWriter, r *http.Request) {
var req model.K8sResourceDeletionRequest
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &req, nil); !ok {
return
}
impact, err := handler.GetClusterHandler().PreviewK8SResourceDeletion(r.Context(), &req)
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, impact)
}
// BatchDeleteResource -
func (c *ClusterController) BatchDeleteResource(w http.ResponseWriter, r *http.Request) {
var req model.K8sResourceDeletionRequest
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &req, nil); !ok {
return
}
result, err := handler.GetClusterHandler().DeleteK8SResources(r.Context(), &req)
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, result)
}
// ReconcileResources removes metadata for resources confirmed absent from Kubernetes.
func (c *ClusterController) ReconcileResources(w http.ResponseWriter, r *http.Request) {
var req model.K8sResourceReconcileRequest
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &req, nil); !ok {
return
}
result, err := handler.GetClusterHandler().ReconcileK8SResources(r.Context(), &req)
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, result)
}
// SyncResource -
func (c *ClusterController) SyncResource(w http.ResponseWriter, r *http.Request) {
var req model.SyncResources
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &req, nil); !ok {
return
}
resources, err := handler.GetClusterHandler().SyncAppK8SResources(r.Context(), &req)
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, resources)
}
// YamlResourceName -
func (c *ClusterController) YamlResourceName(w http.ResponseWriter, r *http.Request) {
var yr model.YamlResource
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &yr, nil); !ok {
return
}
h, err := handler.GetClusterHandler().AppYamlResourceName(yr)
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, h)
}
// YamlResourceDetailed -
func (c *ClusterController) YamlResourceDetailed(w http.ResponseWriter, r *http.Request) {
var yr model.YamlResource
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &yr, nil); !ok {
return
}
h, err := handler.GetClusterHandler().AppYamlResourceDetailed(yr, false)
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, h)
}
// YamlResourceImport -
func (c *ClusterController) YamlResourceImport(w http.ResponseWriter, r *http.Request) {
var yr model.YamlResource
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &yr, nil); !ok {
return
}
ar, err := handler.GetClusterHandler().AppYamlResourceDetailed(yr, true)
if err != nil {
err.Handle(r, w)
return
}
ac, err := handler.GetClusterHandler().AppYamlResourceImport(yr.Namespace, yr.TenantID, yr.AppID, ar)
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, ac)
}
// CreateShellPod -
func (c *ClusterController) CreateShellPod(w http.ResponseWriter, r *http.Request) {
var sp model.ShellPod
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &sp, nil); !ok {
return
}
pod, err := handler.GetClusterHandler().CreateShellPod(sp.RegionName)
if err != nil {
logrus.Error("create shell pod error:", err)
return
}
httputil.ReturnSuccess(r, w, pod)
}
// DeleteShellPod -
func (c *ClusterController) DeleteShellPod(w http.ResponseWriter, r *http.Request) {
var sp model.ShellPod
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &sp, nil); !ok {
return
}
err := handler.GetClusterHandler().DeleteShellPod(sp.PodName)
if err != nil {
logrus.Error("delete shell pod error:", err)
return
}
httputil.ReturnSuccess(r, w, "")
}
// RbdLog -
func (c *ClusterController) RbdLog(w http.ResponseWriter, r *http.Request) {
podName := r.URL.Query().Get("pod_name")
follow, _ := strconv.ParseBool(r.URL.Query().Get("follow"))
err := handler.GetClusterHandler().RbdLog(w, r, podName, follow)
if err != nil {
httputil.ReturnBcodeError(r, w, err)
return
}
return
}
// GetRbdPods -
func (c *ClusterController) GetRbdPods(w http.ResponseWriter, r *http.Request) {
res, err := handler.GetClusterHandler().GetRbdPods()
if err != nil {
httputil.ReturnBcodeError(r, w, err)
return
}
httputil.ReturnSuccess(r, w, res)
}
// ListRainbondComponents -
func (c *ClusterController) ListRainbondComponents(w http.ResponseWriter, r *http.Request) {
components, err := handler.GetClusterHandler().ListRainbondComponents(r.Context())
if err != nil {
logrus.Errorf("get rainbond components error: %v", err)
httputil.ReturnError(r, w, 500, err.Error())
return
}
httputil.ReturnSuccess(r, w, components)
}
// Upgrade 自动升级集群端的镜像
func (c *ClusterController) Upgrade(w http.ResponseWriter, r *http.Request) {
body := make(map[string]string)
err := json.NewDecoder(r.Body).Decode(&body)
if err != nil {
httputil.ReturnBcodeError(r, w, err)
return
}
res := make([]string, 0)
for k, v := range body {
var cpt v1alpha1.RbdComponent
err := k8s.Default().K8sClient.Get(context.Background(),
types.NamespacedName{Namespace: utils.GetenvDefault("RBD_NAMESPACE", constants.Namespace), Name: k}, &cpt)
if err != nil {
res = append(res, fmt.Sprintf(`%s获取异常%s`, k, err.Error()))
continue
}
original := cpt.DeepCopy()
cpt.Spec.Image = v
logrus.Infof("upgrade [%s] image to [%s]", k, v)
// Patch only the image so fields absent from the vendored CRD type are preserved.
err = k8s.Default().K8sClient.Patch(context.Background(), &cpt, k8sclient.MergeFrom(original))
if err != nil {
res = append(res, fmt.Sprintf(`%s更新异常%s`, k, err.Error()))
continue
}
}
httputil.ReturnSuccess(r, w, res)
}
// ListUpgradeStatus -
func (c *ClusterController) ListUpgradeStatus(w http.ResponseWriter, r *http.Request) {
status, err := handler.GetClusterHandler().ListUpgradeStatus()
if err != nil {
httputil.ReturnBcodeError(r, w, err)
return
}
httputil.ReturnSuccess(r, w, status)
}
// ListPlugins -
func (c *ClusterController) ListPlugins(w http.ResponseWriter, r *http.Request) {
official, _ := strconv.ParseBool(r.URL.Query().Get("official"))
res, err := handler.GetClusterHandler().ListPlugins(official)
if err != nil {
httputil.ReturnBcodeError(r, w, err)
return
}
httputil.ReturnSuccess(r, w, res)
}
// PluginExists is a lightweight probe that reports whether an official plugin
// with the given name exists in the cluster. It avoids the heavy enrichment in
// ListPlugins so callers that only need existence (e.g. edition detection) stay
// fast even when the worker/status service is slow.
func (c *ClusterController) PluginExists(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
exist, err := handler.GetClusterHandler().PluginExists(name)
if err != nil {
httputil.ReturnBcodeError(r, w, err)
return
}
httputil.ReturnSuccess(r, w, map[string]bool{"exist": exist})
}
// CreateRBDPlugin creates an RBDPlugin CR
func (c *ClusterController) CreateRBDPlugin(w http.ResponseWriter, r *http.Request) {
var req model.CreateRBDPluginReq
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &req, nil); !ok {
return
}
if err := handler.GetClusterHandler().CreatePlugin(&req); err != nil {
httputil.ReturnBcodeError(r, w, err)
return
}
httputil.ReturnSuccess(r, w, nil)
}
// ListAbilities -
func (c *ClusterController) ListAbilities(w http.ResponseWriter, r *http.Request) {
res, err := handler.GetClusterHandler().ListAbilities()
if err != nil {
httputil.ReturnError(r, w, 400, err.Error())
return
}
var abilities []model.AbilityResp
for _, ability := range res {
abilities = append(abilities, model.AbilityResp{
Name: ability.GetName(),
Kind: ability.GetKind(),
APIVersion: ability.GetAPIVersion(),
AbilityID: handler.GetClusterHandler().GenerateAbilityID(&ability),
})
}
httputil.ReturnSuccess(r, w, abilities)
}
// GetAbility -
func (c *ClusterController) GetAbility(w http.ResponseWriter, r *http.Request) {
abilityID := chi.URLParam(r, "ability_id")
res, err := handler.GetClusterHandler().GetAbility(abilityID)
if err != nil {
httputil.ReturnError(r, w, 400, err.Error())
return
}
httputil.ReturnSuccess(r, w, res)
}
// UpdateAbility -
func (c *ClusterController) UpdateAbility(w http.ResponseWriter, r *http.Request) {
abilityID := chi.URLParam(r, "ability_id")
var req model.UpdateAbilityReq
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &req, nil); !ok {
return
}
if err := handler.GetClusterHandler().UpdateAbility(abilityID, req.Object); err != nil {
httputil.ReturnError(r, w, 400, err.Error())
return
}
httputil.ReturnSuccess(r, w, nil)
}
// GetLangVersion Get the unconnected namespaces under the current cluster
func (c *ClusterController) GetLangVersion(w http.ResponseWriter, r *http.Request) {
// language:查询的语言
// show:版本信息
language := r.URL.Query().Get("language")
show := r.URL.Query().Get("show")
buildStrategy := normalizeLongVersionBuildStrategy(r.URL.Query().Get("build_strategy"))
// 获取版本列表
versions, err := db.GetManager().LongVersionDao().ListVersionByLanguageAndStrategy(language, show, buildStrategy)
if err != nil {
httputil.ReturnBcodeError(r, w, fmt.Errorf("update lang version failure: %v", err))
return
}
httputil.ReturnSuccess(r, w, versions)
}
// UpdateLangVersion -
func (c *ClusterController) UpdateLangVersion(w http.ResponseWriter, r *http.Request) {
var lang model.UpdateLangVersion
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &lang, nil); !ok {
httputil.ReturnError(r, w, 400, "failed to parse parameters")
return
}
existing, err := getExistingLongVersion(&lang)
if err != nil {
httputil.ReturnError(r, w, 400, fmt.Sprintf("get lang version failure: %v", err))
return
}
updatedVersion := buildLangVersionForUpdate(existing, &lang)
// 更新默认语言版本
result, err := db.GetManager().LongVersionDao().DefaultLangVersion(updatedVersion.Lang, updatedVersion.Version, updatedVersion.BuildStrategy, updatedVersion.Show, updatedVersion.FirstChoice, &updatedVersion.IsAllowed)
if err != nil {
httputil.ReturnError(r, w, 400, fmt.Sprintf("update lang version failure: %v", err))
return
}
httputil.ReturnSuccess(r, w, result)
}
// CreateLangVersion -
func (c *ClusterController) CreateLangVersion(w http.ResponseWriter, r *http.Request) {
var lang model.UpdateLangVersion
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &lang, nil); !ok {
httputil.ReturnError(r, w, 400, "failed to parse parameters")
return
}
newVersion := buildLangVersionForCreate(&lang)
// 根据语言标识和版本号,获取语言版本信息。
existing, err := db.GetManager().LongVersionDao().GetVersionByLanguageAndVersionAndStrategy(newVersion.Lang, newVersion.Version, newVersion.BuildStrategy)
if err != nil && err != gorm.ErrRecordNotFound {
httputil.ReturnError(r, w, 400, fmt.Sprintf("get lang version failure: %v", err))
return
}
if err == gorm.ErrRecordNotFound {
// 创建新的语言版本记录。
created, err := db.GetManager().LongVersionDao().CreateLangVersion(newVersion.Lang, newVersion.Version, newVersion.EventID, newVersion.FileName, newVersion.BuildStrategy, newVersion.Show, newVersion.IsAllowed)
if err != nil {
httputil.ReturnError(r, w, 400, fmt.Sprintf("create lang version failure: %v", err))
return
}
if created.EventID != "" {
sourceDir := path.Join(LSUploadPath, created.EventID)
destinationDir := path.Join(BaseUploadPath, created.EventID)
err = copyDirectory(sourceDir, destinationDir)
if err != nil {
httputil.ReturnError(r, w, 400, fmt.Sprintf("rename lang version failure: %v", err))
return
}
}
httputil.ReturnSuccess(r, w, created)
return
}
_ = existing
httputil.ReturnSuccess(r, w, "exist")
return
}
// DeleteLangVersion -
func (c *ClusterController) DeleteLangVersion(w http.ResponseWriter, r *http.Request) {
var lang model.UpdateLangVersion
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &lang, nil); !ok {
httputil.ReturnError(r, w, 400, "failed to parse parameters")
return
}
// 根据语言标识和版本号,删除该语言版本
eventID, err := db.GetManager().LongVersionDao().DeleteLangVersion(lang.Lang, lang.Version, normalizeLongVersionBuildStrategyPtr(lang.BuildStrategy))
if err != nil {
httputil.ReturnError(r, w, 400, fmt.Sprintf("delete lang version failure: %v", err))
return
}
// 删除本地文件
if eventID != "" {
err = os.RemoveAll(path.Join(BaseUploadPath, eventID))
if err != nil {
httputil.ReturnError(r, w, 400, fmt.Sprintf("delete lang version pack failure: %v", err))
return
}
}
httputil.ReturnSuccess(r, w, "删除成功")
}
func getExistingLongVersion(req *model.UpdateLangVersion) (*dbmodel.EnterpriseLanguageVersion, error) {
if req.BuildStrategy != nil {
return db.GetManager().LongVersionDao().GetVersionByLanguageAndVersionAndStrategy(req.Lang, req.Version, normalizeLongVersionBuildStrategyPtr(req.BuildStrategy))
}
return db.GetManager().LongVersionDao().GetVersionByLanguageAndVersion(req.Lang, req.Version)
}
func buildLangVersionForCreate(req *model.UpdateLangVersion) *dbmodel.EnterpriseLanguageVersion {
return &dbmodel.EnterpriseLanguageVersion{
Lang: req.Lang,
Version: req.Version,
EventID: req.EventID,
FileName: req.FileName,
Show: req.Show,
FirstChoice: req.FirstChoice,
BuildStrategy: normalizeLongVersionBuildStrategyPtr(req.BuildStrategy),
IsAllowed: normalizeLongVersionIsAllowed(req.IsAllowed, true),
}
}
func buildLangVersionForUpdate(existing *dbmodel.EnterpriseLanguageVersion, req *model.UpdateLangVersion) *dbmodel.EnterpriseLanguageVersion {
version := *existing
version.Show = req.Show
version.FirstChoice = req.FirstChoice
version.BuildStrategy = normalizeLongVersionBuildStrategy(version.BuildStrategy)
if req.BuildStrategy != nil {
version.BuildStrategy = normalizeLongVersionBuildStrategyPtr(req.BuildStrategy)
}
version.IsAllowed = normalizeLongVersionIsAllowed(req.IsAllowed, version.IsAllowed)
return &version
}
func normalizeLongVersionBuildStrategyPtr(buildStrategy *string) string {
if buildStrategy == nil {
return dbmodel.LongVersionBuildStrategySlug
}
return normalizeLongVersionBuildStrategy(*buildStrategy)
}
func normalizeLongVersionBuildStrategy(buildStrategy string) string {
buildStrategy = strings.TrimSpace(buildStrategy)
if buildStrategy == "" {
return dbmodel.LongVersionBuildStrategySlug
}
return buildStrategy
}
func normalizeLongVersionIsAllowed(isAllowed *bool, defaultValue bool) bool {
if isAllowed == nil {
return defaultValue
}
return *isAllowed
}
func copyFile(src, dst string) error {
sourceFile, err := os.Open(src)
if err != nil {
return err
}
defer sourceFile.Close()
destinationFile, err := os.Create(dst)
if err != nil {
return err
}
defer destinationFile.Close()
_, err = io.Copy(destinationFile, sourceFile)
if err != nil {
return err
}
err = destinationFile.Sync()
if err != nil {
return err
}
return nil
}
func copyDirectory(srcDir, dstDir string) error {
err := os.MkdirAll(dstDir, os.ModePerm)
if err != nil {
return err
}
err = filepath.Walk(srcDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
relativePath, err := filepath.Rel(srcDir, path)
if err != nil {
return err
}
dstPath := filepath.Join(dstDir, relativePath)
if info.IsDir() {
return os.MkdirAll(dstPath, info.Mode())
}
if info.Mode()&os.ModeSymlink != 0 {
return fmt.Errorf("skipping symbolic link: %s", path)
}
return copyFile(path, dstPath)
})
return err
}
// GetRegionStatus returns the region status after validating the Helm request.
func (c *ClusterController) GetRegionStatus(w http.ResponseWriter, r *http.Request) {
token := chi.URLParam(r, "token")
if token != os.Getenv("HELM_TOKEN") {
httputil.ReturnError(r, w, 400, "failed to verify token")
return
}
regionInfo, err := handler.GetClusterHandler().GetClusterRegionStatus()
if err != nil {
httputil.ReturnError(r, w, 400, err.Error())
return
}
httputil.ReturnSuccess(r, w, regionInfo)
}
// SetOverScore updates the cluster resource overcommit rate.
func (c *ClusterController) SetOverScore(w http.ResponseWriter, r *http.Request) {
var overScore model.OverScore
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &overScore, nil); !ok {
httputil.ReturnError(r, w, 400, "failed to parse parameters")
return
}
// 根据语言标识和版本号,获取语言版本信息。
err := db.GetManager().OverScoreDao().UpdateOverScoreRat(overScore.OverScoreRate)
if err != nil {
httputil.ReturnError(r, w, 400, fmt.Sprintf("update over score failure: %v", err))
return
}
return
}
// ListCNBFrameworks returns the supported CNB build frameworks for a given language.
func (c *ClusterController) ListCNBFrameworks(w http.ResponseWriter, r *http.Request) {
lang := r.URL.Query().Get("lang")
if lang == "" {
lang = "nodejs"
}
frameworks := code.GetSupportedFrameworks(lang)
httputil.ReturnSuccess(r, w, frameworks)
}
// RegionReadiness returns whether the region API is ready to serve requests.
// The console can probe this endpoint to detect unavailability before making
// API calls, receiving structured errors instead of raw stack traces.
func (c *ClusterController) RegionReadiness(w http.ResponseWriter, r *http.Request) {
result, err := handler.GetClusterHandler().CheckReadiness(r.Context())
if err != nil {
httputil.ReturnError(r, w, 500, fmt.Sprintf("readiness check failed: %v", err))
return
}
if !result.Ready {
httputil.ReturnError(r, w, 503, result.Message)
return
}
httputil.ReturnSuccess(r, w, result)
}
// BootstrapAgentKubeconfig creates a ServiceAccount-backed kubeconfig for rainbond-agent.
func (c *ClusterController) BootstrapAgentKubeconfig(w http.ResponseWriter, r *http.Request) {
var req handler.AgentKubeconfigBootstrapRequest
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &req, nil); !ok {
return
}
result, err := handler.GetClusterHandler().BootstrapAgentKubeconfig(r.Context(), &req)
if err != nil {
returnAgentKubeconfigBootstrapError(r, w, err)
return
}
httputil.ReturnSuccess(r, w, result)
}
func returnAgentKubeconfigBootstrapError(r *http.Request, w http.ResponseWriter, err error) {
if _, ok := err.(httputil.ErrBadRequest); ok {
httputil.ReturnError(r, w, http.StatusBadRequest, err.Error())
return
}
httputil.ReturnError(r, w, http.StatusInternalServerError, err.Error())
}