Merge branch 'feature-k8s' of https://github.com/goodrain/rainbond into merge-feature-k8s-main

This commit is contained in:
yangk 2022-08-09 23:13:50 +08:00
commit a37cee6456
61 changed files with 4655 additions and 76 deletions

View File

@ -30,6 +30,17 @@ type ClusterInterface interface {
MavenSettingUpdate(w http.ResponseWriter, r *http.Request)
MavenSettingDelete(w http.ResponseWriter, r *http.Request)
MavenSettingDetail(w http.ResponseWriter, r *http.Request)
GetNamespace(w http.ResponseWriter, r *http.Request)
GetNamespaceResource(w http.ResponseWriter, r *http.Request)
ConvertResource(w http.ResponseWriter, r *http.Request)
ResourceImport(w http.ResponseWriter, r *http.Request)
AddResource(w http.ResponseWriter, r *http.Request)
DeleteResource(w http.ResponseWriter, r *http.Request)
UpdateResource(w http.ResponseWriter, r *http.Request)
SyncResource(w http.ResponseWriter, r *http.Request)
YamlResourceName(w http.ResponseWriter, r *http.Request)
YamlResourceDetailed(w http.ResponseWriter, r *http.Request)
YamlResourceImport(w http.ResponseWriter, r *http.Request)
}
//TenantInterface interface
@ -88,6 +99,8 @@ type ServiceInterface interface {
AddServiceMonitors(w http.ResponseWriter, r *http.Request)
DeleteServiceMonitors(w http.ResponseWriter, r *http.Request)
UpdateServiceMonitors(w http.ResponseWriter, r *http.Request)
UploadPackage(w http.ResponseWriter, r *http.Request)
K8sAttributes(w http.ResponseWriter, r *http.Request)
}
//TenantInterfaceWithV1 funcs for both v2 and v1
@ -153,6 +166,7 @@ type AppInterface interface {
NewUpload(w http.ResponseWriter, r *http.Request)
ImportID(w http.ResponseWriter, r *http.Request)
ImportApp(w http.ResponseWriter, r *http.Request)
UploadID(w http.ResponseWriter, r *http.Request)
}
// ApplicationInterface tenant application interface
@ -172,7 +186,6 @@ type ApplicationInterface interface {
Install(w http.ResponseWriter, r *http.Request)
ListServices(w http.ResponseWriter, r *http.Request)
ListHelmAppReleases(w http.ResponseWriter, r *http.Request)
DeleteConfigGroup(w http.ResponseWriter, r *http.Request)
ListConfigGroups(w http.ResponseWriter, r *http.Request)
SyncComponents(w http.ResponseWriter, r *http.Request)

View File

@ -94,6 +94,17 @@ func (v2 *V2) clusterRouter() chi.Router {
r.Get("/builder/mavensetting/{name}", controller.GetManager().MavenSettingDetail)
r.Put("/builder/mavensetting/{name}", controller.GetManager().MavenSettingUpdate)
r.Delete("/builder/mavensetting/{name}", controller.GetManager().MavenSettingDelete)
r.Get("/namespace", controller.GetManager().GetNamespace)
r.Get("/resource", controller.GetManager().GetNamespaceResource)
r.Get("/convert-resource", controller.GetManager().ConvertResource)
r.Post("/convert-resource", controller.GetManager().ResourceImport)
r.Post("/k8s-resource", controller.GetManager().AddResource)
r.Delete("/k8s-resource", controller.GetManager().DeleteResource)
r.Put("/k8s-resource", controller.GetManager().UpdateResource)
r.Post("/sync-k8s-resources", controller.GetManager().SyncResource)
r.Get("/yaml_resource_name", controller.GetManager().YamlResourceName)
r.Get("/yaml_resource_detailed", controller.GetManager().YamlResourceDetailed)
r.Post("/yaml_resource_import", controller.GetManager().YamlResourceImport)
return r
}
@ -273,6 +284,10 @@ func (v2 *V2) serviceRouter() chi.Router {
r.Put("/label", middleware.WrapEL(controller.GetManager().Label, dbmodel.TargetTypeService, "update-service-label", dbmodel.SYNEVENTTYPE))
r.Delete("/label", middleware.WrapEL(controller.GetManager().Label, dbmodel.TargetTypeService, "delete-service-label", dbmodel.SYNEVENTTYPE))
// Component K8s properties are modified
r.Post("/k8s-attributes", middleware.WrapEL(controller.GetManager().K8sAttributes, dbmodel.TargetTypeService, "create-component-k8s-attributes", dbmodel.SYNEVENTTYPE))
r.Put("/k8s-attributes", middleware.WrapEL(controller.GetManager().K8sAttributes, dbmodel.TargetTypeService, "update-component-k8s-attributes", dbmodel.SYNEVENTTYPE))
r.Delete("/k8s-attributes", middleware.WrapEL(controller.GetManager().K8sAttributes, dbmodel.TargetTypeService, "delete-component-k8s-attributes", dbmodel.SYNEVENTTYPE))
//插件
r.Mount("/plugin", v2.serviceRelatePluginRouter())
@ -383,6 +398,10 @@ func (v2 *V2) appRouter() chi.Router {
r.Post("/import", controller.GetManager().ImportApp)
r.Get("/import/{eventID}", controller.GetManager().ImportApp)
r.Delete("/import/{eventID}", controller.GetManager().ImportApp)
r.Post("/upload/events/{eventID}", controller.GetManager().UploadID)
r.Get("/upload/events/{eventID}", controller.GetManager().UploadID)
r.Delete("/upload/events/{eventID}", controller.GetManager().UploadID)
return r
}

View File

@ -52,3 +52,11 @@ func AppRoutes() chi.Router {
r.Options("/upload/{eventID}", controller.GetManager().Upload)
return r
}
//PackageBuildRoutes 本地文件上传路由
func PackageBuildRoutes() chi.Router {
r := chi.NewRouter()
r.Post("/component/events/{eventID}", controller.GetManager().UploadPackage)
r.Options("/component/events/{eventID}", controller.GetManager().UploadPackage)
return r
}

View File

@ -156,6 +156,75 @@ func (a *AppStruct) ImportID(w http.ResponseWriter, r *http.Request) {
}
}
//UploadID -
func (a *AppStruct) UploadID(w http.ResponseWriter, r *http.Request) {
eventID := strings.TrimSpace(chi.URLParam(r, "eventID"))
if eventID == "" {
httputil.ReturnError(r, w, 400, "Failed to parse eventID.")
return
}
dirName := fmt.Sprintf("/grdata/package_build/temp/events/%s", eventID)
switch r.Method {
case "POST":
err := os.MkdirAll(dirName, 0755)
if err != nil {
httputil.ReturnError(r, w, 502, "Failed to create directory by event id: "+err.Error())
return
}
httputil.ReturnSuccess(r, w, map[string]string{"path": dirName})
case "GET":
_, err := os.Stat(dirName)
if err != nil {
if !os.IsExist(err) {
err := os.MkdirAll(dirName, 0755)
if err != nil {
httputil.ReturnError(r, w, 502, "Failed to create directory by event id: "+err.Error())
return
}
}
}
packages, err := ioutil.ReadDir(dirName)
if err != nil {
httputil.ReturnSuccess(r, w, map[string][]string{"packages": {}})
return
}
packageArr := make([]string, 0, 10)
for _, dir := range packages {
if dir.IsDir() {
continue
}
ex := filepath.Ext(dir.Name())
if ex != ".jar" && ex != ".war" && ex != ".yaml" && ex != ".yml"{
continue
}
packageArr = append(packageArr, dir.Name())
}
httputil.ReturnSuccess(r, w, map[string][]string{"packages": packageArr})
case "DELETE":
cmd := exec.Command("rm", "-rf", dirName)
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil && err.Error() != "exit status 1" {
logrus.Errorf("rm -rf %s failed: %s", dirName, err.Error())
httputil.ReturnError(r, w, 501, "Failed to delete directory by id: "+eventID)
return
}
res, err := db.GetManager().AppDao().GetByEventId(eventID)
if err != nil {
httputil.ReturnError(r, w, 404, fmt.Sprintf("Failed to query status of export app by event id %s: %v", eventID, err))
return
}
res.Status = "cleaned"
db.GetManager().AppDao().UpdateModel(res)
httputil.ReturnSuccess(r, w, "successful")
}
}
//NewUpload -
func (a *AppStruct) NewUpload(w http.ResponseWriter, r *http.Request) {
eventID := strings.TrimSpace(chi.URLParam(r, "eventID"))

View File

@ -19,6 +19,7 @@
package controller
import (
"github.com/goodrain/rainbond/api/model"
"net/http"
"github.com/go-chi/chi"
@ -101,3 +102,169 @@ func (t *ClusterController) MavenSettingDetail(w http.ResponseWriter, r *http.Re
}
httputil.ReturnSuccess(r, w, c)
}
//GetNamespace Get the unconnected namespaces under the current cluster
func (t *ClusterController) GetNamespace(w http.ResponseWriter, r *http.Request) {
content := r.FormValue("content")
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 (t *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 (t *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 (t *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)
}
//AddResource -
func (t *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 (t *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 (t *ClusterController) DeleteResource(w http.ResponseWriter, r *http.Request) {
var hr model.HandleResource
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &hr, nil); !ok {
return
}
err := handler.GetClusterHandler().DeleteAppK8SResource(r.Context(), hr.Namespace, hr.AppID, hr.Name, hr.ResourceYaml, hr.Kind)
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, nil)
}
// SyncResource -
func (t *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 (t *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 (t *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 (t *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, ar)
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, ac)
}

View File

@ -0,0 +1,86 @@
// RAINBOND, Application Management Platform
// Copyright (C) 2022-2022 Goodrain Co., Ltd.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package controller
import (
"fmt"
"github.com/goodrain/rainbond/api/handler"
api_model "github.com/goodrain/rainbond/api/model"
ctxutil "github.com/goodrain/rainbond/api/util/ctx"
httputil "github.com/goodrain/rainbond/util/http"
"net/http"
)
// K8sAttributeController -
type K8sAttributeController struct{}
// K8sAttributes -
func (k *K8sAttributeController) K8sAttributes(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "POST":
k.createK8sAttributes(w, r)
case "PUT":
k.updateK8sAttributes(w, r)
case "DELETE":
k.deleteK8sAttributes(w, r)
}
}
func (k *K8sAttributeController) createK8sAttributes(w http.ResponseWriter, r *http.Request) {
tenantID := r.Context().Value(ctxutil.ContextKey("tenant_id")).(string)
componentID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
var k8sAttr api_model.ComponentK8sAttribute
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &k8sAttr, nil); !ok {
httputil.ReturnBcodeError(r, w, fmt.Errorf("k8s attributes is not valid"))
return
}
if err := handler.GetServiceManager().CreateK8sAttribute(tenantID, componentID, &k8sAttr); err != nil {
httputil.ReturnBcodeError(r, w, err)
return
}
w.WriteHeader(http.StatusOK)
}
func (k *K8sAttributeController) updateK8sAttributes(w http.ResponseWriter, r *http.Request) {
componentID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
var k8sAttr api_model.ComponentK8sAttribute
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &k8sAttr, nil); !ok {
httputil.ReturnBcodeError(r, w, fmt.Errorf("k8s attributes is not valid"))
return
}
if err := handler.GetServiceManager().UpdateK8sAttribute(componentID, &k8sAttr); err != nil {
httputil.ReturnBcodeError(r, w, err)
return
}
w.WriteHeader(http.StatusOK)
}
func (k *K8sAttributeController) deleteK8sAttributes(w http.ResponseWriter, r *http.Request) {
componentID := r.Context().Value(ctxutil.ContextKey("service_id")).(string)
var req api_model.DeleteK8sAttributeReq
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &req, nil); !ok {
httputil.ReturnBcodeError(r, w, fmt.Errorf("k8s attributes is not valid"))
return
}
if err := handler.GetServiceManager().DeleteK8sAttribute(componentID, req.Name); err != nil {
httputil.ReturnBcodeError(r, w, err)
return
}
w.WriteHeader(http.StatusOK)
}

View File

@ -61,6 +61,7 @@ type V2Routes struct {
PodController
ApplicationController
RegistryAuthSecretStruct
K8sAttributeController
}
//Show test
@ -733,6 +734,7 @@ func (t *TenantStruct) UpdateService(w http.ResponseWriter, r *http.Request) {
"extend_method": []string{},
"app_id": []string{},
"k8s_component_name": []string{},
"job_strategy": []string{},
}
data, ok := httputil.ValidatorRequestMapAndErrorResponse(r, w, rules, nil)
if !ok {

View File

@ -1,7 +1,12 @@
package controller
import (
"fmt"
"github.com/sirupsen/logrus"
"io"
"net/http"
"os"
"strings"
"github.com/goodrain/rainbond/api/client/prometheus"
@ -60,6 +65,59 @@ func (t *TenantStruct) UpdateServiceMonitors(w http.ResponseWriter, r *http.Requ
httputil.ReturnSuccess(r, w, tsm)
}
//UploadPackage upload package
func (t *TenantStruct) UploadPackage(w http.ResponseWriter, r *http.Request) {
eventID := strings.TrimSpace(chi.URLParam(r, "eventID"))
switch r.Method {
case "POST":
if eventID == "" {
httputil.ReturnError(r, w, 400, "Failed to parse eventID.")
return
}
logrus.Debug("Start receive upload file: ", eventID)
reader, header, err := r.FormFile("packageTarFile")
if err != nil {
logrus.Errorf("Failed to parse upload file: %s", err.Error())
httputil.ReturnError(r, w, 501, "Failed to parse upload file.")
return
}
defer reader.Close()
dirName := fmt.Sprintf("/grdata/package_build/temp/events/%s", eventID)
os.MkdirAll(dirName, 0755)
fileName := fmt.Sprintf("%s/%s", dirName, header.Filename)
file, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
logrus.Errorf("Failed to open file: %s", err.Error())
httputil.ReturnError(r, w, 502, "Failed to open file: "+err.Error())
}
defer file.Close()
logrus.Debug("Start write file to: ", fileName)
if _, err := io.Copy(file, reader); err != nil {
logrus.Errorf("Failed to write file%s", err.Error())
httputil.ReturnError(r, w, 503, "Failed to write file: "+err.Error())
}
logrus.Debug("successful write file to: ", fileName)
origin := r.Header.Get("Origin")
w.Header().Add("Access-Control-Allow-Origin", origin)
w.Header().Add("Access-Control-Allow-Methods", "POST,OPTIONS")
w.Header().Add("Access-Control-Allow-Credentials", "true")
w.Header().Add("Access-Control-Allow-Headers", "x-requested-with,Content-Type,X-Custom-Header")
httputil.ReturnSuccess(r, w, nil)
case "OPTIONS":
origin := r.Header.Get("Origin")
w.Header().Add("Access-Control-Allow-Origin", origin)
w.Header().Add("Access-Control-Allow-Methods", "POST,OPTIONS")
w.Header().Add("Access-Control-Allow-Credentials", "true")
w.Header().Add("Access-Control-Allow-Headers", "x-requested-with,Content-Type,X-Custom-Header")
httputil.ReturnSuccess(r, w, nil)
}
}
//GetMonitorMetrics get monitor metrics
func GetMonitorMetrics(w http.ResponseWriter, r *http.Request) {
target := r.FormValue("target")

View File

@ -646,6 +646,9 @@ func (a *ApplicationAction) SyncComponents(app *dbmodel.Application, components
if err := GetServiceManager().SyncComponentEndpoints(tx, components); err != nil {
return err
}
if err := GetServiceManager().SyncComponentK8sAttributes(tx, app, components); err != nil {
return err
}
if len(deleteComponentIDs) != 0 {
return a.deleteByComponentIDs(tx, app, deleteComponentIDs)
}
@ -719,7 +722,10 @@ func (a *ApplicationAction) deleteByComponentIDs(tx *gorm.DB, app *dbmodel.Appli
if err = db.GetManager().TenantServceAutoscalerRulesDaoTransactions(tx).DeleteByComponentIDs(componentIDs); err != nil {
return err
}
return db.GetManager().TenantServceAutoscalerRuleMetricsDaoTransactions(tx).DeleteByRuleIDs(autoScaleRuleIDs)
if err = db.GetManager().TenantServceAutoscalerRuleMetricsDaoTransactions(tx).DeleteByRuleIDs(autoScaleRuleIDs); err != nil {
return err
}
return db.GetManager().ComponentK8sAttributeDaoTransactions(tx).DeleteByComponentIDs(componentIDs)
}
// ListAppStatuses -

View File

@ -3,20 +3,24 @@ package handler
import (
"context"
"fmt"
"os"
"runtime"
"strconv"
"time"
"github.com/goodrain/rainbond/api/model"
"github.com/goodrain/rainbond/api/util"
dbmodel "github.com/goodrain/rainbond/db/model"
"github.com/goodrain/rainbond/util/constants"
"github.com/shirou/gopsutil/disk"
"github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"os"
"runtime"
"strconv"
"strings"
"time"
)
// ClusterHandler -
@ -27,13 +31,26 @@ type ClusterHandler interface {
MavenSettingUpdate(ctx context.Context, ms *MavenSetting) *util.APIHandleError
MavenSettingDelete(ctx context.Context, name string) *util.APIHandleError
MavenSettingDetail(ctx context.Context, name string) (*MavenSetting, *util.APIHandleError)
GetNamespace(ctx context.Context, content string) ([]string, *util.APIHandleError)
GetNamespaceSource(ctx context.Context, content string, namespace string) (map[string]model.LabelResource, *util.APIHandleError)
ConvertResource(ctx context.Context, namespace string, lr map[string]model.LabelResource) (map[string]model.ApplicationResource, *util.APIHandleError)
ResourceImport(namespace string, as map[string]model.ApplicationResource, eid string) (*model.ReturnResourceImport, *util.APIHandleError)
AddAppK8SResource(ctx context.Context, namespace string, appID string, resourceYaml string) ([]*dbmodel.K8sResource, *util.APIHandleError)
DeleteAppK8SResource(ctx context.Context, namespace, appID, name, yaml, kind string) *util.APIHandleError
UpdateAppK8SResource(ctx context.Context, namespace, appID, name, resourceYaml, kind string) (dbmodel.K8sResource, *util.APIHandleError)
SyncAppK8SResources(ctx context.Context, resources *model.SyncResources) ([]*dbmodel.K8sResource, *util.APIHandleError)
AppYamlResourceName(yamlResource model.YamlResource) (map[string]model.LabelResource, *util.APIHandleError)
AppYamlResourceDetailed(yamlResource model.YamlResource, yamlImport bool) (model.ApplicationResource, *util.APIHandleError)
AppYamlResourceImport(yamlResource model.YamlResource, components model.ApplicationResource) (model.AppComponent, *util.APIHandleError)
}
// NewClusterHandler -
func NewClusterHandler(clientset *kubernetes.Clientset, RbdNamespace string) ClusterHandler {
func NewClusterHandler(clientset *kubernetes.Clientset, RbdNamespace string, config *rest.Config, mapper meta.RESTMapper) ClusterHandler {
return &clusterAction{
namespace: RbdNamespace,
clientset: clientset,
config: config,
mapper: mapper,
}
}
@ -42,8 +59,11 @@ type clusterAction struct {
clientset *kubernetes.Clientset
clusterInfoCache *model.ClusterResource
cacheTime time.Time
config *rest.Config
mapper meta.RESTMapper
}
//GetClusterInfo -
func (c *clusterAction) GetClusterInfo(ctx context.Context) (*model.ClusterResource, error) {
timeout, _ := strconv.Atoi(os.Getenv("CLUSTER_INFO_CACHE_TIME"))
if timeout == 0 {
@ -332,3 +352,34 @@ func (c *clusterAction) MavenSettingDetail(ctx context.Context, name string) (*M
Content: sm.Data["mavensetting"],
}, nil
}
//GetNamespace Get namespace of the current cluster
func (c *clusterAction) GetNamespace(ctx context.Context, content string) ([]string, *util.APIHandleError) {
namespaceList, err := c.clientset.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
if err != nil {
return nil, &util.APIHandleError{Code: 404, Err: fmt.Errorf("failed to get namespace:%v", err)}
}
namespaces := new([]string)
for _, ns := range namespaceList.Items {
if strings.HasPrefix(ns.Name, "kube-") || ns.Name == "rainbond" || ns.Name == "rbd-system" {
continue
}
if labelValue, isRBDNamespace := ns.Labels[constants.ResourceManagedByLabel]; isRBDNamespace && labelValue == "rainbond" && content == "unmanaged" {
continue
}
*namespaces = append(*namespaces, ns.Name)
}
return *namespaces, nil
}
//MergeMap map去重合并
func MergeMap(map1 map[string][]string, map2 map[string][]string) map[string][]string {
for k, v := range map1 {
if _, ok := map2[k]; ok {
map2[k] = append(map2[k], v...)
continue
}
map2[k] = v
}
return map2
}

View File

@ -0,0 +1,493 @@
package handler
import (
"fmt"
"github.com/goodrain/rainbond/api/model"
"github.com/goodrain/rainbond/api/util"
dbmodel "github.com/goodrain/rainbond/db/model"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
v1 "k8s.io/api/autoscaling/v1"
corev1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"strings"
)
//ConvertResource 处理资源
func (c *clusterAction) ConvertResource(ctx context.Context, namespace string, lr map[string]model.LabelResource) (map[string]model.ApplicationResource, *util.APIHandleError) {
logrus.Infof("ConvertResource function begin")
appsServices := make(map[string]model.ApplicationResource)
for label, resource := range lr {
c.workloadHandle(ctx, appsServices, resource, namespace, label)
}
logrus.Infof("ConvertResource function end")
return appsServices, nil
}
func (c *clusterAction) workloadHandle(ctx context.Context, cr map[string]model.ApplicationResource, lr model.LabelResource, namespace string, label string) {
app := label
deployResource := c.workloadDeployments(lr.Workloads.Deployments, namespace)
sfsResource := c.workloadStateFulSets(lr.Workloads.StateFulSets, namespace)
jobResource := c.workloadJobs(lr.Workloads.Jobs, namespace)
cjResource := c.workloadCronJobs(lr.Workloads.CronJobs, namespace)
convertResource := append(deployResource, append(sfsResource, append(jobResource, append(cjResource)...)...)...)
k8sResources := c.getAppKubernetesResources(ctx, lr.Others, namespace)
cr[app] = model.ApplicationResource{
ConvertResource: convertResource,
KubernetesResources: k8sResources,
}
}
func (c *clusterAction) workloadDeployments(dmNames []string, namespace string) []model.ConvertResource {
var componentsCR []model.ConvertResource
for _, dmName := range dmNames {
resources, err := c.clientset.AppsV1().Deployments(namespace).Get(context.Background(), dmName, metav1.GetOptions{})
if err != nil {
logrus.Errorf("Failed to get Deployment %v:%v", dmName, err)
return nil
}
//BasicManagement
basic := model.BasicManagement{
ResourceType: model.Deployment,
Replicas: resources.Spec.Replicas,
Memory: resources.Spec.Template.Spec.Containers[0].Resources.Limits.Memory().Value() / 1024 / 1024,
CPU: resources.Spec.Template.Spec.Containers[0].Resources.Limits.Cpu().Value(),
Image: resources.Spec.Template.Spec.Containers[0].Image,
Cmd: strings.Join(append(resources.Spec.Template.Spec.Containers[0].Command, resources.Spec.Template.Spec.Containers[0].Args...), " "),
}
parameter := model.YamlResourceParameter{
ComponentsCR: &componentsCR,
Basic: basic,
Template: resources.Spec.Template,
Namespace: namespace,
Name: dmName,
RsLabel: resources.Labels,
}
c.PodTemplateSpecResource(parameter)
}
return componentsCR
}
func (c *clusterAction) workloadStateFulSets(sfsNames []string, namespace string) []model.ConvertResource {
var componentsCR []model.ConvertResource
for _, sfsName := range sfsNames {
resources, err := c.clientset.AppsV1().StatefulSets(namespace).Get(context.Background(), sfsName, metav1.GetOptions{})
if err != nil {
logrus.Errorf("Failed to get Deployment %v:%v", sfsName, err)
return nil
}
//BasicManagement
basic := model.BasicManagement{
ResourceType: model.StateFulSet,
Replicas: resources.Spec.Replicas,
Memory: resources.Spec.Template.Spec.Containers[0].Resources.Limits.Memory().Value() / 1024 / 1024,
CPU: resources.Spec.Template.Spec.Containers[0].Resources.Limits.Cpu().Value(),
Image: resources.Spec.Template.Spec.Containers[0].Image,
Cmd: strings.Join(append(resources.Spec.Template.Spec.Containers[0].Command, resources.Spec.Template.Spec.Containers[0].Args...), " "),
}
parameter := model.YamlResourceParameter{
ComponentsCR: &componentsCR,
Basic: basic,
Template: resources.Spec.Template,
Namespace: namespace,
Name: sfsName,
RsLabel: resources.Labels,
}
c.PodTemplateSpecResource(parameter)
}
return componentsCR
}
func (c *clusterAction) workloadJobs(jobNames []string, namespace string) []model.ConvertResource {
var componentsCR []model.ConvertResource
for _, jobName := range jobNames {
resources, err := c.clientset.BatchV1().Jobs(namespace).Get(context.Background(), jobName, metav1.GetOptions{})
if err != nil {
logrus.Errorf("Failed to get Deployment %v:%v", jobName, err)
return nil
}
var BackoffLimit, Parallelism, ActiveDeadlineSeconds, Completions string
if resources.Spec.BackoffLimit != nil {
BackoffLimit = fmt.Sprintf("%v", *resources.Spec.BackoffLimit)
}
if resources.Spec.Parallelism != nil {
Parallelism = fmt.Sprintf("%v", *resources.Spec.Parallelism)
}
if resources.Spec.ActiveDeadlineSeconds != nil {
ActiveDeadlineSeconds = fmt.Sprintf("%v", *resources.Spec.ActiveDeadlineSeconds)
}
if resources.Spec.Completions != nil {
Completions = fmt.Sprintf("%v", *resources.Spec.Completions)
}
job := model.JobStrategy{
Schedule: resources.Spec.Template.Spec.SchedulerName,
BackoffLimit: BackoffLimit,
Parallelism: Parallelism,
ActiveDeadlineSeconds: ActiveDeadlineSeconds,
Completions: Completions,
}
//BasicManagement
basic := model.BasicManagement{
ResourceType: model.Job,
Replicas: resources.Spec.Completions,
Memory: resources.Spec.Template.Spec.Containers[0].Resources.Limits.Memory().Value() / 1024 / 1024,
CPU: resources.Spec.Template.Spec.Containers[0].Resources.Limits.Cpu().Value(),
Image: resources.Spec.Template.Spec.Containers[0].Image,
Cmd: strings.Join(append(resources.Spec.Template.Spec.Containers[0].Command, resources.Spec.Template.Spec.Containers[0].Args...), " "),
JobStrategy: job,
}
parameter := model.YamlResourceParameter{
ComponentsCR: &componentsCR,
Basic: basic,
Template: resources.Spec.Template,
Namespace: namespace,
Name: jobName,
RsLabel: resources.Labels,
}
c.PodTemplateSpecResource(parameter)
}
return componentsCR
}
func (c *clusterAction) workloadCronJobs(cjNames []string, namespace string) []model.ConvertResource {
var componentsCR []model.ConvertResource
for _, cjName := range cjNames {
resources, err := c.clientset.BatchV1beta1().CronJobs(namespace).Get(context.Background(), cjName, metav1.GetOptions{})
if err != nil {
logrus.Errorf("Failed to get Deployment %v:%v", cjName, err)
return nil
}
BackoffLimit, Parallelism, ActiveDeadlineSeconds, Completions := "", "", "", ""
if resources.Spec.JobTemplate.Spec.BackoffLimit != nil {
BackoffLimit = fmt.Sprintf("%v", *resources.Spec.JobTemplate.Spec.BackoffLimit)
}
if resources.Spec.JobTemplate.Spec.Parallelism != nil {
Parallelism = fmt.Sprintf("%v", *resources.Spec.JobTemplate.Spec.Parallelism)
}
if resources.Spec.JobTemplate.Spec.ActiveDeadlineSeconds != nil {
ActiveDeadlineSeconds = fmt.Sprintf("%v", *resources.Spec.JobTemplate.Spec.ActiveDeadlineSeconds)
}
if resources.Spec.JobTemplate.Spec.Completions != nil {
Completions = fmt.Sprintf("%v", *resources.Spec.JobTemplate.Spec.Completions)
}
job := model.JobStrategy{
Schedule: resources.Spec.Schedule,
BackoffLimit: BackoffLimit,
Parallelism: Parallelism,
ActiveDeadlineSeconds: ActiveDeadlineSeconds,
Completions: Completions,
}
//BasicManagement
basic := model.BasicManagement{
ResourceType: model.CronJob,
Replicas: resources.Spec.JobTemplate.Spec.Completions,
Memory: resources.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Resources.Limits.Memory().Value() / 1024 / 1024,
CPU: resources.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Resources.Limits.Cpu().Value(),
Image: resources.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Image,
Cmd: strings.Join(append(resources.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Command, resources.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Args...), " "),
JobStrategy: job,
}
parameter := model.YamlResourceParameter{
ComponentsCR: &componentsCR,
Basic: basic,
Template: resources.Spec.JobTemplate.Spec.Template,
Namespace: namespace,
Name: cjName,
RsLabel: resources.Labels,
}
c.PodTemplateSpecResource(parameter)
}
return componentsCR
}
func (c *clusterAction) getAppKubernetesResources(ctx context.Context, others model.OtherResource, namespace string) []dbmodel.K8sResource {
var k8sResources []dbmodel.K8sResource
servicesMap := make(map[string]corev1.Service)
servicesList, err := c.clientset.CoreV1().Services(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("namespace:%v get services error:%v", namespace, err)
}
if len(others.Services) != 0 && err == nil {
for _, services := range servicesList.Items {
servicesMap[services.Name] = services
}
for _, servicesName := range others.Services {
services, _ := servicesMap[servicesName]
services.Kind = model.Service
services.Status = corev1.ServiceStatus{}
services.APIVersion = "v1"
services.ManagedFields = []metav1.ManagedFieldsEntry{}
kubernetesResourcesYAML, err := ObjectToJSONORYaml("yaml", services)
if err != nil {
logrus.Errorf("namespace:%v service:%v error: %v", namespace, services.Name, err)
}
k8sResources = append(k8sResources, dbmodel.K8sResource{
Name: services.Name,
Kind: model.Service,
Content: kubernetesResourcesYAML,
State: 1,
ErrorOverview: "创建成功",
})
}
}
pvcMap := make(map[string]corev1.PersistentVolumeClaim)
pvcList, err := c.clientset.CoreV1().PersistentVolumeClaims(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("namespace:%v get pvc error:%v", namespace, err)
}
if len(others.PVC) != 0 && err == nil {
for _, pvc := range pvcList.Items {
pvcMap[pvc.Name] = pvc
}
for _, pvcName := range others.PVC {
pvc, _ := pvcMap[pvcName]
pvc.Status = corev1.PersistentVolumeClaimStatus{}
pvc.ManagedFields = []metav1.ManagedFieldsEntry{}
pvc.Kind = model.PVC
pvc.APIVersion = "v1"
kubernetesResourcesYAML, err := ObjectToJSONORYaml("yaml", pvc)
if err != nil {
logrus.Errorf("namespace:%v pvc:%v error: %v", namespace, pvc.Name, err)
}
k8sResources = append(k8sResources, dbmodel.K8sResource{
Name: pvc.Name,
Kind: model.PVC,
Content: kubernetesResourcesYAML,
State: 1,
ErrorOverview: "创建成功",
})
}
}
ingressMap := make(map[string]networkingv1.Ingress)
ingressList, err := c.clientset.NetworkingV1().Ingresses(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("namespace:%v get ingresses error:%v", namespace, err)
}
if len(others.Ingresses) != 0 && err == nil {
for _, ingress := range ingressList.Items {
ingressMap[ingress.Name] = ingress
}
for _, ingressName := range others.Ingresses {
ingresses, _ := ingressMap[ingressName]
ingresses.Status = networkingv1.IngressStatus{}
ingresses.ManagedFields = []metav1.ManagedFieldsEntry{}
ingresses.Kind = model.Ingress
ingresses.APIVersion = "networking.k8s.io/v1"
kubernetesResourcesYAML, err := ObjectToJSONORYaml("yaml", ingresses)
if err != nil {
logrus.Errorf("namespace:%v ingresses:%v error: %v", namespace, ingresses.Name, err)
}
k8sResources = append(k8sResources, dbmodel.K8sResource{
Name: ingresses.Name,
Kind: model.Ingress,
Content: kubernetesResourcesYAML,
State: 1,
ErrorOverview: "创建成功",
})
}
}
networkPoliciesMap := make(map[string]networkingv1.NetworkPolicy)
networkPoliciesList, err := c.clientset.NetworkingV1().NetworkPolicies(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("namespace:%v get NetworkPolicies error:%v", namespace, err)
}
if len(others.NetworkPolicies) != 0 && err == nil {
for _, networkPolicies := range networkPoliciesList.Items {
networkPoliciesMap[networkPolicies.Name] = networkPolicies
}
for _, networkPoliciesName := range others.NetworkPolicies {
networkPolicies, _ := networkPoliciesMap[networkPoliciesName]
networkPolicies.ManagedFields = []metav1.ManagedFieldsEntry{}
networkPolicies.Kind = model.NetworkPolicy
networkPolicies.APIVersion = "networking.k8s.io/v1"
kubernetesResourcesYAML, err := ObjectToJSONORYaml("yaml", networkPolicies)
if err != nil {
logrus.Errorf("namespace:%v NetworkPolicies:%v error: %v", namespace, networkPolicies.Name, err)
}
k8sResources = append(k8sResources, dbmodel.K8sResource{
Name: networkPolicies.Name,
Kind: model.NetworkPolicy,
Content: kubernetesResourcesYAML,
State: 1,
ErrorOverview: "创建成功",
})
}
}
cmMap := make(map[string]corev1.ConfigMap)
cmList, err := c.clientset.CoreV1().ConfigMaps(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("namespace:%v get ConfigMaps error:%v", namespace, err)
}
if len(others.ConfigMaps) != 0 && err == nil {
for _, cm := range cmList.Items {
cmMap[cm.Name] = cm
}
for _, configMapsName := range others.ConfigMaps {
configMaps, _ := cmMap[configMapsName]
configMaps.ManagedFields = []metav1.ManagedFieldsEntry{}
configMaps.Kind = model.ConfigMap
configMaps.APIVersion = "v1"
kubernetesResourcesYAML, err := ObjectToJSONORYaml("yaml", configMaps)
if err != nil {
logrus.Errorf("namespace:%v ConfigMaps:%v error: %v", namespace, configMaps.Name, err)
}
k8sResources = append(k8sResources, dbmodel.K8sResource{
Name: configMaps.Name,
Kind: model.ConfigMap,
Content: kubernetesResourcesYAML,
State: 1,
ErrorOverview: "创建成功",
})
}
}
secretsMap := make(map[string]corev1.Secret)
secretsList, err := c.clientset.CoreV1().Secrets(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("namespace:%v get Secrets error:%v", namespace, err)
}
if len(others.Secrets) != 0 && err == nil {
for _, secrets := range secretsList.Items {
secretsMap[secrets.Name] = secrets
}
for _, secretsName := range others.Secrets {
secrets, _ := secretsMap[secretsName]
secrets.ManagedFields = []metav1.ManagedFieldsEntry{}
secrets.Kind = model.Secret
secrets.APIVersion = "v1"
kubernetesResourcesYAML, err := ObjectToJSONORYaml("yaml", secrets)
if err != nil {
logrus.Errorf("namespace:%v Secrets:%v error: %v", namespace, secrets.Name, err)
}
k8sResources = append(k8sResources, dbmodel.K8sResource{
Name: secrets.Name,
Kind: model.Secret,
Content: kubernetesResourcesYAML,
State: 1,
ErrorOverview: "创建成功",
})
}
}
serviceAccountsMap := make(map[string]corev1.ServiceAccount)
serviceAccountsList, err := c.clientset.CoreV1().ServiceAccounts(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("namespace:%v get ServiceAccounts error:%v", namespace, err)
}
if len(others.ServiceAccounts) != 0 && err == nil {
for _, serviceAccounts := range serviceAccountsList.Items {
serviceAccountsMap[serviceAccounts.Name] = serviceAccounts
}
for _, serviceAccountsName := range others.ServiceAccounts {
serviceAccounts, _ := serviceAccountsMap[serviceAccountsName]
serviceAccounts.ManagedFields = []metav1.ManagedFieldsEntry{}
serviceAccounts.Kind = model.ServiceAccount
serviceAccounts.APIVersion = "v1"
kubernetesResourcesYAML, err := ObjectToJSONORYaml("yaml", serviceAccounts)
if err != nil {
logrus.Errorf("namespace:%v ServiceAccounts:%v error: %v", namespace, serviceAccounts.Name, err)
continue
}
k8sResources = append(k8sResources, dbmodel.K8sResource{
Name: serviceAccounts.Name,
Kind: model.ServiceAccount,
Content: kubernetesResourcesYAML,
State: 1,
ErrorOverview: "创建成功",
})
}
}
roleBindingsMap := make(map[string]rbacv1.RoleBinding)
roleBindingsList, _ := c.clientset.RbacV1().RoleBindings(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("namespace:%v get RoleBindings error:%v", namespace, err)
}
if len(others.RoleBindings) != 0 && err == nil {
for _, roleBindings := range roleBindingsList.Items {
roleBindingsMap[roleBindings.Name] = roleBindings
}
for _, roleBindingsName := range others.RoleBindings {
roleBindings, _ := roleBindingsMap[roleBindingsName]
roleBindings.ManagedFields = []metav1.ManagedFieldsEntry{}
roleBindings.Kind = model.RoleBinding
roleBindings.APIVersion = "rbac.authorization.k8s.io/v1"
kubernetesResourcesYAML, err := ObjectToJSONORYaml("yaml", roleBindings)
if err != nil {
logrus.Errorf("namespace:%v RoleBindings:%v error: %v", namespace, roleBindings.Name, err)
}
k8sResources = append(k8sResources, dbmodel.K8sResource{
Name: roleBindings.Name,
Kind: model.RoleBinding,
Content: kubernetesResourcesYAML,
State: 1,
ErrorOverview: "创建成功",
})
}
}
hpaMap := make(map[string]v1.HorizontalPodAutoscaler)
hpaList, _ := c.clientset.AutoscalingV1().HorizontalPodAutoscalers(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("namespace:%v get HorizontalPodAutoscalers error:%v", namespace, err)
}
if len(others.HorizontalPodAutoscalers) != 0 && err == nil {
for _, hpa := range hpaList.Items {
hpaMap[hpa.Name] = hpa
}
for _, hpaName := range others.HorizontalPodAutoscalers {
hpa, _ := hpaMap[hpaName]
hpa.Status = v1.HorizontalPodAutoscalerStatus{}
hpa.ManagedFields = []metav1.ManagedFieldsEntry{}
hpa.Kind = model.HorizontalPodAutoscaler
hpa.APIVersion = "autoscaling/v1"
kubernetesResourcesYAML, err := ObjectToJSONORYaml("yaml", hpa)
if err != nil {
logrus.Errorf("namespace:%v HorizontalPodAutoscalers:%v error: %v", namespace, hpa.Name, err)
}
k8sResources = append(k8sResources, dbmodel.K8sResource{
Name: hpa.Name,
Kind: model.HorizontalPodAutoscaler,
Content: kubernetesResourcesYAML,
State: 1,
ErrorOverview: "创建成功",
})
}
}
rolesMap := make(map[string]rbacv1.Role)
rolesList, err := c.clientset.RbacV1().Roles(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("namespace:%v get roles error:%v", namespace, err)
}
if len(others.Roles) != 0 && err == nil {
for _, roles := range rolesList.Items {
rolesMap[roles.Name] = roles
}
for _, rolesName := range others.Roles {
roles, _ := rolesMap[rolesName]
roles.Kind = model.Role
roles.APIVersion = "rbac.authorization.k8s.io/v1"
kubernetesResourcesYAML, err := ObjectToJSONORYaml("yaml", roles)
if err != nil {
logrus.Errorf("namespace:%v roles:%v error: %v", namespace, roles.Name, err)
}
k8sResources = append(k8sResources, dbmodel.K8sResource{
Name: roles.Name,
Kind: model.Role,
Content: kubernetesResourcesYAML,
ErrorOverview: "创建成功",
State: 1,
})
}
}
return k8sResources
}

View File

@ -0,0 +1,530 @@
package handler
import (
"github.com/goodrain/rainbond/api/model"
"github.com/goodrain/rainbond/api/util"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
//GetNamespaceSource Get all resources in the current namespace
func (c *clusterAction) GetNamespaceSource(ctx context.Context, content string, namespace string) (map[string]model.LabelResource, *util.APIHandleError) {
logrus.Infof("GetNamespaceSource function begin")
//存储workloads们的ConfigMap
cmsMap := make(map[string][]string)
//存储workloads们的secrets
secretsMap := make(map[string][]string)
deployments, cmMap, secretMap := c.getResourceName(context.Background(), namespace, content, model.Deployment)
if len(cmsMap) != 0 {
cmsMap = MergeMap(cmMap, cmsMap)
}
if len(secretMap) != 0 {
secretsMap = MergeMap(secretMap, secretsMap)
}
jobs, cmMap, secretMap := c.getResourceName(ctx, namespace, content, model.Job)
if len(cmsMap) != 0 {
cmsMap = MergeMap(cmMap, cmsMap)
}
if len(secretMap) != 0 {
secretsMap = MergeMap(secretMap, secretsMap)
}
cronJobs, cmMap, secretMap := c.getResourceName(ctx, namespace, content, model.CronJob)
if len(cmsMap) != 0 {
cmsMap = MergeMap(cmMap, cmsMap)
}
if len(secretMap) != 0 {
secretsMap = MergeMap(secretMap, secretsMap)
}
stateFulSets, cmMap, secretMap := c.getResourceName(ctx, namespace, content, model.StateFulSet)
if len(cmsMap) != 0 {
cmsMap = MergeMap(cmMap, cmsMap)
}
if len(secretMap) != 0 {
secretsMap = MergeMap(secretMap, secretsMap)
}
processWorkloads := model.LabelWorkloadsResourceProcess{
Deployments: deployments,
Jobs: jobs,
CronJobs: cronJobs,
StateFulSets: stateFulSets,
}
services, _, _ := c.getResourceName(ctx, namespace, content, model.Service)
pvc, _, _ := c.getResourceName(ctx, namespace, content, model.PVC)
ingresses, _, _ := c.getResourceName(ctx, namespace, content, model.Ingress)
networkPolicies, _, _ := c.getResourceName(ctx, namespace, content, model.NetworkPolicy)
cms, _, _ := c.getResourceName(ctx, namespace, content, model.ConfigMap)
secrets, _, _ := c.getResourceName(ctx, namespace, content, model.Secret)
serviceAccounts, _, _ := c.getResourceName(ctx, namespace, content, model.ServiceAccount)
roleBindings, _, _ := c.getResourceName(ctx, namespace, content, model.RoleBinding)
horizontalPodAutoscalers, _, _ := c.getResourceName(ctx, namespace, content, model.HorizontalPodAutoscaler)
roles, _, _ := c.getResourceName(ctx, namespace, content, model.Role)
processOthers := model.LabelOthersResourceProcess{
Services: services,
PVC: pvc,
Ingresses: ingresses,
NetworkPolicies: networkPolicies,
ConfigMaps: MergeMap(cmsMap, cms),
Secrets: MergeMap(secretsMap, secrets),
ServiceAccounts: serviceAccounts,
RoleBindings: roleBindings,
HorizontalPodAutoscalers: horizontalPodAutoscalers,
Roles: roles,
}
labelResource := resourceProcessing(processWorkloads, processOthers)
logrus.Infof("GetNamespaceSource function end")
return labelResource, nil
}
//resourceProcessing 将处理好的资源类型数据格式再加工成可作为返回值的数据。
func resourceProcessing(processWorkloads model.LabelWorkloadsResourceProcess, processOthers model.LabelOthersResourceProcess) map[string]model.LabelResource {
labelResource := make(map[string]model.LabelResource)
for label, deployments := range processWorkloads.Deployments {
if val, ok := labelResource[label]; ok {
val.Workloads.Deployments = deployments
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Workloads: model.WorkLoadsResource{
Deployments: deployments,
},
}
}
for label, jobs := range processWorkloads.Jobs {
if val, ok := labelResource[label]; ok {
val.Workloads.Jobs = jobs
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Workloads: model.WorkLoadsResource{
Jobs: jobs,
},
}
}
for label, cronJobs := range processWorkloads.CronJobs {
if val, ok := labelResource[label]; ok {
val.Workloads.CronJobs = cronJobs
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Workloads: model.WorkLoadsResource{
CronJobs: cronJobs,
},
}
}
for label, stateFulSets := range processWorkloads.StateFulSets {
if val, ok := labelResource[label]; ok {
val.Workloads.StateFulSets = stateFulSets
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Workloads: model.WorkLoadsResource{
StateFulSets: stateFulSets,
},
}
}
for label, service := range processOthers.Services {
if val, ok := labelResource[label]; ok {
val.Others.Services = service
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Others: model.OtherResource{
Services: service,
},
}
}
for label, pvc := range processOthers.PVC {
if val, ok := labelResource[label]; ok {
val.Others.PVC = pvc
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Others: model.OtherResource{
PVC: pvc,
},
}
}
for label, ingresses := range processOthers.Ingresses {
if val, ok := labelResource[label]; ok {
val.Others.Ingresses = ingresses
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Others: model.OtherResource{
Ingresses: ingresses,
},
}
}
for label, networkPolicies := range processOthers.NetworkPolicies {
if val, ok := labelResource[label]; ok {
val.Others.NetworkPolicies = networkPolicies
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Others: model.OtherResource{
NetworkPolicies: networkPolicies,
},
}
}
for label, configMaps := range processOthers.ConfigMaps {
if val, ok := labelResource[label]; ok {
val.Others.ConfigMaps = configMaps
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Others: model.OtherResource{
ConfigMaps: configMaps,
},
}
}
for label, secrets := range processOthers.Secrets {
if val, ok := labelResource[label]; ok {
val.Others.Secrets = secrets
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Others: model.OtherResource{
Secrets: secrets,
},
}
}
for label, serviceAccounts := range processOthers.ServiceAccounts {
if val, ok := labelResource[label]; ok {
val.Others.ServiceAccounts = serviceAccounts
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Others: model.OtherResource{
ServiceAccounts: serviceAccounts,
},
}
}
for label, roleBindings := range processOthers.RoleBindings {
if val, ok := labelResource[label]; ok {
val.Others.RoleBindings = roleBindings
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Others: model.OtherResource{
RoleBindings: roleBindings,
},
}
}
for label, horizontalPodAutoscalers := range processOthers.HorizontalPodAutoscalers {
if val, ok := labelResource[label]; ok {
val.Others.HorizontalPodAutoscalers = horizontalPodAutoscalers
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Others: model.OtherResource{
HorizontalPodAutoscalers: horizontalPodAutoscalers,
},
}
}
for label, roles := range processOthers.Roles {
if val, ok := labelResource[label]; ok {
val.Others.Roles = roles
labelResource[label] = val
continue
}
labelResource[label] = model.LabelResource{
Others: model.OtherResource{
Roles: roles,
},
}
}
return labelResource
}
//Resource -
type Resource struct {
ObjectMeta metav1.ObjectMeta
Template corev1.PodTemplateSpec
}
//getResourceName 将指定资源类型按照【label名】[]{资源名...}处理后返回
func (c *clusterAction) getResourceName(ctx context.Context, namespace string, content string, resourcesType string) (map[string][]string, map[string][]string, map[string][]string) {
resourceName := make(map[string][]string)
var tempResources []*Resource
isWorkloads := false
cmMap := make(map[string][]string)
secretMap := make(map[string][]string)
switch resourcesType {
case model.Deployment:
resources, err := c.clientset.AppsV1().Deployments(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get Deployment list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta, Template: dm.Spec.Template})
}
isWorkloads = true
case model.Job:
resources, err := c.clientset.BatchV1().Jobs(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get Job list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
if dm.OwnerReferences != nil {
if dm.OwnerReferences[0].Kind == model.CronJob {
continue
}
}
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta, Template: dm.Spec.Template})
}
isWorkloads = true
case model.CronJob:
resources, err := c.clientset.BatchV1beta1().CronJobs(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get CronJob list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta, Template: dm.Spec.JobTemplate.Spec.Template})
}
isWorkloads = true
case model.StateFulSet:
resources, err := c.clientset.AppsV1().StatefulSets(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get StateFulSets list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta, Template: dm.Spec.Template})
}
isWorkloads = true
case model.Service:
resources, err := c.clientset.CoreV1().Services(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get Services list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta})
}
case model.PVC:
resources, err := c.clientset.CoreV1().PersistentVolumeClaims(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get PersistentVolumeClaims list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta})
}
case model.Ingress:
resources, err := c.clientset.NetworkingV1().Ingresses(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get Ingresses list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta})
}
case model.NetworkPolicy:
resources, err := c.clientset.NetworkingV1().NetworkPolicies(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get NetworkPolicies list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta})
}
case model.ConfigMap:
resources, err := c.clientset.CoreV1().ConfigMaps(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get ConfigMaps list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta})
}
case model.Secret:
resources, err := c.clientset.CoreV1().Secrets(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get Secrets list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta})
}
case model.ServiceAccount:
resources, err := c.clientset.CoreV1().ServiceAccounts(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get ServiceAccounts list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta})
}
case model.RoleBinding:
resources, err := c.clientset.RbacV1().RoleBindings(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get RoleBindings list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta})
}
case model.HorizontalPodAutoscaler:
resources, err := c.clientset.AutoscalingV1().HorizontalPodAutoscalers(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get HorizontalPodAutoscalers list:%v", err)
return nil, cmMap, secretMap
}
for _, hpa := range resources.Items {
rbdResource := false
labels := make(map[string]string)
switch hpa.Spec.ScaleTargetRef.Kind {
case model.Deployment:
deploy, err := c.clientset.AppsV1().Deployments(namespace).Get(context.Background(), hpa.Spec.ScaleTargetRef.Name, metav1.GetOptions{})
if err != nil {
logrus.Errorf("The bound deployment does not exist:%v", err)
}
if hpa.ObjectMeta.Labels["creator"] == "Rainbond" {
rbdResource = true
}
labels = deploy.ObjectMeta.Labels
case model.StateFulSet:
ss, err := c.clientset.AppsV1().StatefulSets(namespace).Get(context.Background(), hpa.Spec.ScaleTargetRef.Name, metav1.GetOptions{})
if err != nil {
logrus.Errorf("The bound deployment does not exist:%v", err)
}
if hpa.ObjectMeta.Labels["creator"] == "Rainbond" {
rbdResource = true
}
labels = ss.ObjectMeta.Labels
}
var app string
if content == "unmanaged" && rbdResource {
continue
}
app = labels["app"]
if labels["app.kubernetes.io/name"] != "" {
app = labels["app.kubernetes.io/name"]
}
if app == "" {
app = "unclassified"
}
if _, ok := resourceName[app]; ok {
resourceName[app] = append(resourceName[app], hpa.Name)
} else {
resourceName[app] = []string{hpa.Name}
}
}
return resourceName, nil, nil
case model.Role:
resources, err := c.clientset.RbacV1().Roles(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get Roles list:%v", err)
return nil, cmMap, secretMap
}
for _, dm := range resources.Items {
tempResources = append(tempResources, &Resource{ObjectMeta: dm.ObjectMeta})
}
}
//这一块是统一处理资源按label划分出来
for _, rs := range tempResources {
if content == "unmanaged" && rs.ObjectMeta.Labels["creator"] == "Rainbond" {
continue
}
app := rs.ObjectMeta.Labels["app"]
if rs.ObjectMeta.Labels["app.kubernetes.io/name"] != "" {
app = rs.ObjectMeta.Labels["app.kubernetes.io/name"]
}
if app == "" {
app = "unclassified"
}
//如果是Workloads类型的资源需要检查其内部configmap、secret、PVC防止没有这三种资源没有label但是用到了
if isWorkloads {
cmList, secretList := c.replenishLabel(ctx, rs, namespace, app)
if _, ok := cmMap[app]; ok {
cmMap[app] = append(cmMap[app], cmList...)
} else {
cmMap[app] = cmList
}
if _, ok := secretMap[app]; ok {
secretMap[app] = append(secretMap[app], secretList...)
} else {
secretMap[app] = secretList
}
}
if _, ok := resourceName[app]; ok {
resourceName[app] = append(resourceName[app], rs.ObjectMeta.Name)
} else {
resourceName[app] = []string{rs.ObjectMeta.Name}
}
}
return resourceName, cmMap, secretMap
}
//replenishLabel 获取workloads资源上携带的ConfigMap和secret以及把pvc加上标签。
func (c *clusterAction) replenishLabel(ctx context.Context, resource *Resource, namespace string, app string) ([]string, []string) {
var cmList []string
var secretList []string
resourceVolume := resource.Template.Spec.Volumes
for _, volume := range resourceVolume {
if pvc := volume.PersistentVolumeClaim; pvc != nil {
PersistentVolumeClaims, err := c.clientset.CoreV1().PersistentVolumeClaims(namespace).Get(context.Background(), pvc.ClaimName, metav1.GetOptions{})
if err != nil {
logrus.Errorf("Failed to get PersistentVolumeClaims %s/%s:%v", namespace, pvc.ClaimName, err)
}
if PersistentVolumeClaims.Labels == nil {
PersistentVolumeClaims.Labels = make(map[string]string)
}
if _, ok := PersistentVolumeClaims.Labels["app"]; !ok {
if _, ok := PersistentVolumeClaims.Labels["app.kubernetes.io/name"]; !ok {
PersistentVolumeClaims.Labels["app"] = app
}
}
_, err = c.clientset.CoreV1().PersistentVolumeClaims(namespace).Update(context.Background(), PersistentVolumeClaims, metav1.UpdateOptions{})
if err != nil {
logrus.Errorf("PersistentVolumeClaims label update error:%v", err)
}
continue
}
if cm := volume.ConfigMap; cm != nil {
cm, err := c.clientset.CoreV1().ConfigMaps(namespace).Get(context.Background(), cm.Name, metav1.GetOptions{})
if err != nil {
logrus.Errorf("Failed to get ConfigMap:%v", err)
}
if _, ok := cm.Labels["app"]; !ok {
if _, ok := cm.Labels["app.kubernetes.io/name"]; !ok {
cmList = append(cmList, cm.Name)
}
}
}
if secret := volume.Secret; secret != nil {
secret, err := c.clientset.CoreV1().Secrets(namespace).Get(context.Background(), secret.SecretName, metav1.GetOptions{})
if err != nil {
logrus.Errorf("Failed to get Scret:%v", err)
}
if _, ok := secret.Labels["app"]; !ok {
if _, ok := secret.Labels["app.kubernetes.io/name"]; !ok {
cmList = append(cmList, secret.Name)
}
}
}
}
return cmList, secretList
}

View File

@ -30,7 +30,9 @@ import (
etcdutil "github.com/goodrain/rainbond/util/etcd"
"github.com/goodrain/rainbond/worker/client"
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
)
@ -42,6 +44,8 @@ func InitHandle(conf option.Config,
kubeClient *kubernetes.Clientset,
rainbondClient versioned.Interface,
k8sClient k8sclient.Client,
config *rest.Config,
mapper meta.RESTMapper,
) error {
mq := api_db.MQManager{
EtcdClientArgs: etcdClientArgs,
@ -80,7 +84,7 @@ func InitHandle(conf option.Config,
batchOperationHandler = CreateBatchOperationHandler(mqClient, statusCli, operationHandler)
defaultAppRestoreHandler = NewAppRestoreHandler()
defPodHandler = NewPodHandler(statusCli)
defClusterHandler = NewClusterHandler(kubeClient, conf.RbdNamespace)
defClusterHandler = NewClusterHandler(kubeClient, conf.RbdNamespace, config, mapper)
defaultVolumeTypeHandler = CreateVolumeTypeManger(statusCli)
defaultEtcdHandler = NewEtcdHandler(etcdcli)
defaultmonitorHandler = NewMonitorHandler(prometheusCli)

View File

@ -0,0 +1,44 @@
// RAINBOND, Application Management Platform
// Copyright (C) 2022-2022 Goodrain Co., Ltd.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package handler
import (
api_model "github.com/goodrain/rainbond/api/model"
"github.com/goodrain/rainbond/db"
)
// CreateK8sAttribute -
func (s *ServiceAction) CreateK8sAttribute(tenantID, componentID string, k8sAttr *api_model.ComponentK8sAttribute) error {
return db.GetManager().ComponentK8sAttributeDao().AddModel(k8sAttr.DbModel(tenantID, componentID))
}
// UpdateK8sAttribute -
func (s *ServiceAction) UpdateK8sAttribute(componentID string, k8sAttributes *api_model.ComponentK8sAttribute) error {
attr, err := db.GetManager().ComponentK8sAttributeDao().GetByComponentIDAndName(componentID, k8sAttributes.Name)
if err != nil {
return err
}
attr.AttributeValue = k8sAttributes.AttributeValue
return db.GetManager().ComponentK8sAttributeDao().UpdateModel(attr)
}
// DeleteK8sAttribute -
func (s *ServiceAction) DeleteK8sAttribute(componentID, name string) error {
return db.GetManager().ComponentK8sAttributeDao().DeleteByComponentIDAndName(componentID, name)
}

249
api/handler/resource.go Normal file
View File

@ -0,0 +1,249 @@
package handler
import (
"bytes"
"context"
"fmt"
"github.com/goodrain/rainbond/api/model"
"github.com/goodrain/rainbond/api/util"
"github.com/goodrain/rainbond/db"
dbmodel "github.com/goodrain/rainbond/db/model"
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer/yaml"
yamlt "k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/dynamic"
)
//AddAppK8SResource -
func (c *clusterAction) AddAppK8SResource(ctx context.Context, namespace string, appID string, resourceYaml string) ([]*dbmodel.K8sResource, *util.APIHandleError) {
logrus.Info("begin AddAppK8SResource")
resourceObjects := c.HandleResourceYaml([]byte(resourceYaml), namespace, "create", "")
var resourceList []*dbmodel.K8sResource
for _, resourceObject := range resourceObjects {
resource := resourceObject
if resourceObject.State == model.CreateError {
rsYaml := resourceYaml
if resourceObject.Resource != nil {
rsYaml, _ = ObjectToJSONORYaml("yaml", resourceObject.Resource)
}
resourceList = append(resourceList, &dbmodel.K8sResource{
AppID: appID,
Name: "未识别",
Kind: "未识别",
Content: rsYaml,
ErrorOverview: resource.ErrorOverview,
State: resource.State,
})
} else {
rsYaml, _ := ObjectToJSONORYaml("yaml", resourceObject.Resource)
resourceList = append(resourceList, &dbmodel.K8sResource{
AppID: appID,
Name: resource.Resource.GetName(),
Kind: resource.Resource.GetKind(),
Content: rsYaml,
ErrorOverview: resource.ErrorOverview,
State: resource.State,
})
err := db.GetManager().K8sResourceDao().CreateK8sResourceInBatch(resourceList)
if err != nil {
return nil, &util.APIHandleError{Code: 400, Err: fmt.Errorf("CreateK8sResource %v", err)}
}
}
}
return resourceList, nil
}
//UpdateAppK8SResource -
func (c *clusterAction) UpdateAppK8SResource(ctx context.Context, namespace, appID, name, resourceYaml, kind string) (dbmodel.K8sResource, *util.APIHandleError) {
logrus.Info("begin UpdateAppK8SResource")
rs, err := db.GetManager().K8sResourceDao().GetK8sResourceByNameInBatch(appID, name, kind)
if err != nil {
return dbmodel.K8sResource{}, &util.APIHandleError{Code: 400, Err: fmt.Errorf("get k8s resource %v", err)}
}
resourceObjects := c.HandleResourceYaml([]byte(resourceYaml), namespace, "update", name)
var rsYaml string
if resourceObjects[0].State == 4 {
rsYaml = resourceYaml
rs[0].State = resourceObjects[0].State
rs[0].ErrorOverview = resourceObjects[0].ErrorOverview
rs[0].Content = rsYaml
db.GetManager().K8sResourceDao().UpdateModel(&rs[0])
} else {
rsYaml, _ = ObjectToJSONORYaml("yaml", resourceObjects[0].Resource)
rs[0].State = resourceObjects[0].State
rs[0].ErrorOverview = resourceObjects[0].ErrorOverview
rs[0].Content = rsYaml
db.GetManager().K8sResourceDao().UpdateModel(&rs[0])
}
return rs[0], nil
}
//DeleteAppK8SResource -
func (c *clusterAction) DeleteAppK8SResource(ctx context.Context, namespace, appID, name, resourceYaml, kind string) *util.APIHandleError {
logrus.Info("begin DeleteAppK8SResource")
c.HandleResourceYaml([]byte(resourceYaml), namespace, "delete", name)
err := db.GetManager().K8sResourceDao().DeleteK8sResourceInBatch(appID, name, kind)
if err != nil {
return &util.APIHandleError{Code: 400, Err: fmt.Errorf("DeleteAppK8SResource %v", err)}
}
return nil
}
// SyncAppK8SResources -
func (c *clusterAction) SyncAppK8SResources(ctx context.Context, req *model.SyncResources) ([]*dbmodel.K8sResource, *util.APIHandleError) {
// Only Add
logrus.Info("begin SyncAppK8SResource")
var resourceList []*dbmodel.K8sResource
for _, k8sResource := range req.K8sResources {
resourceObjects := c.HandleResourceYaml([]byte(k8sResource.ResourceYaml), k8sResource.Namespace, "re-create", k8sResource.Name)
if len(resourceObjects) > 1 {
logrus.Warningf("SyncAppK8SResources resourceObjects [%s] too much, ignore it", k8sResource.Name)
continue
}
if len(resourceObjects) == 1 {
resourceList = append(resourceList, &dbmodel.K8sResource{
AppID: k8sResource.AppID,
Name: k8sResource.Name,
Kind: k8sResource.Kind,
Content: k8sResource.ResourceYaml,
ErrorOverview: resourceObjects[0].ErrorOverview,
State: resourceObjects[0].State,
})
}
}
err := db.GetManager().K8sResourceDao().CreateK8sResourceInBatch(resourceList)
if err != nil {
return nil, &util.APIHandleError{Code: 400, Err: fmt.Errorf("SyncK8sResource %v", err)}
}
return resourceList, nil
}
//HandleResourceYaml -
func (c *clusterAction) HandleResourceYaml(resourceYaml []byte, namespace string, change string, name string) []*model.BuildResource {
var buildResourceList []*model.BuildResource
var state int
if change == "create" || change == "re-create" {
state = model.CreateError
} else if change == "update" {
state = model.UpdateError
}
dc, err := dynamic.NewForConfig(c.config)
if err != nil {
logrus.Errorf("%v", err)
buildResourceList = []*model.BuildResource{{
State: state,
ErrorOverview: err.Error(),
}}
return buildResourceList
}
resourceYamlByte := resourceYaml
if err != nil {
logrus.Errorf("%v", err)
buildResourceList = []*model.BuildResource{{
State: state,
ErrorOverview: err.Error(),
}}
return buildResourceList
}
decoder := yamlt.NewYAMLOrJSONDecoder(bytes.NewReader(resourceYamlByte), 1000)
for {
var rawObj runtime.RawExtension
if err = decoder.Decode(&rawObj); err != nil {
if err.Error() == "EOF" {
break
}
logrus.Errorf("%v", err)
buildResourceList = []*model.BuildResource{{
State: state,
ErrorOverview: err.Error(),
}}
return buildResourceList
}
obj, gvk, err := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme).Decode(rawObj.Raw, nil, nil)
if err != nil {
logrus.Errorf("%v", err)
buildResourceList = []*model.BuildResource{{
State: state,
ErrorOverview: err.Error(),
}}
return buildResourceList
}
//转化成map
unstructuredMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
if err != nil {
logrus.Errorf("%v", err)
buildResourceList = []*model.BuildResource{{
State: state,
ErrorOverview: err.Error(),
}}
return buildResourceList
}
//转化成对象
unstructuredObj := &unstructured.Unstructured{Object: unstructuredMap}
mapping, err := c.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
if err != nil {
logrus.Errorf("%v", err)
buildResourceList = []*model.BuildResource{{
State: state,
ErrorOverview: err.Error(),
}}
return buildResourceList
}
var dri dynamic.ResourceInterface
if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
unstructuredObj.SetNamespace(namespace)
dri = dc.Resource(mapping.Resource).Namespace(unstructuredObj.GetNamespace())
} else {
dri = dc.Resource(mapping.Resource)
}
br := &model.BuildResource{
Resource: unstructuredObj,
Dri: dri,
}
buildResourceList = append(buildResourceList, br)
}
for _, buildResource := range buildResourceList {
unstructuredObj := buildResource.Resource
switch change {
case "re-create":
unstructuredObj.SetResourceVersion("")
unstructuredObj.SetCreationTimestamp(metav1.Time{})
unstructuredObj.SetUID("")
fallthrough
case "create":
obj, err := buildResource.Dri.Create(context.TODO(), unstructuredObj, metav1.CreateOptions{})
if err != nil {
logrus.Errorf("k8s resource create error %v", err)
buildResource.Resource = unstructuredObj
buildResource.State = state
buildResource.ErrorOverview = err.Error()
} else {
buildResource.Resource = obj
buildResource.State = model.CreateSuccess
buildResource.ErrorOverview = fmt.Sprintf("创建成功")
}
case "delete":
err := buildResource.Dri.Delete(context.TODO(), name, metav1.DeleteOptions{})
if err != nil {
logrus.Errorf("delete k8s resource error %v", err)
}
case "update":
obj, err := buildResource.Dri.Update(context.TODO(), unstructuredObj, metav1.UpdateOptions{})
if err != nil {
logrus.Errorf("update k8s resource error %v", err)
buildResource.Resource = unstructuredObj
buildResource.State = state
buildResource.ErrorOverview = err.Error()
} else {
buildResource.Resource = obj
buildResource.State = model.UpdateSuccess
buildResource.ErrorOverview = fmt.Sprintf("更新成功")
}
}
}
return buildResourceList
}

View File

@ -0,0 +1,428 @@
package handler
import (
"context"
"encoding/json"
"fmt"
"github.com/goodrain/rainbond/api/model"
"github.com/goodrain/rainbond/api/util"
"github.com/goodrain/rainbond/db"
dbmodel "github.com/goodrain/rainbond/db/model"
rainbondutil "github.com/goodrain/rainbond/util"
"github.com/goodrain/rainbond/util/constants"
"github.com/jinzhu/gorm"
"github.com/sirupsen/logrus"
"github.com/twinj/uuid"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"strings"
"time"
)
//ResourceImport Import the converted k8s resources into recognition
func (c *clusterAction) ResourceImport(namespace string, as map[string]model.ApplicationResource, eid string) (*model.ReturnResourceImport, *util.APIHandleError) {
logrus.Infof("ResourceImport function begin")
var returnResourceImport model.ReturnResourceImport
err := db.GetManager().DB().Transaction(func(tx *gorm.DB) error {
tenant, err := c.createTenant(eid, namespace, tx)
returnResourceImport.Tenant = tenant
if err != nil {
logrus.Errorf("%v", err)
return &util.APIHandleError{Code: 400, Err: fmt.Errorf("create tenant error:%v", err)}
}
for appName, components := range as {
app, err := c.createApp(eid, tx, appName, tenant.UUID)
if err != nil {
logrus.Errorf("create app:%v err:%v", appName, err)
return &util.APIHandleError{Code: 400, Err: fmt.Errorf("create app:%v error:%v", appName, err)}
}
k8sResource, err := c.CreateK8sResource(tx, components.KubernetesResources, app.AppID)
if err != nil {
logrus.Errorf("create K8sResources err:%v", err)
return &util.APIHandleError{Code: 400, Err: fmt.Errorf("create K8sResources err:%v", err)}
}
var componentAttributes []model.ComponentAttributes
for _, componentResource := range components.ConvertResource {
component, err := c.CreateComponent(app, tenant.UUID, componentResource, namespace, false)
if err != nil {
logrus.Errorf("%v", err)
return &util.APIHandleError{Code: 400, Err: fmt.Errorf("create app error:%v", err)}
}
c.createENV(componentResource.ENVManagement, component)
c.createConfig(componentResource.ConfigManagement, component)
c.createPort(componentResource.PortManagement, component)
componentResource.TelescopicManagement.RuleID = c.createTelescopic(componentResource.TelescopicManagement, component)
componentResource.HealthyCheckManagement.ProbeID = c.createHealthyCheck(componentResource.HealthyCheckManagement, component)
c.createK8sAttributes(componentResource.ComponentK8sAttributesManagement, tenant.UUID, component)
componentAttributes = append(componentAttributes, model.ComponentAttributes{
TS: component,
Image: componentResource.BasicManagement.Image,
Cmd: componentResource.BasicManagement.Cmd,
ENV: componentResource.ENVManagement,
Config: componentResource.ConfigManagement,
Port: componentResource.PortManagement,
Telescopic: componentResource.TelescopicManagement,
HealthyCheck: componentResource.HealthyCheckManagement,
ComponentK8sAttributes: componentResource.ComponentK8sAttributesManagement,
})
}
application := model.AppComponent{
App: app,
Component: componentAttributes,
K8sResources: k8sResource,
}
returnResourceImport.App = append(returnResourceImport.App, application)
}
return nil
})
if err != nil {
return nil, &util.APIHandleError{Code: 400, Err: fmt.Errorf("resource import error:%v", err)}
}
logrus.Infof("ResourceImport function end")
return &returnResourceImport, nil
}
func (c *clusterAction) createTenant(eid string, namespace string, tx *gorm.DB) (*dbmodel.Tenants, error) {
logrus.Infof("begin create tenant")
var dbts dbmodel.Tenants
id, name, errN := GetServiceManager().CreateTenandIDAndName(eid)
if errN != nil {
return nil, errN
}
dbts.EID = eid
dbts.Namespace = namespace
dbts.Name = name
dbts.UUID = id
dbts.LimitMemory = 0
tenant, _ := db.GetManager().TenantDao().GetTenantIDByName(dbts.Name)
if tenant != nil {
logrus.Warningf("tenant %v already exists", dbts.Name)
return tenant, nil
}
if err := db.GetManager().TenantDaoTransactions(tx).AddModel(&dbts); err != nil {
if !strings.HasSuffix(err.Error(), "is exist") {
return nil, err
}
}
ns, err := c.clientset.CoreV1().Namespaces().Get(context.Background(), namespace, metav1.GetOptions{})
if err != nil {
return nil, &util.APIHandleError{Code: 404, Err: fmt.Errorf("failed to get namespace %v:%v", namespace, err)}
}
if ns.Labels == nil {
ns.Labels = make(map[string]string)
}
ns.Labels[constants.ResourceManagedByLabel] = constants.Rainbond
_, err = c.clientset.CoreV1().Namespaces().Update(context.Background(), ns, metav1.UpdateOptions{})
if err != nil {
return nil, &util.APIHandleError{Code: 404, Err: fmt.Errorf("failed to add label to namespace %v:%v", namespace, err)}
}
logrus.Infof("end create tenant")
return &dbts, nil
}
func (c *clusterAction) createApp(eid string, tx *gorm.DB, app string, tenantID string) (*dbmodel.Application, error) {
appID := rainbondutil.NewUUID()
application, _ := db.GetManager().ApplicationDaoTransactions(tx).GetAppByName(tenantID, app)
if application != nil {
logrus.Infof("app %v already exists", app)
return application, nil
}
appReq := &dbmodel.Application{
EID: eid,
TenantID: tenantID,
AppID: appID,
AppName: app,
AppType: "rainbond",
GovernanceMode: dbmodel.GovernanceModeKubernetesNativeService,
K8sApp: app,
}
if err := db.GetManager().ApplicationDaoTransactions(tx).AddModel(appReq); err != nil {
return appReq, err
}
return appReq, nil
}
func (c *clusterAction) CreateK8sResource(tx *gorm.DB, k8sResources []dbmodel.K8sResource, AppID string) ([]dbmodel.K8sResource, error) {
var k8sResourceList []*dbmodel.K8sResource
for _, k8sResource := range k8sResources {
k8sResource.AppID = AppID
kr := k8sResource
k8sResourceList = append(k8sResourceList, &kr)
}
err := db.GetManager().K8sResourceDaoTransactions(tx).CreateK8sResourceInBatch(k8sResourceList)
return k8sResources, err
}
func (c *clusterAction) CreateComponent(app *dbmodel.Application, tenantID string, component model.ConvertResource, namespace string, isYaml bool) (*dbmodel.TenantServices, error) {
var extendMethod string
switch component.BasicManagement.ResourceType {
case model.Deployment:
extendMethod = string(dbmodel.ServiceTypeStatelessMultiple)
case model.Job:
extendMethod = string(dbmodel.ServiceTypeJob)
case model.CronJob:
extendMethod = string(dbmodel.ServiceTypeCronJob)
case model.StateFulSet:
extendMethod = string(dbmodel.ServiceTypeStateMultiple)
}
serviceID := rainbondutil.NewUUID()
serviceAlias := "gr" + serviceID[len(serviceID)-6:]
replicas := 1
if component.BasicManagement.Replicas != nil {
replicas = int(*component.BasicManagement.Replicas)
}
JobStrategy, err := json.Marshal(component.BasicManagement.JobStrategy)
if err != nil {
logrus.Errorf("component %v BasicManagement.JobStrategy json error%v", component.ComponentsName, err)
}
ts := dbmodel.TenantServices{
TenantID: tenantID,
ServiceID: serviceID,
ServiceAlias: serviceAlias,
ServiceName: serviceAlias,
ServiceType: "application",
Comment: "docker run application",
ContainerCPU: int(component.BasicManagement.CPU),
ContainerMemory: int(component.BasicManagement.Memory),
ContainerGPU: 0,
UpgradeMethod: "Rolling",
ExtendMethod: extendMethod,
Replicas: replicas,
DeployVersion: time.Now().Format("20060102150405"),
Category: "app_publish",
CurStatus: "undeploy",
Status: 0,
Namespace: namespace,
UpdateTime: time.Now(),
Kind: "internal",
AppID: app.AppID,
K8sComponentName: component.ComponentsName,
JobStrategy: string(JobStrategy),
}
if err := db.GetManager().TenantServiceDao().AddModel(&ts); err != nil {
logrus.Errorf("add service error, %v", err)
return nil, err
}
if !isYaml {
changeLabel := func(label map[string]string) map[string]string {
label[constants.ResourceManagedByLabel] = constants.Rainbond
label["service_id"] = serviceID
label["version"] = ts.DeployVersion
label["creater_id"] = string(rainbondutil.NewTimeVersion())
label["migrator"] = "rainbond"
label["creator"] = "Rainbond"
return label
}
switch component.BasicManagement.ResourceType {
case model.Deployment:
dm, err := c.clientset.AppsV1().Deployments(namespace).Get(context.Background(), component.ComponentsName, metav1.GetOptions{})
if err != nil {
logrus.Errorf("failed to get %v Deployments %v:%v", namespace, component.ComponentsName, err)
return nil, &util.APIHandleError{Code: 404, Err: fmt.Errorf("failed to get Deployments %v:%v", namespace, err)}
}
if dm.Labels == nil {
dm.Labels = make(map[string]string)
}
if dm.Spec.Template.Labels == nil {
dm.Spec.Template.Labels = make(map[string]string)
}
dm.Labels = changeLabel(dm.Labels)
dm.Spec.Template.Labels = changeLabel(dm.Spec.Template.Labels)
_, err = c.clientset.AppsV1().Deployments(namespace).Update(context.Background(), dm, metav1.UpdateOptions{})
if err != nil {
logrus.Errorf("failed to update Deployments %v:%v", namespace, err)
return nil, &util.APIHandleError{Code: 404, Err: fmt.Errorf("failed to update Deployments %v:%v", namespace, err)}
}
case model.Job:
job, err := c.clientset.BatchV1().Jobs(namespace).Get(context.Background(), component.ComponentsName, metav1.GetOptions{})
if err != nil {
logrus.Errorf("failed to get %v Jobs %v:%v", namespace, component.ComponentsName, err)
return nil, &util.APIHandleError{Code: 404, Err: fmt.Errorf("failed to get Jobs %v:%v", namespace, err)}
}
if job.Labels == nil {
job.Labels = make(map[string]string)
}
job.Labels = changeLabel(job.Labels)
_, err = c.clientset.BatchV1().Jobs(namespace).Update(context.Background(), job, metav1.UpdateOptions{})
if err != nil {
logrus.Errorf("failed to update StatefulSets %v:%v", namespace, err)
return nil, &util.APIHandleError{Code: 404, Err: fmt.Errorf("failed to update StatefulSets %v:%v", namespace, err)}
}
case model.CronJob:
cr, err := c.clientset.BatchV1beta1().CronJobs(namespace).Get(context.Background(), component.ComponentsName, metav1.GetOptions{})
if err != nil {
logrus.Errorf("failed to get %v CronJob %v:%v", namespace, component.ComponentsName, err)
return nil, &util.APIHandleError{Code: 404, Err: fmt.Errorf("failed to get CronJob %v:%v", namespace, err)}
}
if cr.Labels == nil {
cr.Labels = make(map[string]string)
}
cr.Labels = changeLabel(cr.Labels)
if cr.Spec.JobTemplate.Labels == nil {
cr.Spec.JobTemplate.Labels = make(map[string]string)
}
cr.Spec.JobTemplate.Labels = changeLabel(cr.Spec.JobTemplate.Labels)
_, err = c.clientset.BatchV1beta1().CronJobs(namespace).Update(context.Background(), cr, metav1.UpdateOptions{})
if err != nil {
logrus.Errorf("failed to update CronJobs %v:%v", namespace, err)
return nil, &util.APIHandleError{Code: 404, Err: fmt.Errorf("failed to update CronJobs %v:%v", namespace, err)}
}
case model.StateFulSet:
sfs, err := c.clientset.AppsV1().StatefulSets(namespace).Get(context.Background(), component.ComponentsName, metav1.GetOptions{})
if err != nil {
logrus.Errorf("failed to get %v StatefulSets %v:%v", namespace, component.ComponentsName, err)
return nil, &util.APIHandleError{Code: 404, Err: fmt.Errorf("failed to get StatefulSets %v:%v", namespace, err)}
}
if sfs.Labels == nil {
sfs.Labels = make(map[string]string)
}
sfs.Labels = changeLabel(sfs.Labels)
if sfs.Spec.Template.Labels == nil {
sfs.Spec.Template.Labels = make(map[string]string)
}
sfs.Spec.Template.Labels = changeLabel(sfs.Spec.Template.Labels)
_, err = c.clientset.AppsV1().StatefulSets(namespace).Update(context.Background(), sfs, metav1.UpdateOptions{})
if err != nil {
logrus.Errorf("failed to update StatefulSets %v:%v", namespace, err)
return nil, &util.APIHandleError{Code: 404, Err: fmt.Errorf("failed to update StatefulSets %v:%v", namespace, err)}
}
}
}
return &ts, nil
}
func (c *clusterAction) createENV(envs []model.ENVManagement, service *dbmodel.TenantServices) {
var envVar []*dbmodel.TenantServiceEnvVar
for _, env := range envs {
var envD dbmodel.TenantServiceEnvVar
envD.AttrName = env.ENVKey
envD.AttrValue = env.ENVValue
envD.TenantID = service.TenantID
envD.ServiceID = service.ServiceID
envD.ContainerPort = 0
envD.IsChange = true
envD.Name = env.ENVExplain
envD.Scope = "inner"
envVar = append(envVar, &envD)
}
if err := db.GetManager().TenantServiceEnvVarDao().CreateOrUpdateEnvsInBatch(envVar); err != nil {
logrus.Errorf("%v Environment variable creation failed:%v", service.ServiceAlias, err)
}
}
func (c *clusterAction) createConfig(configs []model.ConfigManagement, service *dbmodel.TenantServices) {
var configVar []*dbmodel.TenantServiceVolume
for _, config := range configs {
tsv := &dbmodel.TenantServiceVolume{
ServiceID: service.ServiceID,
VolumeName: config.ConfigName,
VolumePath: config.ConfigPath,
VolumeType: "config-file",
Category: "",
VolumeProviderName: "",
IsReadOnly: false,
VolumeCapacity: 0,
AccessMode: "RWX",
SharePolicy: "exclusive",
BackupPolicy: "exclusive",
ReclaimPolicy: "exclusive",
AllowExpansion: false,
Mode: &config.Mode,
}
configVar = append(configVar, tsv)
}
err := db.GetManager().TenantServiceVolumeDao().CreateOrUpdateVolumesInBatch(configVar)
if err != nil {
logrus.Errorf("%v configuration file creation failed:%v", service.ServiceAlias, err)
}
}
func (c *clusterAction) createPort(ports []model.PortManagement, service *dbmodel.TenantServices) {
var portVar []*dbmodel.TenantServicesPort
for _, port := range ports {
portAlias := strings.Replace(service.ServiceAlias, "-", "_", -1)
var vpD dbmodel.TenantServicesPort
vpD.ServiceID = service.ServiceID
vpD.TenantID = service.TenantID
vpD.IsInnerService = &port.Inner
vpD.IsOuterService = &port.Outer
vpD.ContainerPort = int(port.Port)
vpD.MappingPort = int(port.Port)
vpD.Protocol = port.Protocol
vpD.PortAlias = fmt.Sprintf("%v%v", strings.ToUpper(portAlias), port.Port)
vpD.K8sServiceName = fmt.Sprintf("%v-%v", service.ServiceAlias, port.Port)
portVar = append(portVar, &vpD)
}
if err := db.GetManager().TenantServicesPortDao().CreateOrUpdatePortsInBatch(portVar); err != nil {
logrus.Errorf("%v port creation failed:%v", service.ServiceAlias, err)
}
}
func (c *clusterAction) createTelescopic(telescopic model.TelescopicManagement, service *dbmodel.TenantServices) string {
if !telescopic.Enable {
return ""
}
r := &dbmodel.TenantServiceAutoscalerRules{
RuleID: rainbondutil.NewUUID(),
ServiceID: service.ServiceID,
Enable: true,
XPAType: "hpa",
MinReplicas: int(telescopic.MinReplicas),
MaxReplicas: int(telescopic.MaxReplicas),
}
telescopic.RuleID = r.RuleID
if err := db.GetManager().TenantServceAutoscalerRulesDao().AddModel(r); err != nil {
logrus.Errorf("%v TenantServiceAutoscalerRules creation failed:%v", service.ServiceAlias, err)
return ""
}
for _, metric := range telescopic.CPUOrMemory {
m := &dbmodel.TenantServiceAutoscalerRuleMetrics{
RuleID: r.RuleID,
MetricsType: metric.MetricsType,
MetricsName: metric.MetricsName,
MetricTargetType: metric.MetricTargetType,
MetricTargetValue: metric.MetricTargetValue,
}
if err := db.GetManager().TenantServceAutoscalerRuleMetricsDao().AddModel(m); err != nil {
logrus.Errorf("%v TenantServceAutoscalerRuleMetricsDao creation failed:%v", service.ServiceAlias, err)
}
}
return r.RuleID
}
func (c *clusterAction) createHealthyCheck(telescopic model.HealthyCheckManagement, service *dbmodel.TenantServices) string {
if telescopic.Status == 0 {
return ""
}
var tspD dbmodel.TenantServiceProbe
tspD.ServiceID = service.ServiceID
tspD.Cmd = telescopic.Command
tspD.FailureThreshold = telescopic.FailureThreshold
tspD.HTTPHeader = telescopic.HTTPHeader
tspD.InitialDelaySecond = telescopic.InitialDelaySecond
tspD.IsUsed = &telescopic.Status
tspD.Mode = telescopic.Mode
tspD.Path = telescopic.Path
tspD.PeriodSecond = telescopic.PeriodSecond
tspD.Port = telescopic.Port
tspD.ProbeID = strings.Replace(uuid.NewV4().String(), "-", "", -1)
tspD.Scheme = telescopic.DetectionMethod
tspD.SuccessThreshold = telescopic.SuccessThreshold
tspD.TimeoutSecond = telescopic.TimeoutSecond
tspD.FailureAction = ""
if err := GetServiceManager().ServiceProbe(&tspD, "add"); err != nil {
logrus.Errorf("%v createHealthyCheck creation failed:%v", service.ServiceAlias, err)
}
return tspD.ProbeID
}
func (c *clusterAction) createK8sAttributes(specials []*dbmodel.ComponentK8sAttributes, tenantID string, component *dbmodel.TenantServices) {
for _, specials := range specials {
specials.TenantID = tenantID
specials.ComponentID = component.ServiceID
}
err := db.GetManager().ComponentK8sAttributeDao().CreateOrUpdateAttributesInBatch(specials)
if err != nil {
logrus.Errorf("%v createSpecial creation failed:%v", component.ServiceAlias, err)
}
}

View File

@ -0,0 +1,417 @@
package handler
import (
"context"
"encoding/json"
"fmt"
"github.com/goodrain/rainbond/api/model"
dbmodel "github.com/goodrain/rainbond/db/model"
"github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"path"
"sigs.k8s.io/yaml"
"strconv"
"strings"
)
func (c *clusterAction) PodTemplateSpecResource(parameter model.YamlResourceParameter) {
//Port
var ps []model.PortManagement
for _, port := range parameter.Template.Spec.Containers[0].Ports {
if string(port.Protocol) == "UDP" {
ps = append(ps, model.PortManagement{
Port: port.ContainerPort,
Protocol: "UDP",
Inner: false,
Outer: false,
})
} else {
ps = append(ps, model.PortManagement{
Port: port.ContainerPort,
Protocol: "TCP",
Inner: false,
Outer: false,
})
}
logrus.Warningf("Transport protocol type not recognized%v", port.Protocol)
}
//ENV
var envs []model.ENVManagement
for i := 0; i < len(parameter.Template.Spec.Containers[0].Env); i++ {
if cm := parameter.Template.Spec.Containers[0].Env[i].ValueFrom; cm == nil {
envs = append(envs, model.ENVManagement{
ENVKey: parameter.Template.Spec.Containers[0].Env[i].Name,
ENVValue: parameter.Template.Spec.Containers[0].Env[i].Value,
ENVExplain: "",
})
parameter.Template.Spec.Containers[0].Env = append(parameter.Template.Spec.Containers[0].Env[:i], parameter.Template.Spec.Containers[0].Env[i+1:]...)
}
}
//Configs
var configs []model.ConfigManagement
//这一块是处理配置文件
//配置文件的名字最终都是configmap里面的key值。
//volume在被挂载后存在四种情况
//第一种是volume存在itemsvolumeMount的SubPath不等于空。路径直接是volumeMount里面的mountPath。
//第二种是volume存在itemsvolumeMount的SubPath等于空。路径则变成volumeMount里面的mountPath拼接上items里面每一个元素的key值。
//第三种是volume不存在itemsvolumeMount的SubPath不等于空。路径直接是volumeMount里面的mountPath。
//第四种是volume不存在itemsvolumeMount的SubPath等于空。路径则变成volumeMount里面的mountPath拼接上configmap资源里面每一个元素的key值
cmMap := make(map[string]corev1.ConfigMap)
cmList, err := c.clientset.CoreV1().ConfigMaps(parameter.Namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get ConfigMap%v", err)
}
for _, cm := range cmList.Items {
cmMap[cm.Name] = cm
}
cmList.Items = append(cmList.Items, parameter.CMs...)
for _, volume := range parameter.Template.Spec.Volumes {
if volume.ConfigMap != nil && err == nil {
cm, _ := cmMap[volume.ConfigMap.Name]
cmData := cm.Data
isLog := true
var index int
for i, volumeMount := range parameter.Template.Spec.Containers[0].VolumeMounts {
if volume.Name != volumeMount.Name {
continue
}
isLog = false
index = i
if volume.ConfigMap.Items != nil {
if volumeMount.SubPath != "" {
configName := ""
var mode int32
for _, item := range volume.ConfigMap.Items {
if item.Path == volumeMount.SubPath {
configName = item.Key
mode = *item.Mode
}
}
configs = append(configs, model.ConfigManagement{
ConfigName: configName,
ConfigPath: volumeMount.MountPath,
ConfigValue: cmData[configName],
Mode: mode,
})
continue
}
p := volumeMount.MountPath
for _, item := range volume.ConfigMap.Items {
p := path.Join(p, item.Path)
var mode int32
if item.Mode != nil {
mode = *item.Mode
}
configs = append(configs, model.ConfigManagement{
ConfigName: item.Key,
ConfigPath: p,
ConfigValue: cmData[item.Key],
Mode: mode,
})
}
} else {
mode := int32(777)
if volume.ConfigMap.DefaultMode != nil {
mode = *volume.ConfigMap.DefaultMode
}
if volumeMount.SubPath != "" {
configs = append(configs, model.ConfigManagement{
ConfigName: volumeMount.SubPath,
ConfigPath: volumeMount.MountPath,
ConfigValue: cmData[volumeMount.SubPath],
Mode: mode,
})
continue
}
mountPath := volumeMount.MountPath
for key, val := range cmData {
mountPath = path.Join(mountPath, key)
configs = append(configs, model.ConfigManagement{
ConfigName: key,
ConfigPath: mountPath,
ConfigValue: val,
Mode: *volume.ConfigMap.DefaultMode,
})
}
}
}
if isLog {
logrus.Warningf("configmap type resource %v is not mounted in volumemount", volume.ConfigMap.Name)
continue
}
parameter.Template.Spec.Containers[0].VolumeMounts = append(parameter.Template.Spec.Containers[0].VolumeMounts[:index], parameter.Template.Spec.Containers[0].VolumeMounts[index+1:]...)
}
}
//TelescopicManagement
HPAList, err := c.clientset.AutoscalingV1().HorizontalPodAutoscalers(parameter.Namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to get HorizontalPodAutoscalers list:%v", err)
}
HPAList.Items = append(HPAList.Items, parameter.HPAs...)
var t model.TelescopicManagement
//这一块就是自动伸缩的对应解析,
//需要注意的一点是hpa的cpu和memory的阈值设置是通过Annotations["autoscaling.alpha.kubernetes.io/metrics"]字段设置
//而且它的返回值是个json字符串所以设置了一个结构体进行解析。
for _, hpa := range HPAList.Items {
if (hpa.Spec.ScaleTargetRef.Kind != model.Deployment && hpa.Spec.ScaleTargetRef.Kind != model.StateFulSet) || hpa.Spec.ScaleTargetRef.Name != parameter.Name {
t.Enable = false
continue
}
t.Enable = true
t.MinReplicas = *hpa.Spec.MinReplicas
t.MaxReplicas = hpa.Spec.MaxReplicas
var cpuormemorys []*dbmodel.TenantServiceAutoscalerRuleMetrics
cpuUsage := hpa.Spec.TargetCPUUtilizationPercentage
if cpuUsage != nil {
cpuormemorys = append(cpuormemorys, &dbmodel.TenantServiceAutoscalerRuleMetrics{
MetricsType: "resource_metrics",
MetricsName: "cpu",
MetricTargetType: "utilization",
MetricTargetValue: int(*cpuUsage),
})
}
CPUAndMemoryJSON, ok := hpa.Annotations["autoscaling.alpha.kubernetes.io/metrics"]
if ok {
type com struct {
T string `json:"type"`
Resource map[string]interface{}
}
var c []com
err := json.Unmarshal([]byte(CPUAndMemoryJSON), &c)
if err != nil {
logrus.Errorf("autoscaling.alpha.kubernetes.io/metrics parsing failed%v", err)
}
for _, cpuormemory := range c {
switch cpuormemory.Resource["name"] {
case "cpu":
cpu := fmt.Sprint(cpuormemory.Resource["targetAverageValue"])
cpuUnit := cpu[len(cpu)-1:]
var cpuUsage int
if cpuUnit == "m" {
cpuUsage, _ = strconv.Atoi(cpu[:len(cpu)-1])
}
if cpuUnit == "g" || cpuUnit == "G" {
cpuUsage, _ = strconv.Atoi(cpu[:len(cpu)-1])
cpuUsage = cpuUsage * 1024
}
cpuormemorys = append(cpuormemorys, &dbmodel.TenantServiceAutoscalerRuleMetrics{
MetricsType: "resource_metrics",
MetricsName: "cpu",
MetricTargetType: "average_value",
MetricTargetValue: cpuUsage,
})
case "memory":
memory := fmt.Sprint(cpuormemory.Resource["targetAverageValue"])
memoryUnit := memory[:len(memory)-1]
var MemoryUsage int
if memoryUnit == "m" {
MemoryUsage, _ = strconv.Atoi(memory[:len(memory)-1])
}
if memoryUnit == "g" || memoryUnit == "G" {
MemoryUsage, _ = strconv.Atoi(memory[:len(memory)-1])
MemoryUsage = MemoryUsage * 1024
}
cpuormemorys = append(cpuormemorys, &dbmodel.TenantServiceAutoscalerRuleMetrics{
MetricsType: "resource_metrics",
MetricsName: "cpu",
MetricTargetType: "average_value",
MetricTargetValue: MemoryUsage,
})
}
}
}
t.CPUOrMemory = cpuormemorys
}
//HealthyCheckManagement
var hcm model.HealthyCheckManagement
livenessProbe := parameter.Template.Spec.Containers[0].LivenessProbe
if livenessProbe != nil {
var httpHeaders []string
if livenessProbe.HTTPGet != nil {
for _, httpHeader := range livenessProbe.HTTPGet.HTTPHeaders {
nv := httpHeader.Name + "=" + httpHeader.Value
httpHeaders = append(httpHeaders, nv)
}
hcm.DetectionMethod = strings.ToLower(string(livenessProbe.HTTPGet.Scheme))
hcm.Path = livenessProbe.HTTPGet.Path
hcm.Port = int(livenessProbe.HTTPGet.Port.IntVal)
}
hcm.Status = 1
if livenessProbe.Exec != nil {
hcm.Command = strings.Join(livenessProbe.Exec.Command, " ")
}
hcm.HTTPHeader = strings.Join(httpHeaders, ",")
hcm.Mode = "liveness"
hcm.InitialDelaySecond = int(livenessProbe.InitialDelaySeconds)
hcm.PeriodSecond = int(livenessProbe.PeriodSeconds)
hcm.TimeoutSecond = int(livenessProbe.TimeoutSeconds)
hcm.FailureThreshold = int(livenessProbe.FailureThreshold)
hcm.SuccessThreshold = int(livenessProbe.SuccessThreshold)
} else {
readinessProbe := parameter.Template.Spec.Containers[0].ReadinessProbe
if readinessProbe != nil {
var httpHeaders []string
if readinessProbe.HTTPGet != nil {
for _, httpHeader := range readinessProbe.HTTPGet.HTTPHeaders {
nv := httpHeader.Name + "=" + httpHeader.Value
httpHeaders = append(httpHeaders, nv)
}
hcm.DetectionMethod = strings.ToLower(string(readinessProbe.HTTPGet.Scheme))
hcm.Path = readinessProbe.HTTPGet.Path
hcm.Port = int(readinessProbe.HTTPGet.Port.IntVal)
}
hcm.Status = 1
hcm.Mode = "readiness"
if readinessProbe.Exec != nil {
hcm.Command = strings.Join(readinessProbe.Exec.Command, " ")
}
hcm.HTTPHeader = strings.Join(httpHeaders, ",")
hcm.InitialDelaySecond = int(readinessProbe.InitialDelaySeconds)
hcm.PeriodSecond = int(readinessProbe.PeriodSeconds)
hcm.TimeoutSecond = int(readinessProbe.TimeoutSeconds)
hcm.FailureThreshold = int(readinessProbe.FailureThreshold)
hcm.SuccessThreshold = int(readinessProbe.SuccessThreshold)
}
}
var attributes []*dbmodel.ComponentK8sAttributes
if parameter.Template.Spec.Containers[0].Env != nil && len(parameter.Template.Spec.Containers[0].Env) > 0 {
envYaml, err := ObjectToJSONORYaml("yaml", parameter.Template.Spec.Containers[0].Env)
if err != nil {
logrus.Errorf("deployment:%v env %v", parameter.Name, err)
}
envAttributes := &dbmodel.ComponentK8sAttributes{
Name: dbmodel.K8sAttributeNameENV,
SaveType: "yaml",
AttributeValue: envYaml,
}
attributes = append(attributes, envAttributes)
}
if parameter.Template.Spec.Volumes != nil {
volumesYaml, err := ObjectToJSONORYaml("yaml", parameter.Template.Spec.Volumes)
if err != nil {
logrus.Errorf("deployment:%v volumes %v", parameter.Name, err)
}
volumesAttributes := &dbmodel.ComponentK8sAttributes{
Name: dbmodel.K8sAttributeNameVolumes,
SaveType: "yaml",
AttributeValue: volumesYaml,
}
attributes = append(attributes, volumesAttributes)
}
if parameter.Template.Spec.Containers[0].VolumeMounts != nil {
volumeMountsYaml, err := ObjectToJSONORYaml("yaml", parameter.Template.Spec.Containers[0].VolumeMounts)
if err != nil {
logrus.Errorf("deployment:%v volumeMounts %v", parameter.Name, err)
}
volumeMountsAttributes := &dbmodel.ComponentK8sAttributes{
Name: dbmodel.K8sAttributeNameVolumeMounts,
SaveType: "yaml",
AttributeValue: volumeMountsYaml,
}
attributes = append(attributes, volumeMountsAttributes)
}
if parameter.Template.Spec.ServiceAccountName != "" {
serviceAccountAttributes := &dbmodel.ComponentK8sAttributes{
Name: dbmodel.K8sAttributeNameServiceAccountName,
SaveType: "string",
AttributeValue: parameter.Template.Spec.ServiceAccountName,
}
attributes = append(attributes, serviceAccountAttributes)
}
if parameter.RsLabel != nil {
labelsJSON, err := ObjectToJSONORYaml("json", parameter.RsLabel)
if err != nil {
logrus.Errorf("deployment:%v labels %v", parameter.Name, err)
}
labelsAttributes := &dbmodel.ComponentK8sAttributes{
Name: dbmodel.K8sAttributeNameLabels,
SaveType: "json",
AttributeValue: labelsJSON,
}
attributes = append(attributes, labelsAttributes)
}
if parameter.Template.Spec.NodeSelector != nil {
NodeSelectorJSON, err := ObjectToJSONORYaml("json", parameter.Template.Spec.NodeSelector)
if err != nil {
logrus.Errorf("deployment:%v nodeSelector %v", parameter.Name, err)
}
nodeSelectorAttributes := &dbmodel.ComponentK8sAttributes{
Name: dbmodel.K8sAttributeNameNodeSelector,
SaveType: "json",
AttributeValue: NodeSelectorJSON,
}
attributes = append(attributes, nodeSelectorAttributes)
}
if parameter.Template.Spec.Tolerations != nil {
tolerationsYaml, err := ObjectToJSONORYaml("yaml", parameter.Template.Spec.Tolerations)
if err != nil {
logrus.Errorf("deployment:%v tolerations %v", parameter.Name, err)
}
tolerationsAttributes := &dbmodel.ComponentK8sAttributes{
Name: dbmodel.K8sAttributeNameTolerations,
SaveType: "yaml",
AttributeValue: tolerationsYaml,
}
attributes = append(attributes, tolerationsAttributes)
}
if parameter.Template.Spec.Affinity != nil {
affinityYaml, err := ObjectToJSONORYaml("yaml", parameter.Template.Spec.Affinity)
if err != nil {
logrus.Errorf("deployment:%v affinity %v", parameter.Name, err)
}
affinityAttributes := &dbmodel.ComponentK8sAttributes{
Name: dbmodel.K8sAttributeNameAffinity,
SaveType: "yaml",
AttributeValue: affinityYaml,
}
attributes = append(attributes, affinityAttributes)
}
if securityContext := parameter.Template.Spec.Containers[0].SecurityContext; securityContext != nil && securityContext.Privileged != nil {
privilegedAttributes := &dbmodel.ComponentK8sAttributes{
Name: dbmodel.K8sAttributeNamePrivileged,
SaveType: "string",
AttributeValue: strconv.FormatBool(*securityContext.Privileged),
}
attributes = append(attributes, privilegedAttributes)
}
*parameter.ComponentsCR = append(*parameter.ComponentsCR, model.ConvertResource{
ComponentsName: parameter.Name,
BasicManagement: parameter.Basic,
PortManagement: ps,
ENVManagement: envs,
ConfigManagement: configs,
TelescopicManagement: t,
HealthyCheckManagement: hcm,
ComponentK8sAttributesManagement: attributes,
})
}
//ObjectToJSONORYaml changeType true is json / yaml
func ObjectToJSONORYaml(changeType string, data interface{}) (string, error) {
if data == nil {
return "", nil
}
dataJSON, err := json.Marshal(data)
if err != nil {
return "", fmt.Errorf("json serialization failed err:%v", err)
}
if changeType == "json" {
return string(dataJSON), nil
}
dataYaml, err := yaml.JSONToYAML(dataJSON)
if err != nil {
return "", fmt.Errorf("yaml serialization failed err:%v", err)
}
return string(dataYaml), nil
}

View File

@ -937,6 +937,9 @@ func (s *ServiceAction) ServiceUpdate(sc map[string]interface{}) error {
ts.ExtendMethod = extendMethod
ts.ServiceType = extendMethod
}
if js, ok := sc["job_strategy"].(string); ok {
ts.JobStrategy = js
}
//update component
if err := db.GetManager().TenantServiceDao().UpdateModel(ts); err != nil {
logrus.Errorf("update service error, %v", err)
@ -2914,6 +2917,28 @@ func (s *ServiceAction) SyncComponentEndpoints(tx *gorm.DB, components []*api_mo
return db.GetManager().ThirdPartySvcDiscoveryCfgDaoTransactions(tx).CreateOrUpdate3rdSvcDiscoveryCfgInBatch(thirdPartySvcDiscoveryCfgs)
}
// SyncComponentK8sAttributes -
func (s *ServiceAction) SyncComponentK8sAttributes(tx *gorm.DB, app *dbmodel.Application, components []*api_model.Component) error {
var (
componentIDs []string
k8sAttributes []*dbmodel.ComponentK8sAttributes
)
for _, component := range components {
if component.ComponentK8sAttributes == nil || len(component.ComponentK8sAttributes) == 0 {
continue
}
componentIDs = append(componentIDs, component.ComponentBase.ComponentID)
for _, k8sAttribute := range component.ComponentK8sAttributes {
k8sAttributes = append(k8sAttributes, k8sAttribute.DbModel(app.TenantID, component.ComponentBase.ComponentID))
}
}
if err := db.GetManager().ComponentK8sAttributeDaoTransactions(tx).DeleteByComponentIDs(componentIDs); err != nil {
return err
}
return db.GetManager().ComponentK8sAttributeDaoTransactions(tx).CreateOrUpdateAttributesInBatch(k8sAttributes)
}
// Log returns the logs reader for a container in a pod, a pod or a component.
func (s *ServiceAction) Log(w http.ResponseWriter, r *http.Request, component *dbmodel.TenantServices, podName, containerName string, follow bool) error {
// If podName and containerName is missing, return the logs reader for the component
@ -2985,6 +3010,8 @@ func TransStatus(eStatus string) string {
return "未部署"
case "deployed":
return "已部署"
case "succeeded":
return "已完成"
}
return ""
}

View File

@ -90,6 +90,10 @@ type ServiceHandler interface {
DeleteServiceMonitor(tenantID, serviceID, name string) (*dbmodel.TenantServiceMonitor, error)
AddServiceMonitor(tenantID, serviceID string, add api_model.AddServiceMonitorRequestStruct) (*dbmodel.TenantServiceMonitor, error)
CreateK8sAttribute(tenantID, componentID string, k8sAttr *api_model.ComponentK8sAttribute) error
UpdateK8sAttribute(componentID string, k8sAttributes *api_model.ComponentK8sAttribute) error
DeleteK8sAttribute(componentID, name string) error
SyncComponentBase(tx *gorm.DB, app *dbmodel.Application, components []*api_model.Component) error
SyncComponentMonitors(tx *gorm.DB, app *dbmodel.Application, components []*api_model.Component) error
SyncComponentPorts(tx *gorm.DB, app *dbmodel.Application, components []*api_model.Component) error
@ -103,6 +107,7 @@ type ServiceHandler interface {
SyncComponentPlugins(tx *gorm.DB, app *dbmodel.Application, components []*api_model.Component) error
SyncComponentScaleRules(tx *gorm.DB, components []*api_model.Component) error
SyncComponentEndpoints(tx *gorm.DB, components []*api_model.Component) error
SyncComponentK8sAttributes(tx *gorm.DB, app *dbmodel.Application, components []*api_model.Component) error
Log(w http.ResponseWriter, r *http.Request, component *dbmodel.TenantServices, podName, containerName string, follow bool) error
}

View File

@ -319,6 +319,7 @@ func (o *OperationHandler) buildFromImage(r *model.ComponentBuildReq, service *d
func (o *OperationHandler) buildFromSourceCode(r *model.ComponentBuildReq, service *dbmodel.TenantServices) error {
if r.CodeInfo.RepoURL == "" || r.CodeInfo.Branch == "" || r.DeployVersion == "" {
logrus.Infof("r.CodeInfo.RepoURL:%v, r.CodeInfo.Branch:%v, r.DeployVersion:%v", r.CodeInfo.RepoURL, r.CodeInfo.Branch, r.DeployVersion)
return fmt.Errorf("build from code failure, args error")
}
body := make(map[string]interface{})

View File

@ -0,0 +1,422 @@
package handler
import (
"bytes"
"context"
"encoding/json"
"fmt"
api_model "github.com/goodrain/rainbond/api/model"
"github.com/goodrain/rainbond/api/util"
"github.com/goodrain/rainbond/db"
dbmodel "github.com/goodrain/rainbond/db/model"
"github.com/jinzhu/gorm"
"github.com/sirupsen/logrus"
"io/ioutil"
appv1 "k8s.io/api/apps/v1"
autoscalingv1 "k8s.io/api/autoscaling/v1"
batchv1 "k8s.io/api/batch/v1"
"k8s.io/api/batch/v1beta1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer/yaml"
yamlt "k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/dynamic"
"path"
"path/filepath"
"strings"
)
//AppYamlResourceName -
func (c *clusterAction) AppYamlResourceName(yamlResource api_model.YamlResource) (map[string]api_model.LabelResource, *util.APIHandleError) {
logrus.Infof("AppYamlResourceName begin")
fileResource := make(map[string]api_model.LabelResource)
k8sResourceObjects := c.YamlToResource(yamlResource)
var DeployNames, JobNames, CJNames, SFSNames, RoleNames, HPANames, RBNames, SANames, SecretNames, ServiceNames, CMNames, NetworkPolicyNames, IngressNames, PVCNames []string
defaultResource := make(map[string][]string)
for _, k8sResourceObject := range k8sResourceObjects {
if k8sResourceObject.Error != "" {
fileResource[k8sResourceObject.FileName] = api_model.LabelResource{
Status: k8sResourceObject.Error,
}
continue
}
for _, buildResource := range k8sResourceObject.BuildResources {
switch buildResource.Resource.GetKind() {
case api_model.Deployment:
DeployNames = append(DeployNames, buildResource.Resource.GetName())
case api_model.Job:
JobNames = append(JobNames, buildResource.Resource.GetName())
case api_model.CronJob:
CJNames = append(CJNames, buildResource.Resource.GetName())
case api_model.StateFulSet:
SFSNames = append(SFSNames, buildResource.Resource.GetName())
case api_model.Role:
RoleNames = append(RoleNames, buildResource.Resource.GetName())
case api_model.HorizontalPodAutoscaler:
HPANames = append(HPANames, buildResource.Resource.GetName())
case api_model.RoleBinding:
RBNames = append(RBNames, buildResource.Resource.GetName())
case api_model.ServiceAccount:
SANames = append(SANames, buildResource.Resource.GetName())
case api_model.Secret:
SecretNames = append(SecretNames, buildResource.Resource.GetName())
case api_model.Service:
ServiceNames = append(ServiceNames, buildResource.Resource.GetName())
case api_model.ConfigMap:
CMNames = append(CMNames, buildResource.Resource.GetName())
case api_model.NetworkPolicy:
NetworkPolicyNames = append(NetworkPolicyNames, buildResource.Resource.GetName())
case api_model.Ingress:
IngressNames = append(IngressNames, buildResource.Resource.GetName())
case api_model.PVC:
PVCNames = append(PVCNames, buildResource.Resource.GetName())
default:
defaultNames, ok := defaultResource[buildResource.Resource.GetKind()]
if ok {
defaultResource[buildResource.Resource.GetKind()] = append(defaultNames, buildResource.Resource.GetName())
} else {
defaultResource[buildResource.Resource.GetKind()] = []string{buildResource.Resource.GetName()}
}
}
}
}
fileResource["app_resource"] = api_model.LabelResource{
UnSupport: defaultResource,
Workloads: api_model.WorkLoadsResource{
Deployments: DeployNames,
Jobs: JobNames,
CronJobs: CJNames,
StateFulSets: SFSNames,
},
Others: api_model.OtherResource{
Services: ServiceNames,
PVC: PVCNames,
Ingresses: IngressNames,
NetworkPolicies: NetworkPolicyNames,
ConfigMaps: CMNames,
Secrets: SecretNames,
ServiceAccounts: ServiceNames,
RoleBindings: RoleNames,
HorizontalPodAutoscalers: HPANames,
Roles: RoleNames,
},
Status: "",
}
logrus.Infof("AppYamlResourceName end")
return fileResource, nil
}
//AppYamlResourceDetailed -
func (c *clusterAction) AppYamlResourceDetailed(yamlResource api_model.YamlResource, yamlImport bool) (api_model.ApplicationResource, *util.APIHandleError) {
logrus.Infof("AppYamlResourceDetailed begin")
k8sResourceObjects := c.YamlToResource(yamlResource)
var K8SResource []dbmodel.K8sResource
var ConvertResource []api_model.ConvertResource
for _, k8sResourceObject := range k8sResourceObjects {
if k8sResourceObject.Error != "" {
continue
}
var cms []corev1.ConfigMap
var hpas []autoscalingv1.HorizontalPodAutoscaler
for _, buildResource := range k8sResourceObject.BuildResources {
if buildResource.Resource.GetKind() == api_model.ConfigMap {
var cm corev1.ConfigMap
cmJSON, _ := json.Marshal(buildResource.Resource)
json.Unmarshal(cmJSON, &cm)
cms = append(cms, cm)
continue
}
if buildResource.Resource.GetKind() == api_model.HorizontalPodAutoscaler {
var hpa autoscalingv1.HorizontalPodAutoscaler
cmJSON, _ := json.Marshal(buildResource.Resource)
json.Unmarshal(cmJSON, &hpa)
hpas = append(hpas, hpa)
}
}
for _, buildResource := range k8sResourceObject.BuildResources {
errorOverview := "创建成功"
state := 1
switch buildResource.Resource.GetKind() {
case api_model.Deployment:
deployJSON, _ := json.Marshal(buildResource.Resource)
var deployObject appv1.Deployment
json.Unmarshal(deployJSON, &deployObject)
basic := api_model.BasicManagement{
ResourceType: api_model.Deployment,
Replicas: deployObject.Spec.Replicas,
Memory: deployObject.Spec.Template.Spec.Containers[0].Resources.Limits.Memory().Value() / 1024 / 1024,
CPU: deployObject.Spec.Template.Spec.Containers[0].Resources.Limits.Cpu().Value(),
Image: deployObject.Spec.Template.Spec.Containers[0].Image,
Cmd: strings.Join(append(deployObject.Spec.Template.Spec.Containers[0].Command, deployObject.Spec.Template.Spec.Containers[0].Args...), " "),
}
parameter := api_model.YamlResourceParameter{
ComponentsCR: &ConvertResource,
Basic: basic,
Template: deployObject.Spec.Template,
Namespace: yamlResource.Namespace,
Name: buildResource.Resource.GetName(),
RsLabel: deployObject.Labels,
HPAs: hpas,
CMs: cms,
}
c.PodTemplateSpecResource(parameter)
case api_model.Job:
jobJSON, _ := json.Marshal(buildResource.Resource)
var jobObject batchv1.Job
json.Unmarshal(jobJSON, &jobObject)
basic := api_model.BasicManagement{
ResourceType: api_model.Job,
Replicas: jobObject.Spec.Completions,
Memory: jobObject.Spec.Template.Spec.Containers[0].Resources.Limits.Memory().Value() / 1024 / 1024,
CPU: jobObject.Spec.Template.Spec.Containers[0].Resources.Limits.Cpu().Value(),
Image: jobObject.Spec.Template.Spec.Containers[0].Image,
Cmd: strings.Join(append(jobObject.Spec.Template.Spec.Containers[0].Command, jobObject.Spec.Template.Spec.Containers[0].Args...), " "),
}
parameter := api_model.YamlResourceParameter{
ComponentsCR: &ConvertResource,
Basic: basic,
Template: jobObject.Spec.Template,
Namespace: yamlResource.Namespace,
Name: buildResource.Resource.GetName(),
RsLabel: jobObject.Labels,
HPAs: hpas,
CMs: cms,
}
c.PodTemplateSpecResource(parameter)
case api_model.CronJob:
cjJSON, _ := json.Marshal(buildResource.Resource)
var cjObject v1beta1.CronJob
json.Unmarshal(cjJSON, &cjObject)
basic := api_model.BasicManagement{
ResourceType: api_model.CronJob,
Replicas: cjObject.Spec.JobTemplate.Spec.Completions,
Memory: cjObject.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Resources.Limits.Memory().Value() / 1024 / 1024,
CPU: cjObject.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Resources.Limits.Cpu().Value(),
Image: cjObject.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Image,
Cmd: strings.Join(append(cjObject.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Command, cjObject.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Args...), " "),
}
parameter := api_model.YamlResourceParameter{
ComponentsCR: &ConvertResource,
Basic: basic,
Template: cjObject.Spec.JobTemplate.Spec.Template,
Namespace: yamlResource.Namespace,
Name: buildResource.Resource.GetName(),
RsLabel: cjObject.Labels,
HPAs: hpas,
CMs: cms,
}
c.PodTemplateSpecResource(parameter)
case api_model.StateFulSet:
sfsJSON, _ := json.Marshal(buildResource.Resource)
var sfsObject appv1.StatefulSet
json.Unmarshal(sfsJSON, &sfsObject)
basic := api_model.BasicManagement{
ResourceType: api_model.StateFulSet,
Replicas: sfsObject.Spec.Replicas,
Memory: sfsObject.Spec.Template.Spec.Containers[0].Resources.Limits.Memory().Value() / 1024 / 1024,
CPU: sfsObject.Spec.Template.Spec.Containers[0].Resources.Limits.Cpu().Value(),
Image: sfsObject.Spec.Template.Spec.Containers[0].Image,
Cmd: strings.Join(append(sfsObject.Spec.Template.Spec.Containers[0].Command, sfsObject.Spec.Template.Spec.Containers[0].Args...), " "),
}
parameter := api_model.YamlResourceParameter{
ComponentsCR: &ConvertResource,
Basic: basic,
Template: sfsObject.Spec.Template,
Namespace: yamlResource.Namespace,
Name: buildResource.Resource.GetName(),
RsLabel: sfsObject.Labels,
HPAs: hpas,
CMs: cms,
}
c.PodTemplateSpecResource(parameter)
default:
if yamlImport {
resource, err := c.ResourceCreate(buildResource, yamlResource.Namespace)
if err != nil {
errorOverview = err.Error()
state = api_model.CreateError
} else {
buildResource.Resource = resource
}
}
kubernetesResourcesYAML, err := ObjectToJSONORYaml("yaml", buildResource.Resource)
if err != nil {
logrus.Errorf("namespace:%v %v:%v error: %v", yamlResource.Namespace, buildResource.Resource.GetKind(), buildResource.Resource.GetName(), err)
}
K8SResource = append(K8SResource, dbmodel.K8sResource{
Name: buildResource.Resource.GetName(),
Kind: buildResource.Resource.GetKind(),
Content: kubernetesResourcesYAML,
State: state,
ErrorOverview: errorOverview,
})
}
}
}
logrus.Infof("AppYamlResourceDetailed end")
return api_model.ApplicationResource{
K8SResource,
ConvertResource,
}, nil
}
//AppYamlResourceImport -
func (c *clusterAction) AppYamlResourceImport(yamlResource api_model.YamlResource, components api_model.ApplicationResource) (api_model.AppComponent, *util.APIHandleError) {
logrus.Infof("AppYamlResourceImport begin")
app, err := db.GetManager().ApplicationDao().GetAppByID(yamlResource.AppID)
if err != nil {
return api_model.AppComponent{}, &util.APIHandleError{Code: 400, Err: fmt.Errorf("GetAppByID error %v", err)}
}
var ar api_model.AppComponent
err = db.GetManager().DB().Transaction(func(tx *gorm.DB) error {
k8sResource, err := c.CreateK8sResource(tx, components.KubernetesResources, app.AppID)
if err != nil {
logrus.Errorf("create K8sResources err:%v", err)
return &util.APIHandleError{Code: 400, Err: fmt.Errorf("create K8sResources err:%v", err)}
}
var componentAttributes []api_model.ComponentAttributes
for _, componentResource := range components.ConvertResource {
component, err := c.CreateComponent(app, yamlResource.TenantID, componentResource, yamlResource.Namespace, true)
if err != nil {
logrus.Errorf("%v", err)
return &util.APIHandleError{Code: 400, Err: fmt.Errorf("create app error:%v", err)}
}
c.createENV(componentResource.ENVManagement, component)
c.createConfig(componentResource.ConfigManagement, component)
c.createPort(componentResource.PortManagement, component)
componentResource.TelescopicManagement.RuleID = c.createTelescopic(componentResource.TelescopicManagement, component)
componentResource.HealthyCheckManagement.ProbeID = c.createHealthyCheck(componentResource.HealthyCheckManagement, component)
c.createK8sAttributes(componentResource.ComponentK8sAttributesManagement, yamlResource.TenantID, component)
componentAttributes = append(componentAttributes, api_model.ComponentAttributes{
TS: component,
Image: componentResource.BasicManagement.Image,
Cmd: componentResource.BasicManagement.Cmd,
ENV: componentResource.ENVManagement,
Config: componentResource.ConfigManagement,
Port: componentResource.PortManagement,
Telescopic: componentResource.TelescopicManagement,
HealthyCheck: componentResource.HealthyCheckManagement,
ComponentK8sAttributes: componentResource.ComponentK8sAttributesManagement,
})
}
ar = api_model.AppComponent{
App: app,
K8sResources: k8sResource,
Component: componentAttributes,
}
return nil
})
if err != nil {
return api_model.AppComponent{}, &util.APIHandleError{Code: 400, Err: fmt.Errorf("app yaml resource import error:%v", err)}
}
logrus.Infof("AppYamlResourceImport end")
return ar, nil
}
//YamlToResource -
func (c *clusterAction) YamlToResource(yamlResource api_model.YamlResource) []api_model.K8sResourceObject {
yamlDirectoryPath := path.Join("/grdata/package_build/temp/events", yamlResource.EventID, "*")
yamlFilesPath, _ := filepath.Glob(yamlDirectoryPath)
var fileBuildResourceList []api_model.K8sResourceObject
for _, yamlFilePath := range yamlFilesPath {
pathSplitList := strings.Split(yamlFilePath, "/")
fileName := pathSplitList[len(pathSplitList)-1]
yamlFileBytes, err := ioutil.ReadFile(yamlFilePath)
if err != nil {
logrus.Errorf("%v", err)
fileBuildResourceList = append(fileBuildResourceList, api_model.K8sResourceObject{
FileName: fileName,
BuildResources: nil,
Error: err.Error(),
})
continue
}
dc, err := dynamic.NewForConfig(c.config)
if err != nil {
logrus.Errorf("%v", err)
fileBuildResourceList = append(fileBuildResourceList, api_model.K8sResourceObject{
FileName: fileName,
BuildResources: nil,
Error: err.Error(),
})
continue
}
decoder := yamlt.NewYAMLOrJSONDecoder(bytes.NewReader(yamlFileBytes), 1000)
var buildResourceList []api_model.BuildResource
for {
var rawObj runtime.RawExtension
if err = decoder.Decode(&rawObj); err != nil {
if err.Error() == "EOF" {
break
}
logrus.Errorf("%v", err)
fileBuildResourceList = append(fileBuildResourceList, api_model.K8sResourceObject{
FileName: fileName,
BuildResources: nil,
Error: err.Error(),
})
break
}
obj, gvk, err := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme).Decode(rawObj.Raw, nil, nil)
if err != nil {
logrus.Errorf("%v", err)
fileBuildResourceList = append(fileBuildResourceList, api_model.K8sResourceObject{
FileName: fileName,
BuildResources: nil,
Error: err.Error(),
})
break
}
unstructuredMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
if err != nil {
logrus.Errorf("%v", err)
fileBuildResourceList = append(fileBuildResourceList, api_model.K8sResourceObject{
FileName: fileName,
BuildResources: nil,
Error: err.Error(),
})
break
}
unstructuredObj := &unstructured.Unstructured{Object: unstructuredMap}
buildResourceList = append(buildResourceList, api_model.BuildResource{
Resource: unstructuredObj,
State: api_model.CreateError,
ErrorOverview: "",
Dri: nil,
DC: dc,
GVK: gvk,
})
}
fileBuildResourceList = append(fileBuildResourceList, api_model.K8sResourceObject{
FileName: fileName,
BuildResources: buildResourceList,
Error: "",
})
}
return fileBuildResourceList
}
//ResourceCreate -
func (c *clusterAction) ResourceCreate(buildResource api_model.BuildResource, namespace string) (*unstructured.Unstructured, error) {
mapping, err := c.mapper.RESTMapping(buildResource.GVK.GroupKind(), buildResource.GVK.Version)
if err != nil {
logrus.Errorf("%v", err)
return nil, err
}
if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
buildResource.Resource.SetNamespace(namespace)
buildResource.Dri = buildResource.DC.Resource(mapping.Resource).Namespace(buildResource.Resource.GetNamespace())
} else {
buildResource.Dri = buildResource.DC.Resource(mapping.Resource)
}
obj, err := buildResource.Dri.Create(context.Background(), buildResource.Resource, metav1.CreateOptions{})
if err != nil {
return nil, err
}
return obj, nil
}

View File

@ -241,23 +241,24 @@ func (e *ComponentEnv) DbModel(tenantID, componentID string) *dbmodel.TenantServ
// Component All attributes related to the component
type Component struct {
ComponentBase ComponentBase `json:"component_base"`
HTTPRules []AddHTTPRuleStruct `json:"http_rules"`
TCPRules []AddTCPRuleStruct `json:"tcp_rules"`
HTTPRuleConfigs []HTTPRuleConfig `json:"http_rule_configs"`
Monitors []AddServiceMonitorRequestStruct `json:"monitors"`
Ports []TenantServicesPort `json:"ports"`
Relations []TenantComponentRelation `json:"relations"`
Envs []ComponentEnv `json:"envs"`
Probes []ServiceProbe `json:"probes"`
AppConfigGroupRels []AppConfigGroupRelations `json:"app_config_groups"`
Labels []ComponentLabel `json:"labels"`
Plugins []ComponentPlugin `json:"plugins"`
AutoScaleRule AutoScalerRule `json:"auto_scale_rule"`
ConfigFiles []ComponentConfigFile `json:"config_files"`
VolumeRelations []VolumeRelation `json:"volume_relations"`
Volumes []ComponentVolume `json:"volumes"`
Endpoint *Endpoints `json:"endpoint"`
ComponentBase ComponentBase `json:"component_base"`
HTTPRules []AddHTTPRuleStruct `json:"http_rules"`
TCPRules []AddTCPRuleStruct `json:"tcp_rules"`
HTTPRuleConfigs []HTTPRuleConfig `json:"http_rule_configs"`
Monitors []AddServiceMonitorRequestStruct `json:"monitors"`
Ports []TenantServicesPort `json:"ports"`
Relations []TenantComponentRelation `json:"relations"`
Envs []ComponentEnv `json:"envs"`
Probes []ServiceProbe `json:"probes"`
AppConfigGroupRels []AppConfigGroupRelations `json:"app_config_groups"`
Labels []ComponentLabel `json:"labels"`
Plugins []ComponentPlugin `json:"plugins"`
AutoScaleRule AutoScalerRule `json:"auto_scale_rule"`
ConfigFiles []ComponentConfigFile `json:"config_files"`
VolumeRelations []VolumeRelation `json:"volume_relations"`
Volumes []ComponentVolume `json:"volumes"`
Endpoint *Endpoints `json:"endpoint"`
ComponentK8sAttributes []ComponentK8sAttribute `json:"component_k8s_attributes"`
}
// SyncComponentReq -
@ -265,3 +266,34 @@ type SyncComponentReq struct {
Components []*Component `json:"components"`
DeleteComponentIDs []string `json:"delete_component_ids"`
}
// ComponentK8sAttribute -
type ComponentK8sAttribute struct {
// Name Define the attribute name, which is currently supported
// [nodeSelector/labels/tolerations/volumes/serviceAccountName/privileged/affinity/volumeMounts]
// The field name should be the same as that in the K8s resource yaml file.
Name string `json:"name"`
// The field type defines how the attribute is stored. Currently, `json/yaml/string` are supported
SaveType string `json:"save_type"`
// Define the attribute value, which is stored in the database.
// The value is stored in the database in the form of `json/yaml/string`.
AttributeValue string `json:"attribute_value"`
}
// DbModel return database model
func (k *ComponentK8sAttribute) DbModel(tenantID, componentID string) *dbmodel.ComponentK8sAttributes {
return &dbmodel.ComponentK8sAttributes{
TenantID: tenantID,
ComponentID: componentID,
Name: k.Name,
SaveType: k.SaveType,
AttributeValue: k.AttributeValue,
}
}
// DeleteK8sAttributeReq -
type DeleteK8sAttributeReq struct {
Name string `json:"name"`
}

View File

@ -0,0 +1,133 @@
package model
import (
"github.com/goodrain/rainbond/db/model"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)
//BasicManagement -
type BasicManagement struct {
ResourceType string `json:"resource_type"`
Replicas *int32 `json:"replicas"`
Image string `json:"image"`
Memory int64 `json:"memory"`
Cmd string `json:"command"`
CPU int64 `json:"cpu"`
JobStrategy JobStrategy `json:"job_strategy"`
}
//PortManagement -
type PortManagement struct {
Port int32 `json:"port"`
Protocol string `json:"protocol"`
Inner bool `json:"inner"`
Outer bool `json:"outer"`
}
//ENVManagement -
type ENVManagement struct {
ENVKey string `json:"env_key"`
ENVValue string `json:"env_value"`
ENVExplain string `json:"env_explain"`
}
//ConfigManagement -
type ConfigManagement struct {
ConfigName string `json:"config_name"`
ConfigPath string `json:"config_path"`
Mode int32 `json:"mode"`
ConfigValue string `json:"config_value"`
}
//HealthyCheckManagement -
type HealthyCheckManagement struct {
Status int `json:"status"`
ProbeID string `json:"probe_id"`
Port int `json:"port"`
Path string `json:"path"`
HTTPHeader string `json:"http_header"`
Command string `json:"cmd"`
DetectionMethod string `json:"detection_method"`
Mode string `json:"mode"`
InitialDelaySecond int `json:"initial_delay_second"`
PeriodSecond int `json:"period_second"`
TimeoutSecond int `json:"timeout_second"`
SuccessThreshold int `json:"success_threshold"`
FailureThreshold int `json:"failure_threshold"`
}
//TelescopicManagement -
type TelescopicManagement struct {
Enable bool `json:"enable"`
RuleID string `json:"rule_id"`
MinReplicas int32 `json:"min_replicas"`
MaxReplicas int32 `json:"max_replicas"`
CPUOrMemory []*model.TenantServiceAutoscalerRuleMetrics `json:"cpu_or_memory"`
}
//KubernetesResources -
type KubernetesResources struct {
Name string `json:"name"`
Spec v1.ServiceSpec `json:"spec"`
Namespace string `json:"namespace"`
Labels map[string]string `json:"labels"`
Annotations map[string]string `json:"annotations"`
Kind string `json:"kind"`
APIVersion string `json:"api_version"`
GenerateName string `json:"generate_name"`
UID types.UID `json:"uid"`
ResourceVersion string `json:"resource_version"`
Generation int64 `json:"generation"`
CreationTimestamp metav1.Time `json:"creation_timestamp"`
DeletionTimestamp *metav1.Time `json:"deletion_timestamp"`
DeletionGracePeriodSeconds *int64 `json:"deletion_grace_period_seconds"`
OwnerReferences []metav1.OwnerReference `json:"owner_references"`
Finalizers []string `json:"finalizers"`
ClusterName string `json:"cluster_name"`
}
//ApplicationResource -
type ApplicationResource struct {
KubernetesResources []model.K8sResource `json:"kubernetes_resources"`
ConvertResource []ConvertResource `json:"convert_resource"`
}
//ConvertResource -
type ConvertResource struct {
ComponentsName string `json:"components_name"`
BasicManagement BasicManagement `json:"basic_management"`
PortManagement []PortManagement `json:"port_management"`
ENVManagement []ENVManagement `json:"env_management"`
ConfigManagement []ConfigManagement `json:"config_management"`
HealthyCheckManagement HealthyCheckManagement `json:"health_check_management"`
TelescopicManagement TelescopicManagement `json:"telescopic_management"`
ComponentK8sAttributesManagement []*model.ComponentK8sAttributes `json:"component_k8s_attributes_management"`
}
//ComponentAttributes -
type ComponentAttributes struct {
TS *model.TenantServices `json:"ts"`
Image string `json:"image"`
Cmd string `json:"cmd"`
ENV []ENVManagement `json:"env"`
Config []ConfigManagement `json:"config"`
Port []PortManagement `json:"port"`
Telescopic TelescopicManagement `json:"telescopic"`
HealthyCheck HealthyCheckManagement `json:"healthy_check"`
ComponentK8sAttributes []*model.ComponentK8sAttributes `json:"component_k8s_attributes"`
}
//AppComponent -
type AppComponent struct {
App *model.Application `json:"app"`
K8sResources []model.K8sResource `json:"k8s_resources"`
Component []ComponentAttributes `json:"component"`
}
//ReturnResourceImport -
type ReturnResourceImport struct {
Tenant *model.Tenants `json:"tenant"`
App []AppComponent `json:"app"`
}

View File

@ -359,6 +359,7 @@ type ServiceStruct struct {
HTTPRules []AddHTTPRuleStruct `json:"http_rules" validate:"http_rules"`
TCPRules []AddTCPRuleStruct `json:"tcp_rules" validate:"tcp_rules"`
K8sComponentName string `json:"k8s_component_name" validate:"k8s_component_name"`
JobStrategy string `json:"job_strategy" validate:"job_strategy"`
}
// Endpoints holds third-party service endpoints or configuraion to get endpoints.
@ -449,6 +450,55 @@ type DeleteServicePort struct {
Port int `json:"port"`
}
//AddHandleResource -
type AddHandleResource struct {
Namespace string `json:"namespace"`
AppID string `json:"app_id"`
ResourceYaml string `json:"resource_yaml"`
}
//HandleResource -
type HandleResource struct {
Name string `json:"name"`
AppID string `json:"app_id"`
Kind string `json:"kind"`
Namespace string `json:"namespace"`
ResourceYaml string `json:"resource_yaml"`
}
// SyncResources -
type SyncResources struct {
K8sResources []HandleResource `json:"k8s_resources"`
}
//YamlResource -
type YamlResource struct {
EventID string `json:"event_id"`
AppID string `json:"region_app_id"`
TenantID string `json:"tenant_id"`
Namespace string `json:"namespace"`
}
const (
//CreateSuccess -
CreateSuccess = 1
//UpdateSuccess -
UpdateSuccess = 2
//CreateError -
CreateError = 3
//UpdateError -
UpdateError = 4
)
// JobStrategy -
type JobStrategy struct {
Schedule string `json:"schedule"`
BackoffLimit string `json:"backoff_limit"`
Parallelism string `json:"parallelism"`
ActiveDeadlineSeconds string `json:"active_deadline_seconds"`
Completions string `json:"completions"`
}
//TenantResources TenantResources
// swagger:parameters tenantResources
type TenantResources struct {
@ -980,7 +1030,7 @@ type ServiceCheckStruct struct {
//检测来源类型
// in: body
// required: true
SourceType string `json:"source_type" validate:"source_type|required|in:docker-run,docker-compose,sourcecode,third-party-service"`
SourceType string `json:"source_type" validate:"source_type|required|in:docker-run,docker-compose,sourcecode,third-party-service,package_build"`
CheckOS string `json:"check_os"`
// 检测来源定义,

View File

@ -0,0 +1,32 @@
package model
const (
//Deployment -
Deployment = "Deployment"
//Job -
Job = "Job"
//CronJob -
CronJob = "CronJob"
//StateFulSet -
StateFulSet = "StatefulSet"
//Service -
Service = "Service"
//PVC -
PVC = "PersistentVolumeClaim"
//Ingress -
Ingress = "Ingress"
//NetworkPolicy -
NetworkPolicy = "NetworkPolicy"
//ConfigMap -
ConfigMap = "ConfigMap"
//Secret -
Secret = "Secret"
//ServiceAccount -
ServiceAccount = "ServiceAccount"
//RoleBinding -
RoleBinding = "RoleBinding"
//HorizontalPodAutoscaler -
HorizontalPodAutoscaler = "HorizontalPodAutoscaler"
//Role -
Role = "Role"
)

View File

@ -0,0 +1,90 @@
package model
import (
autoscalingv1 "k8s.io/api/autoscaling/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
)
//LabelResource -
type LabelResource struct {
Workloads WorkLoadsResource `json:"workloads,omitempty"`
Others OtherResource `json:"others,omitempty"`
UnSupport map[string][]string `json:"un_support"`
Status string `json:"status"`
}
//LabelWorkloadsResourceProcess -
type LabelWorkloadsResourceProcess struct {
Deployments map[string][]string `json:"deployments,omitempty"`
Jobs map[string][]string `json:"jobs,omitempty"`
CronJobs map[string][]string `json:"cronJobs,omitempty"`
StateFulSets map[string][]string `json:"stateFulSets,omitempty"`
}
//LabelOthersResourceProcess -
type LabelOthersResourceProcess struct {
Services map[string][]string `json:"services,omitempty"`
PVC map[string][]string `json:"PVC,omitempty"`
Ingresses map[string][]string `json:"ingresses,omitempty"`
NetworkPolicies map[string][]string `json:"networkPolicies,omitempty"`
ConfigMaps map[string][]string `json:"configMaps,omitempty"`
Secrets map[string][]string `json:"secrets,omitempty"`
ServiceAccounts map[string][]string `json:"serviceAccounts,omitempty"`
RoleBindings map[string][]string `json:"roleBindings,omitempty"`
HorizontalPodAutoscalers map[string][]string `json:"horizontalPodAutoscalers,omitempty"`
Roles map[string][]string `json:"roles,omitempty"`
}
//YamlResourceParameter -
type YamlResourceParameter struct {
ComponentsCR *[]ConvertResource
Basic BasicManagement
Template corev1.PodTemplateSpec
Namespace string
Name string
RsLabel map[string]string
CMs []corev1.ConfigMap
HPAs []autoscalingv1.HorizontalPodAutoscaler
}
//K8sResourceObject -
type K8sResourceObject struct {
FileName string
BuildResources []BuildResource
Error string
}
//WorkLoadsResource -
type WorkLoadsResource struct {
Deployments []string `json:"deployments,omitempty"`
Jobs []string `json:"jobs,omitempty"`
CronJobs []string `json:"cron_jobs,omitempty"`
StateFulSets []string `json:"state_ful_sets,omitempty"`
}
//BuildResource -
type BuildResource struct {
Resource *unstructured.Unstructured
State int
ErrorOverview string
Dri dynamic.ResourceInterface
DC dynamic.Interface
GVK *schema.GroupVersionKind
}
//OtherResource -
type OtherResource struct {
Services []string `json:"services,omitempty"`
PVC []string `json:"pvc,omitempty"`
Ingresses []string `json:"ingresses,omitempty"`
NetworkPolicies []string `json:"network_policies,omitempty"`
ConfigMaps []string `json:"config_maps,omitempty"`
Secrets []string `json:"secrets,omitempty"`
ServiceAccounts []string `json:"service_accounts,omitempty"`
RoleBindings []string `json:"role_bindings,omitempty"`
HorizontalPodAutoscalers []string `json:"horizontal_pod_autoscalers,omitempty"`
Roles []string `json:"roles,omitempty"`
}

View File

@ -173,6 +173,7 @@ func (m *Manager) Run() {
websocketRouter.Mount("/", websocket.Routes())
websocketRouter.Mount("/logs", websocket.LogRoutes())
websocketRouter.Mount("/app", websocket.AppRoutes())
websocketRouter.Mount("/package_build", websocket.PackageBuildRoutes())
if m.conf.WebsocketSSL {
logrus.Infof("websocket listen on (HTTPs) %s", m.conf.WebsocketAddr)
logrus.Fatal(http.ListenAndServeTLS(m.conf.WebsocketAddr, m.conf.WebsocketCertFile, m.conf.WebsocketKeyFile, websocketRouter))

View File

@ -314,6 +314,13 @@ func (s *slugBuild) createVolumeAndMount(re *Request, sourceTarFileName string)
})
}
}
if re.ServerType == "pkg" {
volumeMounts = append(volumeMounts, corev1.VolumeMount{
Name: "slug",
MountPath: "/tmp/app",
SubPath: strings.TrimPrefix(re.RepositoryURL, "/grdata/"),
})
}
return volumes, volumeMounts
}
@ -322,7 +329,7 @@ func (s *slugBuild) runBuildJob(re *Request) error {
re.Logger.Info(util.Translation("Start make code package"), map[string]string{"step": "build-exector"})
start := time.Now()
var sourceTarFileName string
if re.ServerType != "oss" {
if re.ServerType != "oss" && re.ServerType != "pkg" {
var err error
sourceTarFileName, err = s.getSourceCodeTarFile(re)
if err != nil {

View File

@ -21,6 +21,8 @@ package exector
import (
"context"
"fmt"
"github.com/goodrain/rainbond/builder/parser"
"io/ioutil"
"os"
"path"
"strings"
@ -31,7 +33,6 @@ import (
"github.com/docker/docker/client"
"github.com/goodrain/rainbond/builder"
"github.com/goodrain/rainbond/builder/build"
"github.com/goodrain/rainbond/builder/parser"
"github.com/goodrain/rainbond/builder/parser/code"
"github.com/goodrain/rainbond/builder/sources"
"github.com/goodrain/rainbond/db"
@ -95,6 +96,7 @@ func NewSouceCodeBuildItem(in []byte) *SourceCodeBuildItem {
Password: gjson.GetBytes(in, "password").String(),
TenantID: gjson.GetBytes(in, "tenant_id").String(),
ServiceID: gjson.GetBytes(in, "service_id").String(),
Configs: gjson.GetBytes(in, "configs").Map(),
}
envs := gjson.GetBytes(in, "envs").String()
be := make(map[string]string)
@ -163,6 +165,69 @@ func (i *SourceCodeBuildItem) Run(timeout time.Duration) error {
}
case "oss":
i.commit = Commit{}
case "pkg":
var filePath string
pathSplit := strings.Split(i.CodeSouceInfo.RepositoryURL, "/")
eventID := pathSplit[len(pathSplit)-1]
// 存放目录
tarPath := fmt.Sprintf("/grdata/package_build/components/%s/events", i.ServiceID)
// 临时目录
oldPath := fmt.Sprintf("/grdata/package_build/temp/events/%s", eventID)
// 快速复制原目录
copyPath := i.CodeSouceInfo.Configs[i.ServiceID]
filePath = fmt.Sprintf("%s/%s", tarPath, eventID)
if copyPath.Str == "" {
files, err := ioutil.ReadDir(filePath)
if err != nil {
logrus.Errorf("read dir error: %s", err.Error())
return err
}
if len(files) == 0 {
filePath = oldPath
}
} else {
// 快速复制
splitCopyPath := strings.Split(copyPath.Str, "/")
splitRes := pathSplit[0 : len(splitCopyPath)-1]
modelPath := strings.Join(splitRes, "/")
tarCopyPath := fmt.Sprintf("/grdata/package_build/components/%s", i.ServiceID)
err := os.MkdirAll(tarCopyPath, 0755)
if err != nil {
return err
}
if err := util.CopyDir(modelPath, tarCopyPath); err != nil {
logrus.Errorf("copy dir error: %s", err.Error())
}
filePath = copyPath.Str
}
packages, err := ioutil.ReadDir(filePath)
if err != nil {
logrus.Errorf("read dir error: %s", err.Error())
return err
}
packageArr := make([]string, 0, 10)
for _, dir := range packages {
if dir.IsDir() {
continue
}
packageArr = append(packageArr, dir.Name())
}
if len(packageArr) != 0 {
fileName := packageArr[0]
file := filePath + "/" + fileName
fileMD5 := util.MD5(file)
i.commit = Commit{
Message: fileName,
Hash: fileMD5,
}
}
if copyPath.Str == "" {
if err = util.MoveDir(oldPath, tarPath); err != nil {
logrus.Errorf("copy dir error: %s", err.Error())
}
}
default:
//default git
rs, err := sources.GitCloneOrPull(i.CodeSouceInfo, rbi.GetCodeHome(), i.Logger, 5)
@ -186,8 +251,10 @@ func (i *SourceCodeBuildItem) Run(timeout time.Duration) error {
}
// clean cache code
defer func() {
if err := os.RemoveAll(rbi.GetCodeHome()); err != nil {
logrus.Warningf("remove source code: %v", err)
if i.CodeSouceInfo.ServerType != "pkg" {
if err := os.RemoveAll(rbi.GetCodeHome()); err != nil {
logrus.Warningf("remove source code: %v", err)
}
}
}()

View File

@ -115,10 +115,12 @@ func (e *exectorManager) serviceCheck(task *pb.TaskMessage) {
yamlbody = string(yamlbyte)
}
pr = parser.CreateDockerComposeParse(yamlbody, e.DockerClient, input.Username, input.Password, logger)
case "sourcecode":
case "sourcecode" :
pr = parser.CreateSourceCodeParse(input.SourceBody, logger)
case "third-party-service":
pr = parser.CreateThirdPartyServiceParse(input.SourceBody, logger)
case "package_build":
pr = parser.CreateSourceCodeParse(input.SourceBody, logger)
}
if pr == nil {
logger.Error("Creating component source types is not supported", map[string]string{"step": "callback", "status": "failure"})

View File

@ -51,8 +51,15 @@ func init() {
//CheckCodeSpecification 检查语言规范
func CheckCodeSpecification(buildPath string, lang Lang, serverType string) Specification {
if serverType == "oss" && lang == JavaJar {
return common()
switch serverType {
case "oss":
if lang == JavaJar{
return common()
}
case "pkg":
if lang == JavaJar || lang == JaveWar{
return common()
}
}
if check, ok := specification[lang]; ok {
return check(buildPath)

View File

@ -22,6 +22,7 @@ import (
"context"
"encoding/base64"
"fmt"
"io/ioutil"
"os"
"path"
"runtime"
@ -110,9 +111,11 @@ func (d *SourceCodeParse) Parse() ParseErrorList {
}
// The source code is useless after the test is completed, and needs to be deleted.
defer func() {
if sources.CheckFileExist(buildInfo.GetCodeHome()) {
if err := sources.RemoveDir(buildInfo.GetCodeHome()); err != nil {
logrus.Warningf("remove source code: %v", err)
if csi.ServerType != "pkg"{
if sources.CheckFileExist(buildInfo.GetCodeHome()) {
if err := sources.RemoveDir(buildInfo.GetCodeHome()); err != nil {
logrus.Warningf("remove source code: %v", err)
}
}
}
}()
@ -203,6 +206,22 @@ func (d *SourceCodeParse) Parse() ParseErrorList {
d.branchs = rs.Branchs
return nil
}
packageFunc :=func() ParseErrorList{
var checkPath string
checkPath = buildInfo.RepostoryURL
pathSplit := strings.Split(buildInfo.RepostoryURL,"/")
eventID := pathSplit[len(pathSplit)-1]
files, err := ioutil.ReadDir(checkPath)
if err != nil {
logrus.Warn("check package error", err)
}
if len(files) == 0 {
// 第一次上传在临时目录下检测
checkPath = fmt.Sprintf("/grdata/package_build/temp/events/%s", eventID)
}
buildInfo.CodeHome = checkPath
return ParseErrorList{}
}
ossFunc := func() ParseErrorList {
g := got.NewWithContext(context.Background())
util.CheckAndCreateDir(buildInfo.GetCodeHome())
@ -260,6 +279,10 @@ func (d *SourceCodeParse) Parse() ParseErrorList {
if err := ossFunc(); err != nil && err.IsFatalError() {
return err
}
case "pkg":
if err := packageFunc(); err != nil && err.IsFatalError() {
return err
}
default:
//default git
logrus.Warningf("do not get void server type,default use git")

View File

@ -21,6 +21,7 @@ package sources
import (
"context"
"fmt"
"github.com/tidwall/gjson"
"io/ioutil"
"net/http"
"net/url"
@ -53,11 +54,12 @@ import (
//CodeSourceInfo 代码源信息
type CodeSourceInfo struct {
ServerType string `json:"server_type"`
RepositoryURL string `json:"repository_url"`
Branch string `json:"branch"`
User string `json:"user"`
Password string `json:"password"`
ServerType string `json:"server_type"`
RepositoryURL string `json:"repository_url"`
Branch string `json:"branch"`
User string `json:"user"`
Password string `json:"password"`
Configs map[string]gjson.Result `json:"configs"`
//避免项目之间冲突,代码缓存目录提高到租户
TenantID string `json:"tenant_id"`
ServiceID string `json:"service_id"`
@ -428,12 +430,11 @@ func GetPrivateFile(tenantID string) string {
}
if ok, _ := util.FileExists(path.Join(home, "/.ssh/"+tenantID)); ok {
return path.Join(home, "/.ssh/"+tenantID)
} else {
if ok, _ := util.FileExists(path.Join(home, "/.ssh/builder_rsa")); ok {
return path.Join(home, "/.ssh/builder_rsa")
}
return path.Join(home, "/.ssh/id_rsa")
}
if ok, _ := util.FileExists(path.Join(home, "/.ssh/builder_rsa")); ok {
return path.Join(home, "/.ssh/builder_rsa")
}
return path.Join(home, "/.ssh/id_rsa")
}

View File

@ -92,6 +92,13 @@ func (r *RepostoryBuildInfo) GetProtocol() string {
//CreateRepostoryBuildInfo 创建源码编译信息
//repoType git or svn
func CreateRepostoryBuildInfo(repoURL, repoType, branch, tenantID string, ServiceID string) (*RepostoryBuildInfo, error) {
if repoType == "pkg" {
return &RepostoryBuildInfo{
RepostoryURL: repoURL,
RepostoryURLType: repoType,
CodeHome: repoURL,
}, nil
}
// repoURL= github.com/goodrain/xxx.git?dir=home
ep, err := transport.NewEndpoint(repoURL)
if err != nil {

View File

@ -20,12 +20,11 @@ package server
import (
"context"
"k8s.io/client-go/restmapper"
"os"
"os/signal"
"syscall"
rainbondscheme "github.com/goodrain/rainbond/pkg/generated/clientset/versioned/scheme"
"github.com/goodrain/rainbond/api/controller"
"github.com/goodrain/rainbond/api/db"
"github.com/goodrain/rainbond/api/discover"
@ -34,6 +33,7 @@ import (
"github.com/goodrain/rainbond/cmd/api/option"
"github.com/goodrain/rainbond/event"
"github.com/goodrain/rainbond/pkg/generated/clientset/versioned"
rainbondscheme "github.com/goodrain/rainbond/pkg/generated/clientset/versioned/scheme"
etcdutil "github.com/goodrain/rainbond/util/etcd"
k8sutil "github.com/goodrain/rainbond/util/k8s"
"github.com/goodrain/rainbond/worker/client"
@ -79,6 +79,7 @@ func Run(s *option.APIServer) error {
if err != nil {
return err
}
rainbondClient := versioned.NewForConfigOrDie(config)
// k8s runtime client
@ -91,6 +92,12 @@ func Run(s *option.APIServer) error {
if err != nil {
return errors.WithMessage(err, "create k8s client")
}
// rest mapper
gr, err := restmapper.GetAPIGroupResources(clientset)
if err != nil {
return err
}
mapper := restmapper.NewDiscoveryRESTMapper(gr)
if err := event.NewManager(event.EventConfig{
EventLogServers: s.Config.EventLogServers,
@ -121,7 +128,7 @@ func Run(s *option.APIServer) error {
//初始化 middleware
handler.InitProxy(s.Config)
//创建handle
if err := handler.InitHandle(s.Config, etcdClientArgs, cli, etcdcli, clientset, rainbondClient, k8sClient); err != nil {
if err := handler.InitHandle(s.Config, etcdClientArgs, cli, etcdcli, clientset, rainbondClient, k8sClient, config, mapper); err != nil {
logrus.Errorf("init all handle error, %v", err)
return err
}

View File

@ -77,6 +77,7 @@ type ApplicationDao interface {
GetByServiceID(sid string) (*model.Application, error)
ListByAppIDs(appIDs []string) ([]*model.Application, error)
IsK8sAppDuplicate(tenantID, AppID, k8sApp string) bool
GetAppByName(tenantID, k8sAppName string) (*model.Application, error)
}
//AppConfigGroupDao Application config group Dao
@ -621,3 +622,21 @@ type TenantServiceMonitorDao interface {
DeleteByComponentIDs(componentIDs []string) error
CreateOrUpdateMonitorInBatch(monitors []*model.TenantServiceMonitor) error
}
// ComponentK8sAttributeDao -
type ComponentK8sAttributeDao interface {
Dao
GetByComponentIDAndName(componentID, name string) (*model.ComponentK8sAttributes, error)
CreateOrUpdateAttributesInBatch(attributes []*model.ComponentK8sAttributes) error
DeleteByComponentIDAndName(componentID, name string) error
DeleteByComponentIDs(componentIDs []string) error
}
// K8sResourceDao -
type K8sResourceDao interface {
Dao
ListByAppID(appID string) ([]model.K8sResource, error)
CreateK8sResourceInBatch(k8sResources []*model.K8sResource) error
DeleteK8sResourceInBatch(appID, name string, kind string) error
GetK8sResourceByNameInBatch(appID, name, kind string) ([]model.K8sResource, error)
}

View File

@ -47,6 +47,8 @@ type Manager interface {
AppConfigGroupServiceDaoTransactions(db *gorm.DB) dao.AppConfigGroupServiceDao
AppConfigGroupItemDao() dao.AppConfigGroupItemDao
AppConfigGroupItemDaoTransactions(db *gorm.DB) dao.AppConfigGroupItemDao
K8sResourceDao() dao.K8sResourceDao
K8sResourceDaoTransactions(db *gorm.DB) dao.K8sResourceDao
EnterpriseDao() dao.EnterpriseDao
TenantDao() dao.TenantDao
TenantDaoTransactions(db *gorm.DB) dao.TenantDao
@ -137,6 +139,9 @@ type Manager interface {
TenantServiceMonitorDao() dao.TenantServiceMonitorDao
TenantServiceMonitorDaoTransactions(db *gorm.DB) dao.TenantServiceMonitorDao
ComponentK8sAttributeDao() dao.ComponentK8sAttributeDao
ComponentK8sAttributeDaoTransactions(db *gorm.DB) dao.ComponentK8sAttributeDao
}
var defaultManager Manager

View File

@ -1,3 +1,21 @@
// RAINBOND, Application Management Platform
// Copyright (C) 2020-2022 Goodrain Co., Ltd.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package model
const (
@ -73,7 +91,27 @@ type ApplicationConfigGroup struct {
Enable bool `gorm:"column:enable" json:"enable"`
}
// TableName return tableName "application"
// TableName return tableName "app_config_group"
func (t *ApplicationConfigGroup) TableName() string {
return "app_config_group"
}
// K8sResource Save k8s resources under the application
type K8sResource struct {
Model
AppID string `gorm:"column:app_id" json:"app_id"`
Name string `gorm:"column:name" json:"name"`
// The resource kind is the same as that in k8s cluster
Kind string `gorm:"column:kind" json:"kind"`
// Yaml file for the storage resource
Content string `gorm:"column:content;type:longtext" json:"content"`
// resource create error overview
ErrorOverview string `gorm:"column:status;type:longtext" json:"error_overview"`
//whether it was created successfully
State int `gorm:"column:success;type:int" json:"state"`
}
// TableName return tableName "k8s_resources"
func (k *K8sResource) TableName() string {
return "k8s_resources"
}

64
db/model/component.go Normal file
View File

@ -0,0 +1,64 @@
// RAINBOND, Application Management Platform
// Copyright (C) 2022-2022 Goodrain Co., Ltd.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package model
const (
//K8sAttributeNameNodeSelector -
K8sAttributeNameNodeSelector = "nodeSelector"
//K8sAttributeNameLabels -
K8sAttributeNameLabels = "labels"
//K8sAttributeNameTolerations -
K8sAttributeNameTolerations = "tolerations"
//K8sAttributeNameVolumes -
K8sAttributeNameVolumes = "volumes"
//K8sAttributeNameServiceAccountName -
K8sAttributeNameServiceAccountName = "serviceAccountName"
//K8sAttributeNamePrivileged -
K8sAttributeNamePrivileged = "privileged"
//K8sAttributeNameAffinity -
K8sAttributeNameAffinity = "affinity"
//K8sAttributeNameVolumeMounts -
K8sAttributeNameVolumeMounts = "volumeMounts"
//K8sAttributeNameENV -
K8sAttributeNameENV = "env"
)
// ComponentK8sAttributes -
type ComponentK8sAttributes struct {
Model
TenantID string `gorm:"column:tenant_id;size:32" validate:"tenant_id|between:30,33" json:"tenant_id"`
ComponentID string `gorm:"column:component_id" json:"component_id"`
// Name Define the attribute name, which is currently supported
// [nodeSelector/labels/tolerations/volumes/serviceAccountName/privileged/affinity/volumeMounts]
// The field name should be the same as that in the K8s resource yaml file.
Name string `gorm:"column:name" json:"name"`
// The field type defines how the attribute is stored. Currently, `json/yaml/string` are supported
SaveType string `gorm:"column:save_type" json:"save_type"`
// Define the attribute value, which is stored in the database.
// The value is stored in the database in the form of `json/yaml/string`.
AttributeValue string `gorm:"column:attribute_value;type:longtext" json:"attribute_value"`
}
// TableName 表名
func (t *ComponentK8sAttributes) TableName() string {
return "component_k8s_attributes"
}

View File

@ -107,6 +107,22 @@ func (s ServiceType) IsState() bool {
return false
}
// IsJob is job
func (s ServiceType) IsJob() bool {
if s == ServiceTypeJob {
return true
}
return false
}
// IsCronJob is cronjob
func (s ServiceType) IsCronJob() bool {
if s == ServiceTypeCronJob {
return true
}
return false
}
// IsSingleton is singleton or not
func (s ServiceType) IsSingleton() bool {
if s == "" {
@ -127,6 +143,22 @@ func (t *TenantServices) IsState() bool {
return ServiceType(t.ExtendMethod).IsState()
}
// IsJob is job
func (t *TenantServices) IsJob() bool {
if ServiceType(t.ExtendMethod).IsJob() {
return true
}
return false
}
// IsCronJob is cronjob
func (t *TenantServices) IsCronJob() bool {
if ServiceType(t.ExtendMethod).IsCronJob() {
return true
}
return false
}
// IsSingleton is singleton or multiple service
func (t *TenantServices) IsSingleton() bool {
if t.ExtendMethod == "" {
@ -150,6 +182,12 @@ var ServiceTypeStateSingleton ServiceType = "state_singleton"
// ServiceTypeStateMultiple state_multiple
var ServiceTypeStateMultiple ServiceType = "state_multiple"
// ServiceTypeJob job
var ServiceTypeJob ServiceType = "job"
// ServiceTypeCronJob cronjob
var ServiceTypeCronJob ServiceType = "cronjob"
//TenantServices app service base info
type TenantServices struct {
Model
@ -203,6 +241,8 @@ type TenantServices struct {
AppID string `gorm:"column:app_id" json:"app_id"`
// Component name in cluster
K8sComponentName string `gorm:"column:k8s_component_name" json:"k8s_component_name"`
// Job任务策略
JobStrategy string `gorm:"column:job_strategy" json:"job_strategy"`
}
// ComponentWorkload -
@ -324,6 +364,8 @@ type TenantServicesDelete struct {
AppID string `gorm:"column:app_id" json:"app_id"`
// Component name in cluster
K8sComponentName string `gorm:"column:k8s_component_name" json:"k8s_component_name"`
// Job任务策略
JobStrategy string `gorm:"column:job_strategy" json:"job_strategy"`
}
//TableName 表名

View File

@ -105,3 +105,15 @@ func (a *ApplicationDaoImpl) IsK8sAppDuplicate(tenantID, AppID, k8sApp string) b
}
return count > 0
}
//GetAppByName -
func (a *ApplicationDaoImpl) GetAppByName(tenantID, k8sAppName string) (*model.Application, error) {
var app model.Application
if err := a.DB.Where("tenant_id=? and k8s_app=?", tenantID, k8sAppName).Find(&app).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, bcode.ErrApplicationNotFound
}
return nil, err
}
return &app, nil
}

View File

@ -0,0 +1,81 @@
// RAINBOND, Application Management Platform
// Copyright (C) 2022-2022 Goodrain Co., Ltd.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package dao
import (
"fmt"
gormbulkups "github.com/atcdot/gorm-bulk-upsert"
"github.com/goodrain/rainbond/db/model"
"github.com/jinzhu/gorm"
pkgerr "github.com/pkg/errors"
)
// K8sResourceDaoImpl k8s resource dao
type K8sResourceDaoImpl struct {
DB *gorm.DB
}
// AddModel add model
func (t *K8sResourceDaoImpl) AddModel(mo model.Interface) error {
return nil
}
// UpdateModel update model
func (t *K8sResourceDaoImpl) UpdateModel(mo model.Interface) error {
resource, ok := mo.(*model.K8sResource)
if !ok {
return fmt.Errorf("mo.(*model.K8sResource) err")
}
return t.DB.Save(resource).Error
}
// ListByAppID list by app id
func (t *K8sResourceDaoImpl) ListByAppID(appID string) ([]model.K8sResource, error) {
var resources []model.K8sResource
if err := t.DB.Where("app_id = ?", appID).Find(&resources).Error; err != nil {
return nil, err
}
return resources, nil
}
//CreateK8sResourceInBatch -
func (t *K8sResourceDaoImpl) CreateK8sResourceInBatch(k8sResources []*model.K8sResource) error {
var objects []interface{}
for _, cg := range k8sResources {
objects = append(objects, *cg)
}
if err := gormbulkups.BulkUpsert(t.DB, objects, 2000); err != nil {
return pkgerr.Wrap(err, "create K8sResource groups in batch")
}
return nil
}
//DeleteK8sResourceInBatch -
func (t *K8sResourceDaoImpl) DeleteK8sResourceInBatch(appID, name string, kind string) error {
return t.DB.Where("app_id=? and name=? and kind=?", appID, name, kind).Delete(&model.K8sResource{}).Error
}
//GetK8sResourceByNameInBatch -
func (t *K8sResourceDaoImpl) GetK8sResourceByNameInBatch(appID, name, kind string) ([]model.K8sResource, error) {
var resources []model.K8sResource
if err := t.DB.Where("app_id=? and name=? and kind=?", appID, name, kind).Find(&resources).Error; err != nil {
return nil, err
}
return resources, nil
}

View File

@ -2019,3 +2019,55 @@ func (t *TenantServiceScalingRecordsDaoImpl) CountByServiceID(serviceID string)
return count, nil
}
// ComponentK8sAttributeDaoImpl The K8s attribute value of the component
type ComponentK8sAttributeDaoImpl struct {
DB *gorm.DB
}
// AddModel -
func (t *ComponentK8sAttributeDaoImpl) AddModel(mo model.Interface) error {
attr := mo.(*model.ComponentK8sAttributes)
var old model.ComponentK8sAttributes
if ok := t.DB.Where("component_id=? and name=?", attr.ComponentID, attr.Name).Find(&old).RecordNotFound(); ok {
return t.DB.Create(attr).Error
}
return errors.ErrRecordAlreadyExist
}
// UpdateModel -
func (t *ComponentK8sAttributeDaoImpl) UpdateModel(mo model.Interface) error {
attr := mo.(*model.ComponentK8sAttributes)
return t.DB.Save(attr).Error
}
// GetByComponentIDAndName -
func (t *ComponentK8sAttributeDaoImpl) GetByComponentIDAndName(componentID, name string) (*model.ComponentK8sAttributes, error) {
var record model.ComponentK8sAttributes
if err := t.DB.Where("component_id=? and name=?", componentID, name).Take(&record).Error; err != nil {
return nil, err
}
return &record, nil
}
// CreateOrUpdateAttributesInBatch Batch insert or update component attributes
func (t *ComponentK8sAttributeDaoImpl) CreateOrUpdateAttributesInBatch(attributes []*model.ComponentK8sAttributes) error {
var objects []interface{}
for _, attribute := range attributes {
objects = append(objects, *attribute)
}
if err := gormbulkups.BulkUpsert(t.DB, objects, 2000); err != nil {
return pkgerr.Wrap(err, "create or update component attributes in batch")
}
return nil
}
// DeleteByComponentIDAndName delete by componentID and name
func (t *ComponentK8sAttributeDaoImpl) DeleteByComponentIDAndName(componentID, name string) error {
return t.DB.Where("component_id=? and name=?", componentID, name).Delete(&model.ComponentK8sAttributes{}).Error
}
// DeleteByComponentIDs delete by componentIDs
func (t *ComponentK8sAttributeDaoImpl) DeleteByComponentIDs(componentIDs []string) error {
return t.DB.Where("component_id in (?)", componentIDs).Delete(&model.ComponentK8sAttributes{}).Error
}

View File

@ -647,3 +647,31 @@ func (m *Manager) TenantServiceMonitorDaoTransactions(db *gorm.DB) dao.TenantSer
DB: db,
}
}
// ComponentK8sAttributeDao -
func (m *Manager) ComponentK8sAttributeDao() dao.ComponentK8sAttributeDao {
return &mysqldao.ComponentK8sAttributeDaoImpl{
DB: m.db,
}
}
// ComponentK8sAttributeDaoTransactions -
func (m *Manager) ComponentK8sAttributeDaoTransactions(db *gorm.DB) dao.ComponentK8sAttributeDao {
return &mysqldao.ComponentK8sAttributeDaoImpl{
DB: db,
}
}
// K8sResourceDao -
func (m *Manager) K8sResourceDao() dao.K8sResourceDao {
return &mysqldao.K8sResourceDaoImpl{
DB: m.db,
}
}
// K8sResourceDaoTransactions -
func (m *Manager) K8sResourceDaoTransactions(db *gorm.DB) dao.K8sResourceDao {
return &mysqldao.K8sResourceDaoImpl{
DB: db,
}
}

View File

@ -155,6 +155,8 @@ func (m *Manager) RegisterTableModel() {
m.models = append(m.models, &model.TenantServiceAutoscalerRuleMetrics{})
m.models = append(m.models, &model.TenantServiceScalingRecords{})
m.models = append(m.models, &model.TenantServiceMonitor{})
m.models = append(m.models, &model.ComponentK8sAttributes{})
m.models = append(m.models, &model.K8sResource{})
}
//CheckTable check and create tables

107
util/copydir.go Normal file
View File

@ -0,0 +1,107 @@
package util
import (
"crypto/md5"
"encoding/hex"
"fmt"
"github.com/sirupsen/logrus"
"io"
"log"
"os"
"os/exec"
"runtime"
"strings"
)
// FormatPath format path
func FormatPath(s string) string {
log.Println("runtime.GOOS:", runtime.GOOS)
switch runtime.GOOS {
case "windows":
return strings.Replace(s, "/", "\\", -1)
case "darwin", "linux":
return strings.Replace(s, "\\", "/", -1)
default:
logrus.Info("only support linux,windows,darwin, but os is " + runtime.GOOS)
return s
}
}
// MoveDir move dir
func MoveDir(src string, dest string) error {
src = FormatPath(src)
dest = FormatPath(dest)
logrus.Info("src", src)
logrus.Info("dest", dest)
var cmd *exec.Cmd
switch runtime.GOOS {
case "windows":
cmd = exec.Command("xcopy", src, dest, "/I", "/E")
case "darwin", "linux":
cmd = exec.Command("cp", "-R", src, dest)
}
outPut, err := cmd.Output()
if err != nil {
logrus.Errorf("Output error: %s", err.Error())
return err
}
fmt.Println(outPut)
if err := os.RemoveAll(src); err != nil {
logrus.Errorf("remove oldpath error: %s", err.Error())
return err
}
return nil
}
// MD5 md5
func MD5(file string) string {
f, err := os.Open(file)
if err != nil {
logrus.Error(err)
}
defer f.Close()
h := md5.New()
_, err = io.Copy(h, f)
if err != nil {
logrus.Error(err)
}
res := hex.EncodeToString(h.Sum(nil))
logrus.Info("md5:", res)
return res
}
// CopyDir move dir
func CopyDir(src string, dest string) error {
_, err := os.Stat(dest)
if err != nil {
if !os.IsExist(err) {
err := os.MkdirAll(dest, 0755)
if err != nil {
logrus.Error("make and copy dir error", err)
}
}
}
src = FormatPath(src)
dest = FormatPath(dest)
logrus.Info("src", src)
logrus.Info("dest", dest)
var cmd *exec.Cmd
switch runtime.GOOS {
case "windows":
cmd = exec.Command("xcopy", src, dest, "/I", "/E")
case "darwin", "linux":
cmd = exec.Command("cp", "-R", src, dest)
}
outPut, err := cmd.Output()
if err != nil {
logrus.Errorf("Output error: %s", err.Error())
return err
}
fmt.Println(outPut)
return nil
}

View File

@ -130,7 +130,7 @@ func (s *startController) startOne(app v1.AppService) error {
return fmt.Errorf("create claims: %v", err)
}
}
//step 2: create statefulset or deployment
//step 2: create statefulset or deployment or job or cronjob
if statefulset := app.GetStatefulSet(); statefulset != nil {
_, err = s.manager.client.AppsV1().StatefulSets(app.GetNamespace()).Create(s.ctx, statefulset, metav1.CreateOptions{})
if err != nil {
@ -143,6 +143,18 @@ func (s *startController) startOne(app v1.AppService) error {
return fmt.Errorf("create deployment failure:%s;", err.Error())
}
}
if job := app.GetJob(); job != nil {
_, err = s.manager.client.BatchV1().Jobs(app.GetNamespace()).Create(s.ctx, job, metav1.CreateOptions{})
if err != nil {
return fmt.Errorf("create job failure:%s;", err.Error())
}
}
if cronjob := app.GetCronJob(); cronjob != nil {
_, err = s.manager.client.BatchV1beta1().CronJobs(app.GetNamespace()).Create(s.ctx, cronjob, metav1.CreateOptions{})
if err != nil {
return fmt.Errorf("create cronjob failure:%s;", err.Error())
}
}
//step 3: create services
if services := app.GetServices(true); services != nil {
if err := CreateKubeService(s.manager.client, app.GetNamespace(), services...); err != nil {

View File

@ -162,6 +162,21 @@ func (s *stopController) stopOne(app v1.AppService) error {
}
s.manager.store.OnDeletes(deployment)
}
if job := app.GetJob(); job != nil {
err := s.manager.client.BatchV1().Jobs(app.GetNamespace()).Delete(s.ctx, job.Name, metav1.DeleteOptions{})
if err != nil && !errors.IsNotFound(err) {
return fmt.Errorf("delete job failure:%s", err.Error())
}
s.manager.store.OnDeletes(job)
}
if cronjob := app.GetCronJob(); cronjob != nil {
propagationPolicy := metav1.DeletePropagationBackground
err := s.manager.client.BatchV1beta1().CronJobs(app.GetNamespace()).Delete(s.ctx, cronjob.Name, metav1.DeleteOptions{PropagationPolicy: &propagationPolicy})
if err != nil && !errors.IsNotFound(err) {
return fmt.Errorf("delete cronjob failure:%s", err.Error())
}
s.manager.store.OnDeletes(cronjob)
}
//step 6: delete all pod
var gracePeriodSeconds int64
if pods := app.GetPods(true); pods != nil {

View File

@ -19,10 +19,17 @@
package conversion
import (
"encoding/json"
"fmt"
"strconv"
"strings"
apimodel "github.com/goodrain/rainbond/api/model"
"github.com/goodrain/rainbond/api/handler/app_governance_mode/adaptor"
"github.com/sirupsen/logrus"
"strings"
batchv1 "k8s.io/api/batch/v1"
"k8s.io/api/batch/v1beta1"
"github.com/goodrain/rainbond/db"
dbmodel "github.com/goodrain/rainbond/db/model"
@ -130,6 +137,14 @@ func TenantServiceBase(as *v1.AppService, dbmanager db.Manager) error {
as.ContainerGPU = tenantService.ContainerGPU
as.ContainerMemory = tenantService.ContainerMemory
as.Replicas = tenantService.Replicas
if tenantService.IsJob() {
initBaseJob(as, tenantService)
return nil
}
if tenantService.IsCronJob() {
initBaseCronJob(as, tenantService)
return nil
}
if !tenantService.IsState() {
initBaseDeployment(as, tenantService)
return nil
@ -216,6 +231,116 @@ func initBaseDeployment(as *v1.AppService, service *dbmodel.TenantServices) {
as.SetDeployment(deployment)
}
func initBaseJob(as *v1.AppService, service *dbmodel.TenantServices) {
as.ServiceType = v1.TypeJob
job := as.GetJob()
if job == nil {
job = &batchv1.Job{}
}
job.Namespace = as.GetNamespace()
if job.Spec.Selector == nil {
job.Spec.Selector = &metav1.LabelSelector{}
}
job.Name = as.GetK8sWorkloadName()
job.GenerateName = strings.Replace(service.ServiceAlias, "_", "-", -1)
injectLabels := getInjectLabels(as)
job.Labels = as.GetCommonLabels(job.Labels, map[string]string{
"name": service.ServiceAlias,
"version": service.DeployVersion,
}, injectLabels)
var js *apimodel.JobStrategy
if service.JobStrategy != ""{
err := json.Unmarshal([]byte(service.JobStrategy), &js)
if err != nil {
logrus.Error("job strategy json unmarshal error", err)
}
if js.ActiveDeadlineSeconds != "" {
ads, err := strconv.ParseInt(js.ActiveDeadlineSeconds, 10, 64)
if err == nil {
job.Spec.ActiveDeadlineSeconds = &ads
}
}
if js.BackoffLimit != "" {
res, err := strconv.ParseInt(js.BackoffLimit, 10, 32)
if err == nil {
bkl := int32(res)
job.Spec.BackoffLimit = &bkl
}
}
if js.Parallelism != "" {
res, err := strconv.ParseInt(js.Parallelism, 10, 32)
if err == nil {
pll := int32(res)
job.Spec.Parallelism = &pll
}
}
if js.Completions != "" {
res, err := strconv.ParseInt(js.Completions, 10, 32)
if err == nil {
cpt := int32(res)
job.Spec.Completions = &cpt
}
}
}
as.SetJob(job)
}
func initBaseCronJob(as *v1.AppService, service *dbmodel.TenantServices) {
as.ServiceType = v1.TypeCronJob
cronJob := as.GetCronJob()
if cronJob == nil {
cronJob = &v1beta1.CronJob{}
}
injectLabels := getInjectLabels(as)
jobTemp := v1beta1.JobTemplateSpec{}
jobTemp.Name = as.GetK8sWorkloadName()
jobTemp.Namespace = as.GetNamespace()
jobTemp.Labels = as.GetCommonLabels(jobTemp.Labels, map[string]string{
"name": service.ServiceAlias,
"version": service.DeployVersion,
}, injectLabels)
if service.JobStrategy != ""{
var js *apimodel.JobStrategy
err := json.Unmarshal([]byte(service.JobStrategy), &js)
if err != nil {
logrus.Error("job strategy json unmarshal error", err)
}
if js.ActiveDeadlineSeconds != "" {
ads, err := strconv.ParseInt(js.ActiveDeadlineSeconds, 10, 64)
if err == nil {
jobTemp.Spec.ActiveDeadlineSeconds = &ads
}
}
if js.BackoffLimit != "" {
res, err := strconv.ParseInt(js.BackoffLimit, 10, 32)
if err == nil {
bkl := int32(res)
jobTemp.Spec.BackoffLimit = &bkl
}
}
if js.Parallelism != "" {
res, err := strconv.ParseInt(js.Parallelism, 10, 32)
if err == nil {
pll := int32(res)
jobTemp.Spec.Parallelism = &pll
}
}
if js.Completions != "" {
res, err := strconv.ParseInt(js.Completions, 10, 32)
if err == nil {
cpt := int32(res)
jobTemp.Spec.Completions = &cpt
}
}
cronJob.Spec.Schedule = js.Schedule
}
cronJob.Spec.JobTemplate = jobTemp
cronJob.Namespace = as.GetNamespace()
cronJob.Name = as.GetK8sWorkloadName()
as.SetCronJob(cronJob)
}
func getInjectLabels(as *v1.AppService) map[string]string {
mode, err := adaptor.NewAppGoveranceModeHandler(as.GovernanceMode, nil)
if err != nil {

View File

@ -19,14 +19,16 @@
package conversion
import (
"encoding/json"
"fmt"
"github.com/goodrain/rainbond/builder/sources"
"net"
"os"
"sort"
"strconv"
"strings"
"github.com/goodrain/rainbond/builder/sources"
"github.com/goodrain/rainbond/builder"
"github.com/goodrain/rainbond/db"
"github.com/goodrain/rainbond/db/model"
@ -41,6 +43,7 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"sigs.k8s.io/yaml"
)
//TenantServiceVersion service deploy version conv. define pod spec
@ -70,20 +73,18 @@ func TenantServiceVersion(as *v1.AppService, dbmanager db.Manager) error {
dv.SetVolume(dbmodel.ShareFileVolumeType, "kube-config", "/etc/kubernetes", "/grdata/kubernetes", corev1.HostPathDirectoryOrCreate, true)
}
nodeSelector := createNodeSelector(as, dbmanager)
tolerations := createToleration(nodeSelector)
injectLabels := getInjectLabels(as)
labels := createLabels(as, dbmanager)
tolerations := createToleration(nodeSelector, as, dbmanager)
volumes := getVolumes(dv, as, dbmanager)
podtmpSpec := corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: as.GetCommonLabels(map[string]string{
"name": as.ServiceAlias,
"version": as.DeployVersion,
}, injectLabels),
Labels: labels,
Annotations: createPodAnnotations(as),
Name: as.GetK8sWorkloadName() + "-pod-spec",
},
Spec: corev1.PodSpec{
ImagePullSecrets: setImagePullSecrets(),
Volumes: dv.GetVolumes(),
Volumes: volumes,
Containers: []corev1.Container{*container},
NodeSelector: nodeSelector,
Tolerations: tolerations,
@ -113,13 +114,20 @@ func TenantServiceVersion(as *v1.AppService, dbmanager db.Manager) error {
}
return ""
}(),
ServiceAccountName: createServiceAccountName(as, dbmanager),
},
}
var terminationGracePeriodSeconds int64 = 10
if as.GetDeployment() != nil {
podtmpSpec.Spec.TerminationGracePeriodSeconds = &terminationGracePeriodSeconds
}
//set to deployment or statefulset
if as.GetJob() != nil {
podtmpSpec.Spec.RestartPolicy = "Never"
}
if as.GetCronJob() != nil {
podtmpSpec.Spec.RestartPolicy = "OnFailure"
}
//set to deployment or statefulset job or cronjob
as.SetPodTemplate(podtmpSpec)
return nil
}
@ -158,7 +166,7 @@ func getMainContainer(as *v1.AppService, version *dbmodel.VersionInfo, dv *volum
Ports: ports,
Env: envs,
EnvFrom: envFromSecrets,
VolumeMounts: dv.GetVolumeMounts(),
VolumeMounts: createVolumeMounts(dv, as, dbmanager),
LivenessProbe: createProbe(as, dbmanager, "liveness"),
ReadinessProbe: createProbe(as, dbmanager, "readiness"),
Resources: resources,
@ -172,6 +180,17 @@ func getMainContainer(as *v1.AppService, version *dbmodel.VersionInfo, dv *volum
logrus.Infof("service id: %s; enable privileged.", as.ServiceID)
c.SecurityContext = &corev1.SecurityContext{Privileged: util.Bool(true)}
}
privilegedAttribute, err := dbmanager.ComponentK8sAttributeDao().GetByComponentIDAndName(as.ServiceID, model.K8sAttributeNamePrivileged)
if err != nil && err != gorm.ErrRecordNotFound {
return nil, fmt.Errorf("get by privileged attribute error: %v", err)
}
if privilegedAttribute != nil {
pril, err := strconv.ParseBool(privilegedAttribute.AttributeValue)
if err != nil {
return nil, err
}
c.SecurityContext = &corev1.SecurityContext{Privileged: util.Bool(pril)}
}
return c, nil
}
@ -364,6 +383,23 @@ func createEnv(as *v1.AppService, dbmanager db.Manager, envVarSecrets []*corev1.
as.ExtensionSet[strings.ToLower(k[3:])] = v
}
}
var customEnvs []corev1.EnvVar
envAttribute, err := dbmanager.ComponentK8sAttributeDao().GetByComponentIDAndName(as.ServiceID, model.K8sAttributeNameENV)
if err != nil {
logrus.Warn("get by env attribute error", err)
return envs, nil
}
envAttributeJSON, err := yaml.YAMLToJSON([]byte(envAttribute.AttributeValue))
if err != nil {
logrus.Warn("envAttribute yaml to json error", err)
return envs, nil
}
err = json.Unmarshal(envAttributeJSON, &customEnvs)
if err != nil {
logrus.Warn("envAttribute json unmarshal error", err)
return envs, nil
}
envs = append(envs, customEnvs...)
return envs, nil
}
@ -494,6 +530,28 @@ func createVolumes(as *v1.AppService, version *dbmodel.VersionInfo, envs []corev
return define, nil
}
func getVolumes(dv *volume.Define, as *v1.AppService, dbmanager db.Manager) []corev1.Volume {
volumes := dv.GetVolumes()
volumeAttribute, err := dbmanager.ComponentK8sAttributeDao().GetByComponentIDAndName(as.ServiceID, model.K8sAttributeNameVolumes)
if err != nil {
logrus.Warn("get by volumes attribute error", err)
return volumes
}
var vs []corev1.Volume
VolumeAttributeJSON, err := yaml.YAMLToJSON([]byte(volumeAttribute.AttributeValue))
if err != nil {
logrus.Warn("volumeAttribute yaml to json error", err)
return volumes
}
err = json.Unmarshal(VolumeAttributeJSON, &vs)
if err != nil {
logrus.Warn("volumeAttribute json unmarshal error", err)
return volumes
}
volumes = append(volumes, vs...)
return volumes
}
func createResources(as *v1.AppService) corev1.ResourceRequirements {
var cpuRequest, cpuLimit int64
if limit, ok := as.ExtensionSet["cpulimit"]; ok {
@ -661,8 +719,36 @@ func createNodeSelector(as *v1.AppService, dbmanager db.Manager) map[string]stri
}
}
}
selectorAttribute, err := dbmanager.ComponentK8sAttributeDao().GetByComponentIDAndName(as.ServiceID, model.K8sAttributeNameNodeSelector)
if err != nil {
logrus.Warn("get by NodeSelector attribute error", err)
return selector
}
err = json.Unmarshal([]byte(selectorAttribute.AttributeValue), &selector)
if err != nil {
logrus.Warn("selector json unmarshal error", err)
return selector
}
return selector
}
func createLabels(as *v1.AppService, dbmanager db.Manager) map[string]string {
labels := make(map[string]string)
labelsAttribute, err := dbmanager.ComponentK8sAttributeDao().GetByComponentIDAndName(as.ServiceID, model.K8sAttributeNameLabels)
if err == nil {
err = json.Unmarshal([]byte(labelsAttribute.AttributeValue), &labels)
if err == nil{
logrus.Infof("labelsAttribute:%s", labels)
}
}
labels["name"] = as.ServiceAlias
labels["version"] = as.DeployVersion
injectLabels := getInjectLabels(as)
resultLabel := as.GetCommonLabels(labels, injectLabels)
return resultLabel
}
func createAffinity(as *v1.AppService, dbmanager db.Manager) *corev1.Affinity {
var affinity corev1.Affinity
nsr := make([]corev1.NodeSelectorRequirement, 0)
@ -769,6 +855,21 @@ func createAffinity(as *v1.AppService, dbmanager db.Manager) *corev1.Affinity {
RequiredDuringSchedulingIgnoredDuringExecution: podAntAffinity,
}
}
affinityAttribute, err := dbmanager.ComponentK8sAttributeDao().GetByComponentIDAndName(as.ServiceID, model.K8sAttributeNameAffinity)
if err != nil {
logrus.Warn("get by affinity attribute error", err)
return &affinity
}
AffinityAttributeJSON, err := yaml.YAMLToJSON([]byte(affinityAttribute.AttributeValue))
if err != nil {
logrus.Warn("Affinity attribute yaml to json error", err)
return &affinity
}
err = json.Unmarshal(AffinityAttributeJSON, &affinity)
if err != nil {
logrus.Warn("affinity json unmarshal error", err)
return &affinity
}
return &affinity
}
@ -795,7 +896,7 @@ func setImagePullSecrets() []corev1.LocalObjectReference {
}
}
func createToleration(nodeSelector map[string]string) []corev1.Toleration {
func createToleration(nodeSelector map[string]string, as *v1.AppService, dbmanager db.Manager) []corev1.Toleration {
var tolerations []corev1.Toleration
if value, exist := nodeSelector["type"]; exist && value == "virtual-kubelet" {
tolerations = append(tolerations, corev1.Toleration{
@ -803,6 +904,24 @@ func createToleration(nodeSelector map[string]string) []corev1.Toleration {
Operator: corev1.TolerationOpExists,
})
}
tolerationAttribute, err := dbmanager.ComponentK8sAttributeDao().GetByComponentIDAndName(as.ServiceID, model.K8sAttributeNameTolerations)
if err != nil {
logrus.Warn("get by toleration attribute error", err)
return tolerations
}
var tolers []corev1.Toleration
tolerationAttributeJSON, err := yaml.YAMLToJSON([]byte(tolerationAttribute.AttributeValue))
if err != nil {
logrus.Warn("toleration attribute yaml to json error", err)
return tolerations
}
err = json.Unmarshal(tolerationAttributeJSON, &tolers)
if err != nil {
logrus.Warn("toleration json unmarshal error", err)
return tolerations
}
tolerations = append(tolerations, tolers...)
return tolerations
}
@ -825,3 +944,37 @@ func createHostAliases(as *v1.AppService) []corev1.HostAlias {
}
return re
}
func createServiceAccountName(as *v1.AppService, dbmanager db.Manager) string {
var serviceAN string
sa, err := dbmanager.ComponentK8sAttributeDao().GetByComponentIDAndName(as.ServiceID, model.K8sAttributeNameServiceAccountName)
if err != nil {
logrus.Warn("get by ServiceAccountName attribute error", err)
return ""
}
serviceAN = sa.AttributeValue
return serviceAN
}
func createVolumeMounts(dv *volume.Define, as *v1.AppService, dbmanager db.Manager) []corev1.VolumeMount {
volumeMounts := dv.GetVolumeMounts()
volumeMountsAttribute, err := dbmanager.ComponentK8sAttributeDao().GetByComponentIDAndName(as.ServiceID, model.K8sAttributeNameVolumeMounts)
if err != nil {
logrus.Warn("get by volumeMounts attribute error", err)
return volumeMounts
}
var vms []corev1.VolumeMount
VolumeMountsAttributeJSON, err := yaml.YAMLToJSON([]byte(volumeMountsAttribute.AttributeValue))
if err != nil {
logrus.Warn("volumeMounts attribute yaml to json error", err)
return volumeMounts
}
err = json.Unmarshal(VolumeMountsAttributeJSON, &vms)
if err != nil {
logrus.Warn("volumeMounts json unmarshal error", err)
return volumeMounts
}
volumeMounts = append(volumeMounts, vms...)
return volumeMounts
}

View File

@ -43,6 +43,8 @@ type Informer struct {
HelmApp cache.SharedIndexInformer
ComponentDefinition cache.SharedIndexInformer
ThirdComponent cache.SharedIndexInformer
Job cache.SharedIndexInformer
CronJob cache.SharedIndexInformer
CRS map[string]cache.SharedIndexInformer
}
@ -74,12 +76,14 @@ func (i *Informer) Start(stop chan struct{}) {
go i.HelmApp.Run(stop)
go i.ComponentDefinition.Run(stop)
go i.ThirdComponent.Run(stop)
go i.Job.Run(stop)
go i.CronJob.Run(stop)
}
//Ready if all kube informers is syncd, store is ready
func (i *Informer) Ready() bool {
if i.Namespace.HasSynced() && i.Ingress.HasSynced() && i.Service.HasSynced() && i.Secret.HasSynced() &&
i.StatefulSet.HasSynced() && i.Deployment.HasSynced() && i.Pod.HasSynced() &&
i.StatefulSet.HasSynced() && i.Deployment.HasSynced() && i.Pod.HasSynced() && i.Pod.HasSynced() && i.CronJob.HasSynced() &&
i.ConfigMap.HasSynced() && i.Nodes.HasSynced() && i.Events.HasSynced() &&
i.HorizontalPodAutoscaler.HasSynced() && i.StorageClass.HasSynced() && i.Claims.HasSynced() && i.CRD.HasSynced() {
return true

View File

@ -23,9 +23,11 @@ import (
crdlisters "k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/v1"
appsv1 "k8s.io/client-go/listers/apps/v1"
autoscalingv2 "k8s.io/client-go/listers/autoscaling/v2beta2"
v1 "k8s.io/client-go/listers/batch/v1"
v1beta1 "k8s.io/client-go/listers/batch/v1beta1"
corev1 "k8s.io/client-go/listers/core/v1"
betav1 "k8s.io/client-go/listers/networking/v1beta1"
networkingv1 "k8s.io/client-go/listers/networking/v1"
betav1 "k8s.io/client-go/listers/networking/v1beta1"
storagev1 "k8s.io/client-go/listers/storage/v1"
)
@ -49,4 +51,6 @@ type Lister struct {
HelmApp v1alpha1.HelmAppLister
ComponentDefinition v1alpha1.ComponentDefinitionLister
ThirdComponent v1alpha1.ThirdComponentLister
Job v1.JobLister
CronJob v1beta1.CronJobLister
}

View File

@ -21,6 +21,8 @@ package store
import (
"context"
"fmt"
batchv1 "k8s.io/api/batch/v1"
"k8s.io/api/batch/v1beta1"
betav1 "k8s.io/api/networking/v1beta1"
"os"
"sync"
@ -206,7 +208,6 @@ func NewStore(
store.informers.ConfigMap = infFactory.Core().V1().ConfigMaps().Informer()
store.listers.ConfigMap = infFactory.Core().V1().ConfigMaps().Lister()
if k8sutil.IsHighVersion() {
store.informers.Ingress = infFactory.Networking().V1().Ingresses().Informer()
store.listers.Ingress = infFactory.Networking().V1().Ingresses().Lister()
@ -245,7 +246,10 @@ func NewStore(
store.listers.ComponentDefinition = rainbondInformer.Rainbond().V1alpha1().ComponentDefinitions().Lister()
store.informers.ComponentDefinition = rainbondInformer.Rainbond().V1alpha1().ComponentDefinitions().Informer()
store.informers.ComponentDefinition.AddEventHandlerWithResyncPeriod(componentdefinition.GetComponentDefinitionBuilder(), time.Second*300)
store.informers.Job = infFactory.Batch().V1().Jobs().Informer()
store.listers.Job = infFactory.Batch().V1().Jobs().Lister()
store.informers.CronJob = infFactory.Batch().V1beta1().CronJobs().Informer()
store.listers.CronJob = infFactory.Batch().V1beta1().CronJobs().Lister()
// Endpoint Event Handler
epEventHandler := cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
@ -301,6 +305,8 @@ func NewStore(
store.informers.Namespace.AddEventHandler(store.nsEventHandler())
store.informers.Deployment.AddEventHandlerWithResyncPeriod(store, time.Second*10)
store.informers.StatefulSet.AddEventHandlerWithResyncPeriod(store, time.Second*10)
store.informers.Job.AddEventHandlerWithResyncPeriod(store, time.Second*10)
store.informers.CronJob.AddEventHandlerWithResyncPeriod(store, time.Second*10)
store.informers.Pod.AddEventHandlerWithResyncPeriod(store.podEventHandler(), time.Second*10)
store.informers.Secret.AddEventHandlerWithResyncPeriod(store, time.Second*10)
store.informers.Service.AddEventHandlerWithResyncPeriod(store, time.Second*10)
@ -400,6 +406,7 @@ func (a *appRuntimeStore) OnAdd(obj interface{}) {
serviceID := deployment.Labels["service_id"]
version := deployment.Labels["version"]
createrID := deployment.Labels["creater_id"]
migrator := deployment.Labels["migrator"]
if serviceID != "" && version != "" && createrID != "" {
appservice, err := a.getAppService(serviceID, version, createrID, true)
if err == conversion.ErrServiceNotFound {
@ -407,14 +414,84 @@ func (a *appRuntimeStore) OnAdd(obj interface{}) {
}
if appservice != nil {
appservice.SetDeployment(deployment)
if migrator == "rainbond" {
label := "service_id=" + serviceID
pods, _ := a.conf.KubeClient.CoreV1().Pods(deployment.Namespace).List(context.Background(), metav1.ListOptions{LabelSelector: label})
if pods != nil {
for _, pod := range pods.Items {
pod := pod
appservice.SetPods(&pod)
}
}
}
return
}
}
}
if job, ok := obj.(*batchv1.Job); ok {
serviceID := job.Labels["service_id"]
version := job.Labels["version"]
createrID := job.Labels["creater_id"]
migrator := job.Labels["migrator"]
if serviceID != "" && version != "" && createrID != "" {
appservice, err := a.getAppService(serviceID, version, createrID, true)
if err == conversion.ErrServiceNotFound {
a.conf.KubeClient.BatchV1().Jobs(job.Namespace).Delete(context.Background(), job.Name, metav1.DeleteOptions{})
}
if appservice != nil {
appservice.SetJob(job)
if migrator == "rainbond" {
label := "controller-uid=" + job.Spec.Selector.MatchLabels["controller-uid"]
pods, _ := a.conf.KubeClient.CoreV1().Pods(job.Namespace).List(context.Background(), metav1.ListOptions{LabelSelector: label})
if pods != nil {
for _, pod := range pods.Items {
pod := pod
appservice.SetPods(&pod)
}
}
}
return
}
}
}
if cjob, ok := obj.(*v1beta1.CronJob); ok {
serviceID := cjob.Labels["service_id"]
version := cjob.Labels["version"]
createrID := cjob.Labels["creater_id"]
migrator := cjob.Labels["migrator"]
if serviceID != "" && version != "" && createrID != "" {
appservice, err := a.getAppService(serviceID, version, createrID, true)
if err == conversion.ErrServiceNotFound {
a.conf.KubeClient.BatchV1beta1().CronJobs(cjob.Namespace).Delete(context.Background(), cjob.Name, metav1.DeleteOptions{})
}
if appservice != nil {
appservice.SetCronJob(cjob)
if migrator == "rainbond" {
label := "service_id=" + serviceID
jobList, _ := a.conf.KubeClient.BatchV1().Jobs(cjob.Namespace).List(context.Background(), metav1.ListOptions{LabelSelector: label})
for _, job := range jobList.Items {
label := "controller-uid=" + job.Spec.Selector.MatchLabels["controller-uid"]
pods, _ := a.conf.KubeClient.CoreV1().Pods(cjob.Namespace).List(context.Background(), metav1.ListOptions{LabelSelector: label})
if pods != nil {
for _, pod := range pods.Items {
pod := pod
appservice.SetPods(&pod)
}
}
}
}
return
}
}
}
if statefulset, ok := obj.(*appsv1.StatefulSet); ok {
serviceID := statefulset.Labels["service_id"]
version := statefulset.Labels["version"]
createrID := statefulset.Labels["creater_id"]
migrator := statefulset.Labels["migrator"]
if serviceID != "" && version != "" && createrID != "" {
appservice, err := a.getAppService(serviceID, version, createrID, true)
if err == conversion.ErrServiceNotFound {
@ -422,6 +499,16 @@ func (a *appRuntimeStore) OnAdd(obj interface{}) {
}
if appservice != nil {
appservice.SetStatefulSet(statefulset)
if migrator == "rainbond" {
label := "service_id=" + serviceID
pods, _ := a.conf.KubeClient.CoreV1().Pods(statefulset.Namespace).List(context.Background(), metav1.ListOptions{LabelSelector: label})
if pods != nil {
for _, pod := range pods.Items {
pod := pod
appservice.SetPods(&pod)
}
}
}
return
}
}
@ -517,6 +604,7 @@ func (a *appRuntimeStore) OnAdd(obj interface{}) {
}
}
}
if hpa, ok := obj.(*autoscalingv2.HorizontalPodAutoscaler); ok {
serviceID := hpa.Labels["service_id"]
version := hpa.Labels["version"]
@ -862,6 +950,24 @@ func (a *appRuntimeStore) UpdateGetAppService(serviceID string) *v1.AppService {
appService.SetDeployment(deploy)
}
}
if job := appService.GetJob(); job != nil {
j, err := a.listers.Job.Jobs(job.Namespace).Get(job.Name)
if err != nil && k8sErrors.IsNotFound(err) {
appService.DeleteJob(job)
}
if j != nil {
appService.SetJob(j)
}
}
if cronjob := appService.GetCronJob(); cronjob != nil {
crjob, err := a.listers.CronJob.CronJobs(cronjob.Namespace).Get(cronjob.Name)
if err != nil && k8sErrors.IsNotFound(err) {
appService.DeleteCronJob(cronjob)
}
if crjob != nil {
appService.SetCronJob(crjob)
}
}
if services := appService.GetServices(true); services != nil {
for _, service := range services {
se, err := a.listers.Service.Services(service.Namespace).Get(service.Name)
@ -1387,6 +1493,22 @@ func (a *appRuntimeStore) scalingRecordServiceAndRuleID(evt *corev1.Event) (stri
}
serviceID = deploy.GetLabels()["service_id"]
ruleID = deploy.GetLabels()["rule_id"]
case "Job":
job, err := a.listers.Job.Jobs(evt.InvolvedObject.Namespace).Get(evt.InvolvedObject.Name)
if err != nil {
logrus.Warningf("retrieve job: %v", err)
return "", ""
}
serviceID = job.GetLabels()["service_id"]
ruleID = job.GetLabels()["rule_id"]
case "CronJob":
cjob, err := a.listers.CronJob.CronJobs(evt.InvolvedObject.Namespace).Get(evt.InvolvedObject.Name)
if err != nil {
logrus.Warningf("retrieve cronjob: %v", err)
return "", ""
}
serviceID = cjob.GetLabels()["service_id"]
ruleID = cjob.GetLabels()["rule_id"]
case "Statefulset":
statefulset, err := a.listers.StatefulSet.StatefulSets(evt.InvolvedObject.Namespace).Get(evt.InvolvedObject.Name)
if err != nil {

View File

@ -210,6 +210,7 @@ func getStatefulsetAllowFields(s *v1.StatefulSet) *v1.StatefulSet {
},
UpdateStrategy: s.Spec.UpdateStrategy,
},
ObjectMeta: s.Spec.Template.ObjectMeta,
}
}
@ -241,6 +242,7 @@ func getDeploymentAllowFields(d *v1.Deployment) *v1.Deployment {
HostNetwork: d.Spec.Template.Spec.HostNetwork,
SchedulerName: d.Spec.Template.Spec.SchedulerName,
},
ObjectMeta: d.Spec.Template.ObjectMeta,
},
},
}

View File

@ -24,7 +24,7 @@ import (
//IsPodTerminated Exception evicted pod
func IsPodTerminated(pod *corev1.Pod) bool {
if phase := pod.Status.Phase; phase != corev1.PodPending && phase != corev1.PodRunning && phase != corev1.PodUnknown {
if phase := pod.Status.Phase; phase != corev1.PodPending && phase != corev1.PodRunning && phase != corev1.PodUnknown && phase != corev1.PodSucceeded && phase != corev1.PodFailed {
return true
}
return false

View File

@ -93,6 +93,8 @@ var (
BUILDEFAILURE = "build_failure"
//UNDEPLOY init status
UNDEPLOY = "undeploy"
//SUCCEEDED if job and cronjob is succeeded
SUCCEEDED = "succeeded"
)
func conversionThirdComponent(obj runtime.Object) *v1alpha1.ThirdComponent {
@ -158,6 +160,44 @@ func (a *AppService) GetServiceStatus() string {
if a.IsClosed() {
return CLOSED
}
if a.job != nil {
succeed := 0
failed := 0
for _, po := range a.pods {
if po.Status.Phase == "Succeeded" {
succeed++
}
if po.Status.Phase == "Failed" {
failed++
}
}
if len(a.pods) == succeed {
return SUCCEEDED
}
if failed > 0 {
return ABNORMAL
}
return RUNNING
}
if a.cronjob != nil {
succeed := 0
failed := 0
for _, po := range a.pods {
if po.Status.Phase == "Succeeded" {
succeed++
}
if po.Status.Phase == "Failed" {
failed++
}
}
if len(a.pods) == succeed {
return RUNNING
}
if failed > 0 {
return ABNORMAL
}
return RUNNING
}
if a.statefulset == nil && a.deployment == nil && len(a.pods) > 0 {
return STOPPING
}

View File

@ -20,11 +20,13 @@ package v1
import (
"fmt"
"github.com/goodrain/rainbond/util/k8s"
"os"
"strconv"
"strings"
"github.com/goodrain/rainbond/util/k8s"
betav1 "k8s.io/api/networking/v1beta1"
"github.com/goodrain/rainbond/builder"
"github.com/goodrain/rainbond/db/model"
dbmodel "github.com/goodrain/rainbond/db/model"
@ -33,9 +35,10 @@ import (
"github.com/sirupsen/logrus"
v1 "k8s.io/api/apps/v1"
autoscalingv2 "k8s.io/api/autoscaling/v2beta2"
batchv1 "k8s.io/api/batch/v1"
"k8s.io/api/batch/v1beta1"
corev1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
betav1 "k8s.io/api/networking/v1beta1"
storagev1 "k8s.io/api/storage/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
@ -71,6 +74,12 @@ var TypeStatefulSet AppServiceType = "statefulset"
//TypeDeployment deployment
var TypeDeployment AppServiceType = "deployment"
//TypeJob deployment
var TypeJob AppServiceType = "job"
//TypeCronJob deployment
var TypeCronJob AppServiceType = "cronjob"
//TypeReplicationController rc
var TypeReplicationController AppServiceType = "replicationcontroller"
@ -153,6 +162,8 @@ type AppService struct {
tenant *corev1.Namespace
statefulset *v1.StatefulSet
deployment *v1.Deployment
job *batchv1.Job
cronjob *v1beta1.CronJob
workload runtime.Object
hpas []*autoscalingv2.HorizontalPodAutoscaler
delHPAs []*autoscalingv2.HorizontalPodAutoscaler
@ -218,6 +229,16 @@ func (a *AppService) DeleteDeployment(d *v1.Deployment) {
a.deployment = nil
}
//DeleteJob delete kubernetes job model
func (a *AppService) DeleteJob(d *batchv1.Job) {
a.job = nil
}
//DeleteCronJob delete kubernetes cronjob model
func (a *AppService) DeleteCronJob(d *v1beta1.CronJob) {
a.cronjob = nil
}
//GetStatefulSet get kubernetes statefulset model
func (a AppService) GetStatefulSet() *v1.StatefulSet {
return a.statefulset
@ -234,6 +255,36 @@ func (a *AppService) SetStatefulSet(d *v1.StatefulSet) {
a.calculateComponentMemoryRequest()
}
//GetJob get kubernetes job model
func (a AppService) GetJob() *batchv1.Job {
return a.job
}
//SetJob set kubernetes job model
func (a *AppService) SetJob(d *batchv1.Job) {
a.job = d
a.workload = d
if v, ok := d.Spec.Template.Labels["version"]; ok && v != "" {
a.DeployVersion = v
}
a.calculateComponentMemoryRequest()
}
//GetCronJob get kubernetes cronjob model
func (a AppService) GetCronJob() *v1beta1.CronJob {
return a.cronjob
}
//SetCronJob set kubernetes cronjob model
func (a *AppService) SetCronJob(d *v1beta1.CronJob) {
a.cronjob = d
a.workload = d
if v, ok := d.Spec.JobTemplate.Labels["version"]; ok && v != "" {
a.DeployVersion = v
}
a.calculateComponentMemoryRequest()
}
//SetReplicaSets set kubernetes replicaset
func (a *AppService) SetReplicaSets(d *v1.ReplicaSet) {
if len(a.replicasets) > 0 {
@ -522,6 +573,12 @@ func (a *AppService) SetPodTemplate(d corev1.PodTemplateSpec) {
if a.deployment != nil {
a.deployment.Spec.Template = d
}
if a.job != nil {
a.job.Spec.Template = d
}
if a.cronjob != nil {
a.cronjob.Spec.JobTemplate.Spec.Template = d
}
a.calculateComponentMemoryRequest()
}
@ -533,6 +590,12 @@ func (a *AppService) GetPodTemplate() *corev1.PodTemplateSpec {
if a.deployment != nil {
return &a.deployment.Spec.Template
}
if a.job != nil {
return &a.job.Spec.Template
}
if a.cronjob != nil {
return &a.cronjob.Spec.JobTemplate.Spec.Template
}
return nil
}

View File

@ -118,6 +118,12 @@ func (g *GarbageCollector) DelKubernetesObjects(serviceGCReq model.ServiceGCTask
if err := g.clientset.AppsV1().StatefulSets(namespace).DeleteCollection(context.Background(), deleteOpts, listOpts); err != nil {
logrus.Warningf("[DelKubernetesObjects] delete statefulsets(%s): %v", serviceGCReq.ServiceID, err)
}
if err := g.clientset.BatchV1().Jobs(namespace).DeleteCollection(context.Background(), deleteOpts, listOpts); err != nil {
logrus.Warningf("[DelKubernetesObjects] delete job(%s): %v", serviceGCReq.ServiceID, err)
}
if err := g.clientset.BatchV1beta1().CronJobs(namespace).DeleteCollection(context.Background(), deleteOpts, listOpts); err != nil {
logrus.Warningf("[DelKubernetesObjects] delete cronjob(%s): %v", serviceGCReq.ServiceID, err)
}
if err := g.clientset.ExtensionsV1beta1().Ingresses(namespace).DeleteCollection(context.Background(), deleteOpts, listOpts); err != nil {
logrus.Warningf("[DelKubernetesObjects] delete extensions ingresses(%s): %v", serviceGCReq.ServiceID, err)
}
@ -151,6 +157,7 @@ func (g *GarbageCollector) DelKubernetesObjects(serviceGCReq model.ServiceGCTask
}
}
// listOptionsServiceID -
func (g *GarbageCollector) listOptionsServiceID(serviceID string) metav1.ListOptions {
labelSelector := metav1.LabelSelector{MatchLabels: map[string]string{
"creator": "Rainbond",
@ -160,3 +167,13 @@ func (g *GarbageCollector) listOptionsServiceID(serviceID string) metav1.ListOpt
LabelSelector: labels.Set(labelSelector.MatchLabels).String(),
}
}
// DelComponentPkg deletes component package
func (g *GarbageCollector) DelComponentPkg(serviceGCReq model.ServiceGCTaskBody) {
logrus.Infof("service id: %s; delete component package.", serviceGCReq.ServiceID)
// log generated during service running
pkgPath := fmt.Sprintf("/grdata/package_build/components/%s", serviceGCReq.ServiceID)
if err := os.RemoveAll(pkgPath); err != nil {
logrus.Warningf("remove component package: %v", err)
}
}

View File

@ -463,6 +463,7 @@ func (m *Manager) ExecServiceGCTask(task *model.Task) error {
m.garbageCollector.DelPvPvcByServiceID(serviceGCReq)
m.garbageCollector.DelVolumeData(serviceGCReq)
m.garbageCollector.DelKubernetesObjects(serviceGCReq)
m.garbageCollector.DelComponentPkg(serviceGCReq)
return nil
}

View File

@ -701,10 +701,10 @@ func (r *RuntimeServer) GetAppVolumeStatus(ctx context.Context, re *pb.ServiceRe
pods := as.GetPods(false)
for _, pod := range pods {
// if pod is terminated, volume status of pod is NOT_READY
if v1.IsPodTerminated(pod) {
continue
}
// Exception pod information due to node loss is no longer displayed, so volume status is NOT_READY
// if v1.IsPodTerminated(pod) {
// continue
// }
// // Exception pod information due to node loss is no longer displayed, so volume status is NOT_READY
if v1.IsPodNodeLost(pod) {
continue
}

View File

@ -94,6 +94,19 @@ func DescribePodStatus(clientset kubernetes.Interface, pod *corev1.Pod, podStatu
if cstatus.State.Terminated.Reason == "OOMKilled" {
podStatus.Advice = PodStatusAdviceOOM.String()
}
for _, OwnerReference := range pod.OwnerReferences{
if OwnerReference.Kind == "Job"{
if cstatus.State.Terminated.Reason == "Completed" {
podStatus.Type = pb.PodStatus_SUCCEEDED
}
if cstatus.State.Terminated.Reason == "DeadlineExceeded" {
podStatus.Type = pb.PodStatus_FAILED
}
if cstatus.State.Terminated.Reason == "Error" {
podStatus.Type = pb.PodStatus_ABNORMAL
}
}
}
return
}
if !cstatus.Ready {