├── .gitignore
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── apidoc
├── apidoc.yaml
├── favicon-16x16.png
├── favicon-32x32.png
├── index.html
├── oauth2-redirect.html
├── swagger-ui-bundle.js
├── swagger-ui-bundle.js.map
├── swagger-ui-standalone-preset.js
├── swagger-ui-standalone-preset.js.map
├── swagger-ui.css
├── swagger-ui.css.map
├── swagger-ui.js
└── swagger-ui.js.map
├── backend
├── controllermanager
│ ├── controllermanager.go
│ ├── register.go
│ └── register
│ │ ├── application.go
│ │ ├── endpoint.go
│ │ ├── event.go
│ │ ├── harbor.go
│ │ ├── ingress.go
│ │ ├── namespace.go
│ │ ├── node.go
│ │ ├── resourcequota.go
│ │ ├── secret.go
│ │ └── service.go
├── controllers
│ ├── application
│ │ ├── appcontroller.go
│ │ └── syncapp.go
│ ├── endpoint
│ │ ├── endpointcontroller.go
│ │ ├── endpointutil.go
│ │ └── syncendpoint.go
│ ├── event
│ │ └── eventcontroller.go
│ ├── harbor
│ │ └── harborcontroller.go
│ ├── ingress
│ │ ├── ingresscontroller.go
│ │ ├── ingressutil.go
│ │ └── syncingress.go
│ ├── namespace
│ │ └── namespace_controller.go
│ ├── node
│ │ └── nodecontroller.go
│ ├── resourcequota
│ │ └── resourcequota_controller.go
│ ├── secret
│ │ ├── secretcontroller.go
│ │ ├── secretutil.go
│ │ └── syncsecret.go
│ ├── service
│ │ ├── servicecontroller.go
│ │ ├── serviceutil.go
│ │ └── syncservice.go
│ └── util
│ │ └── util.go
├── dao
│ ├── application.go
│ ├── appversion.go
│ ├── cluster.go
│ ├── common.go
│ ├── event.go
│ ├── harbor.go
│ ├── k8s_endpoint.go
│ ├── k8s_ingress.go
│ ├── k8s_ingress_rule.go
│ ├── k8s_namespace.go
│ ├── k8s_secret.go
│ ├── k8s_service.go
│ ├── node.go
│ └── template.go
├── models
│ ├── application.go
│ ├── appversion.go
│ ├── cluster.go
│ ├── common.go
│ ├── event.go
│ ├── harbor.go
│ ├── k8s_endpoint.go
│ ├── k8s_endpoint_address.go
│ ├── k8s_ingress.go
│ ├── k8s_ingress_rule.go
│ ├── k8s_namespace.go
│ ├── k8s_secret.go
│ ├── k8s_service.go
│ ├── k8s_service_port.go
│ ├── node.go
│ └── template.go
├── resource
│ ├── application.go
│ ├── apptemplate.go
│ ├── cluster.go
│ ├── common.go
│ ├── configmap.go
│ ├── deployment.go
│ ├── deployworker.go
│ ├── event.go
│ ├── exec_command.go
│ ├── harbor.go
│ ├── ingress.go
│ ├── ingressconfig.go
│ ├── kubeappres.go
│ ├── namespace.go
│ ├── nativeapptemplate.go
│ ├── nativetemplate.go
│ ├── node.go
│ ├── objectvalidator.go
│ ├── pod.go
│ ├── secret.go
│ ├── service.go
│ └── template.go
├── service
│ ├── appconfig.go
│ ├── clienthelper.go
│ ├── clientset.go
│ └── service.go
└── util
│ ├── kubeutil
│ ├── ingressutil.go
│ └── serviceutil.go
│ └── labels
│ └── labels.go
├── cmd
└── kubecloud
│ └── main.go
├── common
├── const.go
├── errors.go
├── errors_test.go
├── keyword
│ └── keyword.go
├── utils
│ ├── nettools.go
│ ├── ormfilter.go
│ ├── simplelocker.go
│ ├── synclocker.go
│ ├── utils.go
│ ├── utils_test.go
│ ├── visgraph.go
│ └── vizgraph.go
└── validate
│ └── validate.go
├── conf
└── app.conf
├── controllers
├── application.go
├── base.go
├── clusters.go
├── common.go
├── configmap.go
├── error.go
├── event.go
├── harbor.go
├── ingress.go
├── namespaces.go
├── node.go
├── secret.go
├── services.go
├── terminal.go
└── version.go
├── deploy
└── kubecloud.yaml
├── docs
└── images
│ └── kubecloud-architecture.png
├── gitops
├── git.go
└── gitops.go
├── go.mod
├── go.sum
└── routers
└── router.go
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX leaves these everywhere on SMB shares
2 | ._*
3 |
4 | # OSX trash
5 | .DS_Store
6 |
7 | # Eclipse files
8 | .classpath
9 | .project
10 | .settings/**
11 |
12 | # Files generated by JetBrains IDEs, e.g. IntelliJ IDEA
13 | .idea/
14 | *.iml
15 |
16 | # Vscode files
17 | .vscode
18 |
19 | # Emacs save files
20 | *~
21 | \#*\#
22 | .\#*
23 |
24 | # Vim-related files
25 | [._]*.s[a-w][a-z]
26 | [._]s[a-w][a-z]
27 | *.un~
28 | Session.vim
29 | .netrwhist
30 |
31 | # Go test binaries
32 | *.test
33 |
34 | *.exe
35 | *.log
36 | *.swp
37 | /conf/app.local.conf
38 | /conf/k8sconfig
39 | /kubecloud
40 | /configRepo/
41 | /k8sconfig/
42 | /vendor/
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.12.9-alpine as base
2 | RUN apk --update upgrade
3 | RUN apk --no-cache add tzdata make bash curl g++ git
4 | RUN rm -rf /var/cache/apk/*
5 | RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
6 | RUN echo "Asia/Shanghai" > /etc/timezone
7 |
8 | FROM base as builder
9 | WORKDIR $GOPATH/src/kubecloud
10 | COPY . .
11 | RUN CGO_ENABLED=1 INSTALL_DIR=/kubecloud make install clean
12 |
13 | FROM base
14 | WORKDIR /kubecloud
15 | COPY --from=builder /kubecloud .
16 | EXPOSE 8080
17 | CMD ["./kubecloud"]
18 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: all build
2 |
3 | INSTALL_DIR?=./kubecloud
4 | GO_TAGS?=
5 | CGO_ENABLED?=0
6 | GO_PACKAGES?=./...
7 |
8 | GO_BUILD=go build -v -tags="${GO_TAGS}"
9 |
10 | export CGO_ENABLED := ${CGO_ENABLED}
11 |
12 | all: build
13 |
14 | build:
15 | ${GO_BUILD} ./cmd/kubecloud
16 |
17 | lint:
18 | go vet -structtag=false -tags="${GO_TAGS}" ${GO_PACKAGES}
19 |
20 | # NOTE: cgo is required by go test
21 | test:
22 | CGO_ENABLED=1 go test -race -cover -failfast -vet=off -tags="${GO_TAGS}" ${GO_PACKAGES}
23 |
24 | install: build
25 | mkdir -p ${INSTALL_DIR}
26 | mkdir -p ${INSTALL_DIR}/log
27 | cp -Rp kubecloud conf apidoc ${INSTALL_DIR}
28 |
29 | docker:
30 | docker build -t kubecloud .
31 |
32 | clean:
33 | rm -rf cli web
34 | go clean -cache
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Kubecloud
2 |
3 | Kubecloud是一个多云多集群容器云管理平台,他可以帮助用户方便的部署和管理容器,以及搭建在公有云、私有云或自建IDC环境的K8s集群。
4 | Kubecloud是以Gitops的方式管理各个Kubernetes集群声明式资源的yaml配置将其存放在git配置仓库中,提供安全可靠的基础设施服务。
5 | 同时Kubecloud也积极跟随云原生社区发展,后续将不断完善产品功能并向ServiseMesh、Serverless、边缘计算等方向探索。
6 |
7 | Kubecloud的主要功能以及特性:
8 |
9 | - 提供API服务管理k8s集群以及资源编排
10 | - 方便易用的可视化界面
11 | - 多云环境的k8s集群及主机、网络、存储、容器等资源管理
12 | - 基于Harbor的镜像仓库管理功能
13 | - 使用Gitops管理k8s集群声明式基础设施的yaml配置文件
14 | - 应用市场功能方便快速部署海量企业级应用
15 | - 支持基于Istio的Servise Mesh功能
16 | - 支持基于Knative的Serverless功能
17 | - 支持机器学习训练及模型部署
18 | - 支持基于KubeEdge的容器化的边缘计算平台
19 |
20 | ## 架构概述
21 | 
22 |
23 |
24 | ## 部署Kubecloud
25 |
26 | ### 依赖条件
27 |
28 | 1. Mysql 5.7版本或以上
29 | 2. Harbor 1.7.5版本或以上
30 | 3. K8s集群 1.12版本或以上
31 | 4. Git配置仓库,例如gitlab,github等
32 | 5. Seaman 0.1.0版本
33 |
34 |
35 | ### 使用kubernets部署
36 |
37 | $ kubectl apply -f deploy/kubecloud.yaml
38 |
39 |
40 | ### 使用Docker镜像部署
41 |
42 | 你可以使用以下命令启动kubecloud:
43 |
44 | $ mkdir -p $GOPATH/src/github.com/kubecloud
45 | $ cd $GOPATH/src/github.com/kubecloud
46 | $ git clone https://github.com/ZhongAnTech/kubecloud.git
47 | $ cd kubecloud
48 | $ make docker
49 | $ docker run --name kubecloud -d -p 8080:8080 kubecloud
50 |
51 |
52 | ### 编译源码
53 |
54 | $ mkdir -p $GOPATH/src/github.com/kubecloud
55 | $ cd $GOPATH/src/github.com/kubecloud
56 | $ git clone https://github.com/ZhongAnTech/kubecloud.git
57 | $ cd kubecloud
58 | $ make build
59 | $ ./kubecloud
60 |
61 | ## API文档
62 |
63 | http://localhost:8080/apidoc/idex.html
64 |
65 |
66 |
67 | ## License
68 |
69 | Apache License 2.0, see [LICENSE](LICENSE).
70 |
--------------------------------------------------------------------------------
/apidoc/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhongAnTech/kubecloud/0fc068f5f55a7d10935dcecf52e0813b1a4fa6a3/apidoc/favicon-16x16.png
--------------------------------------------------------------------------------
/apidoc/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhongAnTech/kubecloud/0fc068f5f55a7d10935dcecf52e0813b1a4fa6a3/apidoc/favicon-32x32.png
--------------------------------------------------------------------------------
/apidoc/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Swagger UI
7 |
8 |
9 |
10 |
11 |
30 |
31 |
32 |
33 |
34 |
67 |
68 |
69 |
70 |
71 |
72 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/apidoc/oauth2-redirect.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
54 |
--------------------------------------------------------------------------------
/apidoc/swagger-ui-bundle.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"swagger-ui-bundle.js","sources":["webpack:///swagger-ui-bundle.js"],"mappings":"AAAA;;;;;AAsyKA;;;;;;AAktEA;;;;;;;;;;;;;;;;;;;;;;;;;;AAkqTA;;;;;;;;;;;;;;AAs8JA;;;;;;;;;AA4tnBA;;;;;AAmpQA;;;;;;AA+mXA","sourceRoot":""}
--------------------------------------------------------------------------------
/apidoc/swagger-ui-standalone-preset.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"swagger-ui-standalone-preset.js","sources":["webpack:///swagger-ui-standalone-preset.js"],"mappings":"AAAA;;;;;AA40CA;;;;;;AAqlFA","sourceRoot":""}
--------------------------------------------------------------------------------
/apidoc/swagger-ui.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"swagger-ui.css","sources":[],"mappings":";;;","sourceRoot":""}
--------------------------------------------------------------------------------
/apidoc/swagger-ui.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"swagger-ui.js","sources":["webpack:///swagger-ui.js"],"mappings":"AAAA;;;;;;AAk/aA","sourceRoot":""}
--------------------------------------------------------------------------------
/backend/controllermanager/register.go:
--------------------------------------------------------------------------------
1 | package controllermanager
2 |
3 | type StartController func(ctx ControllerContext) error
4 |
5 | var controllerList map[string]StartController
6 |
7 | func RegisterController(name string, startFunc StartController) {
8 | if controllerList == nil {
9 | controllerList = make(map[string]StartController)
10 | }
11 | controllerList[name] = startFunc
12 | }
13 |
14 | func GetControllerList() map[string]StartController {
15 | return controllerList
16 | }
17 |
--------------------------------------------------------------------------------
/backend/controllermanager/register/application.go:
--------------------------------------------------------------------------------
1 | package register
2 |
3 | import (
4 | "fmt"
5 | cm "kubecloud/backend/controllermanager"
6 | "kubecloud/backend/controllers/application"
7 | )
8 |
9 | func startApplicationController(ctx cm.ControllerContext) error {
10 | ac, err := application.NewApplicationController(
11 | ctx.Cluster, ctx.Client,
12 | ctx.InformerFactory.Apps().V1beta1().Deployments())
13 | if err != nil {
14 | return fmt.Errorf("error creating deployment controller: %v", err)
15 | }
16 | go ac.Run(ctx.Option.NormalConcurrentSyncs, ctx.Stop)
17 | return nil
18 | }
19 |
20 | func init() {
21 | cm.RegisterController("application", startApplicationController)
22 | }
23 |
--------------------------------------------------------------------------------
/backend/controllermanager/register/endpoint.go:
--------------------------------------------------------------------------------
1 | package register
2 |
3 | import (
4 | "fmt"
5 | cm "kubecloud/backend/controllermanager"
6 | "kubecloud/backend/controllers/endpoint"
7 | )
8 |
9 | func startEndpointController(ctx cm.ControllerContext) error {
10 | ac, err := endpoint.NewEndpointController(
11 | ctx.Cluster, ctx.Client,
12 | ctx.InformerFactory.Core().V1().Endpoints())
13 | if err != nil {
14 | return fmt.Errorf("error creating service controller: %v", err)
15 | }
16 | go ac.Run(ctx.Option.NormalConcurrentSyncs, ctx.Stop)
17 | return nil
18 | }
19 |
20 | func init() {
21 | cm.RegisterController("endpoint", startEndpointController)
22 | }
23 |
--------------------------------------------------------------------------------
/backend/controllermanager/register/event.go:
--------------------------------------------------------------------------------
1 | package register
2 |
3 | import (
4 | cm "kubecloud/backend/controllermanager"
5 | "kubecloud/backend/controllers/event"
6 | )
7 |
8 | func startEventController(ctx cm.ControllerContext) error {
9 | ec := event.NewEventController(
10 | ctx.Cluster,
11 | ctx.InformerFactory.Core().V1().Events(),
12 | ctx.Client)
13 |
14 | go ec.Run(1, ctx.Stop)
15 | return nil
16 | }
17 |
18 | func init() {
19 | cm.RegisterController("event", startEventController)
20 | }
21 |
--------------------------------------------------------------------------------
/backend/controllermanager/register/harbor.go:
--------------------------------------------------------------------------------
1 | package register
2 |
3 | import (
4 | cm "kubecloud/backend/controllermanager"
5 | "kubecloud/backend/controllers/harbor"
6 | )
7 |
8 | func startHarborController(ctx cm.ControllerContext) error {
9 | hc, err := harbor.NewHarborController(ctx.Cluster)
10 | if err != nil {
11 | return err
12 | }
13 | go hc.Run(ctx.Stop)
14 | return nil
15 | }
16 |
17 | func init() {
18 | cm.RegisterController("harbor", startHarborController)
19 | }
20 |
--------------------------------------------------------------------------------
/backend/controllermanager/register/ingress.go:
--------------------------------------------------------------------------------
1 | package register
2 |
3 | import (
4 | "fmt"
5 | cm "kubecloud/backend/controllermanager"
6 | "kubecloud/backend/controllers/ingress"
7 | )
8 |
9 | func startIngressController(ctx cm.ControllerContext) error {
10 | ic, err := ingress.NewIngressController(
11 | ctx.Cluster, ctx.Client,
12 | ctx.InformerFactory.Extensions().V1beta1().Ingresses())
13 | if err != nil {
14 | return fmt.Errorf("error creating ingress controller: %v", err)
15 | }
16 | go ic.Run(ctx.Option.NormalConcurrentSyncs, ctx.Stop)
17 | return nil
18 | }
19 |
20 | func init() {
21 | cm.RegisterController("ingress", startIngressController)
22 | }
23 |
--------------------------------------------------------------------------------
/backend/controllermanager/register/namespace.go:
--------------------------------------------------------------------------------
1 | package register
2 |
3 | import (
4 | cm "kubecloud/backend/controllermanager"
5 | "kubecloud/backend/controllers/namespace"
6 | )
7 |
8 | func startNamespaceController(ctx cm.ControllerContext) error {
9 | controller := namespace.NewNamespaceController(
10 | ctx.Cluster, ctx.Client,
11 | ctx.InformerFactory.Core().V1().Namespaces())
12 | go controller.Run(ctx.Option.NormalConcurrentSyncs, ctx.Stop)
13 | return nil
14 | }
15 |
16 | func init() {
17 | //cm.RegisterController("namespace", startNamespaceController)
18 | }
19 |
--------------------------------------------------------------------------------
/backend/controllermanager/register/node.go:
--------------------------------------------------------------------------------
1 | package register
2 |
3 | import (
4 | cm "kubecloud/backend/controllermanager"
5 | "kubecloud/backend/controllers/node"
6 | )
7 |
8 | func startNodeController(ctx cm.ControllerContext) error {
9 | nc, err := node.NewNodeController(
10 | ctx.Cluster,
11 | ctx.InformerFactory.Core().V1().Nodes(),
12 | ctx.Client)
13 | if err != nil {
14 | return err
15 | }
16 |
17 | go nc.Run(1, ctx.Stop)
18 | return nil
19 | }
20 |
21 | func init() {
22 | cm.RegisterController("node", startNodeController)
23 | }
24 |
--------------------------------------------------------------------------------
/backend/controllermanager/register/resourcequota.go:
--------------------------------------------------------------------------------
1 | package register
2 |
3 | import (
4 | cm "kubecloud/backend/controllermanager"
5 | "kubecloud/backend/controllers/resourcequota"
6 | )
7 |
8 | func startResourceQuotaController(ctx cm.ControllerContext) error {
9 | controller := resourcequota.NewResourceQuotaController(
10 | ctx.Cluster, ctx.Client,
11 | ctx.InformerFactory.Core().V1().ResourceQuotas())
12 | go controller.Run(ctx.Option.NormalConcurrentSyncs, ctx.Stop)
13 | return nil
14 | }
15 |
16 | func init() {
17 | //cm.RegisterController("resourcequota", startResourceQuotaController)
18 | }
19 |
--------------------------------------------------------------------------------
/backend/controllermanager/register/secret.go:
--------------------------------------------------------------------------------
1 | package register
2 |
3 | import (
4 | "fmt"
5 | cm "kubecloud/backend/controllermanager"
6 | "kubecloud/backend/controllers/secret"
7 | )
8 |
9 | func startSecretController(ctx cm.ControllerContext) error {
10 | sc, err := secret.NewSecretController(
11 | ctx.Cluster, ctx.Client,
12 | ctx.InformerFactory.Core().V1().Secrets())
13 | if err != nil {
14 | return fmt.Errorf("error creating secret controller: %v", err)
15 | }
16 | go sc.Run(ctx.Option.NormalConcurrentSyncs, ctx.Stop)
17 | return nil
18 | }
19 |
20 | func init() {
21 | cm.RegisterController("secret", startSecretController)
22 | }
23 |
--------------------------------------------------------------------------------
/backend/controllermanager/register/service.go:
--------------------------------------------------------------------------------
1 | package register
2 |
3 | import (
4 | "fmt"
5 | cm "kubecloud/backend/controllermanager"
6 | "kubecloud/backend/controllers/service"
7 | )
8 |
9 | func startServiceController(ctx cm.ControllerContext) error {
10 | ac, err := service.NewServiceController(
11 | ctx.Cluster, ctx.Client,
12 | ctx.InformerFactory.Core().V1().Services())
13 | if err != nil {
14 | return fmt.Errorf("error creating service controller: %v", err)
15 | }
16 | go ac.Run(ctx.Option.NormalConcurrentSyncs, ctx.Stop)
17 | return nil
18 | }
19 |
20 | func init() {
21 | cm.RegisterController("service", startServiceController)
22 | }
23 |
--------------------------------------------------------------------------------
/backend/controllers/application/syncapp.go:
--------------------------------------------------------------------------------
1 | package application
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 |
7 | "github.com/astaxie/beego"
8 | v1beta1 "k8s.io/api/apps/v1beta1"
9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10 |
11 | "kubecloud/backend/controllers/util"
12 | "kubecloud/backend/dao"
13 | "kubecloud/backend/resource"
14 | "kubecloud/common/keyword"
15 | )
16 |
17 | type appStatus struct {
18 | ReadyReplicas int32
19 | AvailableReplicas int32
20 | AvailableStatus string
21 | Message string
22 | }
23 |
24 | type syncApplication struct {
25 | appHandler *dao.AppModel
26 | cluster string
27 | }
28 |
29 | func newSyncApplication(cluster string) *syncApplication {
30 | return &syncApplication{
31 | appHandler: dao.NewAppModel(),
32 | cluster: cluster,
33 | }
34 | }
35 |
36 | // update if the app is existed, or add it
37 | func (sa *syncApplication) syncDeployApplication(deployment v1beta1.Deployment) error {
38 | appname := getAppNameByDeploy(deployment)
39 | if !sa.appHandler.AppExist(sa.cluster, deployment.Namespace, appname) {
40 | return fmt.Errorf("application(%s/%s/%s) is not existed in db, the deployment is %s!", sa.cluster, deployment.Namespace, appname, deployment.Name)
41 | } else {
42 | return sa.updateDeployStatus(appname, deployment)
43 | }
44 | }
45 |
46 | func (sa *syncApplication) updateDeployStatus(appname string, deployment v1beta1.Deployment) error {
47 | app, err := sa.appHandler.GetAppByName(sa.cluster, deployment.Namespace, appname)
48 | if err != nil {
49 | return err
50 | }
51 | if deployment.Labels["heritage"] != "Tiller" {
52 | version := resource.GetResourceVersion(&deployment, resource.ResTypeDeploy, app.Image)
53 | if resource.GetResourceVersion(app, resource.ResTypeApp, "") != version {
54 | beego.Warn(fmt.Sprintf("application(%s/%s/%s) dont need update for versions(%s/%s) are not equal!", app.Cluster, app.Namespace, app.Name, version, resource.GetResourceVersion(app, resource.ResTypeApp, "")))
55 | return nil
56 | }
57 | }
58 |
59 | needUpdate := false
60 | smFlag := util.GetAnnotationStringValue(resource.InjectSidecarAnnotationKey, deployment.Annotations, "")
61 |
62 | if deployment.Labels["heritage"] == "Tiller" {
63 | deployment.TypeMeta = metav1.TypeMeta{
64 | Kind: "Deployment",
65 | APIVersion: "apps/v1beta1",
66 | }
67 | deployment.ObjectMeta.ResourceVersion = ""
68 | deployment.ObjectMeta.SelfLink = ""
69 | deployment.ObjectMeta.UID = ""
70 | deployment.ObjectMeta.Generation = 0
71 | deployment.ObjectMeta.CreationTimestamp = metav1.Time{}
72 | nativeTemplate := resource.NativeAppTemplate{
73 | TypeMeta: deployment.TypeMeta,
74 | ObjectMeta: deployment.ObjectMeta,
75 | Deployment: &deployment,
76 | }
77 | jsonData, err := json.Marshal(nativeTemplate)
78 | if err != nil {
79 | return err
80 | }
81 |
82 | app.Template = string(jsonData)
83 | selector, err := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{
84 | MatchLabels: deployment.Spec.Selector.MatchLabels,
85 | })
86 | if err != nil {
87 | return err
88 | }
89 | app.LabelSelector = selector.String()
90 | needUpdate = true
91 | }
92 |
93 | // because app.InjectServiceMesh "" is equal with "false"
94 | if (app.InjectServiceMesh == "" || app.InjectServiceMesh == "false") && smFlag == "true" ||
95 | (smFlag == "false" && app.InjectServiceMesh == "true") {
96 | app.InjectServiceMesh = smFlag
97 | needUpdate = true
98 | }
99 | if app.Replicas != int(*deployment.Spec.Replicas) {
100 | app.Replicas = int(*deployment.Spec.Replicas)
101 | template, err := resource.CreateAppTemplateByApp(*app)
102 | if err != nil {
103 | beego.Error("create app template by app failed:", err)
104 | } else {
105 | //synchronize information
106 | if tstr, err := template.Replicas(app.Replicas).String(); err == nil {
107 | app.Template = tstr
108 | needUpdate = true
109 | } else {
110 | beego.Error("template switch to string failed:", err)
111 | }
112 | }
113 | }
114 | if app.StatusReplicas != deployment.Status.Replicas {
115 | app.StatusReplicas = deployment.Status.Replicas
116 | needUpdate = true
117 | }
118 | if app.ReadyReplicas != deployment.Status.ReadyReplicas {
119 | app.ReadyReplicas = deployment.Status.ReadyReplicas
120 | needUpdate = true
121 | }
122 | if app.AvailableReplicas != deployment.Status.AvailableReplicas {
123 | app.AvailableReplicas = deployment.Status.AvailableReplicas
124 | needUpdate = true
125 | }
126 | if app.UpdatedReplicas != deployment.Status.UpdatedReplicas {
127 | app.UpdatedReplicas = deployment.Status.UpdatedReplicas
128 | needUpdate = true
129 | }
130 | for _, condition := range deployment.Status.Conditions {
131 | if condition.Type == v1beta1.DeploymentAvailable {
132 | if string(condition.Status) != app.AvailableStatus {
133 | app.AvailableStatus = string(condition.Status)
134 | needUpdate = true
135 | }
136 | if condition.Message != app.Message {
137 | app.Message = condition.Message
138 | needUpdate = true
139 | }
140 | break
141 | }
142 | }
143 | if podv, ok := deployment.Labels[keyword.LABEL_PODVERSION_KEY]; !ok || podv == "" {
144 | app.PodVersion = ""
145 | needUpdate = true
146 | }
147 | if needUpdate {
148 | err = sa.appHandler.UpdateApp(app, false)
149 | if err != nil {
150 | beego.Error("Update application", sa.cluster, app.Namespace, app.Name, "failed for", err)
151 | } else {
152 | beego.Info("Update application", sa.cluster, app.Namespace, app.Name, "successfully")
153 | }
154 | return err
155 | }
156 | return nil
157 | }
158 |
159 | func getAppNameByDeploy(deploy v1beta1.Deployment) string {
160 | appname := deploy.Name
161 | if deploy.Labels["heritage"] != "Tiller" {
162 | if v, ok := deploy.Labels[keyword.LABEL_APPNAME_KEY]; ok {
163 | appname = v
164 | }
165 | }
166 | return appname
167 | }
168 |
--------------------------------------------------------------------------------
/backend/controllers/endpoint/endpointutil.go:
--------------------------------------------------------------------------------
1 | package endpoint
2 |
3 | import (
4 | "kubecloud/backend/controllers/util"
5 | "kubecloud/backend/models"
6 | "kubecloud/backend/resource"
7 |
8 | core "k8s.io/api/core/v1"
9 | )
10 |
11 | //generate endpoint template by service. return endpoint struct
12 | func genEndpointRecord(cluster string, endpoint core.Endpoints, subnetIndex, portIndex int) models.K8sEndpoint {
13 | record := models.K8sEndpoint{
14 | Cluster: cluster,
15 | Namespace: endpoint.Namespace,
16 | Name: endpoint.Name,
17 | }
18 | record.OwnerName = util.GetAnnotationStringValue(resource.OwnerNameAnnotationKey,
19 | endpoint.Annotations, resource.GetApplicationNameBySvcName(endpoint.Name))
20 | port := endpoint.Subsets[subnetIndex].Ports[portIndex]
21 | record.Port = port.Port
22 | record.PortName = port.Name
23 | record.Protocol = string(port.Protocol)
24 | // generate address
25 | for _, item := range endpoint.Subsets[subnetIndex].Addresses {
26 | address := models.K8sEndpointAddress{
27 | Cluster: cluster,
28 | Namespace: endpoint.Namespace,
29 | EndpointName: endpoint.Name,
30 | IP: item.IP,
31 | }
32 | if item.NodeName != nil {
33 | address.NodeName = *item.NodeName
34 | }
35 | if item.TargetRef != nil {
36 | address.TargetRefName = item.TargetRef.Name
37 | }
38 | record.Addresses = append(record.Addresses, &address)
39 | }
40 |
41 | return record
42 | }
43 |
44 | func serviceIsEqual(os, ns models.K8sService) bool {
45 | equal := true
46 | if os.ClusterIP != ns.ClusterIP ||
47 | os.Type != ns.Type ||
48 | len(os.Ports) != len(ns.Ports) {
49 | equal = false
50 | }
51 | for _, np := range ns.Ports {
52 | if !equal {
53 | break
54 | }
55 | exist := false
56 | for _, op := range os.Ports {
57 | if np.Name == op.Name {
58 | exist = true
59 | if np.Port != op.Port ||
60 | np.TargetPort != op.TargetPort ||
61 | np.Protocol != op.Protocol ||
62 | np.NodePort != op.NodePort {
63 | equal = false
64 | }
65 | break
66 | }
67 | }
68 | if !exist {
69 | equal = false
70 | }
71 | }
72 | return equal
73 | }
74 |
--------------------------------------------------------------------------------
/backend/controllers/endpoint/syncendpoint.go:
--------------------------------------------------------------------------------
1 | package endpoint
2 |
3 | import (
4 | "kubecloud/backend/models"
5 |
6 | "fmt"
7 | "github.com/astaxie/beego"
8 | "github.com/astaxie/beego/orm"
9 | core "k8s.io/api/core/v1"
10 | )
11 |
12 | // update if the endpoint is existed, or add it
13 | func (ec *EndpointController) syncEndpointRecord(endpoint core.Endpoints) error {
14 | if len(endpoint.Subsets) == 0 {
15 | return nil
16 | }
17 | for index, port := range endpoint.Subsets[0].Ports {
18 | old, err := ec.endpointHandler.Get(ec.cluster, endpoint.Namespace,
19 | endpoint.Name, port.Port)
20 | if err != nil {
21 | if err != orm.ErrNoRows {
22 | return err
23 | }
24 | err = ec.createEndpointRecord(endpoint, 0, index)
25 | } else {
26 | err = ec.updateEndpointRecord(endpoint, *old, 0, index)
27 | }
28 | if err != nil {
29 | return err
30 | }
31 | }
32 | // delete some ash port
33 | list, err := ec.endpointHandler.ListByName(ec.cluster, endpoint.Namespace, endpoint.Name)
34 | if err != nil {
35 | return err
36 | }
37 | for _, item := range list {
38 | found := false
39 | for _, port := range endpoint.Subsets[0].Ports {
40 | if item.Port == port.Port {
41 | found = true
42 | break
43 | }
44 | }
45 | if !found {
46 | ec.endpointHandler.DeleteByID(item.Id)
47 | }
48 | }
49 | return nil
50 | }
51 |
52 | func (ec *EndpointController) deleteEndpointRecord(namespace, name string) error {
53 | // delete service
54 | err := ec.endpointHandler.Delete(ec.cluster, namespace, name)
55 | if err != nil {
56 | beego.Error("Delete kube service record", ec.cluster, namespace, name, "failed for", err)
57 | }
58 | return err
59 | }
60 |
61 | func (ec *EndpointController) createEndpointRecord(endpoint core.Endpoints, subnetIndex, portIndex int) error {
62 | record := genEndpointRecord(ec.cluster, endpoint, subnetIndex, portIndex)
63 | if len(record.Addresses) == 0 {
64 | return fmt.Errorf("Create kube endpoint(%s/%s/%s) record failed: has no endpoint addresses!", ec.cluster, record.Namespace, record.Name)
65 | }
66 | err := ec.endpointHandler.Create(record)
67 | if err != nil {
68 | beego.Error("Create kube endpoint record", ec.cluster, record.Namespace, record.Name, "failed for", err)
69 | return err
70 | }
71 | return nil
72 | }
73 |
74 | func (ec *EndpointController) updateEndpointRecord(endpoint core.Endpoints, old models.K8sEndpoint, subnetIndex, portIndex int) error {
75 | record := genEndpointRecord(ec.cluster, endpoint, subnetIndex, portIndex)
76 | err := ec.endpointHandler.Update(old, record)
77 | if err != nil {
78 | beego.Error("Update kube endpoint", ec.cluster, record.Namespace, record.Name, "failed for", err)
79 | return err
80 | }
81 | return nil
82 | }
83 |
--------------------------------------------------------------------------------
/backend/controllers/harbor/harborcontroller.go:
--------------------------------------------------------------------------------
1 | package harbor
2 |
3 | import (
4 | "fmt"
5 | "time"
6 |
7 | "kubecloud/backend/resource"
8 |
9 | "github.com/astaxie/beego"
10 | "k8s.io/apimachinery/pkg/util/wait"
11 | "kubecloud/backend/dao"
12 | "kubecloud/backend/models"
13 | )
14 |
15 | // harbor controller is a global controller in system
16 | type HarborController struct {
17 | cluster *models.ZcloudCluster
18 | harbor *models.ZcloudHarbor
19 | handler func() error
20 | }
21 |
22 | var clusterHarbor map[string]interface{}
23 |
24 | func harborControllerIsRunning(harborAddr string) bool {
25 | _, ok := clusterHarbor[harborAddr]
26 | return ok
27 | }
28 |
29 | // NewHarborController creates a new HarborController.
30 | func NewHarborController(cluster string) (*HarborController, error) {
31 | info, err := dao.GetCluster(cluster)
32 | if err != nil {
33 | return nil, err
34 | }
35 | harbor, err := dao.GetHarbor(info.Registry)
36 | if err != nil {
37 | return nil, err
38 | }
39 | if harborControllerIsRunning(harbor.HarborAddr) {
40 | return nil, fmt.Errorf("harbor controller of this harbor is running!")
41 | }
42 | if clusterHarbor == nil {
43 | clusterHarbor = make(map[string]interface{})
44 | }
45 | clusterHarbor[harbor.HarborAddr] = nil
46 | hc := &HarborController{cluster: info, harbor: harbor}
47 |
48 | hc.handler = hc.syncHarbor
49 |
50 | return hc, nil
51 | }
52 |
53 | // Run begins watching and syncing.
54 | func (hc *HarborController) Run(stopCh <-chan struct{}) {
55 | syncTime, err := beego.AppConfig.Int64("harbor::syncTime")
56 | if syncTime == 0 || err != nil {
57 | syncTime = 60
58 | }
59 | go wait.Until(hc.worker, time.Duration(syncTime)*time.Second, stopCh)
60 | <-stopCh
61 | }
62 |
63 | // worker runs a worker thread that just dequeues items, processes them, and marks them done.
64 | // It enforces that the syncHandler is never invoked concurrently with the same key.
65 | func (hc *HarborController) worker() {
66 | beego.Debug("start synchronizing harbor infomation for cluster " + hc.cluster.Name + "...")
67 | hc.processNextWorkItem()
68 | beego.Debug("finish synchronizing harbor infomation for cluster " + hc.cluster.Name + "!")
69 | }
70 |
71 | func (hc *HarborController) processNextWorkItem() bool {
72 | if err := hc.handler(); err != nil {
73 | beego.Warn("sync harbor failed:", err)
74 | return false
75 | }
76 | return true
77 | }
78 |
79 | func (hc *HarborController) syncHarbor() error {
80 | startTime := time.Now()
81 | defer func() {
82 | beego.Info(fmt.Sprintf("Finished syncing harbor %s/%q (%v)", hc.harbor.HarborId, hc.harbor.HarborName, time.Now().Sub(startTime)))
83 | }()
84 |
85 | return resource.SyncHarborRepositoryData(hc.harbor)
86 | }
87 |
--------------------------------------------------------------------------------
/backend/controllers/ingress/ingresscontroller.go:
--------------------------------------------------------------------------------
1 | package ingress
2 |
3 | import (
4 | "fmt"
5 | "time"
6 |
7 | "kubecloud/backend/controllers/util"
8 | dao "kubecloud/backend/dao"
9 |
10 | "github.com/astaxie/beego"
11 |
12 | extensions "k8s.io/api/extensions/v1beta1"
13 | "k8s.io/apimachinery/pkg/api/errors"
14 | "k8s.io/apimachinery/pkg/util/wait"
15 | extensionsinformers "k8s.io/client-go/informers/extensions/v1beta1"
16 | "k8s.io/client-go/kubernetes"
17 | extensionslisters "k8s.io/client-go/listers/extensions/v1beta1"
18 | "k8s.io/client-go/tools/cache"
19 | "k8s.io/client-go/util/workqueue"
20 | )
21 |
22 | const (
23 | // maxRetries is the number of times a ingress will be retried before it is dropped out of the queue.
24 | // With the current rate-limiter in use (5ms*2^(maxRetries-1)) the following numbers represent the times
25 | // a ingress is going to be requeued:
26 | //
27 | // 5ms, 10ms, 20ms, 40ms, 80ms, 160ms, 320ms, 640ms, 1.3s, 2.6s, 5.1s, 10.2s, 20.4s, 41s, 82s
28 | maxRetries = 10
29 | )
30 |
31 | // IngressController is responsible for synchronizing ingress objects stored
32 | type IngressController struct {
33 | cluster string
34 | client kubernetes.Interface
35 |
36 | // To allow injection of syncIngress for testing.
37 | syncHandler func(dKey string) error
38 | // used for unit testing
39 | enqueueIngress func(ing *extensions.Ingress)
40 |
41 | // ingLister can list/get ingresses from the shared informer's store
42 | ingLister extensionslisters.IngressLister
43 |
44 | // ingListerSynced returns true if the ingress store has been synced at least once.
45 | // Added as a member to the struct to allow injection for testing.
46 | ingListerSynced cache.InformerSynced
47 |
48 | // Ingresses that need to be synced
49 | queue workqueue.RateLimitingInterface
50 | // ingress dbhandler
51 | kubeIngHandler *dao.K8sIngressModel
52 | }
53 |
54 | // NewIngressController creates a new IngressController.
55 | func NewIngressController(cluster string,
56 | client kubernetes.Interface,
57 | ingInformer extensionsinformers.IngressInformer) (*IngressController, error) {
58 | ic := &IngressController{
59 | cluster: cluster,
60 | client: client,
61 | queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "ingress"),
62 | }
63 |
64 | ingInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
65 | AddFunc: ic.addIngress,
66 | UpdateFunc: ic.updateIngress,
67 | DeleteFunc: ic.deleteIngress,
68 | })
69 | ic.syncHandler = ic.syncIngress
70 | ic.enqueueIngress = ic.enqueue
71 |
72 | ic.ingLister = ingInformer.Lister()
73 | ic.ingListerSynced = ingInformer.Informer().HasSynced
74 |
75 | ic.kubeIngHandler = dao.NewK8sIngressModel()
76 |
77 | return ic, nil
78 | }
79 |
80 | // Run begins watching and syncing.
81 | func (ic *IngressController) Run(workers int, stopCh <-chan struct{}) {
82 | defer ic.queue.ShutDown()
83 |
84 | if !cache.WaitForCacheSync(stopCh, ic.ingListerSynced) {
85 | beego.Error("ingress controller cache sync failed!")
86 | return
87 | }
88 |
89 | for i := 0; i < workers; i++ {
90 | go wait.Until(ic.worker, time.Second, stopCh)
91 | }
92 |
93 | <-stopCh
94 | }
95 |
96 | func (ic *IngressController) addIngress(obj interface{}) {
97 | ing := obj.(*extensions.Ingress)
98 | ic.enqueueIngress(ing)
99 | }
100 |
101 | func (ic *IngressController) updateIngress(old, cur interface{}) {
102 | //olding := old.(*extensions.Ingress)
103 | curing := cur.(*extensions.Ingress)
104 | ic.enqueueIngress(curing)
105 | }
106 |
107 | func (ic *IngressController) deleteIngress(obj interface{}) {
108 | ing, ok := obj.(*extensions.Ingress)
109 | if !ok {
110 | tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
111 | if !ok {
112 | beego.Error(fmt.Errorf("Couldn't get object from tombstone %#v", obj))
113 | return
114 | }
115 | ing, ok = tombstone.Obj.(*extensions.Ingress)
116 | if !ok {
117 | beego.Error(fmt.Errorf("Tombstone contained object that is not a Ingress %#v", obj))
118 | return
119 | }
120 | }
121 | ic.enqueueIngress(ing)
122 | }
123 |
124 | func (ic *IngressController) enqueue(ing *extensions.Ingress) {
125 | key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(ing)
126 | if err != nil {
127 | beego.Error(fmt.Errorf("Couldn't get key for object %#v: %v", ing, err))
128 | return
129 | }
130 |
131 | ic.queue.Add(key)
132 | }
133 |
134 | // worker runs a worker thread that just dequeues items, processes them, and marks them done.
135 | // It enforces that the syncHandler is never invoked concurrently with the same key.
136 | func (ic *IngressController) worker() {
137 | for ic.processNextWorkItem() {
138 | }
139 | }
140 |
141 | func (ic *IngressController) processNextWorkItem() bool {
142 | key, quit := ic.queue.Get()
143 | if quit {
144 | beego.Error("get item from workqueue failed!")
145 | return false
146 | }
147 | defer ic.queue.Done(key)
148 |
149 | err := ic.syncHandler(key.(string))
150 | ic.handleErr(err, key)
151 |
152 | return true
153 | }
154 |
155 | func (ic *IngressController) handleErr(err error, key interface{}) {
156 | if err == nil {
157 | ic.queue.Forget(key)
158 | return
159 | }
160 |
161 | if ic.queue.NumRequeues(key) < maxRetries {
162 | ic.queue.AddRateLimited(key)
163 | return
164 | }
165 |
166 | beego.Warn(fmt.Sprintf("Dropping ingress %q out of the queue: %v, cluster: %s", key, err, ic.cluster))
167 | ic.queue.Forget(key)
168 | }
169 |
170 | // syncIngress will sync the ingress with the given key.
171 | // This function is not meant to be invoked concurrently with the same key.
172 | func (ic *IngressController) syncIngress(key string) error {
173 | namespace, name, err := cache.SplitMetaNamespaceKey(key)
174 | if err != nil {
175 | return err
176 | }
177 | //check namespace
178 | if util.FilterNamespace(ic.cluster, namespace) {
179 | //beego.Warn("Skip this syncing of cluster "+ic.cluster, namespace)
180 | return nil
181 | }
182 | ing, err := ic.ingLister.Ingresses(namespace).Get(name)
183 | if errors.IsNotFound(err) {
184 | err = ic.deleteIngressRecord(namespace, name)
185 | if err != nil {
186 | beego.Error("Delete ingress from database failed: ", err)
187 | }
188 | return err
189 | }
190 | if err != nil {
191 | return err
192 | }
193 |
194 | return ic.syncIngressRecord(*ing)
195 | }
196 |
--------------------------------------------------------------------------------
/backend/controllers/ingress/ingressutil.go:
--------------------------------------------------------------------------------
1 | package ingress
2 |
3 | import (
4 | "kubecloud/backend/models"
5 | commutil "kubecloud/common/utils"
6 |
7 | extensions "k8s.io/api/extensions/v1beta1"
8 | )
9 |
10 | func genIngressRecord(cluster string, ing extensions.Ingress) models.K8sIngress {
11 | record := models.K8sIngress{}
12 | record.Name = ing.Name
13 | record.Namespace = ing.Namespace
14 | record.Cluster = cluster
15 | record.Annotation = commutil.SimpleJsonMarshal(ing.Annotations, "")
16 | for _, rule := range ing.Spec.Rules {
17 | if rule.Host == "" {
18 | continue
19 | }
20 | ruleList := genIngressRuleRecords(cluster, ing, rule)
21 | record.Rules = append(record.Rules, ruleList...)
22 | }
23 | return record
24 | }
25 |
26 | func genIngressRuleRecords(cluster string, ing extensions.Ingress, rule extensions.IngressRule) []*models.K8sIngressRule {
27 | recordList := []*models.K8sIngressRule{}
28 | record := models.K8sIngressRule{}
29 | record.Namespace = ing.Namespace
30 | record.Cluster = cluster
31 | record.IngressName = ing.Name
32 | record.IsTls = false
33 | record.Host = rule.Host
34 | for _, tls := range ing.Spec.TLS {
35 | for _, h := range tls.Hosts {
36 | if h == rule.Host {
37 | record.IsTls = true
38 | record.SecretName = tls.SecretName
39 | break
40 | }
41 | }
42 | if record.IsTls {
43 | break
44 | }
45 | }
46 | // just one path
47 | if rule.HTTP != nil {
48 | for _, path := range rule.HTTP.Paths {
49 | item := models.K8sIngressRule{}
50 | item = record
51 | item.Path = path.Path
52 | item.ServiceName = path.Backend.ServiceName
53 | item.ServicePort = path.Backend.ServicePort.IntValue()
54 | recordList = append(recordList, &item)
55 | }
56 | }
57 | return recordList
58 | }
59 |
--------------------------------------------------------------------------------
/backend/controllers/ingress/syncingress.go:
--------------------------------------------------------------------------------
1 | package ingress
2 |
3 | import (
4 | "kubecloud/backend/models"
5 |
6 | "github.com/astaxie/beego"
7 | "github.com/astaxie/beego/orm"
8 | extensions "k8s.io/api/extensions/v1beta1"
9 | )
10 |
11 | // update if the app is existed, or add it
12 | func (ic *IngressController) syncIngressRecord(ing extensions.Ingress) error {
13 | old, err := ic.kubeIngHandler.Get(ic.cluster, ing.Namespace, ing.Name)
14 | if err != nil {
15 | if err != orm.ErrNoRows {
16 | return err
17 | }
18 | err = ic.createIngressRecord(ing)
19 | } else {
20 | err = ic.updateIngressRecord(ing, *old)
21 | }
22 |
23 | return err
24 | }
25 |
26 | func (ic *IngressController) deleteIngressRecord(namespace, name string) error {
27 | // delete ingress
28 | err := ic.kubeIngHandler.Delete(ic.cluster, namespace, name)
29 | if err != nil {
30 | beego.Error("Delete kube ingress record", ic.cluster, namespace, name, "failed for", err)
31 | }
32 | return err
33 | }
34 |
35 | func (ic *IngressController) createIngressRecord(ing extensions.Ingress) error {
36 | record := genIngressRecord(ic.cluster, ing)
37 | if len(record.Rules) == 0 {
38 | beego.Warn("no rules of ingress", ic.cluster, record.Namespace, record.Name)
39 | return nil
40 | }
41 | err := ic.kubeIngHandler.Create(record)
42 | if err != nil {
43 | beego.Error("Create kube ingress record", ic.cluster, record.Namespace, record.Name, "failed for", err)
44 | return err
45 | }
46 | return nil
47 | }
48 |
49 | func (ic *IngressController) updateIngressRecord(ing extensions.Ingress, old models.K8sIngress) error {
50 | record := genIngressRecord(ic.cluster, ing)
51 | err := ic.kubeIngHandler.Update(old, record)
52 | if err != nil {
53 | beego.Error("Update kube ingress", ic.cluster, record.Namespace, record.Name, "failed for", err)
54 | return err
55 | }
56 | return nil
57 | }
58 |
--------------------------------------------------------------------------------
/backend/controllers/secret/secretutil.go:
--------------------------------------------------------------------------------
1 | package secret
2 |
3 | import (
4 | "kubecloud/backend/controllers/util"
5 | "kubecloud/backend/models"
6 | "kubecloud/backend/resource"
7 | "time"
8 |
9 | "encoding/json"
10 |
11 | "github.com/astaxie/beego"
12 | core "k8s.io/api/core/v1"
13 | )
14 |
15 | //generate secret template by Secret. return K8sSecret struct
16 | func genSecretRecord(cluster string, s core.Secret) models.K8sSecret {
17 | record := models.K8sSecret{
18 | Cluster: cluster,
19 | Namespace: s.Namespace,
20 | Name: s.Name,
21 | Addons: models.NewAddons(),
22 | }
23 | record.CreateAt, _ = time.Parse("2006-01-02 15:04:05", s.CreationTimestamp.Local().Format("2006-01-02 15:04:05"))
24 | record.OwnerName = util.GetAnnotationStringValue(resource.OwnerNameAnnotationKey,
25 | s.Annotations, "")
26 | record.Description = util.GetAnnotationStringValue(resource.DescriptionAnnotationKey,
27 | s.Annotations, "")
28 | record.Type = string(s.Type)
29 | data, err := json.Marshal(&s.Data)
30 | if err != nil {
31 | beego.Error("marshal secret data failed:", s.Name, err)
32 | } else {
33 | record.Data = string(data)
34 | }
35 |
36 | return record
37 | }
38 |
39 | func secretIsEqual(os, ns models.K8sSecret) bool {
40 | equal := true
41 | if os.Type != ns.Type ||
42 | os.Description != ns.Description ||
43 | os.Data != ns.Data {
44 | equal = false
45 | }
46 | return equal
47 | }
48 |
--------------------------------------------------------------------------------
/backend/controllers/secret/syncsecret.go:
--------------------------------------------------------------------------------
1 | package secret
2 |
3 | import (
4 | "kubecloud/backend/models"
5 |
6 | "github.com/astaxie/beego"
7 | "github.com/astaxie/beego/orm"
8 | core "k8s.io/api/core/v1"
9 | )
10 |
11 | func (sc *SecretController) syncSecretRecord(s core.Secret) error {
12 | old, err := sc.secretHandler.GetSecret(sc.cluster, s.Namespace, s.Name)
13 | if err != nil {
14 | if err != orm.ErrNoRows {
15 | return err
16 | }
17 | err = sc.createSecretRecord(s)
18 | } else {
19 | err = sc.updateSecretRecord(s, *old)
20 | }
21 |
22 | return err
23 | }
24 |
25 | func (sc *SecretController) deleteSecretRecord(namespace, name string) error {
26 | err := sc.secretHandler.DeleteSecret(sc.cluster, namespace, name)
27 | if err != nil {
28 | beego.Error("Delete kube secret record", sc.cluster, namespace, name, "failed for", err)
29 | }
30 | return err
31 | }
32 |
33 | func (sc *SecretController) createSecretRecord(s core.Secret) error {
34 | record := genSecretRecord(sc.cluster, s)
35 | err := sc.secretHandler.CreateSecret(record, false)
36 | if err != nil {
37 | beego.Error("Create kube secret record", sc.cluster, record.Namespace, record.Name, "failed for", err)
38 | return err
39 | }
40 | return nil
41 | }
42 |
43 | func (sc *SecretController) updateSecretRecord(s core.Secret, old models.K8sSecret) error {
44 | record := genSecretRecord(sc.cluster, s)
45 | if !secretIsEqual(old, record) {
46 | err := sc.secretHandler.UpdateSecret(old, record, false)
47 | if err != nil {
48 | beego.Error("Update kube secret", sc.cluster, record.Namespace, record.Name, "failed for", err)
49 | return err
50 | }
51 | }
52 | return nil
53 | }
54 |
--------------------------------------------------------------------------------
/backend/controllers/service/serviceutil.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "kubecloud/backend/controllers/util"
5 | "kubecloud/backend/models"
6 | "kubecloud/backend/resource"
7 | commutil "kubecloud/common/utils"
8 |
9 | core "k8s.io/api/core/v1"
10 | )
11 |
12 | //generate service template by service. return service struct
13 | func genServiceRecord(cluster string, svc core.Service) models.K8sService {
14 | record := models.K8sService{
15 | Cluster: cluster,
16 | Namespace: svc.Namespace,
17 | Name: svc.Name,
18 | }
19 | record.OwnerName = util.GetAnnotationStringValue(resource.OwnerNameAnnotationKey,
20 | svc.Annotations, resource.GetApplicationNameBySvcName(svc.Name))
21 | record.Type = string(svc.Spec.Type)
22 | record.ClusterIP = svc.Spec.ClusterIP
23 | record.Annotation = commutil.SimpleJsonMarshal(svc.Annotations, "")
24 | for _, item := range svc.Spec.Ports {
25 | port := models.K8sServicePort{
26 | Name: item.Name,
27 | Cluster: cluster,
28 | Namespace: svc.Namespace,
29 | ServiceName: svc.Name,
30 | Protocol: string(item.Protocol),
31 | Port: int(item.Port),
32 | TargetPort: item.TargetPort.IntValue(),
33 | NodePort: int(item.NodePort),
34 | }
35 | record.Ports = append(record.Ports, &port)
36 | }
37 |
38 | return record
39 | }
40 |
41 | func serviceIsEqual(os, ns models.K8sService) bool {
42 | equal := true
43 | if os.ClusterIP != ns.ClusterIP ||
44 | os.Type != ns.Type ||
45 | len(os.Ports) != len(ns.Ports) ||
46 | os.Annotation != ns.Annotation {
47 | equal = false
48 | }
49 | for _, np := range ns.Ports {
50 | if !equal {
51 | break
52 | }
53 | exist := false
54 | for _, op := range os.Ports {
55 | if np.Name == op.Name {
56 | exist = true
57 | if np.Port != op.Port ||
58 | np.TargetPort != op.TargetPort ||
59 | np.Protocol != op.Protocol ||
60 | np.NodePort != op.NodePort {
61 | equal = false
62 | }
63 | break
64 | }
65 | }
66 | if !exist {
67 | equal = false
68 | }
69 | }
70 | return equal
71 | }
72 |
--------------------------------------------------------------------------------
/backend/controllers/service/syncservice.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "kubecloud/backend/controllers/util"
5 | "kubecloud/backend/models"
6 | "kubecloud/backend/resource"
7 |
8 | "github.com/astaxie/beego"
9 | "github.com/astaxie/beego/orm"
10 | core "k8s.io/api/core/v1"
11 | )
12 |
13 | // update if the app is existed, or add it
14 | func (sc *ServiceController) syncServiceRecord(svc core.Service) error {
15 | old, err := sc.svcHandler.Get(sc.cluster, svc.Namespace,
16 | util.GetAnnotationStringValue(resource.OwnerNameAnnotationKey, svc.Annotations, resource.GetApplicationNameBySvcName(svc.Name)),
17 | svc.Name)
18 | if err != nil {
19 | if err != orm.ErrNoRows {
20 | return err
21 | }
22 | err = sc.createServiceRecord(svc)
23 | } else {
24 | err = sc.updateServiceRecord(svc, *old)
25 | }
26 | return err
27 | }
28 |
29 | func (sc *ServiceController) deleteServiceRecord(namespace, name string) error {
30 | // delete service
31 | err := sc.svcHandler.Delete(sc.cluster, namespace, name)
32 | if err != nil {
33 | beego.Error("Delete kube service record", sc.cluster, namespace, name, "failed for", err)
34 | }
35 | return err
36 | }
37 |
38 | func (sc *ServiceController) createServiceRecord(svc core.Service) error {
39 | record := genServiceRecord(sc.cluster, svc)
40 | err := sc.svcHandler.Create(record)
41 | if err != nil {
42 | beego.Error("Create kube service record", sc.cluster, record.Namespace, record.Name, "failed for", err)
43 | return err
44 | }
45 | return nil
46 | }
47 |
48 | func (sc *ServiceController) updateServiceRecord(svc core.Service, old models.K8sService) error {
49 | record := genServiceRecord(sc.cluster, svc)
50 | if !serviceIsEqual(old, record) {
51 | record.Id = old.Id
52 | }
53 | if record.Id != 0 {
54 | err := sc.svcHandler.Update(old, record)
55 | if err != nil {
56 | beego.Error("Update kube service", sc.cluster, record.Namespace, record.Name, "failed for", err)
57 | return err
58 | }
59 | }
60 | return nil
61 | }
62 |
--------------------------------------------------------------------------------
/backend/controllers/util/util.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "kubecloud/backend/dao"
5 | )
6 |
7 | var filterNamespaces = []string{"default", "kube-system", "kube-public", "tekton-pipelines"}
8 |
9 | func GetAnnotationStringValue(key string, ann map[string]string, def string) string {
10 | if ann == nil {
11 | return def
12 | }
13 | if value, exist := ann[key]; exist {
14 | return value
15 | }
16 | return def
17 | }
18 |
19 | func GetAnnotationBoolValue(key string, ann map[string]string, def bool) bool {
20 | if ann == nil {
21 | return def
22 | }
23 | if value, exist := ann[key]; exist {
24 | return value == "true" || value == "TRUE"
25 | }
26 | return def
27 | }
28 |
29 | func FilterNamespace(cluster string, namespace string) bool {
30 | for _, ns := range filterNamespaces {
31 | if ns == namespace {
32 | return true
33 | }
34 | }
35 | return !dao.NamespaceExists(cluster, namespace)
36 | }
37 |
--------------------------------------------------------------------------------
/backend/dao/appversion.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/astaxie/beego/orm"
7 | "kubecloud/backend/models"
8 | )
9 |
10 | type VersionModel struct {
11 | tOrmer orm.Ormer
12 | TableName string
13 | }
14 |
15 | func NewVersionModel() *VersionModel {
16 | return &VersionModel{
17 | tOrmer: GetOrmer(),
18 | TableName: (&models.ZcloudVersion{}).TableName(),
19 | }
20 | }
21 |
22 | func (vm *VersionModel) GetVersionList(cluster, namespace, appname string) ([]models.ZcloudVersion, error) {
23 | list := []models.ZcloudVersion{}
24 |
25 | query := vm.tOrmer.QueryTable(vm.TableName).
26 | Filter("cluster", cluster).
27 | Filter("namespace", namespace).
28 | Filter("name", appname)
29 | _, err := query.All(&list)
30 | if err == orm.ErrNoRows {
31 | return list, nil
32 | }
33 | for i, item := range list {
34 | // compatible with old application
35 | if item.PodVersion == "" {
36 | list[i].PodVersion = item.Version
37 | }
38 | }
39 | return list, err
40 | }
41 |
42 | func (vm *VersionModel) GetVersion(cluster, namespace, appname, version string) (*models.ZcloudVersion, error) {
43 | v := models.ZcloudVersion{}
44 |
45 | query := vm.tOrmer.QueryTable(vm.TableName).
46 | Filter("cluster", cluster).
47 | Filter("namespace", namespace).
48 | Filter("name", appname).
49 | Filter("version", version)
50 | err := query.One(&v)
51 | if err != nil {
52 | return nil, err
53 | }
54 | // compatible with old application
55 | if v.PodVersion == "" {
56 | v.PodVersion = v.Version
57 | }
58 | return &v, nil
59 | }
60 |
61 | func (vm *VersionModel) SetVersionWeight(version *models.ZcloudVersion) error {
62 | if version == nil {
63 | return fmt.Errorf("version must be given!")
64 | }
65 | v, err := vm.GetVersion(version.Cluster, version.Namespace, version.Name, version.Version)
66 | if err == orm.ErrNoRows {
67 | // insert
68 | version.Addons = models.NewAddons()
69 | _, err = vm.tOrmer.Insert(version)
70 | } else {
71 | if err != nil {
72 | return err
73 | }
74 | v.Weight = version.Weight
75 | v.CurReplicas = version.CurReplicas
76 | v.Addons = v.Addons.UpdateAddons()
77 | _, err = vm.tOrmer.Update(v)
78 | }
79 | return err
80 | }
81 |
82 | func (vm *VersionModel) DeleteVersion(cluster, namespace, appname, version string) error {
83 | _, err := vm.tOrmer.Raw("DELETE FROM "+vm.TableName+" WHERE cluster=? AND namespace=? AND name=? AND version=?",
84 | cluster, namespace, appname, version).Exec()
85 | return err
86 | }
87 |
88 | func (vm *VersionModel) DeleteAllVersion(cluster, namespace, appname string) error {
89 | _, err := vm.tOrmer.Raw("DELETE FROM "+vm.TableName+" WHERE cluster=? AND namespace=? AND name=?",
90 | cluster, namespace, appname).Exec()
91 | return err
92 | }
93 |
--------------------------------------------------------------------------------
/backend/dao/cluster.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/astaxie/beego/orm"
7 |
8 | "kubecloud/backend/models"
9 | "kubecloud/common/utils"
10 | )
11 |
12 | var clusterEnableFilterKeys = []string{
13 | "name",
14 | "display_name",
15 | "env",
16 | "registry_name",
17 | "status",
18 | "domain_suffix",
19 | "creator",
20 | "create_at",
21 | }
22 |
23 | func GetClusterListByFilter(filterQuery *utils.FilterQuery) (*utils.QueryResult, error) {
24 | var clusters []models.ZcloudCluster
25 | queryCond := orm.NewCondition().And("deleted", 0)
26 | if filterQuery != nil {
27 | filterCond := filterQuery.FilterCondition(clusterEnableFilterKeys)
28 | if filterCond != nil {
29 | queryCond = queryCond.AndCond(filterCond)
30 | }
31 | }
32 |
33 | query := GetOrmer().QueryTable("zcloud_cluster").OrderBy("-create_at").SetCond(queryCond)
34 |
35 | count, err := query.Count()
36 | if err != nil {
37 | return nil, err
38 | }
39 |
40 | if filterQuery != nil && filterQuery.PageSize != 0 && filterQuery.PageIndex > 0 {
41 | query = query.Limit(filterQuery.PageSize, filterQuery.PageSize*(filterQuery.PageIndex-1))
42 | }
43 | if _, err := query.All(&clusters); err != nil {
44 | return nil, err
45 | }
46 | return &utils.QueryResult{
47 | Base: utils.PageInfo{
48 | TotalNum: count,
49 | PageIndex: filterQuery.PageIndex,
50 | PageSize: filterQuery.PageSize,
51 | },
52 | List: clusters}, err
53 | }
54 |
55 | func GetClusterList() ([]models.ZcloudCluster, error) {
56 | var clusters []models.ZcloudCluster
57 | qs := GetOrmer().QueryTable("zcloud_cluster").Filter("deleted", 0).OrderBy("-create_at")
58 | _, err := qs.All(&clusters)
59 | return clusters, err
60 | }
61 |
62 | func GetAllClusters() ([]models.ZcloudCluster, error) {
63 | return GetClusterList()
64 | }
65 |
66 | func GetClusterByTenant(tenant, name string) (*models.ZcloudCluster, error) {
67 | var cluster models.ZcloudCluster
68 | if err := GetOrmer().QueryTable("zcloud_cluster").
69 | Filter("tenant", tenant).Filter("name", name).One(&cluster); err != nil {
70 | return nil, err
71 | }
72 | return &cluster, nil
73 | }
74 |
75 | func GetCluster(clusterId string) (*models.ZcloudCluster, error) {
76 | var cluster models.ZcloudCluster
77 | if err := GetOrmer().QueryTable("zcloud_cluster").
78 | Filter("cluster_id", clusterId).One(&cluster); err != nil {
79 | if err == orm.ErrMultiRows {
80 | return nil, err
81 | }
82 | if err == orm.ErrNoRows {
83 | return nil, err
84 | }
85 | return nil, fmt.Errorf("database error: get cluster(%s) info failed: %s!", clusterId, err.Error())
86 | }
87 |
88 | return &cluster, nil
89 | }
90 |
91 | func CreateCluster(cluster models.ZcloudCluster) error {
92 | if _, err := GetOrmer().Insert(&cluster); err != nil {
93 | return fmt.Errorf("database error: insert culster info failed: %s", cluster.Name)
94 | }
95 | return nil
96 | }
97 |
98 | func UpdateCluster(cluster models.ZcloudCluster) error {
99 | if !ClusterNameExist(cluster.ClusterId, cluster.Name) {
100 | return fmt.Errorf("database error: cluster(%s) is not existed!", cluster.Name)
101 | }
102 |
103 | if !HarborIsExist(cluster.Tenant, cluster.Registry) {
104 | return fmt.Errorf("default registry(%s) is not existed!", cluster.Registry)
105 | }
106 |
107 | _, err := GetOrmer().Update(&cluster)
108 |
109 | return err
110 | }
111 |
112 | func DeleteCluster(clusterId string) error {
113 | _, err := GetOrmer().Raw("delete from zcloud_cluster WHERE cluster_id=?", clusterId).Exec()
114 | return err
115 | }
116 |
117 | func ClusterIsExist(clusterId string) bool {
118 | return GetOrmer().QueryTable("zcloud_cluster").Filter("cluster_id", clusterId).Exist()
119 | }
120 |
121 | func ClusterNameExist(id, name string) bool {
122 | return GetOrmer().QueryTable("zcloud_cluster").Filter("cluster_id", id).Filter("name", name).Exist()
123 | }
124 |
125 | func IsClusterParamExistInTenant(tenant, fieldName, fieldValue string) bool {
126 | return GetOrmer().QueryTable("zcloud_cluster").Filter("tenant", tenant).Filter(fieldName, fieldValue).Exist()
127 | }
128 |
129 | func UpdateClusterStatus(clusterId, status string) error {
130 | _, err := GetOrmer().Raw("update zcloud_cluster set status = ? where cluster_id = ?", status, clusterId).Exec()
131 | return err
132 | }
133 |
134 | func GetClusterDomainSuffixList(clusterId string) ([]*models.ZcloudClusterDomainSuffix, error) {
135 | domainSuffixList := []*models.ZcloudClusterDomainSuffix{}
136 | _, err := GetOrmer().QueryTable("zcloud_cluster_domain_suffix").Filter("cluster", clusterId).OrderBy("-create_at").All(&domainSuffixList)
137 | return domainSuffixList, err
138 | }
139 |
140 | func AddClusterDomainSuffix(domainSuffix *models.ZcloudClusterDomainSuffix) error {
141 | _, err := GetOrmer().Insert(domainSuffix)
142 | return err
143 | }
144 |
145 | func DeleteClusterDomainSuffix(clusterId string) error {
146 | _, err := GetOrmer().Raw("delete from zcloud_cluster_domain_suffix where cluster=?", clusterId).Exec()
147 | return err
148 | }
149 |
--------------------------------------------------------------------------------
/backend/dao/common.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "sync"
5 |
6 | "github.com/astaxie/beego/orm"
7 | )
8 |
9 | var globalOrm orm.Ormer
10 | var once sync.Once
11 |
12 | // GetOrmer get ormer singleton. Pitfall: each transaction requires a separate orm
13 | func GetOrmer() orm.Ormer {
14 | once.Do(func() {
15 | globalOrm = orm.NewOrm()
16 | })
17 | return globalOrm
18 | }
19 |
--------------------------------------------------------------------------------
/backend/dao/event.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "fmt"
5 | "kubecloud/backend/models"
6 | "kubecloud/common/utils"
7 |
8 | "github.com/astaxie/beego/orm"
9 | )
10 |
11 | var eventEnableFilterKeys = []string{
12 | "event_type",
13 | "source_component",
14 | "source_host",
15 | "object_kind",
16 | "object_name",
17 | "reason",
18 | "message",
19 | "last_time",
20 | }
21 |
22 | func CreateEvent(event models.ZcloudEvent) error {
23 | _, err := GetOrmer().InsertOrUpdate(&event)
24 | return err
25 | }
26 |
27 | func GetEvents(clusterName, namespace, sourceHost, objectKind, objectName, eventLevel string, limitCount int64) ([]models.ZcloudEvent, error) {
28 | var events []models.ZcloudEvent
29 | qs := GetOrmer().QueryTable("zcloud_event").OrderBy("-last_time")
30 | if clusterName != "" {
31 | qs = qs.Filter("cluster", clusterName)
32 | }
33 | if namespace != "" {
34 | qs = qs.Filter("namespace", namespace)
35 | }
36 | if sourceHost != "" {
37 | qs = qs.Filter("source_host", sourceHost)
38 | }
39 | if eventLevel != "" {
40 | qs = qs.Filter("event_level", eventLevel)
41 | }
42 |
43 | if objectKind != "" {
44 | switch objectKind {
45 | case "Pod", "Node":
46 | qs = qs.Filter("object_kind", objectKind)
47 | default:
48 | err := fmt.Errorf("don't supported object kind: %s", objectKind)
49 | return events, err
50 | }
51 | }
52 | if objectName != "" {
53 | qs = qs.Filter("object_name", objectName)
54 | }
55 |
56 | if limitCount != -1 {
57 | qs = qs.Limit(limitCount)
58 | }
59 | if _, err := qs.All(&events); err != nil {
60 | return events, err
61 | }
62 |
63 | return events, nil
64 | }
65 |
66 | func GetAppEvents(cluster, namespace string, app string) ([]*models.ZcloudEvent, error) {
67 | events := []*models.ZcloudEvent{}
68 | sql := `select * from zcloud_event where cluster=? and namespace=? and object_name like '` + app + "%' order by last_time desc"
69 | if _, err := GetOrmer().Raw(sql, cluster, namespace).QueryRows(&events); err != nil {
70 | return nil, err
71 | }
72 | return events, nil
73 | }
74 |
75 | func GetNodeEvents(cluster, host string) ([]*models.ZcloudEvent, error) {
76 | events := []*models.ZcloudEvent{}
77 | ormer := GetOrmer()
78 | _, err := ormer.QueryTable("zcloud_event").
79 | Filter("cluster", cluster).
80 | Filter("object_kind", "Node").
81 | Filter("source_host", host).
82 | OrderBy("-last_time").All(&events)
83 | return events, err
84 | }
85 |
86 | func GetNodeEventsByFilter(cluster, host string, filterQuery *utils.FilterQuery) (*utils.QueryResult, error) {
87 | var events []models.ZcloudEvent
88 | queryCond := orm.NewCondition().And("cluster", cluster).And("source_host", host)
89 | if filterQuery != nil {
90 | filterCond := filterQuery.FilterCondition(eventEnableFilterKeys)
91 | if filterCond != nil {
92 | queryCond = queryCond.AndCond(filterCond)
93 | }
94 | }
95 |
96 | query := GetOrmer().QueryTable("zcloud_event").OrderBy("-last_time").SetCond(queryCond)
97 |
98 | count, err := query.Count()
99 | if err != nil {
100 | return nil, err
101 | }
102 |
103 | if filterQuery != nil && filterQuery.PageSize != 0 && filterQuery.PageIndex > 0 {
104 | query = query.Limit(filterQuery.PageSize, filterQuery.PageSize*(filterQuery.PageIndex-1))
105 | }
106 | if _, err := query.All(&events); err != nil {
107 | return nil, err
108 | }
109 | return &utils.QueryResult{
110 | Base: utils.PageInfo{
111 | TotalNum: count,
112 | PageIndex: filterQuery.PageIndex,
113 | PageSize: filterQuery.PageSize,
114 | },
115 | List: events}, err
116 | }
117 |
--------------------------------------------------------------------------------
/backend/dao/k8s_endpoint.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | models "kubecloud/backend/models"
5 |
6 | "github.com/astaxie/beego"
7 | "github.com/astaxie/beego/orm"
8 | )
9 |
10 | type K8sEndpointModel struct {
11 | tOrmer orm.Ormer
12 | endpointTable string
13 | addressTable string
14 | }
15 |
16 | func NewK8sEndpointModel() *K8sEndpointModel {
17 | return &K8sEndpointModel{
18 | tOrmer: GetOrmer(),
19 | endpointTable: (&models.K8sEndpoint{}).TableName(),
20 | addressTable: (&models.K8sEndpointAddress{}).TableName(),
21 | }
22 | }
23 |
24 | func (em *K8sEndpointModel) Create(endpoint models.K8sEndpoint) error {
25 | endpoint.Addons = models.NewAddons()
26 | _, err := em.tOrmer.Insert(&endpoint)
27 | if err != nil {
28 | return err
29 | }
30 | var addresses []models.K8sEndpointAddress
31 | for _, item := range endpoint.Addresses {
32 | addr := *item
33 | addr.Endpoint = &endpoint
34 | addr.Addons = models.NewAddons()
35 | addresses = append(addresses, addr)
36 | }
37 | _, err = em.tOrmer.InsertMulti(len(addresses), addresses)
38 | return err
39 | }
40 |
41 | func (em *K8sEndpointModel) Update(old, cur models.K8sEndpoint) error {
42 | cur.Id = old.Id
43 | if !em.endpointBaseIsEqual(old, cur) {
44 | cur.OwnerName = old.OwnerName
45 | if err := em.updateEndpoint(old, cur); err != nil {
46 | return err
47 | }
48 | }
49 | for _, na := range cur.Addresses {
50 | index := -1
51 | for i, oa := range old.Addresses {
52 | if em.addressIsEqual(*na, *oa) {
53 | index = i
54 | }
55 | }
56 | if index == -1 {
57 | // create
58 | if err := em.insertAddress(na, &cur); err != nil {
59 | return err
60 | }
61 | }
62 | }
63 | // delete old other
64 | for _, oa := range old.Addresses {
65 | index := -1
66 | for i, na := range cur.Addresses {
67 | if em.addressIsEqual(*oa, *na) {
68 | index = i
69 | break
70 | }
71 | }
72 | if index == -1 {
73 | err := em.deleteAddress(oa.Id)
74 | if err != nil {
75 | beego.Error(err)
76 | }
77 | }
78 | }
79 |
80 | return nil
81 | }
82 |
83 | func (em *K8sEndpointModel) Delete(cluster, namespace, name string) error {
84 | err := em.deleteEndpoint(cluster, namespace, name)
85 | if err == nil {
86 | err = em.deleteAddresses(cluster, namespace, name)
87 | }
88 | return err
89 | }
90 |
91 | func (em *K8sEndpointModel) DeleteByID(id int64) error {
92 | sql := "delete from " + em.endpointTable + " where id=?"
93 | _, err := em.tOrmer.Raw(sql, id).Exec()
94 | if err == nil {
95 | sql := "delete from " + em.addressTable + " where endpoint=?"
96 | _, err = em.tOrmer.Raw(sql, id).Exec()
97 | }
98 | return err
99 | }
100 |
101 | func (em *K8sEndpointModel) Get(cluster, namespace, name string, port int32) (*models.K8sEndpoint, error) {
102 | var obj models.K8sEndpoint
103 |
104 | if err := em.tOrmer.QueryTable(em.endpointTable).
105 | Filter("cluster", cluster).
106 | Filter("namespace", namespace).
107 | Filter("name", name).
108 | Filter("port", port).
109 | Filter("deleted", 0).One(&obj); err != nil {
110 | beego.Error(err)
111 | return nil, err
112 | }
113 | if _, err := em.tOrmer.LoadRelated(&obj, "addresses"); err != nil {
114 | beego.Error(err)
115 | return nil, err
116 | }
117 | var addresses []*models.K8sEndpointAddress
118 | for _, addr := range obj.Addresses {
119 | if addr.Deleted == 0 {
120 | addresses = append(addresses, addr)
121 | }
122 | }
123 | obj.Addresses = addresses
124 | return &obj, nil
125 | }
126 |
127 | func (em *K8sEndpointModel) ListByName(cluster, namespace, name string) ([]models.K8sEndpoint, error) {
128 | list := []models.K8sEndpoint{}
129 | var err error
130 |
131 | query := em.tOrmer.QueryTable(em.endpointTable).
132 | Filter("cluster", cluster).
133 | Filter("namespace", namespace).
134 | Filter("name", name).
135 | Filter("deleted", 0).OrderBy("-create_at")
136 | _, err = query.All(&list)
137 | if err != nil {
138 | return list, err
139 | }
140 |
141 | return list, nil
142 | }
143 |
144 | func (im *K8sEndpointModel) updateEndpoint(old, cur models.K8sEndpoint) error {
145 | cur.Addons = old.Addons.UpdateAddons()
146 | _, err := im.tOrmer.Update(&cur)
147 | return err
148 | }
149 |
150 | func (em *K8sEndpointModel) deleteEndpoint(cluster, namespace, name string) error {
151 | sql := "delete from " + em.endpointTable + " where cluster=? and namespace=? and name=?"
152 | _, err := em.tOrmer.Raw(sql, cluster, namespace, name).Exec()
153 | return err
154 | }
155 |
156 | func (im *K8sEndpointModel) insertAddress(addr *models.K8sEndpointAddress, endpoint *models.K8sEndpoint) error {
157 | addr.Addons = models.NewAddons()
158 | addr.Endpoint = endpoint
159 | _, err := im.tOrmer.Insert(addr)
160 | return err
161 | }
162 |
163 | func (em *K8sEndpointModel) deleteAddresses(cluster, namespace, name string) error {
164 | sql := "delete from " + em.addressTable + " where cluster=? and namespace=? and endpoint_name=?"
165 | _, err := em.tOrmer.Raw(sql, cluster, namespace, name).Exec()
166 |
167 | return err
168 | }
169 |
170 | func (em *K8sEndpointModel) deleteAddress(id int64) error {
171 | sql := "delete from " + em.addressTable + " where id=?"
172 | _, err := em.tOrmer.Raw(sql, id).Exec()
173 |
174 | return err
175 | }
176 |
177 | func (em *K8sEndpointModel) addressIsEqual(a1, a2 models.K8sEndpointAddress) bool {
178 | if a1.Cluster != a2.Cluster ||
179 | a1.Namespace != a2.Namespace ||
180 | a1.EndpointName != a2.EndpointName ||
181 | a1.IP != a2.IP ||
182 | a1.NodeName != a2.NodeName ||
183 | a1.TargetRefName != a2.TargetRefName {
184 | return false
185 | }
186 | return true
187 | }
188 |
189 | func (em *K8sEndpointModel) endpointBaseIsEqual(e1, e2 models.K8sEndpoint) bool {
190 | if e1.OwnerName != e2.OwnerName ||
191 | e1.Port != e2.Port ||
192 | e1.PortName != e2.PortName ||
193 | e1.Protocol != e2.Protocol {
194 | return false
195 | }
196 |
197 | return true
198 | }
199 |
--------------------------------------------------------------------------------
/backend/dao/k8s_ingress_rule.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/astaxie/beego"
7 | "github.com/astaxie/beego/orm"
8 | models "kubecloud/backend/models"
9 | "kubecloud/common/utils"
10 | )
11 |
12 | type IngressRuleModel struct {
13 | tOrmer orm.Ormer
14 | TableName string
15 | }
16 |
17 | type hostOwner struct {
18 | cluster string
19 | namespace string
20 | }
21 |
22 | const (
23 | SIMPLE_UNIQUE_MODE = "simple"
24 | )
25 |
26 | var ruleEnableFilterKeys = map[string]interface{}{
27 | "namespace": nil,
28 | "ingress_name": nil,
29 | "secret_name": nil,
30 | "service_name": nil,
31 | "host": nil,
32 | "creator": nil,
33 | "create_at": nil,
34 | }
35 |
36 | func NewIngressRuleModel() *IngressRuleModel {
37 | return &IngressRuleModel{
38 | tOrmer: GetOrmer(),
39 | TableName: (&models.K8sIngressRule{}).TableName(),
40 | }
41 | }
42 |
43 | func (im *IngressRuleModel) List(cluster string, nslist []string, svcname string, filterQuery *utils.FilterQuery) (*utils.QueryResult, error) {
44 | rulelist := []models.K8sIngressRule{}
45 | query := im.tOrmer.QueryTable(im.TableName).
46 | Filter("deleted", 0).OrderBy("-create_at")
47 | if cluster != "" {
48 | query = query.Filter("cluster", cluster)
49 | }
50 | if len(nslist) >= 1 {
51 | query = query.Filter("namespace__in", nslist)
52 | }
53 | if svcname != "" {
54 | query = query.Filter("service_name", svcname)
55 | }
56 | if filterQuery.FilterVal != "" {
57 | if _, exist := ruleEnableFilterKeys[filterQuery.FilterKey]; exist {
58 | suffix := "__icontains"
59 | if !filterQuery.IsLike {
60 | suffix = ""
61 | }
62 | query = query.Filter(filterQuery.FilterKey+suffix, filterQuery.FilterVal)
63 | }
64 | }
65 | if filterQuery.PageSize != 0 {
66 | realIndex := 0
67 | if filterQuery.PageIndex > 0 {
68 | realIndex = filterQuery.PageIndex - 1
69 | }
70 | query = query.Limit(filterQuery.PageSize, filterQuery.PageSize*realIndex)
71 | }
72 | if _, err := query.RelatedSel().All(&rulelist); err != nil {
73 | return nil, err
74 | }
75 | count, err := query.Count()
76 | if err != nil {
77 | return nil, err
78 | }
79 | return &utils.QueryResult{
80 | Base: utils.PageInfo{
81 | TotalNum: count,
82 | PageIndex: filterQuery.PageIndex,
83 | PageSize: filterQuery.PageSize,
84 | },
85 | List: rulelist}, err
86 | }
87 |
88 | func (im *IngressRuleModel) GetByID(cluster, namespace string, id int64) (*models.K8sIngressRule, error) {
89 | var rule models.K8sIngressRule
90 |
91 | if err := im.tOrmer.QueryTable(im.TableName).
92 | Filter("id", id).
93 | Filter("cluster", cluster).
94 | Filter("namespace", namespace).
95 | Filter("deleted", 0).RelatedSel().One(&rule); err != nil {
96 | return nil, err
97 | }
98 | return &rule, nil
99 | }
100 |
101 | func (im *IngressRuleModel) CheckHostUnique(cluster, namespace, host string) error {
102 | //check the host is existed in other cluster or other namespace
103 | filter := &utils.FilterQuery{}
104 | filter.FilterKey = "host"
105 | filter.FilterVal = host
106 | filterCluster := ""
107 | if beego.AppConfig.String("other::hostUniqueMode") == SIMPLE_UNIQUE_MODE {
108 | filterCluster = cluster
109 | }
110 | res, err := im.List(filterCluster, nil, "", filter)
111 | if err != nil {
112 | return err
113 | }
114 | ownerList := []hostOwner{}
115 | for _, item := range res.List.([]models.K8sIngressRule) {
116 | if item.Cluster != cluster || item.Namespace != namespace {
117 | ownerList = append(ownerList, hostOwner{cluster: item.Cluster, namespace: item.Namespace})
118 | }
119 | }
120 | if len(ownerList) > 0 {
121 | beego.Warn(fmt.Sprintf("domain name(%s) is existed in cluster(%v)!", host, ownerList))
122 | return fmt.Errorf("domain name(%s) is existed in cluster!", host)
123 | }
124 | return nil
125 | }
126 |
127 | //just check path is unique under host
128 | func (im *IngressRuleModel) CheckPathsUniqueInHost(cluster, namespace, host string, paths []string, id int64) error {
129 | //check the host is existed in other cluster or other namespace
130 | filter := &utils.FilterQuery{}
131 | filter.FilterKey = "host"
132 | filter.FilterVal = host
133 | res, err := im.List(cluster, []string{namespace}, "", filter)
134 | if err != nil {
135 | return err
136 | }
137 | for _, path := range paths {
138 | for _, item := range res.List.([]models.K8sIngressRule) {
139 | if utils.PathsIsEqual(item.Path, path) {
140 | if (id > 0 && item.Id != id) || id < 0 {
141 | return fmt.Errorf("path(%s) is existed in domain(%s)!", utils.GetRootPath(path), host)
142 | }
143 | }
144 | }
145 | }
146 | return nil
147 | }
148 |
149 | //just check path is unique under host
150 | func (im *IngressRuleModel) GetIngressNameByHost(cluster, namespace, host string) string {
151 | var rule models.K8sIngressRule
152 |
153 | if err := im.tOrmer.QueryTable(im.TableName).
154 | Filter("cluster", cluster).
155 | Filter("namespace", namespace).
156 | Filter("host", host).
157 | Filter("deleted", 0).RelatedSel().One(&rule); err != nil {
158 | return ""
159 | }
160 | return rule.IngressName
161 | }
162 |
--------------------------------------------------------------------------------
/backend/dao/k8s_namespace.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "fmt"
5 | "kubecloud/common/utils"
6 |
7 | "kubecloud/backend/models"
8 |
9 | "github.com/astaxie/beego/orm"
10 | )
11 |
12 | var nsEnableFilterKeys = []string{
13 | "cluster",
14 | "name",
15 | "desc",
16 | "cpu_quota",
17 | "memory_quota",
18 | }
19 |
20 | type NamespaceModel struct {
21 | tOrmer orm.Ormer
22 | TableName string
23 | }
24 |
25 | func NewNamespaceModel() *NamespaceModel {
26 | return &NamespaceModel{
27 | tOrmer: GetOrmer(),
28 | TableName: (&models.K8sNamespace{}).TableName(),
29 | }
30 | }
31 |
32 | func NamespaceList(clusters []string, names []string, filterQuery *utils.FilterQuery) (*utils.QueryResult, error) {
33 | var rows []models.K8sNamespace
34 | PageIndex := 0
35 | PageSize := 0
36 | realIndex := 0
37 | queryCond := orm.NewCondition().And("deleted", 0)
38 | defFilter := utils.NewDefaultFilter().AppendFilter("cluster", clusters, utils.FilterOperatorIn).
39 | AppendFilter("name", names, utils.FilterOperatorIn)
40 | normalCond := defFilter.DefaultFilterCondition()
41 | queryCond = queryCond.AndCond(normalCond)
42 | if filterQuery != nil {
43 | filterCond := filterQuery.FilterCondition(nsEnableFilterKeys)
44 | if filterCond != nil {
45 | queryCond = queryCond.AndCond(filterCond)
46 | }
47 | if filterQuery.PageSize != 0 {
48 | if filterQuery.PageIndex > 0 {
49 | realIndex = filterQuery.PageIndex - 1
50 | }
51 | }
52 | PageIndex = filterQuery.PageIndex
53 | PageSize = filterQuery.PageSize
54 | }
55 | query := GetOrmer().QueryTable("k8s_namespace").OrderBy("-updated_at").SetCond(queryCond)
56 | if PageSize != 0 {
57 | query = query.Limit(PageSize, PageSize*realIndex)
58 | }
59 | if _, err := query.All(&rows); err != nil {
60 | return nil, err
61 | }
62 | count, err := query.Count()
63 | if err != nil {
64 | return nil, err
65 | }
66 | return &utils.QueryResult{
67 | Base: utils.PageInfo{
68 | TotalNum: count,
69 | PageIndex: PageIndex,
70 | PageSize: PageSize,
71 | },
72 | List: rows}, err
73 | }
74 |
75 | func GetClusterNamespaceList(cluster string) ([]string, error) {
76 | var nsList []string
77 | _, err := GetOrmer().QueryTable("k8s_namespace").Filter("cluster", cluster).All(&nsList, "name")
78 | return nsList, err
79 | }
80 |
81 | func NamespaceGet(cluster string, name string) (*models.K8sNamespace, error) {
82 | var row models.K8sNamespace
83 | err := GetOrmer().
84 | QueryTable("k8s_namespace").
85 | Filter("cluster", cluster).
86 | Filter("name", name).
87 | Filter("deleted", 0).
88 | One(&row)
89 | return &row, err
90 | }
91 |
92 | func NamespaceInsert(row *models.K8sNamespace) error {
93 | _, err := GetOrmer().Insert(row)
94 | return err
95 | }
96 |
97 | func NamespaceUpdate(row *models.K8sNamespace) error {
98 | _, err := GetOrmer().Update(row)
99 | return err
100 | }
101 |
102 | func NamespaceDelete(cluster, namespace string) error {
103 | ormer := GetOrmer()
104 | if ormer.QueryTable("zcloud_application").
105 | Filter("cluster", cluster).
106 | Filter("namespace", namespace).
107 | Filter("deleted", 0).Exist() {
108 | return fmt.Errorf("can't delete a namesapce which still has running applications")
109 | }
110 | if ormer.QueryTable("zcloud_job").
111 | Filter("cluster", cluster).
112 | Filter("namespace", namespace).
113 | Filter("deleted", 0).Exist() {
114 | return fmt.Errorf("can't delete a namesapce which still has running jobs")
115 | }
116 | row, err := NamespaceGet(cluster, namespace)
117 | if err != nil {
118 | return err
119 | }
120 | row.MarkDeleted()
121 | return NamespaceUpdate(row)
122 | }
123 |
124 | func NamespaceExists(cluster, namespace string) bool {
125 | return GetOrmer().
126 | QueryTable("k8s_namespace").
127 | Filter("cluster", cluster).
128 | Filter("name", namespace).
129 | Filter("deleted", 0).
130 | Exist()
131 | }
132 |
--------------------------------------------------------------------------------
/backend/dao/k8s_secret.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/astaxie/beego/orm"
7 |
8 | "kubecloud/backend/models"
9 | "kubecloud/common/utils"
10 | )
11 |
12 | type SecretModel struct {
13 | ormer orm.Ormer
14 | TableName string
15 | }
16 |
17 | var secretEnableFilterKeys = map[string]interface{}{
18 | "namespace": nil,
19 | "name": nil,
20 | "description": nil,
21 | "owner_kind": nil,
22 | "owner_name": nil,
23 | "type": nil,
24 | "creator": nil,
25 | "create_at": nil,
26 | }
27 |
28 | func NewSecretModel() *SecretModel {
29 | return &SecretModel{
30 | ormer: GetOrmer(),
31 | TableName: (&models.K8sSecret{}).TableName(),
32 | }
33 | }
34 |
35 | func (sm *SecretModel) Exists(cluster, namespace, name string) bool {
36 | return sm.ormer.QueryTable(sm.TableName).
37 | Filter("cluster", cluster).
38 | Filter("namespace", namespace).
39 | Filter("name", name).
40 | Filter("deleted", 0).
41 | Exist()
42 | }
43 |
44 | func (sm *SecretModel) CreateSecret(secret models.K8sSecret, setAddon bool) (err error) {
45 | if setAddon {
46 | secret.Addons = models.NewAddons()
47 | }
48 | _, err = sm.ormer.Insert(&secret)
49 | return
50 | }
51 |
52 | func (sm *SecretModel) UpdateSecret(old, cur models.K8sSecret, setAddon bool) (err error) {
53 | cur.Id = old.Id
54 | if setAddon {
55 | cur.Addons = old.Addons.UpdateAddons()
56 | }
57 | _, err = sm.ormer.Update(&cur)
58 | return
59 | }
60 |
61 | func (sm *SecretModel) DeleteSecret(cluster, namespace, name string) error {
62 | sql := "delete from " + sm.TableName + " where cluster=? and namespace=? and name=?"
63 | _, err := sm.ormer.Raw(sql, cluster, namespace, name).Exec()
64 |
65 | return err
66 | }
67 |
68 | func (sm *SecretModel) GetSecret(cluster, namespace, name string) (*models.K8sSecret, error) {
69 | var secret models.K8sSecret
70 |
71 | if err := sm.ormer.QueryTable(sm.TableName).
72 | Filter("cluster", cluster).
73 | Filter("namespace", namespace).
74 | Filter("name", name).
75 | Filter("deleted", 0).One(&secret); err != nil {
76 | return nil, err
77 | }
78 |
79 | return &secret, nil
80 | }
81 |
82 | func (sm *SecretModel) GetSecretList(cluster string, nslist []string, filterQuery *utils.FilterQuery) (*utils.QueryResult, error) {
83 | secretList := []models.K8sSecret{}
84 | var err error
85 | query := sm.ormer.QueryTable(sm.TableName).
86 | Filter("cluster", cluster).
87 | Filter("deleted", 0).OrderBy("-create_at")
88 | if len(nslist) >= 1 {
89 | query = query.Filter("namespace__in", nslist)
90 | } else {
91 | return nil, fmt.Errorf("namespace must be given!")
92 | }
93 | if filterQuery.FilterVal != "" {
94 | if _, exist := secretEnableFilterKeys[filterQuery.FilterKey]; exist {
95 | query = query.Filter(filterQuery.FilterKey+"__icontains", filterQuery.FilterVal)
96 | }
97 | }
98 | if filterQuery.PageSize != 0 {
99 | realIndex := 0
100 | if filterQuery.PageIndex > 0 {
101 | realIndex = filterQuery.PageIndex - 1
102 | }
103 | query = query.Limit(filterQuery.PageSize, filterQuery.PageSize*realIndex)
104 | }
105 | if _, err := query.All(&secretList); err != nil {
106 | return nil, err
107 | }
108 | count, err := query.Count()
109 | if err != nil {
110 | return nil, err
111 | }
112 | return &utils.QueryResult{
113 | Base: utils.PageInfo{
114 | TotalNum: count,
115 | PageIndex: filterQuery.PageIndex,
116 | PageSize: filterQuery.PageSize,
117 | },
118 | List: secretList}, err
119 | }
120 |
--------------------------------------------------------------------------------
/backend/dao/k8s_service.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "fmt"
5 | models "kubecloud/backend/models"
6 | "kubecloud/common"
7 |
8 | "github.com/astaxie/beego"
9 | "github.com/astaxie/beego/orm"
10 | )
11 |
12 | type K8sServiceModel struct {
13 | tOrmer orm.Ormer
14 | svcTable string
15 | portTable string
16 | }
17 |
18 | func NewK8sServiceModel() *K8sServiceModel {
19 | return &K8sServiceModel{
20 | tOrmer: GetOrmer(),
21 | svcTable: (&models.K8sService{}).TableName(),
22 | portTable: (&models.K8sServicePort{}).TableName(),
23 | }
24 | }
25 |
26 | func (km *K8sServiceModel) Create(svc models.K8sService) error {
27 | svc.Addons = models.NewAddons()
28 | _, err := km.tOrmer.Insert(&svc)
29 | if err != nil {
30 | return err
31 | }
32 | var ports []models.K8sServicePort
33 | for _, item := range svc.Ports {
34 | port := *item
35 | port.Service = &svc
36 | port.Addons = models.NewAddons()
37 | ports = append(ports, port)
38 | }
39 | _, err = km.tOrmer.InsertMulti(len(ports), ports)
40 | return err
41 | }
42 |
43 | func (km *K8sServiceModel) Update(oldsvc, newsvc models.K8sService) error {
44 | if err := km.updateService(oldsvc, newsvc); err != nil {
45 | return err
46 | }
47 | for _, np := range newsvc.Ports {
48 | index := -1
49 | for i, op := range oldsvc.Ports {
50 | if np.Name == op.Name {
51 | index = i
52 | break
53 | }
54 | }
55 | if index >= 0 {
56 | // update
57 | if np.Port != oldsvc.Ports[index].Port ||
58 | np.TargetPort != oldsvc.Ports[index].TargetPort ||
59 | np.Protocol != oldsvc.Ports[index].Protocol ||
60 | np.NodePort != oldsvc.Ports[index].NodePort {
61 | if err := km.updatePort(oldsvc.Ports[index], np); err != nil {
62 | return err
63 | }
64 | }
65 | } else {
66 | // create
67 | if err := km.insertPort(np, &newsvc); err != nil {
68 | return err
69 | }
70 | }
71 | }
72 | // delete old other
73 | for _, op := range oldsvc.Ports {
74 | index := -1
75 | for i, np := range newsvc.Ports {
76 | if op.Name == np.Name {
77 | index = i
78 | break
79 | }
80 | }
81 | if index == -1 {
82 | err := km.deletePort(op.Id)
83 | if err != nil {
84 | beego.Error(err)
85 | }
86 | }
87 | }
88 |
89 | return nil
90 | }
91 |
92 | func (km *K8sServiceModel) Delete(cluster, namespace, name string) error {
93 | beego.Debug("delete service: %v", name)
94 | // delete svc
95 | if err := km.deleteService(cluster, namespace, name); err != nil {
96 | return err
97 | }
98 | // delete ports
99 | if err := km.deletePorts(cluster, namespace, name); err != nil {
100 | return err
101 | }
102 | return nil
103 | }
104 |
105 | func (km *K8sServiceModel) List(cluster, namespace string, nodePort int) ([]models.K8sServicePort, error) {
106 | var svcs []models.K8sServicePort
107 |
108 | query := km.tOrmer.QueryTable(km.portTable).
109 | Filter("cluster", cluster).
110 | Filter("deleted", 0)
111 | if namespace != common.AllNamespace {
112 | query = query.Filter("namespace", namespace)
113 | }
114 | if nodePort != 0 {
115 | query = query.Filter("node_port", nodePort)
116 | }
117 | if _, err := query.RelatedSel().All(&svcs); err != nil {
118 | return nil, err
119 | }
120 |
121 | return svcs, nil
122 | }
123 |
124 | func (km *K8sServiceModel) Get(cluster, namespace, oname, name string) (*models.K8sService, error) {
125 | var svc models.K8sService
126 | if oname == "" && name == "" {
127 | return nil, fmt.Errorf("service name or owner name must be given!")
128 | }
129 | query := km.tOrmer.QueryTable(km.svcTable).
130 | Filter("cluster", cluster).
131 | Filter("deleted", 0)
132 | if namespace != "" && namespace != common.AllNamespace {
133 | query = query.Filter("namespace", namespace)
134 | }
135 | if oname != "" {
136 | query = query.Filter("owner_name", oname)
137 | }
138 | if name != "" {
139 | query = query.Filter("name", name)
140 | }
141 | if err := query.One(&svc); err != nil {
142 | return nil, err
143 | }
144 | if _, err := km.tOrmer.LoadRelated(&svc, "ports"); err != nil {
145 | return nil, err
146 | }
147 | var ports []*models.K8sServicePort
148 | for _, port := range svc.Ports {
149 | if port.Deleted == 0 {
150 | ports = append(ports, port)
151 | }
152 | }
153 | svc.Ports = ports
154 |
155 | return &svc, nil
156 | }
157 |
158 | func (km *K8sServiceModel) updateService(oldsvc, newsvc models.K8sService) error {
159 | newsvc.Addons = oldsvc.Addons.UpdateAddons()
160 | _, err := km.tOrmer.Update(&newsvc)
161 | return err
162 | }
163 |
164 | func (km *K8sServiceModel) deleteService(cluster, namespace, name string) error {
165 | sql := "delete from " + km.svcTable + " where cluster=? and namespace=? and name=?"
166 | _, err := km.tOrmer.Raw(sql, cluster, namespace, name).Exec()
167 | return err
168 | }
169 |
170 | func (km *K8sServiceModel) insertPort(port *models.K8sServicePort, svc *models.K8sService) error {
171 | port.Service = svc
172 | port.Addons = models.NewAddons()
173 | _, err := km.tOrmer.Insert(port)
174 | return err
175 | }
176 |
177 | func (km *K8sServiceModel) updatePort(oldport, newport *models.K8sServicePort) error {
178 | newport.Id = oldport.Id
179 | newport.Addons = oldport.Addons.UpdateAddons()
180 | newport.Service = oldport.Service
181 | _, err := km.tOrmer.Update(newport)
182 | return err
183 | }
184 |
185 | func (km *K8sServiceModel) deletePorts(cluster, namespace, name string) error {
186 | sql := "delete from " + km.portTable + " where cluster=? and namespace=? and service_name=?"
187 | _, err := km.tOrmer.Raw(sql, cluster, namespace, name).Exec()
188 | return err
189 | }
190 |
191 | func (km *K8sServiceModel) deletePort(id int64) error {
192 | sql := "delete from " + km.portTable + " where id=?"
193 | _, err := km.tOrmer.Raw(sql, id).Exec()
194 | return err
195 | }
196 |
--------------------------------------------------------------------------------
/backend/dao/node.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/astaxie/beego/orm"
7 |
8 | "kubecloud/backend/models"
9 | "kubecloud/common/utils"
10 | )
11 |
12 | var nodeEnableFilterKeys = []string{
13 | "name",
14 | "department",
15 | "bizcluster",
16 | "status",
17 | }
18 |
19 | func CheckNodeExist(cluster, ip string) bool {
20 | ormer := GetOrmer()
21 | return ormer.QueryTable("zcloud_node").
22 | Filter("cluster", cluster).
23 | Filter("name", ip).Exist()
24 | }
25 |
26 | func CreateNode(node models.ZcloudNode) error {
27 | ormer := GetOrmer()
28 | if ormer.QueryTable("zcloud_node").
29 | Filter("cluster", node.Cluster).
30 | Filter("name", node.Name).Exist() {
31 | return fmt.Errorf("node(%s) is existed!", node.Name)
32 | }
33 |
34 | if _, err := ormer.Insert(&node); err != nil {
35 | return fmt.Errorf("database error, %s", err.Error())
36 | }
37 | return nil
38 | }
39 |
40 | func UpdateNode(node models.ZcloudNode) error {
41 | ormer := GetOrmer()
42 | if !ormer.QueryTable("zcloud_node").
43 | Filter("cluster", node.Cluster).
44 | Filter("name", node.Name).Exist() {
45 | return fmt.Errorf("node(%s) is not existed!", node.Name)
46 | }
47 |
48 | _, err := ormer.Update(&node)
49 |
50 | return err
51 | }
52 |
53 | func DeleteNode(cluster, name string) error {
54 | _, err := GetOrmer().Raw("delete from zcloud_node where cluster=? AND name=?", cluster, name).Exec()
55 | return err
56 | }
57 |
58 | func DeleteNodesByClusterName(clusterId string) error {
59 | _, err := GetOrmer().Raw("delete from zcloud_node where cluster=?", clusterId).Exec()
60 | return err
61 | }
62 |
63 | func GetNodeList(cluster string, filterQuery *utils.FilterQuery) (*utils.QueryResult, error) {
64 | var nodes []*models.ZcloudNode
65 | queryCond := orm.NewCondition()
66 | if cluster != "" {
67 | queryCond = queryCond.And("cluster", cluster)
68 | }
69 | if filterQuery != nil {
70 | filterCond := filterQuery.FilterCondition(nodeEnableFilterKeys)
71 | if filterCond != nil {
72 | queryCond = queryCond.AndCond(filterCond)
73 | }
74 | }
75 |
76 | query := GetOrmer().QueryTable("zcloud_node").OrderBy("-create_at").SetCond(queryCond)
77 |
78 | count, err := query.Count()
79 | if err != nil {
80 | return nil, err
81 | }
82 |
83 | if filterQuery != nil && filterQuery.PageSize != 0 && filterQuery.PageIndex > 0 {
84 | query = query.Limit(filterQuery.PageSize, filterQuery.PageSize*(filterQuery.PageIndex-1))
85 | }
86 | if _, err := query.All(&nodes); err != nil {
87 | return nil, err
88 | }
89 | return &utils.QueryResult{
90 | Base: utils.PageInfo{
91 | TotalNum: count,
92 | PageIndex: filterQuery.PageIndex,
93 | PageSize: filterQuery.PageSize,
94 | },
95 | List: nodes}, err
96 | }
97 | func GetNodeByName(cluster, name string) (*models.ZcloudNode, error) {
98 | var node models.ZcloudNode
99 | if err := GetOrmer().QueryTable("zcloud_node").
100 | Filter("cluster", cluster).
101 | Filter("name", name).One(&node); err != nil {
102 | if err == orm.ErrMultiRows {
103 | return nil, fmt.Errorf("node %s has multi rows", name)
104 | }
105 | return nil, err
106 | }
107 |
108 | return &node, nil
109 | }
110 |
111 | func GetNodeByIP(cluster, nodeIP string) (*models.ZcloudNode, error) {
112 | var node models.ZcloudNode
113 | if err := GetOrmer().QueryTable("zcloud_node").
114 | Filter("cluster", cluster).
115 | Filter("ip", nodeIP).One(&node); err != nil {
116 | if err == orm.ErrMultiRows {
117 | return nil, fmt.Errorf("node %s has multi rows", nodeIP)
118 | }
119 | if err == orm.ErrNoRows {
120 | return nil, fmt.Errorf("node %s no row found", nodeIP)
121 | }
122 | return nil, err
123 | }
124 |
125 | return &node, nil
126 | }
127 |
128 | func GetNodeListByStatus(cluster string, department string, status string) ([]*models.ZcloudNode, error) {
129 | qs := GetOrmer().QueryTable("zcloud_node")
130 | if cluster != "" {
131 | qs = qs.Filter("cluster", cluster)
132 | }
133 | if department != "all" {
134 | qs = qs.Filter("department__in", department)
135 | }
136 | if status != "all" {
137 | qs = qs.Filter("status", status)
138 | }
139 | var nodes []*models.ZcloudNode
140 | if _, err := qs.All(&nodes); err != nil {
141 | return nil, err
142 | }
143 | return nodes, nil
144 | }
145 |
--------------------------------------------------------------------------------
/backend/dao/template.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "fmt"
5 | models "kubecloud/backend/models"
6 |
7 | "kubecloud/common/utils"
8 |
9 | "github.com/astaxie/beego/orm"
10 | )
11 |
12 | type TemplateModel struct {
13 | tOrmer orm.Ormer
14 | TableName string
15 | }
16 |
17 | var templateEnableFilterKeys = map[string]interface{}{
18 | "namespace": nil,
19 | "name": nil,
20 | "description": nil,
21 | "creator": nil,
22 | "create_at": nil,
23 | }
24 |
25 | func NewTemplateModel() *TemplateModel {
26 | return &TemplateModel{
27 | tOrmer: GetOrmer(),
28 | TableName: (&models.ZcloudTemplate{}).TableName(),
29 | }
30 | }
31 |
32 | func (tm *TemplateModel) CreateTemplate(template models.ZcloudTemplate) (*models.ZcloudTemplate, error) {
33 | template.Addons = models.NewAddons()
34 | _, err := tm.tOrmer.Insert(&template)
35 | if err != nil {
36 | return nil, err
37 | }
38 |
39 | return tm.GetTemplate(template.Namespace, template.Name)
40 | }
41 |
42 | func (tm *TemplateModel) UpdateTemplate(template models.ZcloudTemplate) error {
43 | template.Addons = template.Addons.UpdateAddons()
44 | _, err := tm.tOrmer.Update(&template)
45 |
46 | return err
47 | }
48 |
49 | func (tm *TemplateModel) DeleteTemplate(namespace, name string) error {
50 | sql := "update " + tm.TableName + " set deleted=1, delete_at=now() where namespace=? and name=? and deleted=0"
51 | _, err := tm.tOrmer.Raw(sql, namespace, name).Exec()
52 |
53 | return err
54 | }
55 |
56 | func (tm *TemplateModel) GetTemplate(namespace, name string) (*models.ZcloudTemplate, error) {
57 | var template models.ZcloudTemplate
58 |
59 | if err := tm.tOrmer.QueryTable(tm.TableName).
60 | Filter("namespace", namespace).
61 | Filter("name", name).
62 | Filter("deleted", 0).One(&template); err != nil {
63 | return nil, err
64 | }
65 |
66 | return &template, nil
67 | }
68 |
69 | func (tm *TemplateModel) GetTemplateByID(templateId int64) (*models.ZcloudTemplate, error) {
70 | var template models.ZcloudTemplate
71 |
72 | if err := tm.tOrmer.QueryTable(tm.TableName).
73 | Filter("id", templateId).
74 | Filter("deleted", 0).One(&template); err != nil {
75 | return nil, err
76 | }
77 |
78 | return &template, nil
79 | }
80 |
81 | func (tm *TemplateModel) GetTemplateList(nslist []string, filterQuery *utils.FilterQuery) (*utils.QueryResult, error) {
82 | tList := []models.ZcloudTemplate{}
83 |
84 | query := tm.tOrmer.QueryTable(tm.TableName).
85 | Filter("deleted", 0).OrderBy("-create_at")
86 | if len(nslist) >= 1 {
87 | query = query.Filter("namespace__in", nslist)
88 | } else {
89 | return nil, fmt.Errorf("namespace must be given!")
90 | }
91 | if filterQuery.FilterVal != "" {
92 | if _, exist := templateEnableFilterKeys[filterQuery.FilterKey]; exist {
93 | query = query.Filter(filterQuery.FilterKey+"__icontains", filterQuery.FilterVal)
94 | }
95 | }
96 | if filterQuery.PageSize != 0 {
97 | realIndex := 0
98 | if filterQuery.PageIndex > 0 {
99 | realIndex = filterQuery.PageIndex - 1
100 | }
101 | query = query.Limit(filterQuery.PageSize, filterQuery.PageSize*realIndex)
102 | }
103 | if _, err := query.All(&tList); err != nil {
104 | return nil, err
105 | }
106 | count, err := query.Count()
107 | if err != nil {
108 | return nil, err
109 | }
110 | return &utils.QueryResult{
111 | Base: utils.PageInfo{
112 | TotalNum: count,
113 | PageIndex: filterQuery.PageIndex,
114 | PageSize: filterQuery.PageSize,
115 | },
116 | List: tList}, err
117 | }
118 |
--------------------------------------------------------------------------------
/backend/models/application.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | type ZcloudApplication struct {
4 | Id int64 `orm:"pk;column(id);auto" json:"id"`
5 | Name string `orm:"column(name)" json:"name"`
6 | Version string `orm:"column(version)" json:"version"`
7 | // pod_version is deployment version: deployment_name=app_name+pod_version, if pod_version is not empty
8 | // or version when pod_version is empty
9 | PodVersion string `orm:"column(pod_version)" json:"pod_version"`
10 | BaseName string `orm:"column(base_name)" json:"base_name"` //saas name
11 | Env string `orm:"column(env)" json:"env"`
12 | DomainName string `orm:"column(domain_name)" json:"domain_name"`
13 | Kind string `orm:"column(kind)" json:"kind"`
14 | TemplateName string `orm:"column(template_name)" json:"template_name"`
15 | Cluster string `orm:"column(cluster)" json:"cluster"`
16 | Namespace string `orm:"column(namespace)" json:"namespace"`
17 | Replicas int `orm:"column(replicas)" json:"replicas"`
18 | Image string `orm:"column(image)" json:"image"`
19 | Template string `orm:"column(template);type(text)" json:"template,omitempty"`
20 | LabelSelector string `orm:"column(label_selector)" json:"label_selector,omitempty"`
21 | InjectServiceMesh string `orm:"column(inject_service_mesh)" json:"inject_service_mesh,omitempty"`
22 | StatusReplicas int32 `orm:"column(status_replicas)" json:"status_replicas"`
23 | ReadyReplicas int32 `orm:"column(ready_replicas)" json:"ready_replicas"`
24 | UpdatedReplicas int32 `orm:"column(updated_replicas)" json:"updated_replicas"`
25 | AvailableReplicas int32 `orm:"column(available_replicas)" json:"available_replicas"`
26 | AvailableStatus string `orm:"column(available_status)" json:"available_status"`
27 | Message string `orm:"column(message)" json:"message"`
28 | DeployStatus string `orm:"column(deploy_status)" json:"deploy_status"`
29 | DefaultDomainAddr string `orm:"column(default_domain_addr)" json:"default_domain_addr"`
30 | Labels string `orm:"column(labels);type(text)" json:"labels"`
31 | Addons
32 | }
33 |
34 | func (t *ZcloudApplication) TableName() string {
35 | return "zcloud_application"
36 | }
37 |
--------------------------------------------------------------------------------
/backend/models/appversion.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | type ZcloudVersion struct {
4 | Id int64 `orm:"pk;column(id);auto" json:"id"`
5 | Cluster string `orm:"column(cluster)" json:"cluster"`
6 | Namespace string `orm:"column(namespace)" json:"namespace"`
7 | Name string `orm:"column(name)" json:"name"` // name is application name// name is application name
8 | Kind string `orm:"column(kind)" json:"kind"`
9 | Version string `orm:"column(version)" json:"version"`
10 | PodVersion string `orm:"column(pod_version)" json:"pod_version"`
11 | Weight int `orm:"column(weight)" json:"weight"`
12 | Stage string `orm:"column(stage)" json:"stage"` // new or normal
13 | Replicas int `orm:"column(replicas)" json:"replicas"`
14 | CurReplicas int `orm:"column(cur_replicas)" json:"cur_replicas"`
15 | TemplateName string `orm:"column(template_name)" json:"template_name"`
16 | Image string `orm:"column(image)" json:"image"`
17 | Template string `orm:"column(template);type(text)" json:"template,omitempty"`
18 | InjectServiceMesh string `orm:"column(inject_service_mesh)" json:"inject_service_mesh,omitempty"`
19 | Addons
20 | }
21 |
22 | const (
23 | STAGE_NEW = "new"
24 | STAGE_NORMAL = "normal"
25 | MIN_WEIGHT = 0
26 | DEF_INIT_WEIGHT = 10
27 | MAX_WEIGHT = 100
28 | DEFAULT_WEIGHT = 1
29 | INIT_CUR_REPLICAS = 1
30 | )
31 |
32 | func (t *ZcloudVersion) TableName() string {
33 | return "zcloud_version"
34 | }
35 |
--------------------------------------------------------------------------------
/backend/models/cluster.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | const (
4 | ClusterStatusToBeConfigured string = "ToBeConfigured"
5 | ClusterStatusPending string = "Pending"
6 | ClusterStatusRunning string = "Running"
7 | ClusterStatusError string = "Error"
8 | ClusterStatusUpdating string = "Updating"
9 | )
10 |
11 | type ZcloudCluster struct {
12 | Id int64 `orm:"pk;column(id);auto" json:"id"`
13 | Name string `orm:"column(name)" json:"name"`
14 | ClusterId string `orm:"column(cluster_id)" json:"cluster_id"`
15 | Tenant string `orm:"column(tenant)" json:"tenant"`
16 | Env string `orm:"column(env)" json:"env"`
17 | Usage string `orm:"column(usage)" json:"usage"`
18 | DisplayName string `orm:"column(display_name)" json:"display_name"`
19 | Registry string `orm:"column(registry)" json:"registry"`
20 | RegistryName string `orm:"column(registry_name)" json:"registry_name"`
21 | ImagePullAddr string `orm:"column(image_pull_addr)" json:"image_pull_addr"`
22 | DockerVersion string `orm:"column(docker_version)" json:"docker_version"`
23 | NetworkPlugin string `orm:"column(network_plugin)" json:"network_plugin"`
24 | DomainSuffix string `orm:"column(domain_suffix)" json:"domain_suffix"`
25 | Certificate string `orm:"column(certificate);type(text)" json:"certificate"`
26 | PrometheusAddr string `orm:"column(prometheus_addr)" json:"prometheus_addr"`
27 | Status string `orm:"column(status)" json:"status"`
28 | KubeVersion string `orm:"column(kube_version)" json:"kube_version"`
29 | LoadbalancerDomainName string `orm:"column(lb_name)" json:"loadbalancer_domain_name"`
30 | LoadbalancerIP string `orm:"column(lb_ip)" json:"loadbalancer_ip"`
31 | LoadbalancerPort string `orm:"column(lb_port)" json:"loadbalancer_port"`
32 | KubeServiceAddress string `orm:"column(kube_service_addr)" json:"kube_service_address"`
33 | KubePodSubnet string `orm:"column(kube_pods_subnet)" json:"kube_pod_subnet"`
34 | TillerHost string `orm:"column(tiller_host)" json:"tiller_host"`
35 | PromRuleIndex int64 `orm:"column(prom_rule_index);default(0)" json:"prom_rule_index"`
36 | IngressSLB string `orm:"column(ingress_slb)" json:"ingress_slb"`
37 | LabelPrefix string `orm:"column(label_prefix)" json:"label_prefix"`
38 | ConfigRepo string `orm:"column(config_repo)" json:"config_repo"`
39 | ConfigRepoBranch string `orm:"column(config_repo_branch)" json:"config_repo_branch"`
40 | ConfigRepoToken string `orm:"column(config_repo_token)" json:"config_repo_token"`
41 | LastCommitId string `orm:"column(last_commit_id)" json:"last_commit_id"`
42 | Addons
43 | }
44 |
45 | func (t *ZcloudCluster) TableName() string {
46 | return "zcloud_cluster"
47 | }
48 |
49 | type ZcloudClusterDomainSuffix struct {
50 | Id int64 `orm:"pk;column(id);auto" json:"id"`
51 | Cluster string `orm:"column(cluster)" json:"cluster"`
52 | DomainSuffix string `orm:"column(domain_suffix)" json:"domain_suffix"`
53 | IsDefault bool `orm:"column(is_default)" json:"is_default"`
54 | Addons
55 | }
56 |
57 | func (t *ZcloudClusterDomainSuffix) TableName() string {
58 | return "zcloud_cluster_domain_suffix"
59 | }
60 |
--------------------------------------------------------------------------------
/backend/models/common.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | "time"
7 |
8 | "kubecloud/backend/service"
9 |
10 | "github.com/astaxie/beego/orm"
11 | "github.com/go-sql-driver/mysql"
12 | )
13 |
14 | // NOTE: should perfer AddonsUnix, Addons will be deprecated in future
15 |
16 | // AddonsUnix ...
17 | type AddonsUnix struct {
18 | Deleted int8 `orm:"column(deleted)" json:"deleted"`
19 | CreatedAt int64 `orm:"column(created_at)" json:"created_at"`
20 | UpdatedAt int64 `orm:"column(updated_at)" json:"updated_at"`
21 | DeletedAt *int64 `orm:"column(deleted_at);null" json:"deleted_at"`
22 | }
23 |
24 | // NewAddonsUnix ...
25 | func NewAddonsUnix() AddonsUnix {
26 | now := time.Now().Unix()
27 | return AddonsUnix{
28 | Deleted: 0,
29 | CreatedAt: now,
30 | UpdatedAt: now,
31 | DeletedAt: nil,
32 | }
33 | }
34 |
35 | // MarkUpdated ...
36 | func (a *AddonsUnix) MarkUpdated() {
37 | now := time.Now().Unix()
38 | a.UpdatedAt = now
39 | }
40 |
41 | // MarkDeleted ...
42 | func (a *AddonsUnix) MarkDeleted() {
43 | now := time.Now().Unix()
44 | a.DeletedAt = &now
45 | a.Deleted = 1
46 | }
47 |
48 | type Addons struct {
49 | Deleted int8 `orm:"column(deleted)" json:"deleted"`
50 | CreateAt time.Time `orm:"column(create_at)" json:"create_at"`
51 | UpdateAt time.Time `orm:"column(update_at)" json:"update_at"`
52 | DeleteAt time.Time `orm:"column(delete_at);null" json:"delete_at"`
53 | }
54 |
55 | func NewAddonsWithCreationTimestamp(timestamp time.Time) Addons {
56 | timeNow := timestamp
57 | timeDel, _ := time.Parse("2006-01-02 15:04:05", time.Unix(0, 0).Local().Format("2006-01-02 15:04:05"))
58 | return Addons{
59 | Deleted: 0,
60 | CreateAt: timeNow,
61 | UpdateAt: timeNow,
62 | DeleteAt: timeDel,
63 | }
64 | }
65 |
66 | func NewAddons() Addons {
67 | timeNow, _ := time.Parse("2006-01-02 15:04:05", time.Now().Local().Format("2006-01-02 15:04:05"))
68 | timeDel, _ := time.Parse("2006-01-02 15:04:05", time.Unix(0, 0).Local().Format("2006-01-02 15:04:05"))
69 | return Addons{
70 | Deleted: 0,
71 | CreateAt: timeNow,
72 | UpdateAt: timeNow,
73 | DeleteAt: timeDel,
74 | }
75 | }
76 |
77 | func (ons Addons) UpdateAddons() Addons {
78 | ons.CreateAt, _ = time.Parse("2006-01-02 15:04:05", ons.CreateAt.Format("2006-01-02 15:04:05"))
79 | ons.UpdateAt, _ = time.Parse("2006-01-02 15:04:05", time.Now().Local().Format("2006-01-02 15:04:05"))
80 | return ons
81 | }
82 |
83 | func (ons Addons) FormatAddons() Addons {
84 | ons.CreateAt, _ = time.Parse("2006-01-02 15:04:05", ons.CreateAt.Format("2006-01-02 15:04:05"))
85 | ons.UpdateAt, _ = time.Parse("2006-01-02 15:04:05", ons.UpdateAt.Format("2006-01-02 15:04:05"))
86 | return ons
87 | }
88 |
89 | type HardAddons struct {
90 | CreateAt time.Time `orm:"column(create_at)" json:"create_at"`
91 | UpdateAt time.Time `orm:"column(update_at)" json:"update_at"`
92 | }
93 |
94 | func NewHardAddons() HardAddons {
95 | timeNow, _ := time.Parse("2006-01-02 15:04:05", time.Now().Local().Format("2006-01-02 15:04:05"))
96 | return HardAddons{
97 | CreateAt: timeNow,
98 | UpdateAt: timeNow,
99 | }
100 | }
101 |
102 | var (
103 | dbName string
104 | tableNames []string
105 | )
106 |
107 | func initOrm() {
108 | config := service.GetAppConfig()
109 | DatabaseDebug, _ := config.Bool("DB::databaseDebug")
110 | DefaultRowsLimit, _ := config.Int("DB::defaultRowsLimit")
111 | DatabaseUrl := config.String("DB::databaseUrl")
112 |
113 | if cfg, err := mysql.ParseDSN(DatabaseUrl); err == nil {
114 | dbName = cfg.DBName
115 | }
116 |
117 | orm.Debug = DatabaseDebug
118 | if DefaultRowsLimit != 0 {
119 | orm.DefaultRowsLimit = DefaultRowsLimit
120 | }
121 |
122 | if err := orm.RegisterDriver("mysql", orm.DRMySQL); err != nil {
123 | panic(fmt.Sprintf(`failed to register driver, error: "%s"`, err.Error()))
124 | }
125 | if err := orm.RegisterDataBase("default", "mysql", DatabaseUrl); err != nil {
126 | panic(fmt.Sprintf(`failed to register database, error: "%s", url: "%s"`, err.Error(), DatabaseUrl))
127 | }
128 | registerModel := func(models ...interface{}) {
129 | tableNames = make([]string, len(models))
130 | for i, model := range models {
131 | obj := model.(interface {
132 | TableName() string
133 | })
134 | tableNames[i] = obj.TableName()
135 | }
136 | orm.RegisterModel(models...)
137 | }
138 | registerModel(
139 | new(ZcloudCluster),
140 | new(ZcloudNode),
141 | new(ZcloudHarbor),
142 | new(ZcloudHarborProject),
143 | new(ZcloudHarborRepository),
144 | new(K8sSecret),
145 | new(ZcloudEvent),
146 | new(ZcloudTemplate),
147 | new(ZcloudApplication),
148 | new(ZcloudVersion),
149 | new(K8sIngress),
150 | new(K8sIngressRule),
151 | new(K8sService),
152 | new(K8sServicePort),
153 | new(K8sEndpoint),
154 | new(K8sEndpointAddress),
155 | new(K8sNamespace),
156 | new(ZcloudRepositoryTag),
157 | new(ZcloudClusterDomainSuffix),
158 | )
159 | }
160 |
161 | func Init() {
162 | initOrm()
163 | orm.RunSyncdb("default", false, true)
164 | }
165 |
166 | func InitMock() {
167 | initOrm()
168 |
169 | // check database name and clear all tables
170 | if !strings.Contains(dbName, "test") {
171 | panic(fmt.Sprintf(`invalid database: "%v"`, dbName))
172 | }
173 |
174 | orm.RunSyncdb("default", false, true)
175 |
176 | ormer := orm.NewOrm()
177 | for _, name := range tableNames {
178 | if _, err := ormer.Raw(fmt.Sprintf("truncate table %v", name)).Exec(); err != nil {
179 | panic(err)
180 | }
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/backend/models/event.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "time"
5 | )
6 |
7 | type ZcloudEvent struct {
8 | ID int64 `orm:"pk;column(id);auto" json:"id"`
9 | EventUid string `orm:"column(event_uid);size(36)" json:"event_uid"`
10 | ActionType string `orm:"column(action_type);size(10)" json:"action_type"`
11 | EventType string `orm:"column(event_type);size(10)" json:"event_type"`
12 | Cluster string `orm:"column(cluster)" json:"cluster"`
13 | Namespace string `orm:"column(namespace);size(100)" json:"namespace"`
14 | SourceComponent string `orm:"column(source_component);size(50)" json:"source_component"`
15 | SourceHost string `orm:"column(source_host);size(20)" json:"source_host"`
16 | ObjectKind string `orm:"column(object_kind);size(20)" json:"object_kind"`
17 | ObjectName string `orm:"column(object_name);size(100)" json:"object_name"`
18 | ObjectUid string `orm:"column(object_uid);size(36)" json:"object_uid"`
19 | FieldPath string `orm:"column(field_path);size(200)" json:"field_path"`
20 | Reason string `orm:"column(reason);size(100)" json:"reason"`
21 | Message string `orm:"column(message);type(text)" json:"message"`
22 | Count int32 `orm:"column(count)" json:"count"`
23 | FirstTimestamp time.Time `orm:"column(first_time)" json:"first_time"`
24 | LastTimestamp time.Time `orm:"column(last_time);index" json:"last_time"`
25 | }
26 |
27 | func (t *ZcloudEvent) TableName() string {
28 | return "zcloud_event"
29 | }
30 |
--------------------------------------------------------------------------------
/backend/models/harbor.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "fmt"
5 | "sort"
6 |
7 | validate "kubecloud/common/validate"
8 | )
9 |
10 | type ZcloudHarbor struct {
11 | ID int64 `orm:"pk;column(id);auto" json:"id"`
12 | HarborId string `orm:"column(harbor_id)" json:"harbor_id"`
13 | Tenant string `orm:"column(tenant)" json:"-"`
14 | HarborName string `orm:"column(harbor_name);size(100)" json:"harbor_name"`
15 | HarborAddr string `orm:"column(harbor_addr);size(255)" json:"harbor_addr"`
16 | HarborDesc string `orm:"column(harbor_desc);size(255)" json:"harbor_desc"`
17 | HarborUser string `orm:"column(harbor_user);size(255)" json:"harbor_user"`
18 | HarborPassword string `orm:"column(harbor_password);size(255)" json:"harbor_password"`
19 | HarborAuth string `orm:"column(harbor_auth);size(255)" json:"-"`
20 | HarborHTTPSMode bool `orm:"column(harbor_https_mode);size(255)" json:"harbor_https_mode"`
21 | HarborVersion string `orm:"column(harbor_version);size(255)" json:"harbor_version"`
22 | Addons
23 | }
24 |
25 | func (t *ZcloudHarbor) TableName() string {
26 | return "zcloud_harbor"
27 | }
28 |
29 | type HarborReq struct {
30 | Tenant string `json:"tenant"`
31 | HarborId string `json:"harbor_id"`
32 | HarborName string `json:"harbor_name"`
33 | HarborDesc string `json:"harbor_desc"`
34 | HarborAddr string `json:"harbor_addr"`
35 | HarborUser string `json:"harbor_user"`
36 | HarborPassword string `json:"harbor_password"`
37 | HarborHTTPSMode bool `json:"harbor_https_mode"`
38 | HarborVersion string `json:"harbor_version"`
39 | }
40 |
41 | func (hr *HarborReq) Verify(harborReq *HarborReq) error {
42 | if err := validate.ValidateString(harborReq.HarborName); err != nil {
43 | return fmt.Errorf("harbor_name erro: %v", err.Error())
44 | }
45 | if err := validate.ValidateString(harborReq.Tenant); err != nil {
46 | return fmt.Errorf("harbor's tenant is not right: %v", err.Error())
47 | }
48 | return nil
49 | }
50 |
51 | type ZcloudHarborProject struct {
52 | ID int64 `orm:"pk;column(id);auto" json:"id"`
53 | Harbor string `orm:"column(harbor)" json:"harbor"`
54 | ProjectID int `orm:"column(project_id)" json:"project_id"`
55 | ProjectName string `orm:"column(project_name)" json:"project_name"`
56 | ProjectPublic int `orm:"column(project_public)" json:"project_public"`
57 | RepoCount int `orm:"column(repo_count)" json:"repo_count"`
58 | }
59 |
60 | func (t *ZcloudHarborProject) TableName() string {
61 | return "zcloud_harbor_project"
62 | }
63 |
64 | func (u *ZcloudHarborProject) TableUnique() [][]string {
65 | return [][]string{
66 | []string{"Harbor", "ProjectID", "ProjectName"},
67 | }
68 | }
69 |
70 | type ZcloudHarborRepository struct {
71 | ID int64 `orm:"pk;column(id);auto" json:"id"`
72 | Harbor string `orm:"column(harbor)" json:"harbor"`
73 | ProjectID int `orm:"column(project_id)" json:"project_id"`
74 | ProjectName string `orm:"column(project_name)" json:"project_name"`
75 | ProjectPublic int `orm:"column(project_public)" json:"project_public"`
76 | RepositoryName string `orm:"column(repository_name)" json:"repository_name"`
77 | PullCount int `orm:"column(pull_count)" json:"pull_count"`
78 | TagsCount int `orm:"column(tags_count)" json:"tags_count"`
79 | }
80 |
81 | func (t *ZcloudHarborRepository) TableName() string {
82 | return "zcloud_harbor_repository"
83 | }
84 |
85 | func (u *ZcloudHarborRepository) TableUnique() [][]string {
86 | return [][]string{
87 | []string{"Harbor", "ProjectID", "ProjectName", "RepositoryName"},
88 | }
89 | }
90 |
91 | type ZcloudRepositoryTag struct {
92 | ID int64 `orm:"pk;column(id);auto" json:"id"`
93 | Harbor string `orm:"column(harbor)" json:"harbor"`
94 | RepositoryName string `orm:"column(repository_name)" json:"repository_name"`
95 | Tag string `orm:"column(tag)" json:"tag"`
96 | CodeBranch string `orm:"column(code_branch)" json:"code_branch"`
97 | Labels string `orm:"column(labels)" json:"labels"`
98 | CreateAt string `orm:"column(create_at)" json:"create_at"`
99 | }
100 |
101 | func (tag *ZcloudRepositoryTag) TableName() string {
102 | return "zcloud_repository_tag"
103 | }
104 |
105 | func (u *ZcloudRepositoryTag) TableUnique() [][]string {
106 | return [][]string{
107 | []string{"Harbor", "RepositoryName", "Tag"},
108 | }
109 | }
110 |
111 | type RepositoryTagSortBy func(o1, o2 *ZcloudRepositoryTag) bool
112 |
113 | func (by RepositoryTagSortBy) Sort(list []*ZcloudRepositoryTag) {
114 | ts := &repositoryTagSorter{
115 | Objects: list,
116 | by: by,
117 | }
118 | sort.Sort(ts)
119 | }
120 |
121 | type repositoryTagSorter struct {
122 | Objects []*ZcloudRepositoryTag
123 | by func(o1, o2 *ZcloudRepositoryTag) bool
124 | }
125 |
126 | func (s *repositoryTagSorter) Len() int {
127 | return len(s.Objects)
128 | }
129 |
130 | func (s *repositoryTagSorter) Swap(i, j int) {
131 | s.Objects[i], s.Objects[j] = s.Objects[j], s.Objects[i]
132 | }
133 |
134 | func (s *repositoryTagSorter) Less(i, j int) bool {
135 | return s.by(s.Objects[i], s.Objects[j])
136 | }
137 |
--------------------------------------------------------------------------------
/backend/models/k8s_endpoint.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | type K8sEndpoint struct {
4 | Id int64 `orm:"pk;column(id);auto"`
5 | Name string `orm:"column(name)"`
6 | Cluster string `orm:"column(cluster)"`
7 | Namespace string `orm:"column(namespace)"`
8 | OwnerName string `orm:"column(owner_name)"`
9 | // port is unique: port is service port's target_port
10 | Port int32 `orm:"column(port)"`
11 | PortName string `orm:"column(port_name)"`
12 | Protocol string `orm:"column(protocol)"`
13 | Addresses []*K8sEndpointAddress `orm:"reverse(many);column(addresses)"` //设置一对多关系
14 | Addons
15 | }
16 |
17 | func (t *K8sEndpoint) TableName() string {
18 | return "k8s_endpoint"
19 | }
20 |
21 | func (u *K8sEndpoint) TableUnique() [][]string {
22 | return [][]string{
23 | []string{"Cluster", "Namespace", "Name"},
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/backend/models/k8s_endpoint_address.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | type K8sEndpointAddress struct {
4 | Id int64 `orm:"pk;column(id);auto"`
5 | Cluster string `orm:"column(cluster)"`
6 | Namespace string `orm:"column(namespace)"`
7 | EndpointName string `orm:"column(endpoint_name)"`
8 | IP string `orm:"column(ip)"`
9 | NodeName string `orm:"column(node_name)"`
10 | TargetRefName string `orm:"column(target_ref_name)"`
11 | Endpoint *K8sEndpoint `orm:"rel(fk);column(endpoint)"`
12 | Addons
13 | }
14 |
15 | func (t *K8sEndpointAddress) TableName() string {
16 | return "k8s_endpoint_address"
17 | }
18 |
--------------------------------------------------------------------------------
/backend/models/k8s_ingress.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | type K8sIngress struct {
4 | Id int64 `orm:"pk;column(id);auto"`
5 | // name is unique
6 | Name string `orm:"column(name)"`
7 | Namespace string `orm:"column(namespace)"`
8 | Cluster string `orm:"column(cluster)"`
9 | Rules []*K8sIngressRule `orm:"reverse(many);column(rules)"`
10 | Annotation string `orm:"column(annotation);type(text)"`
11 | Addons
12 | }
13 |
14 | func (t *K8sIngress) TableName() string {
15 | return "k8s_ingress"
16 | }
17 |
18 | func (u *K8sIngress) TableUnique() [][]string {
19 | return [][]string{
20 | []string{"Cluster", "Namespace", "Name"},
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/backend/models/k8s_ingress_rule.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | type K8sIngressRule struct {
4 | Id int64 `orm:"pk;column(id);auto" json:"id"`
5 | Cluster string `orm:"column(cluster)" json:"cluster"`
6 | Namespace string `orm:"column(namespace)" json:"namespace"`
7 | IngressName string `orm:"column(ingress_name)" json:"ingress_name,omitempty"`
8 | // host is unique
9 | Host string `orm:"column(host)" json:"host,omitempty"`
10 | Path string `orm:"column(path)" json:"path,omitempty"`
11 | SecretName string `orm:"column(secret_name)" json:"secret_name"`
12 | IsTls bool `orm:"column(is_tls)" json:"is_tls,omitempty"`
13 | ServiceName string `orm:"column(service_name)" json:"service_name"`
14 | ServicePort int `orm:"column(service_port)" json:"service_port"`
15 | Ingress *K8sIngress `orm:"rel(fk);column(ingress)" json:"-"`
16 | Addons
17 | }
18 |
19 | func (t *K8sIngressRule) TableName() string {
20 | return "k8s_ingress_rule"
21 | }
22 |
23 | func (u *K8sIngressRule) TableUnique() [][]string {
24 | return [][]string{
25 | []string{"Cluster", "Namespace", "IngressName"},
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/backend/models/k8s_namespace.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | type K8sNamespace struct {
4 | ID int64 `orm:"pk;column(id);auto" json:"id"`
5 | Cluster string `orm:"column(cluster);index" json:"cluster"`
6 | Name string `orm:"column(name);index" json:"name"`
7 | Desc string `orm:"column(desc)" json:"desc"`
8 | CPUQuota string `orm:"column(cpu_quota)" json:"cpu_quota"`
9 | MemoryQuota string `orm:"column(memory_quota)" json:"memory_quota"`
10 | AddonsUnix
11 | }
12 |
13 | func (t *K8sNamespace) TableName() string {
14 | return "k8s_namespace"
15 | }
16 |
--------------------------------------------------------------------------------
/backend/models/k8s_secret.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | type K8sSecret struct {
4 | Id int64 `orm:"pk;column(id);auto" json:"id"`
5 | Name string `orm:"column(name)" json:"name"`
6 | Cluster string `orm:"column(cluster)" json:"cluster"`
7 | OwnerName string `orm:"column(owner_name)"`
8 | Namespace string `orm:"column(namespace)" json:"namespace"`
9 | Description string `orm:"column(description)" json:"description,omitempty"`
10 | Type string `orm:"column(type)" json:"type,omitempty"`
11 | Data string `orm:"column(data);type(text)" json:"data,omitempty"`
12 | Addons
13 | }
14 |
15 | func (t *K8sSecret) TableName() string {
16 | return "k8s_secret"
17 | }
18 |
19 | func (u *K8sSecret) TableUnique() [][]string {
20 | return [][]string{
21 | []string{"Cluster", "Namespace", "Name"},
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/backend/models/k8s_service.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | type K8sService struct {
4 | Id int64 `orm:"pk;column(id);auto"`
5 | Name string `orm:"column(name)"`
6 | Cluster string `orm:"column(cluster)"`
7 | Namespace string `orm:"column(namespace)"`
8 | OwnerName string `orm:"column(owner_name)"`
9 | Type string `orm:"column(type)"`
10 | ClusterIP string `orm:"column(cluster_ip)"`
11 | Ports []*K8sServicePort `orm:"reverse(many);column(ports)"`
12 | Annotation string `orm:"column(annotation);type(text)"`
13 | Addons
14 | }
15 |
16 | func (t *K8sService) TableName() string {
17 | return "k8s_service"
18 | }
19 |
20 | func (u *K8sService) TableUnique() [][]string {
21 | return [][]string{
22 | []string{"Cluster", "Namespace", "Name"},
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/backend/models/k8s_service_port.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | type K8sServicePort struct {
4 | Id int64 `orm:"pk;column(id);auto"`
5 | Name string `orm:"column(name)"`
6 | Cluster string `orm:"column(cluster)"`
7 | Namespace string `orm:"column(namespace)"`
8 | ServiceName string `orm:"column(service_name)"`
9 | Protocol string `orm:"column(protocol)"`
10 | Port int `orm:"column(port)"`
11 | TargetPort int `orm:"column(target_port)"`
12 | NodePort int `orm:"column(node_port)"`
13 | Service *K8sService `orm:"rel(fk);column(service)"` //设置一对多关系
14 | Addons
15 | }
16 |
17 | func (t *K8sServicePort) TableName() string {
18 | return "k8s_service_port"
19 | }
20 |
21 | func (u *K8sServicePort) TableUnique() [][]string {
22 | return [][]string{
23 | []string{"Cluster", "Namespace", "ServiceName", "Port"},
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/backend/models/node.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | const (
4 | NodeStatusPending string = "Pending"
5 | NodeStatusRunning string = "Running"
6 | NodeStatusOffline string = "Offline"
7 | NodeStatusError string = "Error"
8 | )
9 |
10 | type ZcloudNode struct {
11 | Id int64 `orm:"pk;column(id);auto" json:"id"`
12 | Name string `orm:"column(name)" json:"name"`
13 | Cluster string `orm:"column(cluster)" json:"cluster"`
14 | Department string `orm:"column(department)" json:"department"`
15 | BizCluster string `orm:"column(bizcluster)" json:"bizcluster"`
16 | IP string `orm:"column(ip)" json:"ip"`
17 | PodCidr string `orm:"column(pod_cidr)" json:"pod_cidr"`
18 | Labels string `orm:"column(labels);size(2048)" json:"labels"`
19 | CPU int64 `orm:"column(cpu)" json:"cpu"`
20 | CPURequests int64 `orm:"column(cpu_requests)" json:"cpu_requests"`
21 | CPULimits int64 `orm:"column(cpu_limits)" json:"cpu_limits"`
22 | Memory int64 `orm:"column(memory)" json:"memory"`
23 | MemoryRequests int64 `orm:"column(memory_requests)" json:"memory_requests"`
24 | MemoryLimits int64 `orm:"column(memory_limits)" json:"memory_limits"`
25 | Status string `orm:"column(status)" json:"status"`
26 | InitStatus string `orm:"column(init_status)" json:"init_status"`
27 | Creator string `orm:"column(creator)" json:"creator"`
28 | BizLabelStatus string `orm:"column(biz_label_status)" json:"biz_label_status"`
29 | DeployMode string `orm:"column(deploy_mode)" json:"deploy_mode"`
30 | DeployError string `orm:"column(deploy_err)" json:"deploy_err"`
31 | Monitor bool `orm:"column(monitor);default(false)" json:"monitor"`
32 | Influxdb bool `orm:"column(influxdb);default(false)" json:"influxdb"`
33 | Ingress bool `orm:"column(ingress);default(false)" json:"ingress"`
34 | External bool `orm:"column(external);default(false)" json:"external"`
35 | HardAddons
36 | }
37 |
38 | const (
39 | BizLabelSetWaiting string = "waiting"
40 | BizLabelSetFinished string = ""
41 | )
42 |
43 | func (u *ZcloudNode) TableUnique() [][]string {
44 | return [][]string{
45 | []string{"Name", "Cluster"},
46 | }
47 | }
48 |
49 | func (t *ZcloudNode) TableName() string {
50 | return "zcloud_node"
51 | }
52 |
--------------------------------------------------------------------------------
/backend/models/template.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | type ZcloudTemplate struct {
4 | Id int64 `orm:"pk;column(id);auto" json:"id"`
5 | Name string `orm:"column(name)" json:"name"`
6 | Namespace string `orm:"column(namespace)" json:"namespace"`
7 | Description string `orm:"column(description)" json:"description,omitempty"`
8 | Spec string `orm:"column(spec);type(text)" json:"spec"` //TemplateSpec
9 | Kind string `orm:"column(kind)" json:"kind"`
10 | Addons
11 | }
12 |
13 | func (t *ZcloudTemplate) TableName() string {
14 | return "zcloud_template"
15 | }
16 |
--------------------------------------------------------------------------------
/backend/resource/apptemplate.go:
--------------------------------------------------------------------------------
1 | package resource
2 |
3 | import (
4 | "fmt"
5 | "reflect"
6 | "strings"
7 | "sync"
8 |
9 | "kubecloud/backend/models"
10 |
11 | "github.com/astaxie/beego"
12 | yamlencoder "github.com/ghodss/yaml"
13 | )
14 |
15 | const TemplateMaxSize = 1024000 //1000KB
16 |
17 | //single app template interface
18 | //simpleapp and nativeapp template support this interface
19 | type AppTemplate interface {
20 | GenerateAppObject(cluster, namespace, tplname, domainSuffix string) (*models.ZcloudApplication, error)
21 | UpdateAppObject(app *models.ZcloudApplication, domainSuffix string) error
22 | GenerateKubeObject(cluster, namespace, podVersion, domainSuffix string) (map[string]interface{}, error)
23 | GetAppName() string
24 | GetAppKind() string
25 | GetAppVersion() string
26 | String() (string, error)
27 | Image(param []ContainerParam) AppTemplate
28 | DefaultLabel() AppTemplate
29 | Replicas(replicas int) AppTemplate
30 | IsInjectServiceMesh() bool
31 | }
32 |
33 | type WorkerResult struct {
34 | AppName string
35 | AppVersion string
36 | AppKind string
37 | Result error
38 | }
39 |
40 | func CreateAppTemplateByApp(app models.ZcloudApplication) (AppTemplate, error) {
41 | return CreateNativeAppTemplate(app, "", nil)
42 | }
43 |
44 | func DeployAppTemplates(appTplList []AppTemplate, projectid int64, cluster, namespace, tname string, eparam *ExtensionParam) error {
45 | if len(appTplList) == 0 {
46 | return nil
47 | }
48 | errInfoList := []string{}
49 | var workers []*DeployWorker
50 | ar, err := NewAppRes(cluster, nil)
51 | if err != nil {
52 | return err
53 | }
54 | workerResult := make(chan WorkerResult)
55 | var wg sync.WaitGroup
56 | for _, tpl := range appTplList {
57 | wg.Add(1)
58 | wk := NewDeployWorker(tpl.GetAppName(), namespace, tpl.GetAppKind(), ar, eparam, tpl)
59 | workers = append(workers, wk)
60 | param := AppParam{Name: tpl.GetAppName()}
61 | go func(app AppTemplate) {
62 | defer wg.Done()
63 | err := wk.Start(tname, param)
64 | workerResult <- WorkerResult{
65 | AppName: app.GetAppName(),
66 | AppVersion: app.GetAppVersion(),
67 | AppKind: app.GetAppKind(),
68 | Result: err,
69 | }
70 | }(tpl)
71 | }
72 | go func() {
73 | wg.Wait()
74 | close(workerResult)
75 | }()
76 | for res := range workerResult {
77 | if res.Result != nil {
78 | errInfoList = append(errInfoList, res.AppName+":"+res.Result.Error())
79 | beego.Error(res.Result)
80 | } else {
81 | beego.Info("deploy application " + res.AppName + " successfully!")
82 | }
83 | }
84 | if len(errInfoList) != 0 {
85 | return fmt.Errorf(strings.Join(errInfoList, ";"))
86 | }
87 |
88 | return nil
89 | }
90 |
91 | func AppTemplateToYamlString(tpl AppTemplate, cluster, namespace, podVersion, domainSuffix string) (string, error) {
92 | objs, err := tpl.GenerateKubeObject(cluster, namespace, podVersion, domainSuffix)
93 | if err != nil && objs == nil {
94 | beego.Error("generate kubernetes object failed:", err)
95 | return "", err
96 | }
97 | ctx := []byte{}
98 | elems := []reflect.Value{}
99 | for _, obj := range objs {
100 | v := reflect.ValueOf(obj)
101 | switch v.Kind() {
102 | case reflect.Ptr:
103 | elems = append(elems, v)
104 | case reflect.Slice, reflect.Array:
105 | for i := 0; i < v.Len(); i++ {
106 | elems = append(elems, v.Index(i))
107 | }
108 | default:
109 | beego.Debug("object kind:", v.Kind())
110 | }
111 | }
112 | for _, elem := range elems {
113 | yamlBytes, err := yamlencoder.Marshal(elem.Interface())
114 | if err != nil {
115 | beego.Error("yaml marshal object failed:", err)
116 | }
117 | ctx = append(ctx, yamlBytes...)
118 | ctx = append(ctx, []byte(YamlSeparator)...)
119 | }
120 |
121 | return strings.TrimSuffix(string(ctx), YamlSeparator), nil
122 | }
123 |
--------------------------------------------------------------------------------
/backend/resource/configmap.go:
--------------------------------------------------------------------------------
1 | package resource
2 |
3 | import (
4 | "fmt"
5 | "github.com/astaxie/beego"
6 | corev1 "k8s.io/api/core/v1"
7 | "k8s.io/apimachinery/pkg/api/errors"
8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9 | "kubecloud/backend/util/labels"
10 | "sort"
11 | // "k8s.io/apimachinery/pkg/fields"
12 |
13 | "kubecloud/backend/service"
14 | "kubecloud/common"
15 | "kubecloud/common/keyword"
16 | "kubecloud/common/validate"
17 | "kubecloud/gitops"
18 | )
19 |
20 | type ConfigMapVolume struct {
21 | Name string `json:"name,omitempty"`
22 | Items []string `json:"items,omitempty"`
23 | }
24 |
25 | type ConfigMaps []corev1.ConfigMap
26 |
27 | func (c ConfigMaps) Len() int { return len(c) }
28 | func (c ConfigMaps) Less(i, j int) bool {
29 | return c[i].ObjectMeta.CreationTimestamp.Time.After(c[j].ObjectMeta.CreationTimestamp.Time)
30 | }
31 | func (c ConfigMaps) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
32 |
33 | func ConfigMapCreate(cluster string, configMap *corev1.ConfigMap) (*corev1.ConfigMap, error) {
34 | if ok, err := configMapValidator(configMap); !ok {
35 | return nil, err
36 | }
37 | go gitops.CommitK8sResource(cluster, []interface{}{configMap})
38 | check := func(param interface{}) error {
39 | _, err := ConfigMapInspect(cluster, configMap.Namespace, configMap.Name)
40 | if errors.IsNotFound(err) {
41 | return fmt.Errorf("waiting to create......")
42 | } else {
43 | return nil
44 | }
45 | }
46 | result := make(chan error)
47 | go func() {
48 | result <- WaitSync(nil, SYNC_CHECK_STEP*10, SYNC_TIMEOUT*10, check)
49 | }()
50 | err := <-result
51 | return configMap, err
52 | }
53 |
54 | func ConfigMapList(cluster string, namespace string) ([]corev1.ConfigMap, error) {
55 | client, err := service.GetClientset(cluster)
56 | if err != nil {
57 | beego.Error(fmt.Sprintf("Get ConfigMap list error: %v", err.Error()))
58 | return nil, common.NewInternalServerError().SetCause(err)
59 | }
60 | if namespace == "all" {
61 | namespace = corev1.NamespaceAll
62 | }
63 | configMaps := ConfigMaps{}
64 | configMapList, err := client.CoreV1().ConfigMaps(namespace).List(metav1.ListOptions{})
65 | if err != nil {
66 | beego.Error(fmt.Sprintf("Get ConfigMap list error: %v", err.Error()))
67 | return nil, common.NewInternalServerError().SetCause(err)
68 | }
69 | configMaps = configMapList.Items
70 | sort.Sort(configMaps)
71 | return configMaps, nil
72 | }
73 |
74 | func ConfigMapInspect(cluster, namespace, name string) (*corev1.ConfigMap, error) {
75 | client, err := service.GetClientset(cluster)
76 | if err != nil {
77 | beego.Error(fmt.Sprintf("Get ConfigMap inspect error: %v", err.Error()))
78 | return nil, err
79 | }
80 | configMap, err := client.CoreV1().ConfigMaps(namespace).Get(name, metav1.GetOptions{})
81 | if err != nil {
82 | beego.Error(fmt.Sprintf("Get ConfigMap inspect error: %v", err.Error()))
83 | return nil, err
84 | }
85 | return configMap, nil
86 | }
87 |
88 | func ConfigMapUpdate(cluster, namespace, name string, configData *corev1.ConfigMap) (*corev1.ConfigMap, error) {
89 | configMap, err := ConfigMapInspect(cluster, namespace, name)
90 | if err != nil {
91 | beego.Error(fmt.Sprintf("Update ConfigMap error: %v", err.Error()))
92 | return nil, common.NewInternalServerError().SetCause(err)
93 | }
94 | configMap.Data = configData.Data
95 | if ok, err := configMapValidator(configMap); !ok {
96 | return nil, err
97 | }
98 | go gitops.CommitK8sResource(cluster, []interface{}{configMap})
99 | return configMap, nil
100 | }
101 |
102 | func ConfigMapDelete(cluster, namespace, name string) error {
103 | configMap, err := ConfigMapInspect(cluster, namespace, name)
104 | if err != nil {
105 | beego.Error(fmt.Sprintf("Delete ConfigMap error: %v", err.Error()))
106 | return common.NewInternalServerError().SetCause(err)
107 | }
108 | configMap.ObjectMeta.Annotations = labels.AddLabel(configMap.ObjectMeta.Annotations, keyword.DELETE_LABLE, keyword.DELETE_LABLE_VALUE)
109 | go gitops.CommitK8sResource(cluster, []interface{}{configMap})
110 | check := func(param interface{}) error {
111 | _, err = ConfigMapInspect(cluster, namespace, name)
112 | if errors.IsNotFound(err) {
113 | return nil
114 | } else {
115 | return fmt.Errorf("waiting to delete......")
116 | }
117 | }
118 | result := make(chan error)
119 | go func() {
120 | result <- WaitSync(nil, SYNC_CHECK_STEP*10, SYNC_TIMEOUT*10, check)
121 | }()
122 | err = <-result
123 | return err
124 | }
125 |
126 | func configMapValidator(configMap *corev1.ConfigMap) (bool, error) {
127 | if validate.IsChineseChar(configMap.Name) {
128 | return false, common.NewBadRequest().
129 | SetCode("NotSupportChineseChar").
130 | SetMessage("not support chinese char")
131 | }
132 | for key, _ := range configMap.Data {
133 | if validate.IsChineseChar(key) {
134 | return false, common.NewBadRequest().
135 | SetCode("NotSupportChineseChar").
136 | SetMessage("not support chinese char")
137 | }
138 | }
139 | return true, nil
140 | }
141 |
--------------------------------------------------------------------------------
/backend/resource/deployment.go:
--------------------------------------------------------------------------------
1 | package resource
2 |
3 | import (
4 | "fmt"
5 | "kubecloud/common/keyword"
6 | "kubecloud/gitops"
7 | "strconv"
8 | "time"
9 |
10 | "github.com/astaxie/beego"
11 | "k8s.io/api/apps/v1beta1"
12 | apiv1 "k8s.io/api/core/v1"
13 | "k8s.io/apimachinery/pkg/api/errors"
14 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
15 | "k8s.io/client-go/kubernetes"
16 | "kubecloud/backend/util/labels"
17 | )
18 |
19 | type KubeAppInterface interface {
20 | CreateOrUpdate(obj interface{}) (interface{}, error)
21 | Status(appname, podVersion string) (*AppStatus, error)
22 | Delete(obj interface{}) (interface{}, error)
23 | AppIsExisted(appname, podVersion string) (bool, error)
24 | Scale(obj interface{}, replicas int) error
25 | Restart(obj interface{}) error
26 | GetOwnerForPod(pod apiv1.Pod, ref *metav1.OwnerReference) interface{}
27 | }
28 |
29 | type AppStatus struct {
30 | ReadyReplicas int32
31 | UpdatedReplicas int32
32 | AvailableReplicas int32
33 | AvailableStatus string
34 | Message string
35 | }
36 |
37 | type DeploymentRes struct {
38 | Cluster string
39 | Namespace string
40 | client kubernetes.Interface
41 | }
42 |
43 | func NewDeploymentRes(client kubernetes.Interface, cluster, namespace string) KubeAppInterface {
44 | return &DeploymentRes{
45 | Cluster: cluster,
46 | Namespace: namespace,
47 | client: client,
48 | }
49 | }
50 |
51 | func (kr *DeploymentRes) CreateOrUpdate(obj interface{}) (interface{}, error) {
52 | dp, ok := obj.(*v1beta1.Deployment)
53 | if !ok {
54 | return nil, fmt.Errorf("can not generate deployment object!")
55 | }
56 | beego.Info("creating or updating deployment, " + dp.Name)
57 | //go gitops.CommitK8sResource(kr.Cluster, []interface{}{dp})
58 | return dp, nil
59 | }
60 |
61 | func (kr *DeploymentRes) Status(appname, suffix string) (*AppStatus, error) {
62 | deployment, err := kr.client.AppsV1beta1().Deployments(kr.Namespace).Get(GenerateDeployName(appname, suffix), metav1.GetOptions{})
63 | if err != nil {
64 | return nil, err
65 | }
66 | status := &AppStatus{
67 | ReadyReplicas: deployment.Status.ReadyReplicas,
68 | AvailableReplicas: deployment.Status.AvailableReplicas,
69 | UpdatedReplicas: deployment.Status.UpdatedReplicas,
70 | }
71 | for _, condition := range deployment.Status.Conditions {
72 | if condition.Type == v1beta1.DeploymentAvailable {
73 | status.AvailableStatus = string(condition.Status)
74 | status.Message = condition.Message
75 | break
76 | }
77 | }
78 | return status, nil
79 | }
80 |
81 | func (kr *DeploymentRes) Delete(obj interface{}) (interface{}, error) {
82 | dp, ok := obj.(*v1beta1.Deployment)
83 | if !ok {
84 | return nil, fmt.Errorf("can not generate deployment object!")
85 | }
86 | dp.ObjectMeta.Annotations = labels.AddLabel(dp.ObjectMeta.Annotations, keyword.DELETE_LABLE, keyword.DELETE_LABLE_VALUE)
87 | //go gitops.CommitK8sResource(kr.Cluster, []interface{}{dp})
88 | beego.Warn(fmt.Sprintf("delete deployment %s successfully!", dp.Name))
89 | return dp, nil
90 | }
91 |
92 | func (kr *DeploymentRes) AppIsExisted(appname, suffix string) (bool, error) {
93 | name := GenerateDeployName(appname, suffix)
94 | _, err := kr.client.AppsV1beta1().Deployments(kr.Namespace).Get(name, metav1.GetOptions{})
95 | if err != nil {
96 | if !errors.IsNotFound(err) {
97 | return false, err
98 | } else {
99 | return false, nil
100 | }
101 | }
102 | return true, nil
103 | }
104 |
105 | func (kr *DeploymentRes) Scale(obj interface{}, replicas int) error {
106 | dp, ok := obj.(*v1beta1.Deployment)
107 | if !ok {
108 | return fmt.Errorf("can not generate deployment object!")
109 | }
110 | num := int32(replicas)
111 | if *dp.Spec.Replicas == num {
112 | return nil
113 | }
114 | dp.Spec.Replicas = &num
115 | go gitops.CommitK8sResource(kr.Cluster, []interface{}{dp})
116 | return nil
117 | }
118 |
119 | func (kr *DeploymentRes) Restart(obj interface{}) error {
120 | dp, ok := obj.(*v1beta1.Deployment)
121 | if !ok {
122 | return fmt.Errorf("can not generate deployment object!")
123 | }
124 | dp.Spec.Template.ObjectMeta.Annotations = labels.AddLabel(dp.Spec.Template.ObjectMeta.Annotations, keyword.RESTART_LABLE, strconv.FormatInt(time.Now().Unix(), 10))
125 | go gitops.CommitK8sResource(kr.Cluster, []interface{}{dp})
126 | return nil
127 | }
128 |
129 | func (kr *DeploymentRes) GetOwnerForPod(pod apiv1.Pod, ref *metav1.OwnerReference) interface{} {
130 | if ref == nil {
131 | return nil
132 | }
133 | rs, err := kr.client.ExtensionsV1beta1().ReplicaSets(pod.Namespace).Get(ref.Name, metav1.GetOptions{})
134 | if err != nil || rs.UID != ref.UID {
135 | beego.Warn(fmt.Sprintf("Cannot get replicaset %s for pod %s: %v", ref.Name, pod.Name, err))
136 | return nil
137 | }
138 | // Now find the Deployment that owns that ReplicaSet.
139 | depRef := metav1.GetControllerOf(rs)
140 | if depRef == nil {
141 | return nil
142 | }
143 | // We can't look up by UID, so look up by Name and then verify UID.
144 | // Don't even try to look up by Name if it's the wrong Kind.
145 | if depRef.Kind != v1beta1.SchemeGroupVersion.WithKind("Deployment").Kind {
146 | return nil
147 | }
148 | d, err := kr.client.AppsV1beta1().Deployments(pod.Namespace).Get(depRef.Name, metav1.GetOptions{})
149 | if err != nil {
150 | return nil
151 | }
152 | if d.UID != depRef.UID {
153 | return nil
154 | }
155 | return d
156 | }
157 |
--------------------------------------------------------------------------------
/backend/resource/event.go:
--------------------------------------------------------------------------------
1 | package resource
2 |
3 | import (
4 | "kubecloud/backend/dao"
5 | "time"
6 | )
7 |
8 | type eventInfo struct {
9 | ClusterName string `json:"cluster_name"`
10 | Namespace string `json:"namespace"`
11 | EventLevel string `json:"event_level"`
12 | ObjectKind string `json:"object_kind"`
13 | ObjectName string `json:"object_name"`
14 | ObjectPhase string `json:"object_phase"`
15 | ObjectMessage string `json:"object_message"`
16 | SourceHost string `json:"source_host"`
17 | LastTime time.Time `json:"last_time"`
18 | }
19 |
20 | func GetEvents(clusterName, namespace, sourceHost, objectKind, objectName, eventLevel string, limitCount int64) ([]eventInfo, error) {
21 | eventsInfo := []eventInfo{}
22 | events, err := dao.GetEvents(clusterName, namespace, sourceHost, objectKind, objectName, eventLevel, limitCount)
23 | if err != nil {
24 | return eventsInfo, err
25 | }
26 | for _, event := range events {
27 | eventsInfo = append(eventsInfo, eventInfo{
28 | ClusterName: event.Cluster,
29 | Namespace: event.Namespace,
30 | EventLevel: event.EventType,
31 | ObjectKind: event.ObjectKind,
32 | ObjectName: event.ObjectName,
33 | ObjectPhase: event.Reason,
34 | ObjectMessage: event.Message,
35 | SourceHost: event.SourceHost,
36 | LastTime: event.LastTimestamp,
37 | })
38 | }
39 | return eventsInfo, nil
40 | }
41 |
--------------------------------------------------------------------------------
/backend/resource/exec_command.go:
--------------------------------------------------------------------------------
1 | package resource
2 |
3 | type ExecRequest struct {
4 | Command string `json:"command"`
5 | }
6 |
--------------------------------------------------------------------------------
/backend/resource/objectvalidator.go:
--------------------------------------------------------------------------------
1 | package resource
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/astaxie/beego"
7 | v1beta1 "k8s.io/api/apps/v1beta1"
8 | apiv1 "k8s.io/api/core/v1"
9 | errors "k8s.io/apimachinery/pkg/api/errors"
10 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11 | "k8s.io/client-go/kubernetes"
12 | "kubecloud/backend/dao"
13 | "kubecloud/common"
14 | )
15 |
16 | type ObjectValidator interface {
17 | Validator(obj interface{}) error
18 | }
19 |
20 | type KubeAppValidator struct {
21 | namespace string
22 | client kubernetes.Interface
23 | }
24 |
25 | func NewKubeAppValidator(client kubernetes.Interface, namespace string) *KubeAppValidator {
26 | return &KubeAppValidator{
27 | namespace: namespace,
28 | client: client,
29 | }
30 | }
31 |
32 | func (validator *KubeAppValidator) Validator(obj interface{}) error {
33 | var volumes []apiv1.Volume
34 | if obj == nil {
35 | return nil
36 | }
37 | if dp, ok := obj.(*v1beta1.Deployment); ok {
38 | volumes = dp.Spec.Template.Spec.Volumes
39 | }
40 | for _, vol := range volumes {
41 | if vol.PersistentVolumeClaim != nil {
42 | _, err := validator.client.CoreV1().PersistentVolumeClaims(validator.namespace).Get(vol.PersistentVolumeClaim.ClaimName, metav1.GetOptions{})
43 | if errors.IsNotFound(err) {
44 | return fmt.Errorf("PVC(%s) is not existed in namespace %s!", vol.PersistentVolumeClaim.ClaimName, validator.namespace)
45 | }
46 | if err != nil {
47 | beego.Error("get PVC info failed:", err)
48 | return fmt.Errorf("get PVC(%s) info failed!", vol.PersistentVolumeClaim.ClaimName)
49 | }
50 | }
51 | if vol.ConfigMap != nil {
52 | _, err := validator.client.CoreV1().ConfigMaps(validator.namespace).Get(vol.ConfigMap.Name, metav1.GetOptions{})
53 | if errors.IsNotFound(err) {
54 | return fmt.Errorf("Configmap:%s is not existed in namespace %s!", vol.ConfigMap.Name, validator.namespace)
55 | }
56 | if err != nil {
57 | beego.Error("get configmap info failed:", err)
58 | return fmt.Errorf("get configmap:%s info failed!", vol.ConfigMap.Name)
59 | }
60 | }
61 | }
62 | return nil
63 | }
64 |
65 | type KubeSvcValidator struct {
66 | cluster string
67 | namespace string
68 | appname string
69 | }
70 |
71 | func NewKubeSvcValidator(cluster, namespace, appname string) *KubeSvcValidator {
72 | return &KubeSvcValidator{
73 | cluster: cluster,
74 | namespace: namespace,
75 | appname: appname,
76 | }
77 | }
78 |
79 | func (validator *KubeSvcValidator) Validator(obj interface{}) error {
80 | if obj == nil {
81 | return nil
82 | }
83 | svc, ok := obj.(*apiv1.Service)
84 | if !ok {
85 | return nil
86 | }
87 | for _, port := range svc.Spec.Ports {
88 | if err := checkNodePort(validator.cluster, validator.appname, int(port.NodePort)); err != nil {
89 | return err
90 | }
91 | }
92 |
93 | return nil
94 | }
95 |
96 | func checkNodePort(cluster, newAppname string, nodePort int) error {
97 | if nodePort != 0 && cluster != "" {
98 | svcPorts, err := dao.NewK8sServiceModel().List(cluster, common.AllNamespace, nodePort)
99 | if err != nil {
100 | return err
101 | }
102 | for _, item := range svcPorts {
103 | if item.Service.OwnerName != newAppname {
104 | return fmt.Errorf("NodePort(%d) is used by application(%s/%s/%s)!",
105 | nodePort, cluster, item.Namespace, item.Service.OwnerName)
106 | }
107 | }
108 | }
109 | return nil
110 | }
111 |
--------------------------------------------------------------------------------
/backend/resource/service.go:
--------------------------------------------------------------------------------
1 | package resource
2 |
3 | import (
4 | "kubecloud/backend/dao"
5 | "kubecloud/common/utils"
6 |
7 | "fmt"
8 |
9 | "github.com/astaxie/beego"
10 | "github.com/astaxie/beego/orm"
11 | apiv1 "k8s.io/api/core/v1"
12 | )
13 |
14 | type SimpleService struct {
15 | Name string `json:"name"`
16 | Namespace string `json:"namespace"`
17 | Ports []int `json:"ports"`
18 | Type apiv1.ServiceType `json:"type"`
19 | ClusterIP string `json:"clusterIP"`
20 | }
21 |
22 | type serviceAddress struct {
23 | Port int `json:"port"`
24 | TargetPort int `json:"target_port"`
25 | NodePort int `json:"node_port"`
26 | Protocol string `json:"protocol"`
27 | NodePortAddr string `json:"node_port_addr"`
28 | ClusterAddr string `json:"cluster_addr"`
29 | }
30 |
31 | type podSvcAddress struct {
32 | TargetPort int `json:"target_port"`
33 | Protocol string `json:"protocol"`
34 | ClusterAddr string `json:"cluster_addr"`
35 | }
36 |
37 | type ServiceDetail struct {
38 | Name string `json:"name"`
39 | Type string `json:"type"`
40 | ClusterIP string `json:"cluster_ip"`
41 | AddressList []serviceAddress `json:"address_list"`
42 | PodsvcAddrList []podSvcAddress `json:"podsvc_addr_list"`
43 | }
44 |
45 | type ServiceRes struct {
46 | cluster string
47 | modelSvc *dao.K8sServiceModel
48 | listNSFunc NamespaceListFunction
49 | }
50 |
51 | func NewServiceRes(cluster string, get NamespaceListFunction) *ServiceRes {
52 | return &ServiceRes{cluster: cluster, modelSvc: dao.NewK8sServiceModel(), listNSFunc: get}
53 | }
54 |
55 | func (sr *ServiceRes) GetServiceList(namespace string) ([]SimpleService, error) {
56 | list := []SimpleService{}
57 | svcPortList, err := sr.modelSvc.List(sr.cluster, namespace, 0)
58 | if err != nil {
59 | beego.Debug("error:", err)
60 | return list, err
61 | }
62 | nslist := sr.listNSFunc()
63 | for _, port := range svcPortList {
64 | // filter headless service
65 | if !utils.ContainsString(nslist, port.Namespace) ||
66 | port.Service.ClusterIP == "None" {
67 | continue
68 | }
69 | index := -1
70 | for i, item := range list {
71 | if port.Service.Name == item.Name {
72 | index = i
73 | break
74 | }
75 | }
76 | if index >= 0 {
77 | // just append port
78 | list[index].Ports = append(list[index].Ports, port.Port)
79 | } else {
80 | svc := SimpleService{
81 | Name: port.Service.Name,
82 | Namespace: port.Service.Namespace,
83 | Type: apiv1.ServiceType(port.Service.Type),
84 | ClusterIP: port.Service.ClusterIP,
85 | }
86 | svc.Ports = append(svc.Ports, port.Port)
87 | list = append(list, svc)
88 | }
89 | }
90 |
91 | return list, nil
92 | }
93 |
94 | func (sr *ServiceRes) GetService(namespace, owner, name string) ([]SimpleService, error) {
95 | service, err := sr.modelSvc.Get(sr.cluster, namespace, owner, name)
96 | if err != nil {
97 | return nil, err
98 | }
99 | ss := SimpleService{
100 | Name: service.Name,
101 | Namespace: service.Namespace,
102 | Type: apiv1.ServiceType(service.Type),
103 | ClusterIP: service.ClusterIP,
104 | }
105 | for _, port := range service.Ports {
106 | ss.Ports = append(ss.Ports, port.Port)
107 | }
108 |
109 | return []SimpleService{ss}, nil
110 | }
111 |
112 | func (sr *ServiceRes) GetServiceDetail(namespace, name, nodeip string) (ServiceDetail, error) {
113 | svcDetail := ServiceDetail{}
114 | svc, err := sr.modelSvc.Get(sr.cluster, namespace, name, "")
115 | if err != nil {
116 | if err != orm.ErrNoRows {
117 | beego.Error("Get service information failed: " + err.Error())
118 | return svcDetail, err
119 | }
120 | return svcDetail, nil
121 | }
122 | svcDetail.Name = svc.OwnerName
123 | svcDetail.Type = svc.Type
124 | svcDetail.ClusterIP = svc.ClusterIP
125 | for _, item := range svc.Ports {
126 | var address serviceAddress
127 | address.Port = item.Port
128 | address.TargetPort = item.TargetPort
129 | address.Protocol = item.Protocol
130 | address.NodePort = item.NodePort
131 | address.ClusterAddr = fmt.Sprintf("%s.%s:%v", svc.Name, svc.Namespace, address.Port)
132 | if apiv1.ServiceType(svcDetail.Type) == apiv1.ServiceTypeNodePort && nodeip != "" {
133 | address.NodePortAddr = fmt.Sprintf("%s:%v", nodeip, address.NodePort)
134 | } else {
135 | address.NodePortAddr = ""
136 | }
137 | svcDetail.AddressList = append(svcDetail.AddressList, address)
138 | }
139 |
140 | return svcDetail, nil
141 | }
142 |
143 | func (sr *ServiceRes) GetHeadlessSvcDetail(namespace, name string) (ServiceDetail, error) {
144 | svcDetail := ServiceDetail{}
145 | svc, err := sr.modelSvc.Get(sr.cluster, namespace, name, "")
146 | if err != nil {
147 | beego.Error("Get service information failed:", err)
148 | return svcDetail, nil
149 | }
150 | svcDetail.Name = svc.OwnerName
151 | svcDetail.Type = svc.Type
152 | svcDetail.ClusterIP = svc.ClusterIP
153 | for _, item := range svc.Ports {
154 | var address serviceAddress
155 | address.Port = item.Port
156 | address.TargetPort = item.TargetPort
157 | address.Protocol = item.Protocol
158 | address.ClusterAddr = fmt.Sprintf("%s.%s.svc.cluster.local:%v", svc.Name, svc.Namespace, address.Port)
159 | address.NodePortAddr = ""
160 | svcDetail.AddressList = append(svcDetail.AddressList, address)
161 | ep, err := dao.NewK8sEndpointModel().Get(sr.cluster, namespace, svc.Name, int32(item.TargetPort))
162 | if err != nil {
163 | beego.Warn("get endpoint failed:", err)
164 | } else {
165 | for _, addr := range ep.Addresses {
166 | svcDetail.PodsvcAddrList = append(svcDetail.PodsvcAddrList, podSvcAddress{
167 | TargetPort: item.TargetPort,
168 | Protocol: item.Protocol,
169 | ClusterAddr: fmt.Sprintf("%s.%s.%s:%v", addr.TargetRefName, svc.Name, svc.Namespace, item.TargetPort),
170 | })
171 | }
172 | }
173 | }
174 |
175 | return svcDetail, nil
176 | }
177 |
--------------------------------------------------------------------------------
/backend/resource/template.go:
--------------------------------------------------------------------------------
1 | package resource
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 |
7 | "github.com/astaxie/beego/orm"
8 |
9 | "kubecloud/backend/dao"
10 | "kubecloud/backend/models"
11 | "kubecloud/common"
12 | "kubecloud/common/utils"
13 | )
14 |
15 | type TemplateInfo struct {
16 | models.ZcloudTemplate
17 | CreateAt string `json:"create_at"`
18 | UpdateAt string `json:"update_at"`
19 | DeleteAt string `json:"delete_at"`
20 | }
21 |
22 | type TemplateRes struct {
23 | modelHandle *dao.TemplateModel
24 | listNSFunc NamespaceListFunction
25 | }
26 |
27 | func NewTemplateRes(get NamespaceListFunction) *TemplateRes {
28 | return &TemplateRes{
29 | modelHandle: dao.NewTemplateModel(),
30 | listNSFunc: get,
31 | }
32 | }
33 |
34 | // template interface, nativetemplate support this interface
35 | type Template interface {
36 | Default(cluster string) Template
37 | Validate() error
38 | GetExample() []byte
39 | Deploy(projectid int64, cluster, namespace, tname string, eparam *ExtensionParam) error
40 | }
41 |
42 | func NewTemplate() Template {
43 | return NewNativeTemplate()
44 | }
45 |
46 | func (tr *TemplateRes) CreateTemplate(template models.ZcloudTemplate) (*models.ZcloudTemplate, error) {
47 | texist, err := tr.modelHandle.GetTemplate(template.Namespace, template.Name)
48 | if texist != nil {
49 | return nil, common.NewConflict().SetCode("TemplateAlreadyExists").SetMessage("template already exists")
50 | } else {
51 | if err != nil {
52 | if err != orm.ErrNoRows {
53 | return nil, common.NewInternalServerError().SetCause(err)
54 | }
55 | }
56 | }
57 | temp, err := tr.modelHandle.CreateTemplate(template)
58 | if err != nil {
59 | return nil, common.NewInternalServerError().SetCause(err)
60 | }
61 |
62 | return temp, nil
63 | }
64 |
65 | func (tr *TemplateRes) DeleteTemplate(namespace, name string) error {
66 | _, err := tr.modelHandle.GetTemplate(namespace, name)
67 | if err != nil {
68 | if err == orm.ErrNoRows {
69 | return common.NewNotFound().SetCause(err)
70 | } else {
71 | return common.NewInternalServerError().SetCause(err)
72 | }
73 | }
74 |
75 | //todo if template is used by some apps, it can not be deleted
76 | err = tr.modelHandle.DeleteTemplate(namespace, name)
77 | if err != nil {
78 | return common.NewInternalServerError().SetCause(err)
79 | }
80 |
81 | return nil
82 | }
83 |
84 | func (tr *TemplateRes) UpdateTemplate(template models.ZcloudTemplate) (*models.ZcloudTemplate, error) {
85 | told, err := tr.modelHandle.GetTemplate(template.Namespace, template.Name)
86 | if err != nil {
87 | if err == orm.ErrNoRows {
88 | return nil, common.NewNotFound().SetCause(err)
89 | } else {
90 | return nil, common.NewInternalServerError().SetCause(err)
91 | }
92 | }
93 | told.Kind = template.Kind
94 | told.Spec = template.Spec
95 | told.Description = template.Description
96 |
97 | err = tr.modelHandle.UpdateTemplate(*told)
98 | if err != nil {
99 | return nil, common.NewInternalServerError().SetCause(err)
100 | }
101 |
102 | return told, nil
103 | }
104 |
105 | func (tr *TemplateRes) GetTemplateList(namespace string, filterQuery *utils.FilterQuery) (*utils.QueryResult, error) {
106 | nslist := []string{}
107 | if namespace != common.AllNamespace {
108 | nslist = append(nslist, namespace)
109 | } else {
110 | nslist = tr.listNSFunc()
111 | }
112 | res, err := tr.modelHandle.GetTemplateList(nslist, filterQuery)
113 | if err != nil {
114 | if err == orm.ErrNoRows {
115 | return nil, common.NewNotFound().SetCause(err)
116 | }
117 | return nil, common.NewInternalServerError().SetCause(err)
118 | }
119 | tList, ok := res.List.([]models.ZcloudTemplate)
120 | if !ok {
121 | return nil, common.NewInternalServerError().SetCause(fmt.Errorf("invalid data type"))
122 | }
123 | list := []TemplateInfo{}
124 | for _, item := range tList {
125 | temp := TemplateInfo{}
126 | temp.ZcloudTemplate = item
127 | temp.UpdateAt = item.UpdateAt.Format("2006-01-02 15:04:05")
128 | temp.CreateAt = item.CreateAt.Format("2006-01-02 15:04:05")
129 | list = append(list, temp)
130 | }
131 | res.List = list
132 | return res, nil
133 | }
134 |
135 | func (tr *TemplateRes) GetTemplateByName(namespace, name string) (*models.ZcloudTemplate, int, error) {
136 | template, err := tr.modelHandle.GetTemplate(namespace, name)
137 | if err != nil {
138 | if err == orm.ErrNoRows {
139 | return template, http.StatusNotFound, err
140 | }
141 | return template, http.StatusInternalServerError, err
142 | }
143 |
144 | return template, http.StatusOK, nil
145 | }
146 |
147 | func (tr *TemplateRes) GetTemplateByID(id int64) (*models.ZcloudTemplate, int, error) {
148 | template, err := tr.modelHandle.GetTemplateByID(id)
149 | if err != nil {
150 | if err == orm.ErrNoRows {
151 | return template, http.StatusNotFound, err
152 | }
153 | return template, http.StatusInternalServerError, err
154 | }
155 |
156 | return template, http.StatusOK, nil
157 | }
158 |
--------------------------------------------------------------------------------
/backend/service/appconfig.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import "github.com/astaxie/beego/config"
4 |
5 | func GetAppConfig() config.Configer {
6 | return appConfigProvider()
7 | }
8 |
--------------------------------------------------------------------------------
/backend/service/clienthelper.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "fmt"
5 | "path"
6 |
7 | "github.com/astaxie/beego"
8 | "k8s.io/client-go/rest"
9 | "k8s.io/client-go/tools/clientcmd"
10 | )
11 |
12 | func GetKubeRestConfig(cluster string) (*rest.Config, error) {
13 | return clientcmd.BuildConfigFromFlags("", path.Join(beego.AppConfig.String("k8s::configPath"), cluster))
14 | }
15 |
16 | // Heapster client
17 | type HeapsterClient interface {
18 | Get(path string) RequestInterface
19 | }
20 |
21 | type InClusterHeapsterClient struct {
22 | client rest.Interface
23 | }
24 |
25 | type RequestInterface interface {
26 | DoRaw() ([]byte, error)
27 | }
28 |
29 | func NewHeapsterClient(cluster string) (HeapsterClient, error) {
30 | client, err := GetClientset(cluster)
31 | if err != nil {
32 | return nil, fmt.Errorf("get client error %v", err)
33 | }
34 |
35 | return InClusterHeapsterClient{client: client.CoreV1().RESTClient()}, nil
36 | }
37 |
38 | func (c InClusterHeapsterClient) Get(path string) RequestInterface {
39 | return c.client.Get().Prefix("proxy").
40 | Namespace("kube-system").
41 | Resource("services").
42 | Name("heapster").
43 | Suffix("/api/v1" + path)
44 | }
45 |
--------------------------------------------------------------------------------
/backend/service/clientset.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "sync"
5 |
6 | "k8s.io/client-go/kubernetes"
7 | )
8 |
9 | var (
10 | clusterClientsetMapMutex sync.RWMutex
11 | clusterClientsetMap = make(map[string]kubernetes.Interface)
12 | )
13 |
14 | func findClientset(cluster string) (client kubernetes.Interface, ok bool) {
15 | clusterClientsetMapMutex.RLock()
16 | defer clusterClientsetMapMutex.RUnlock()
17 | client, ok = clusterClientsetMap[cluster]
18 | return client, ok
19 | }
20 |
21 | func newClientset(cluster string) (client kubernetes.Interface, err error) {
22 | var ok bool
23 | clusterClientsetMapMutex.Lock()
24 | defer clusterClientsetMapMutex.Unlock()
25 | client, ok = clusterClientsetMap[cluster]
26 | if !ok {
27 | client, err = clientsetProvider(cluster)
28 | if err == nil {
29 | clusterClientsetMap[cluster] = client
30 | }
31 | }
32 | return client, err
33 | }
34 |
35 | func GetClientset(cluster string) (client kubernetes.Interface, err error) {
36 | var ok bool
37 | client, ok = findClientset(cluster)
38 | if !ok {
39 | client, err = newClientset(cluster)
40 | }
41 | return client, err
42 | }
43 |
44 | func UpdateClientset(cluster string) (client kubernetes.Interface, err error) {
45 | clusterClientsetMapMutex.Lock()
46 | defer clusterClientsetMapMutex.Unlock()
47 | client, err = clientsetProvider(cluster)
48 | if err != nil {
49 | return
50 | }
51 | clusterClientsetMap[cluster] = client
52 | return
53 | }
54 |
--------------------------------------------------------------------------------
/backend/service/service.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "fmt"
5 | "path"
6 | "sync"
7 |
8 | "github.com/astaxie/beego"
9 | "github.com/astaxie/beego/config"
10 | "github.com/astaxie/beego/utils"
11 | "k8s.io/client-go/kubernetes"
12 | "k8s.io/client-go/kubernetes/fake"
13 | "k8s.io/client-go/tools/clientcmd"
14 | )
15 |
16 | var clientsetProvider func(cluster string) (kubernetes.Interface, error)
17 |
18 | var appConfigProvider func() config.Configer
19 |
20 | func Init() {
21 | clientsetProvider = func(cluster string) (kubernetes.Interface, error) {
22 | configPath := path.Join(beego.AppConfig.String("k8s::configPath"), cluster)
23 | config, err := clientcmd.BuildConfigFromFlags("", configPath)
24 | if err != nil {
25 | return nil, err
26 | }
27 | return kubernetes.NewForConfig(config)
28 | }
29 |
30 | appConfigProvider = func() config.Configer {
31 | return beego.AppConfig
32 | }
33 | }
34 |
35 | func InitMock() {
36 | clientsetProvider = func(cluster string) (kubernetes.Interface, error) {
37 | return fake.NewSimpleClientset(), nil
38 | }
39 |
40 | appConfigContext := struct {
41 | once sync.Once
42 | configer config.Configer
43 | }{}
44 |
45 | appConfigProvider = func() config.Configer {
46 | appConfigContext.once.Do(func() {
47 | confDir := "../../conf"
48 | configPath := ""
49 | searchPath := []string{"app.local.conf", "app.test.conf"}
50 | for _, fileName := range searchPath {
51 | path := path.Join(confDir, fileName)
52 | if utils.FileExists(path) {
53 | configPath = path
54 | break
55 | }
56 | }
57 | if configPath == "" {
58 | panic(fmt.Sprintf(`config file not found, search path: %v`, searchPath))
59 | }
60 | configer, err := config.NewConfig("ini", configPath)
61 | if err != nil {
62 | panic(err)
63 | }
64 | appConfigContext.configer = configer
65 | })
66 | return appConfigContext.configer
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/backend/util/kubeutil/ingressutil.go:
--------------------------------------------------------------------------------
1 | package kubeutil
2 |
3 | import (
4 | "fmt"
5 |
6 | "kubecloud/backend/dao"
7 | "kubecloud/common/utils"
8 |
9 | extensions "k8s.io/api/extensions/v1beta1"
10 | "kubecloud/backend/util/labels"
11 | )
12 |
13 | const (
14 | DefaultIngressAnnotationKey = "created_default"
15 | )
16 |
17 | func DeleteHostFromTLS(TLS []extensions.IngressTLS, delHost string) []extensions.IngressTLS {
18 | // delete host from tls
19 | // if tls.hosts is empty, delete tls from Spec.TLS
20 | newTLS := []extensions.IngressTLS{}
21 | for _, tls := range TLS {
22 | exHosts := []string{}
23 | for _, host := range tls.Hosts {
24 | if host != delHost {
25 | exHosts = append(exHosts, host)
26 | }
27 | }
28 | if len(exHosts) != 0 {
29 | tls.Hosts = exHosts
30 | newTLS = append(newTLS, tls)
31 | }
32 | }
33 | return newTLS
34 | }
35 |
36 | func MergeIngressRuleValue(first, second *extensions.IngressRule) extensions.IngressRule {
37 | if first.HTTP == nil {
38 | return *second
39 | }
40 | if second.HTTP == nil {
41 | return *first
42 | }
43 | rule := first.DeepCopy()
44 | for _, spath := range second.HTTP.Paths {
45 | found := false
46 | for _, fpath := range first.HTTP.Paths {
47 | if utils.PathsIsEqual(fpath.Path, spath.Path) {
48 | found = true
49 | break
50 | }
51 | }
52 | if !found {
53 | rule.HTTP.Paths = append(rule.HTTP.Paths, spath)
54 | }
55 | }
56 | return *rule
57 | }
58 |
59 | func MergeIngressRuleList(firstList, secondList []extensions.IngressRule) []extensions.IngressRule {
60 | for _, orule := range secondList {
61 | index := -1
62 | for i, rule := range firstList {
63 | if orule.Host == rule.Host {
64 | index = i
65 | break
66 | }
67 | }
68 | if index < 0 {
69 | firstList = append(firstList, orule)
70 | } else {
71 | firstList[index] = MergeIngressRuleValue(&firstList[index], &orule)
72 | }
73 | }
74 | return firstList
75 | }
76 |
77 | func MergeIngressTLSList(firstList, secondList []extensions.IngressTLS) []extensions.IngressTLS {
78 | // check TLS, if not existed in new ing, then add it
79 | for _, otls := range secondList {
80 | index := -1
81 | for i, ntls := range firstList {
82 | if ntls.SecretName == otls.SecretName {
83 | index = i
84 | break
85 | }
86 | }
87 | if index < 0 {
88 | firstList = deleteHostsFromTLS(firstList, otls)
89 | firstList = append(firstList, otls)
90 | } else {
91 | firstList[index] = MergeIngressTLSHost(&firstList[index], &otls)
92 | }
93 | }
94 | return firstList
95 | }
96 |
97 | func MergeIngressTLSHost(first, second *extensions.IngressTLS) extensions.IngressTLS {
98 | tls := first.DeepCopy()
99 | for _, shost := range second.Hosts {
100 | found := true
101 | for _, nhost := range first.Hosts {
102 | if shost == nhost {
103 | break
104 | }
105 | }
106 | if !found {
107 | tls.Hosts = append(tls.Hosts, shost)
108 | }
109 | }
110 | return *tls
111 | }
112 |
113 | func CheckIngressRule(cluster, namespace string, rules []extensions.IngressRule) error {
114 | ruleModel := dao.NewIngressRuleModel()
115 | for _, rule := range rules {
116 | if err := ruleModel.CheckHostUnique(cluster, namespace, rule.Host); err != nil {
117 | return err
118 | }
119 | if rule.HTTP == nil {
120 | continue
121 | }
122 | paths := []string{}
123 | for i, path := range rule.HTTP.Paths {
124 | paths = append(paths, path.Path)
125 | for j, item := range rule.HTTP.Paths {
126 | if i != j && utils.PathsIsEqual(path.Path, item.Path) {
127 | return fmt.Errorf("the path in the ingress rule is duplicated!")
128 | }
129 | }
130 | }
131 | }
132 | return nil
133 | }
134 |
135 | func SetCreatedDefaultAnno(ing *extensions.Ingress) *extensions.Ingress {
136 | if ing == nil {
137 | return nil
138 | }
139 | // set default annotation
140 | labels.AddLabel(ing.ObjectMeta.Annotations, DefaultIngressAnnotationKey, "true")
141 | return ing
142 | }
143 |
144 | func DeleteCreatedDefaultAnno(ing *extensions.Ingress) *extensions.Ingress {
145 | if ing == nil {
146 | return nil
147 | }
148 | if ing.ObjectMeta.Annotations == nil {
149 | return ing
150 | }
151 | // set default annotation
152 | delete(ing.ObjectMeta.Annotations, DefaultIngressAnnotationKey)
153 | return ing
154 | }
155 |
156 | func IngressIsCreatedDefault(ing *extensions.Ingress) bool {
157 | if ing == nil {
158 | return false
159 | }
160 | if ing.ObjectMeta.Annotations == nil {
161 | return false
162 | }
163 | return (ing.ObjectMeta.Annotations[DefaultIngressAnnotationKey] == "true")
164 | }
165 |
166 | func deleteHostsFromTLS(TLSList []extensions.IngressTLS, one extensions.IngressTLS) []extensions.IngressTLS {
167 | for _, host := range one.Hosts {
168 | TLSList = DeleteHostFromTLS(TLSList, host)
169 | }
170 | return TLSList
171 | }
172 |
--------------------------------------------------------------------------------
/backend/util/kubeutil/serviceutil.go:
--------------------------------------------------------------------------------
1 | package kubeutil
2 |
3 | import (
4 | apiv1 "k8s.io/api/core/v1"
5 | "strconv"
6 | )
7 |
8 | func SetTrafficWeight(svc *apiv1.Service, versionKey string, weight int) {
9 | if svc == nil {
10 | return
11 | }
12 | if svc.Annotations == nil {
13 | svc.Annotations = make(map[string]string)
14 | }
15 | svc.Annotations[versionKey] = strconv.Itoa(weight)
16 | }
17 |
--------------------------------------------------------------------------------
/backend/util/labels/labels.go:
--------------------------------------------------------------------------------
1 | package labels
2 |
3 | // AddLabel returns a map with the given key and value added to the given map.
4 | func AddLabel(labels map[string]string, labelKey, labelValue string) map[string]string {
5 | if labelKey == "" {
6 | // Don't need to add a label.
7 | return labels
8 | }
9 | if labels == nil {
10 | labels = make(map[string]string)
11 | }
12 | labels[labelKey] = labelValue
13 | return labels
14 | }
15 |
--------------------------------------------------------------------------------
/cmd/kubecloud/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | //"flag"
5 | "runtime"
6 |
7 | "github.com/astaxie/beego"
8 | "github.com/astaxie/beego/logs"
9 | _ "github.com/astaxie/beego/session/mysql"
10 |
11 | //kubelogs "k8s.io/apiserver/pkg/util/logs"
12 | "kubecloud/backend/controllermanager"
13 | _ "kubecloud/backend/controllermanager/register"
14 | "kubecloud/backend/models"
15 | "kubecloud/backend/resource"
16 | "kubecloud/backend/service"
17 | "kubecloud/gitops"
18 | "kubecloud/routers"
19 | )
20 |
21 | func init() {
22 | service.Init()
23 |
24 | // for glog
25 | //kubelogs.InitLogs()
26 | //defer kubelogs.FlushLogs()
27 | //flag.Parse()
28 |
29 | logFilename := beego.AppConfig.String("log::logfile")
30 | logLevel := beego.AppConfig.String("log::level")
31 | logSeparate := beego.AppConfig.String("log::separate")
32 | if logFilename == "" {
33 | logFilename = "log/kubecloud.log"
34 | }
35 | if logLevel == "" {
36 | logLevel = "7"
37 | }
38 | if logSeparate == "" {
39 | logSeparate = "[\"error\"]"
40 | }
41 | logconfig := `{
42 | "filename": "` + logFilename + `",
43 | "level": ` + logLevel + `,
44 | "separate": ` + logSeparate + `
45 | }`
46 | logs.SetLogger(logs.AdapterMultiFile, logconfig)
47 |
48 | // init mysql models
49 | models.Init()
50 | // init k8sConfig
51 | resource.InitK8sConfig()
52 | gitops.CloneClusterConfigRepo()
53 |
54 | controllermanager.Init()
55 |
56 | routers.Init()
57 | }
58 |
59 | func main() {
60 | beego.Info("Beego version:", beego.VERSION)
61 | beego.Info("Golang version:", runtime.Version())
62 | beego.Run()
63 | }
64 |
--------------------------------------------------------------------------------
/common/const.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | // const variables
4 | const (
5 | AllNamespace = "all"
6 | AllCluster = "all"
7 | ReplicasMin = 0
8 | ReplicasMax = 100
9 | )
10 |
--------------------------------------------------------------------------------
/common/errors.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 |
7 | k8serrors "k8s.io/apimachinery/pkg/api/errors"
8 | )
9 |
10 | type Error struct {
11 | status int
12 | code string
13 | message string
14 | cause error
15 | }
16 |
17 | func (this *Error) Error() string {
18 | return fmt.Sprintf("Error: %v, %v, %v, %v", this.status, this.code, this.message, this.cause)
19 | }
20 |
21 | func (this *Error) Status() int {
22 | return this.status
23 | }
24 |
25 | func (this *Error) Code() string {
26 | return this.code
27 | }
28 |
29 | func (this *Error) Message() string {
30 | return this.message
31 | }
32 |
33 | func (this *Error) Cause() error {
34 | return this.cause
35 | }
36 |
37 | func (this *Error) SetCode(code string) *Error {
38 | this.code = code
39 | return this
40 | }
41 |
42 | func (this *Error) SetMessage(format string, args ...interface{}) *Error {
43 | this.message = fmt.Sprintf(format, args...)
44 | return this
45 | }
46 |
47 | func (this *Error) SetCause(err error) *Error {
48 | this.cause = err
49 | return this
50 | }
51 |
52 | // FromK8sError convert a k8s error to Error
53 | func FromK8sError(err error) *Error {
54 | if k8serrors.IsNotFound(err) {
55 | return NewNotFound().SetCause(err)
56 | }
57 | if k8serrors.IsAlreadyExists(err) || k8serrors.IsConflict(err) {
58 | return NewConflict().SetCause(err)
59 |
60 | }
61 | return NewInternalServerError().SetCause(err)
62 | }
63 |
64 | // Check following URL before add any new functions:
65 | // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
66 |
67 | // NewBadRequest create a bad request error
68 | func NewBadRequest() *Error {
69 | return &Error{
70 | status: http.StatusBadRequest,
71 | code: "BadRequest",
72 | message: "bad request",
73 | }
74 | }
75 |
76 | // NewConflict create a conflict error
77 | func NewConflict() *Error {
78 | return &Error{
79 | status: http.StatusConflict,
80 | code: "Conflict",
81 | message: "conflict",
82 | }
83 | }
84 |
85 | // NewUnauthorized create a unauthorized error
86 | func NewUnauthorized() *Error {
87 | return &Error{
88 | status: http.StatusUnauthorized,
89 | code: "Unauthorized",
90 | message: "unauthorized",
91 | }
92 | }
93 |
94 | // NewForbidden create a forbidden error
95 | func NewForbidden() *Error {
96 | return &Error{
97 | status: http.StatusForbidden,
98 | code: "Forbidden",
99 | message: "forbidden",
100 | }
101 | }
102 |
103 | // NewNotFound create a not found error
104 | func NewNotFound() *Error {
105 | return &Error{
106 | status: http.StatusNotFound,
107 | code: "NotFound",
108 | message: "not found",
109 | }
110 | }
111 |
112 | // NewMethodNotAllowed create a method not allowed error
113 | func NewMethodNotAllowed() *Error {
114 | return &Error{
115 | status: http.StatusMethodNotAllowed,
116 | code: "MethodNotAllowed",
117 | message: "method not allowed",
118 | }
119 | }
120 |
121 | // NewInternalServerError create a internal server error
122 | func NewInternalServerError() *Error {
123 | return &Error{
124 | status: http.StatusInternalServerError,
125 | code: "InternalServerError",
126 | message: "internal server error",
127 | }
128 | }
129 |
130 | // NewNotImplemented create a not implemented error
131 | func NewNotImplemented() *Error {
132 | return &Error{
133 | status: http.StatusNotImplemented,
134 | code: "NotImplemented",
135 | message: "not implemented",
136 | }
137 | }
138 |
139 | // NewPayloadTooLarge create a payload too large error
140 | func NewPayloadTooLarge() *Error {
141 | return &Error{
142 | status: http.StatusRequestEntityTooLarge,
143 | code: "PayloadTooLarge",
144 | message: "payload too large",
145 | }
146 | }
147 |
148 | // NewServiceUnavailable create a service unavailabe error
149 | func NewServiceUnavailable() *Error {
150 | return &Error{
151 | status: http.StatusServiceUnavailable,
152 | code: "ServiceUnavailable",
153 | message: "service unavailable",
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/common/errors_test.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 |
7 | "github.com/stretchr/testify/assert"
8 | v1 "k8s.io/api/storage/v1"
9 | k8serrors "k8s.io/apimachinery/pkg/api/errors"
10 | )
11 |
12 | func TestError(t *testing.T) {
13 | t.Run("Error", func(t *testing.T) {
14 | code := "oops"
15 | message := "don't panic"
16 | cause := fmt.Errorf("undefined reference")
17 |
18 | err := Error{}
19 | assert.Equal(t, 0, err.Status())
20 |
21 | assert.Equal(t, &err, err.SetCode(code))
22 | assert.Equal(t, code, err.Code())
23 |
24 | assert.Equal(t, &err, err.SetMessage(message))
25 | assert.Equal(t, message, err.Message())
26 |
27 | assert.Equal(t, &err, err.SetCause(cause))
28 | assert.Equal(t, cause, err.Cause())
29 |
30 | assert.NotEmpty(t, err.Error())
31 | })
32 |
33 | t.Run("4XX", func(t *testing.T) {
34 | errs := []*Error{
35 | NewBadRequest(),
36 | NewNotFound(),
37 | NewConflict(),
38 | NewUnauthorized(),
39 | NewForbidden(),
40 | NewMethodNotAllowed(),
41 | NewPayloadTooLarge(),
42 | }
43 | for _, err := range errs {
44 | assert.NotNil(t, err)
45 | assert.True(t, err.Status() >= 400 && err.Status() < 500, fmt.Sprintf(`should be 4XX: %#v`, err))
46 | }
47 | })
48 |
49 | t.Run("5XX", func(t *testing.T) {
50 | errs := []*Error{
51 | NewNotImplemented(),
52 | NewServiceUnavailable(),
53 | NewInternalServerError(),
54 | }
55 | for _, err := range errs {
56 | assert.NotNil(t, err)
57 | assert.True(t, err.Status() >= 500 && err.Status() < 600, fmt.Sprintf(`should be 5XX: %#v`, err))
58 | }
59 | })
60 |
61 | t.Run("FromRawError", func(t *testing.T) {
62 | rawErr := fmt.Errorf("oops")
63 | err := FromK8sError(rawErr)
64 | assert.NotNil(t, err)
65 | assert.Equal(t, rawErr, err.Cause())
66 | assert.Equal(t, NewInternalServerError().Status(), err.Status())
67 | })
68 |
69 | t.Run("FromK8sError", func(t *testing.T) {
70 | t.Run("NotFound", func(t *testing.T) {
71 | k8sErr := k8serrors.NewNotFound(v1.Resource("sc"), "not found")
72 | err := FromK8sError(k8sErr)
73 | assert.NotNil(t, err)
74 | assert.Equal(t, k8sErr, err.Cause())
75 | assert.Equal(t, NewNotFound().Status(), err.Status())
76 | })
77 | })
78 | }
79 |
--------------------------------------------------------------------------------
/common/keyword/keyword.go:
--------------------------------------------------------------------------------
1 | package keyword
2 |
3 | const (
4 | LABEL_PRIMEDEPARTENT_ID_KEY = "prime_department_id"
5 | LABEL_SUBDEPARTENT_ID_KEY = "sub_department_id"
6 | LABEL_APPNAME_KEY = "app"
7 | LABEL_APPVERSION_KEY = "appversion"
8 | LABEL_PODVERSION_KEY = "version"
9 |
10 | ISTIO_INJECTION_POLICY = "istio-injection"
11 | ISTIO_INJECTION_ENABLE = "enabled"
12 | ISTIO_INJECTION_DISABLE = "disabled"
13 |
14 | RESTART_LABLE = "kubecloud/restart"
15 | RESTART_LABLE_VALUE = "true"
16 | DELETE_LABLE = "kubecloud/delete"
17 | DELETE_LABLE_VALUE = "true"
18 | ENV_TEST_PUBLIC = "test_public"
19 | ENV_DEV = "dev"
20 |
21 | K8S_RESOURCE_TYPE_NODE = "node"
22 | K8S_RESOURCE_TYPE_APP = "app"
23 | )
24 |
--------------------------------------------------------------------------------
/common/utils/nettools.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "crypto/tls"
5 | "net"
6 | "net/http"
7 | "time"
8 | )
9 |
10 | var (
11 | HttpClient = &http.Client{
12 | Transport: &http.Transport{
13 | Dial: (&net.Dialer{
14 | Timeout: 10 * time.Second,
15 | }).Dial,
16 | MaxIdleConns: 200,
17 | MaxIdleConnsPerHost: 200,
18 | IdleConnTimeout: 30 * time.Second,
19 | TLSHandshakeTimeout: 5 * time.Second,
20 | TLSClientConfig: &tls.Config{
21 | InsecureSkipVerify: true,
22 | },
23 | },
24 | Timeout: 60 * time.Second,
25 | }
26 | )
27 |
28 | func TcpConnTest(server string) error {
29 | conn, err := net.DialTimeout("tcp", server, time.Second*3)
30 | if err != nil {
31 | return err
32 | }
33 | defer conn.Close()
34 | return nil
35 | }
36 |
--------------------------------------------------------------------------------
/common/utils/ormfilter.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "fmt"
5 | "github.com/astaxie/beego/orm"
6 | "kubecloud/common/validate"
7 | "reflect"
8 | "strings"
9 | )
10 |
11 | type FilterContext struct {
12 | FilterKey string `json:"filter_key"`
13 | FilterVal interface{} `json:"filter_val"`
14 | Operator string `json:"operator"`
15 | }
16 |
17 | type FilterQuery struct {
18 | PageIndex int `json:"page_index"`
19 | PageSize int `json:"page_size"`
20 | FilterContext `json:",inline"`
21 | IsLike bool `json:"is_like"`
22 | RequestBody interface{} `json:"request_body"`
23 | }
24 |
25 | type DefaultFilter struct {
26 | Filters []FilterContext
27 | }
28 |
29 | const (
30 | FilterOperatorIn = "in"
31 | FilterOperatorEqual = ""
32 | FilterOperatorExclude = "not"
33 |
34 | DEF_PAGE_INDEX = 1
35 | DEF_PAGE_SIZE = 10
36 | )
37 |
38 | func NewFilterQuery(isLike bool) *FilterQuery {
39 | return &FilterQuery{IsLike: isLike}
40 | }
41 |
42 | func (filter *FilterQuery) FilterCondition(filterKeys []string) *orm.Condition {
43 | filterSuffix := "icontains"
44 | cond := orm.NewCondition()
45 | if filter == nil {
46 | return nil
47 | }
48 | var items []string
49 | v := reflect.ValueOf(filter.FilterVal)
50 | switch v.Kind() {
51 | case reflect.String:
52 | val := filter.FilterVal.(string)
53 | if val == "" {
54 | return nil
55 | }
56 | items = strings.Split(val, ";")
57 | if validate.IsChineseChar(val) {
58 | filterSuffix = "contains"
59 | }
60 | case reflect.Int, reflect.Int64:
61 | if v.Int() == 0 {
62 | return nil
63 | }
64 | default:
65 | return nil
66 | }
67 | for i, item := range items {
68 | items[i] = strings.TrimSpace(item)
69 | }
70 | if filter.IsLike {
71 | for _, key := range filterKeys {
72 | if len(items) > 1 {
73 | for _, item := range items {
74 | cond = cond.Or(fmt.Sprintf("%s__%s", key, filterSuffix), item)
75 | }
76 | } else {
77 | cond = cond.Or(fmt.Sprintf("%s__%s", key, filterSuffix), filter.FilterVal)
78 | }
79 | }
80 | } else {
81 | if filter.FilterKey != "" {
82 | if len(items) > 1 {
83 | cond = cond.And(filter.FilterKey+"__in", items)
84 | } else {
85 | cond = cond.And(filter.FilterKey, filter.FilterVal)
86 | }
87 | } else {
88 | for _, key := range filterKeys {
89 | if len(items) > 1 {
90 | for _, item := range items {
91 | cond = cond.Or(key, item)
92 | }
93 | } else {
94 | cond = cond.Or(key, filter.FilterVal)
95 | }
96 | }
97 | }
98 | }
99 | if cond.IsEmpty() {
100 | return nil
101 | }
102 | return cond
103 | }
104 |
105 | func (filter *FilterQuery) SetFilter(key string, val interface{}, operator string) *FilterQuery {
106 | filter.FilterVal = val
107 | filter.FilterKey = key
108 | filter.Operator = operator
109 | return filter
110 | }
111 |
112 | func NewDefaultFilter() *DefaultFilter {
113 | return &DefaultFilter{}
114 | }
115 |
116 | func (filter *DefaultFilter) DefaultFilterCondition() *orm.Condition {
117 | if filter == nil {
118 | return nil
119 | }
120 | cond := orm.NewCondition()
121 | for _, f := range filter.Filters {
122 | v := reflect.ValueOf(f.FilterVal)
123 | switch f.Operator {
124 | case FilterOperatorIn:
125 | if (v.Kind() == reflect.Array || v.Kind() == reflect.Slice) && v.Len() != 0 {
126 | cond = cond.And(f.FilterKey+"__in", f.FilterVal)
127 | }
128 | case FilterOperatorEqual:
129 | switch v.Kind() {
130 | case reflect.String:
131 | if f.FilterVal.(string) != "" {
132 | cond = cond.And(f.FilterKey, f.FilterVal)
133 | }
134 | default:
135 | cond = cond.And(f.FilterKey, f.FilterVal)
136 | }
137 | case FilterOperatorExclude:
138 | switch v.Kind() {
139 | case reflect.String:
140 | if f.FilterVal.(string) != "" {
141 | cond = cond.AndNot(f.FilterKey, f.FilterVal)
142 | }
143 | default:
144 | cond = cond.AndNot(f.FilterKey, f.FilterVal)
145 | }
146 | }
147 | }
148 | if cond.IsEmpty() {
149 | return nil
150 | }
151 | return cond
152 | }
153 |
154 | func (filter *DefaultFilter) AppendFilter(key string, val interface{}, op string) *DefaultFilter {
155 | filter.Filters = append(filter.Filters, FilterContext{FilterKey: key, FilterVal: val, Operator: op})
156 | return filter
157 | }
158 |
--------------------------------------------------------------------------------
/common/utils/simplelocker.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "time"
7 |
8 | "k8s.io/api/core/v1"
9 | "k8s.io/apimachinery/pkg/api/errors"
10 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11 | "k8s.io/client-go/kubernetes"
12 | corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
13 | )
14 |
15 | const (
16 | LockerRecordAnnotationKey = "zcloud.kubernetes.io/locker"
17 | LockerStatusUnkown = "unkown"
18 | LockerStatusAcquire = "acquire"
19 | LockerStatusRelease = "release"
20 | LockerAutoReleaseDuration = int64(60 * time.Second) //90s
21 | )
22 |
23 | type LockerRecord struct {
24 | TimeStamp int64 //nano level
25 | Identity string
26 | Status string
27 | }
28 |
29 | type EndpointLocker struct {
30 | Namespace string
31 | Name string
32 | Client corev1.CoreV1Interface
33 | e *v1.Endpoints
34 | }
35 |
36 | func NewSimpleLocker(namespace, name string, client kubernetes.Interface) *EndpointLocker {
37 | return &EndpointLocker{
38 | Namespace: namespace,
39 | Name: name,
40 | Client: client.CoreV1(),
41 | }
42 | }
43 |
44 | // Get returns the election record from a Endpoints Annotation
45 | func (cl *EndpointLocker) Get() (*LockerRecord, error) {
46 | var record LockerRecord
47 | var err error
48 | cl.e, err = cl.Client.Endpoints(cl.Namespace).Get(cl.Name, metav1.GetOptions{})
49 | if err != nil {
50 | return nil, err
51 | }
52 | if cl.e.Annotations == nil {
53 | cl.e.Annotations = make(map[string]string)
54 | }
55 | if recordBytes, found := cl.e.Annotations[LockerRecordAnnotationKey]; found {
56 | if err := json.Unmarshal([]byte(recordBytes), &record); err != nil {
57 | return nil, err
58 | }
59 | }
60 | return &record, nil
61 | }
62 |
63 | // Create attempts to create a LeaderElectionRecord annotation
64 | func (cl *EndpointLocker) Create(ler LockerRecord) error {
65 | var err error
66 | cl.e, err = cl.Client.Endpoints(cl.Namespace).Get(cl.Name, metav1.GetOptions{})
67 | if err == nil {
68 | return nil
69 | }
70 | if !errors.IsNotFound(err) {
71 | return err
72 | }
73 | recordBytes, err := json.Marshal(ler)
74 | if err != nil {
75 | return err
76 | }
77 | cl.e, err = cl.Client.Endpoints(cl.Namespace).Create(&v1.Endpoints{
78 | ObjectMeta: metav1.ObjectMeta{
79 | Name: cl.Name,
80 | Namespace: cl.Namespace,
81 | Annotations: map[string]string{
82 | LockerRecordAnnotationKey: string(recordBytes),
83 | },
84 | },
85 | })
86 |
87 | return err
88 | }
89 |
90 | // Update will update and existing annotation on a given resource.
91 | func (cl *EndpointLocker) Update(ler LockerRecord) (string, error) {
92 | var err error
93 | if cl.e == nil {
94 | return LockerStatusUnkown, fmt.Errorf("endpoints not initialized, call get or create first")
95 | }
96 | cl.e, err = cl.Client.Endpoints(cl.Namespace).Get(cl.Name, metav1.GetOptions{})
97 | recordBytes, err := json.Marshal(ler)
98 | if err != nil {
99 | return LockerStatusUnkown, err
100 | }
101 | info := cl.e.Annotations[LockerRecordAnnotationKey]
102 | if info != "" && ler.Status == LockerStatusAcquire {
103 | curLockerRecord := LockerRecord{
104 | Status: LockerStatusRelease,
105 | }
106 | json.Unmarshal([]byte(info), &curLockerRecord)
107 | now := time.Now().UnixNano()
108 | timeDiff := int64(0)
109 | if now >= curLockerRecord.TimeStamp {
110 | timeDiff = now - curLockerRecord.TimeStamp
111 | }
112 | if curLockerRecord.Status == LockerStatusAcquire && timeDiff < LockerAutoReleaseDuration {
113 | return curLockerRecord.Status, fmt.Errorf("the locker is locked, please wait!")
114 | }
115 | }
116 | cl.e.Annotations[LockerRecordAnnotationKey] = string(recordBytes)
117 | cl.e, err = cl.Client.Endpoints(cl.Namespace).Update(cl.e)
118 | return ler.Status, err
119 | }
120 |
--------------------------------------------------------------------------------
/common/utils/synclocker.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | type locker struct {
8 | lock *sync.Mutex
9 | count int
10 | }
11 |
12 | type SyncLocker struct {
13 | lockerList map[string]*locker
14 | mapLocker *sync.Mutex
15 | }
16 |
17 | func NewSyncLocker() *SyncLocker {
18 | lock := &SyncLocker{
19 | mapLocker: &sync.Mutex{},
20 | }
21 | lock.lockerList = make(map[string]*locker)
22 |
23 | return lock
24 | }
25 |
26 | func (l *SyncLocker) Lock(key string) {
27 | var tmp *locker
28 | l.mapLocker.Lock()
29 | if l.lockerList[key] == nil {
30 | tmp = &locker{
31 | lock: &sync.Mutex{},
32 | count: 0,
33 | }
34 | l.lockerList[key] = tmp
35 | } else {
36 | tmp = l.lockerList[key]
37 | }
38 | l.lockerList[key].count++
39 | l.mapLocker.Unlock()
40 | tmp.lock.Lock()
41 | }
42 |
43 | func (l *SyncLocker) Unlock(key string) {
44 | if l.lockerList[key] == nil {
45 | return
46 | }
47 | var tmp *locker
48 | l.mapLocker.Lock()
49 | tmp = l.lockerList[key]
50 | l.lockerList[key].count--
51 | if l.lockerList[key].count == 0 {
52 | delete(l.lockerList, key)
53 | }
54 | l.mapLocker.Unlock()
55 | tmp.lock.Unlock()
56 | }
57 |
--------------------------------------------------------------------------------
/common/utils/visgraph.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "strings"
7 | )
8 |
9 | type (
10 | font struct {
11 | Size int `json:"size"`
12 | }
13 | highlight struct {
14 | Border string `json:"border"`
15 | Background string `json:"background"`
16 | }
17 | NodeColor struct {
18 | Border string `json:"border"`
19 | Background string `json:"background"`
20 | Highlight highlight `json:"highlight"`
21 | }
22 | EdgeColor struct {
23 | Color string `json:"color"`
24 | Highlight string `json:"highlight"`
25 | }
26 | VisNode struct {
27 | Id string `json:"id"`
28 | Label string `json:"label"`
29 | Shape string `json:"shape"`
30 | Color NodeColor `json:"color"`
31 | Size int `json:"size"`
32 | Font font `json:"font"`
33 | BorderWidth int `json:"borderWidth"`
34 | }
35 | VisEdge struct {
36 | Id string `json:"id"`
37 | Label string `json:"label"`
38 | From string `json:"from"`
39 | To string `json:"to"`
40 | Arrows string `json:"arrows"`
41 | Color EdgeColor `json:"color"`
42 | LabelMap map[string]string `json:"-"`
43 | Length int `json:"length"`
44 | Width int `json:"width"`
45 | }
46 |
47 | VisGraph struct {
48 | Nodes []VisNode `json:"nodes"`
49 | Edges []VisEdge `json:"edges"`
50 | }
51 | )
52 |
53 | const (
54 | UNKOWN_NODE = "unknown"
55 | USER = "user"
56 | DEFAULT_COLOR = "#848690"
57 | HIGHLIGHT_COLOR = "#621ba4"
58 | NODE_BACKGROUND_COLOR = "#fff"
59 | NODE_BORDER_COLOR = "#848690"
60 | NODE_SIZE = 40
61 | NODE_FONT_SIZE = 28
62 | NODE_BORDER_WIDTH = 2
63 | NODE_SHAPE = "ellipse"
64 | EDGE_LENGTH = 220
65 | EDGE_WIDTH = 2
66 | )
67 |
68 | func NewVisGraph() *VisGraph {
69 | return &VisGraph{}
70 | }
71 |
72 | // AddEdge adds a new edge to an existing dynamic graph.
73 | func (g *VisGraph) AddEdge(src, target string, lbls map[string]string) {
74 | var sn, tn *VisNode
75 | sn = g.getNode(src)
76 | if sn == nil {
77 | sn = g.newNode(src)
78 | g.Nodes = append(g.Nodes, *sn)
79 | }
80 | tn = g.getNode(target)
81 | if tn == nil {
82 | tn = g.newNode(target)
83 | g.Nodes = append(g.Nodes, *tn)
84 | }
85 | e := VisEdge{
86 | Id: sn.Id + "→" + tn.Id,
87 | From: sn.Id,
88 | To: tn.Id,
89 | Arrows: "to",
90 | Color: EdgeColor{
91 | Color: DEFAULT_COLOR,
92 | Highlight: HIGHLIGHT_COLOR,
93 | },
94 | LabelMap: make(map[string]string),
95 | Length: EDGE_LENGTH,
96 | Width: EDGE_WIDTH,
97 | }
98 | index := g.checkEdgeExist(e)
99 | if index == -1 {
100 | e.LabelMap = lbls
101 | g.Edges = append(g.Edges, e)
102 | } else {
103 | for nk, nv := range lbls {
104 | value, exist := g.Edges[index].LabelMap[nk]
105 | if !exist {
106 | g.Edges[index].LabelMap[nk] = nv
107 | } else {
108 | if nv != value {
109 | g.Edges[index].LabelMap[nk] = nv
110 | }
111 | }
112 | }
113 | }
114 | }
115 |
116 | func (g *VisGraph) MakeEdgeLabel() {
117 | for i, e := range g.Edges {
118 | g.Edges[i].Label = labelStr(e.LabelMap)
119 | }
120 | }
121 |
122 | func (g *VisGraph) checkEdgeExist(e VisEdge) int {
123 | for i, item := range g.Edges {
124 | if item.From == e.From &&
125 | item.To == e.To &&
126 | item.Arrows == e.Arrows {
127 | return i
128 | }
129 | }
130 |
131 | return -1
132 | }
133 |
134 | func (g *VisGraph) getNode(name string) *VisNode {
135 | for _, node := range g.Nodes {
136 | if node.Label == name {
137 | return &node
138 | }
139 | }
140 |
141 | return nil
142 | }
143 |
144 | func (g *VisGraph) newNode(name string) *VisNode {
145 | //maxId := 0
146 | //label := name
147 | //for _, node := range g.Nodes {
148 | // if node.Id > maxId {
149 | // maxId = node.Id
150 | // }
151 | //}
152 | //if name == UNKOWN_NODE {
153 | // label = USER
154 | //}
155 | return &VisNode{
156 | Id: name,
157 | Label: name,
158 | Color: NodeColor{
159 | Border: NODE_BORDER_COLOR,
160 | Background: NODE_BACKGROUND_COLOR,
161 | Highlight: highlight{
162 | Border: HIGHLIGHT_COLOR,
163 | Background: NODE_BACKGROUND_COLOR,
164 | },
165 | },
166 | Shape: NODE_SHAPE,
167 | Size: NODE_SIZE,
168 | BorderWidth: NODE_BORDER_WIDTH,
169 | Font: font{
170 | Size: NODE_FONT_SIZE,
171 | },
172 | }
173 | }
174 |
175 | func labelStr(m map[string]string) string {
176 | var labelBuf bytes.Buffer
177 | for _, v := range m {
178 | labelBuf.WriteString(fmt.Sprintf("%s\n", v))
179 | }
180 | return strings.TrimRight(labelBuf.String(), "\n")
181 | }
182 |
--------------------------------------------------------------------------------
/common/utils/vizgraph.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import "io"
4 |
5 | type (
6 | // Static represents a service graph generated by API calls that is
7 | // meant to persist across generation requests. It must be merged with
8 | // a Dynamic graph to provide a complete service graph for Istio.
9 | Static struct {
10 | Nodes map[string]struct{}
11 | }
12 |
13 | // Dynamic represents a service graph generated on the fly.
14 | Dynamic struct {
15 | Nodes map[string]struct{} `json:"nodes"`
16 | Edges []*Edge `json:"edges"`
17 | }
18 |
19 | // Edge represents an edge in a dynamic service graph.
20 | Edge struct {
21 | Source string `json:"source"`
22 | Target string `json:"target"`
23 | Labels Attributes `json:"labels"`
24 | }
25 |
26 | // Attributes contain a set of annotations for an edge.
27 | Attributes map[string]string
28 |
29 | // SerializeFn provides a mechanism for writing out the service graph.
30 | SerializeFn func(w io.Writer, g *Dynamic) error
31 | )
32 |
33 | func NewVizGraph() *Dynamic {
34 | return &Dynamic{Nodes: map[string]struct{}{}, Edges: []*Edge{}}
35 | }
36 |
37 | // AddEdge adds a new edge to an existing dynamic graph.
38 | func (d *Dynamic) AddEdge(src, target string, lbls map[string]string) {
39 | d.Edges = append(d.Edges, &Edge{src, target, lbls})
40 | d.Nodes[src] = struct{}{}
41 | d.Nodes[target] = struct{}{}
42 | }
43 |
44 | // Merge adds all of the nodes in the static graph into the dynamic graph.
45 | func (d *Dynamic) Merge(static *Static) {
46 | for node := range static.Nodes {
47 | d.Nodes[node] = struct{}{}
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/conf/app.conf:
--------------------------------------------------------------------------------
1 | appname = kubecloud
2 | httpport = 8080
3 | runmode = dev
4 | copyrequestbody = true
5 |
6 | [k8s]
7 | configPath = ./k8sconfig
8 | domainSuffix =
9 | defaultDepartment =
10 | defaultBizCluster =
11 | timeout = 120
12 | nodeMgrYamlsPath=./yamls
13 | resourcelock_namespace = kubecloud
14 | clusterMode = one
15 | environment = dev
16 | syncResourceDisable=false
17 | syncResourcePeriod=5
18 |
19 | [DB]
20 | databaseUrl = ***:***@tcp(***:3306)/***?charset=utf8
21 | databaseDebug = false
22 | defaultRowsLimit = 5000
23 |
24 | [log]
25 | logfile = "log/kubecloud.log"
26 | perm = 0640
27 | level = 7
28 | separate = ["error"]
29 |
--------------------------------------------------------------------------------
/controllers/clusters.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/astaxie/beego/orm"
7 |
8 | "kubecloud/backend/resource"
9 | "kubecloud/common"
10 | "kubecloud/common/utils"
11 | )
12 |
13 | type ClusterController struct {
14 | BaseController
15 | }
16 |
17 | func (cc *ClusterController) ClusterList() {
18 | filter := cc.GetFilterQuery()
19 | res, err := resource.GetClusterList(filter)
20 | if err != nil {
21 | cc.ServeError(common.NewInternalServerError().SetCause(err))
22 | return
23 | }
24 | if res.Base.PageSize == 0 {
25 | // compatible old model
26 | cc.Data["json"] = NewResult(true, res.List, "")
27 | } else {
28 | cc.Data["json"] = NewResult(true, res, "")
29 | }
30 | cc.ServeJSON()
31 | }
32 |
33 | func (cc *ClusterController) InspectCluster() {
34 | clusterId := cc.GetStringFromPath(":cluster")
35 |
36 | result, err := resource.GetClusterDetail(clusterId)
37 | if err != nil {
38 | if err == orm.ErrNoRows {
39 | cc.ServeError(common.NewNotFound().SetCause(fmt.Errorf("database error: cluster(%s) is not existed!", clusterId)))
40 | } else if err == orm.ErrMultiRows {
41 | cc.ServeError(common.NewConflict().SetCause(fmt.Errorf("database error: cluster(%s) info is duplicated: %s!", clusterId, err.Error())))
42 | } else {
43 | cc.ServeError(common.NewInternalServerError().SetCause(err))
44 | }
45 | return
46 | }
47 | cc.Data["json"] = NewResult(true, result, "")
48 | cc.ServeJSON()
49 | }
50 |
51 | func (cc *ClusterController) CreateCluster() {
52 | var cluster resource.Cluster
53 | cc.DecodeJSONReq(&cluster)
54 | if cluster.ClusterId == "" {
55 | cluster.ClusterId = utils.NewUUID()
56 | }
57 | if err := cluster.Verify(); err != nil {
58 | cc.ServeError(common.NewBadRequest().SetCause(err))
59 | return
60 | }
61 | result, err := resource.CreateCluster(cluster)
62 | if err != nil {
63 | cc.ServeError(common.NewInternalServerError().SetCause(err))
64 | return
65 | }
66 |
67 | cc.Data["json"] = NewResult(true, result, "")
68 | cc.ServeJSON()
69 | }
70 |
71 | func (cc *ClusterController) DeleteCluster() {
72 | clusterId := cc.GetStringFromPath(":cluster")
73 |
74 | if err := resource.DeleteCluster(clusterId); err != nil {
75 | cc.ServeError(common.NewInternalServerError().SetCause(err))
76 | return
77 | }
78 | cc.Data["json"] = NewResult(true, nil, "")
79 | cc.ServeJSON()
80 | }
81 |
82 | func (cc *ClusterController) UpdateCluster() {
83 | clusterId := cc.GetStringFromPath(":cluster")
84 |
85 | var cluster resource.Cluster
86 | cc.DecodeJSONReq(&cluster)
87 | cluster.ClusterId = clusterId
88 | if err := cluster.Verify(); err != nil {
89 | cc.ServeError(common.NewBadRequest().SetCause(err))
90 | return
91 | }
92 |
93 | result, err := resource.UpdateCluster(cluster)
94 | if err != nil {
95 | cc.ServeError(common.NewInternalServerError().SetCause(err))
96 | return
97 | }
98 | cc.Data["json"] = NewResult(true, result, "")
99 | cc.ServeJSON()
100 | }
101 |
102 | func (cc *ClusterController) Certificate() {
103 | clusterId := cc.GetStringFromPath(":cluster")
104 |
105 | var cluster resource.Cluster
106 | cc.DecodeJSONReq(&cluster)
107 | if err := cluster.Verify(); err != nil {
108 | cc.ServeError(common.NewBadRequest().SetCause(err))
109 | return
110 | }
111 |
112 | if err := resource.SetClusterCertificate(clusterId, cluster.Certificate); err != nil {
113 | cc.ServeError(common.NewInternalServerError().SetCause(err))
114 | return
115 | }
116 |
117 | cc.Data["json"] = NewResult(true, nil, "")
118 | cc.ServeJSON()
119 | }
120 |
--------------------------------------------------------------------------------
/controllers/common.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "kubecloud/backend/dao"
5 | "kubecloud/common"
6 | )
7 |
8 | // TODO: deprecate IsSuccess, Origin
9 | type Result struct {
10 | IsSuccess bool `json:"IsSuccess"`
11 | Data interface{} `json:"Data,omitempty"`
12 | ErrCode string `json:"ErrCode,omitempty"`
13 | ErrMsg string `json:"ErrMsg,omitempty"`
14 | ErrDetail string `json:"ErrDetail,omitempty"`
15 | Origin string `json:"Origin,omitempty"`
16 | }
17 |
18 | func NewResult(isSuccess bool, data interface{}, errMsg string) *Result {
19 | return &Result{IsSuccess: isSuccess, Data: data, ErrMsg: errMsg}
20 | }
21 |
22 | func NewErrorResult(errCode, errMsg, errDetail string) *Result {
23 | return &Result{
24 | IsSuccess: false,
25 | ErrCode: errCode,
26 | ErrMsg: errMsg,
27 | ErrDetail: errDetail,
28 | }
29 | }
30 |
31 | //NamespaceListFunc ...
32 | func NamespaceListFunc(cluster string, initns string) func() []string {
33 | return func() []string {
34 | nsList := []string{}
35 | if initns == common.AllNamespace {
36 | nsList, _ = dao.GetClusterNamespaceList(cluster)
37 | } else {
38 | nsList = append(nsList, initns)
39 | }
40 | return nsList
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/controllers/configmap.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | corev1 "k8s.io/api/core/v1"
5 |
6 | "kubecloud/backend/resource"
7 | )
8 |
9 | type ConfigMapController struct {
10 | BaseController
11 | }
12 |
13 | func (cc *ConfigMapController) List() {
14 | clusterId := cc.GetStringFromPath(":cluster")
15 | namespace := cc.GetStringFromPath(":namespace")
16 | result, err := resource.ConfigMapList(clusterId, namespace)
17 | if err != nil {
18 | cc.ServeError(err)
19 | return
20 | }
21 |
22 | cc.Data["json"] = NewResult(true, result, "")
23 | cc.ServeJSON()
24 | }
25 |
26 | func (cc *ConfigMapController) Create() {
27 | clusterId := cc.GetStringFromPath(":cluster")
28 | namespace := cc.GetStringFromPath(":namespace")
29 |
30 | var configMapSpec corev1.ConfigMap
31 | cc.DecodeJSONReq(&configMapSpec)
32 | configMapSpec.ObjectMeta.Namespace = namespace
33 | if configMapSpec.ObjectMeta.Annotations == nil {
34 | configMapSpec.ObjectMeta.Annotations = make(map[string]string)
35 | }
36 | result, err := resource.ConfigMapCreate(clusterId, &configMapSpec)
37 | if err != nil {
38 | cc.ServeError(err)
39 | return
40 | }
41 |
42 | cc.Data["json"] = NewResult(true, result, "")
43 | cc.ServeJSON()
44 | }
45 |
46 | func (cc *ConfigMapController) Inspect() {
47 | clusterId := cc.GetStringFromPath(":cluster")
48 | namespace := cc.GetStringFromPath(":namespace")
49 | name := cc.GetStringFromPath(":configmap")
50 |
51 | result, err := resource.ConfigMapInspect(clusterId, namespace, name)
52 | if err != nil {
53 | cc.ServeError(err)
54 | return
55 | }
56 | cc.Data["json"] = NewResult(true, result, "")
57 | cc.ServeJSON()
58 | }
59 |
60 | func (cc *ConfigMapController) Update() {
61 | clusterId := cc.GetStringFromPath(":cluster")
62 | namespace := cc.GetStringFromPath(":namespace")
63 | name := cc.GetStringFromPath(":configmap")
64 |
65 | var configData corev1.ConfigMap
66 | cc.DecodeJSONReq(&configData)
67 | result, err := resource.ConfigMapUpdate(clusterId, namespace, name, &configData)
68 | if err != nil {
69 | cc.ServeError(err)
70 | return
71 | }
72 | cc.Data["json"] = NewResult(true, result, "")
73 | cc.ServeJSON()
74 | }
75 |
76 | func (cc *ConfigMapController) Delete() {
77 | clusterId := cc.GetStringFromPath(":cluster")
78 | namespace := cc.GetStringFromPath(":namespace")
79 | name := cc.GetStringFromPath(":configmap")
80 |
81 | if err := resource.ConfigMapDelete(clusterId, namespace, name); err != nil {
82 | cc.ServeError(err)
83 | return
84 | }
85 | cc.Data["json"] = NewResult(true, nil, "")
86 | cc.ServeJSON()
87 | }
88 |
--------------------------------------------------------------------------------
/controllers/error.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "kubecloud/common"
5 | )
6 |
7 | type ErrorController struct {
8 | BaseController
9 | }
10 |
11 | func (this *ErrorController) Error404() {
12 | err := common.NewNotFound()
13 | this.ServeError(err)
14 | }
15 |
16 | func (this *ErrorController) Error405() {
17 | err := common.NewMethodNotAllowed()
18 | this.ServeError(err)
19 | }
20 |
21 | func (this *ErrorController) Error501() {
22 | err := common.NewNotImplemented()
23 | this.ServeError(err)
24 | }
25 |
--------------------------------------------------------------------------------
/controllers/event.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "github.com/astaxie/beego"
5 |
6 | "kubecloud/backend/resource"
7 | "kubecloud/common"
8 | )
9 |
10 | type EventsController struct {
11 | BaseController
12 | }
13 |
14 | func (this *EventsController) Get() {
15 | clusterName := this.Ctx.Input.Query("cluster_name")
16 | namespace := this.Ctx.Input.Query("namespace")
17 | sourceHost := this.Ctx.Input.Query("source_host")
18 | objectKind := this.Ctx.Input.Query("object_kind")
19 | objectName := this.Ctx.Input.Query("object_name")
20 | eventLevel := this.Ctx.Input.Query("event_level")
21 | limitCount, err := this.GetInt64FromQuery("limit_count")
22 | if err != nil {
23 | beego.Error("Parse int error of limit_count: ", err.Error())
24 | this.ServeError(common.NewInternalServerError().SetCause(err))
25 | return
26 | }
27 |
28 | events, err := resource.GetEvents(clusterName, namespace, sourceHost, objectKind, objectName, eventLevel, limitCount)
29 | if err != nil {
30 | beego.Error("Get all events occur err: ", err.Error())
31 | this.ServeError(common.NewInternalServerError().SetCause(err))
32 | return
33 | }
34 | this.Data["json"] = NewResult(true, events, "")
35 | this.ServeJSON()
36 | }
37 |
--------------------------------------------------------------------------------
/controllers/namespaces.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "kubecloud/backend/resource"
5 | "net/http"
6 | )
7 |
8 | type NamespaceController struct {
9 | BaseController
10 | }
11 |
12 | func (this *NamespaceController) NamespaceList() {
13 | clusterId := this.GetStringFromPath(":cluster")
14 | filterQuery := this.GetFilterQuery()
15 | res, err := resource.NamespaceGetAll(clusterId, filterQuery)
16 | if err != nil {
17 | this.ServeError(err)
18 | return
19 | }
20 | this.ServeResult(NewResult(true, res, ""))
21 | }
22 |
23 | func (this *NamespaceController) Create() {
24 | clusterId := this.GetStringFromPath(":cluster")
25 | var data resource.NamespaceData
26 | this.DecodeJSONReq(&data)
27 | if err := resource.NamespaceValidate(&data); err != nil {
28 | this.ServeError(err)
29 | return
30 | }
31 | row, err := resource.NamespaceCreate(clusterId, &data)
32 | if err != nil {
33 | this.ServeError(err)
34 | return
35 | }
36 | this.SetStatus(http.StatusCreated)
37 | this.ServeResult(NewResult(true, row, ""))
38 | }
39 |
40 | func (this *NamespaceController) Update() {
41 | clusterId := this.GetStringFromPath(":cluster")
42 | namespace := this.GetStringFromPath(":namespace")
43 | var data resource.NamespaceData
44 | this.DecodeJSONReq(&data)
45 | data.Name = namespace
46 | if err := resource.NamespaceValidate(&data); err != nil {
47 | this.ServeError(err)
48 | return
49 | }
50 | row, err := resource.NamespaceUpdate(clusterId, &data)
51 | if err != nil {
52 | this.ServeError(err)
53 | return
54 | }
55 | this.ServeResult(NewResult(true, row, ""))
56 | }
57 |
58 | func (this *NamespaceController) Delete() {
59 | clusterId := this.GetStringFromPath(":cluster")
60 | namespace := this.GetStringFromPath(":namespace")
61 | if err := resource.NamespaceDelete(clusterId, namespace); err != nil {
62 | this.ServeError(err)
63 | return
64 | }
65 | this.ServeResult(NewResult(true, nil, ""))
66 | }
67 |
68 | func (this *NamespaceController) Inspect() {
69 | clusterId := this.GetStringFromPath(":cluster")
70 | namespace := this.GetStringFromPath(":namespace")
71 | row, err := resource.NamespaceGetOne(clusterId, namespace)
72 | if err != nil {
73 | this.ServeError(err)
74 | return
75 | }
76 | this.ServeResult(NewResult(true, row, ""))
77 | }
78 |
--------------------------------------------------------------------------------
/controllers/node.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "kubecloud/backend/resource"
5 | "kubecloud/common"
6 | )
7 |
8 | type NodeController struct {
9 | BaseController
10 | }
11 |
12 | func (nc *NodeController) NodeList() {
13 | clusterId := nc.GetStringFromPath(":cluster")
14 | filter := nc.GetFilterQuery()
15 | result, err := resource.GetNodeListFilter(clusterId, filter)
16 | if err != nil {
17 | nc.ServeError(common.NewInternalServerError().SetCause(err))
18 | return
19 | }
20 | if result.Base.PageSize == 0 {
21 | // compatible old model
22 | nc.Data["json"] = NewResult(true, result.List, "")
23 | } else {
24 | nc.Data["json"] = NewResult(true, result, "")
25 | }
26 | nc.ServeJSON()
27 | }
28 |
29 | func (nc *NodeController) NodeInspect() {
30 | clusterId := nc.GetStringFromPath(":cluster")
31 | nodeName := nc.GetStringFromPath(":node")
32 |
33 | result, err := resource.GetNodeDetail(clusterId, nodeName)
34 | if err != nil {
35 | nc.ServeError(common.NewInternalServerError().SetCause(err))
36 | return
37 | }
38 | nc.Data["json"] = NewResult(true, result, "")
39 | nc.ServeJSON()
40 | }
41 |
42 | func (nc *NodeController) NodeUpdate() {
43 | clusterId := nc.GetStringFromPath(":cluster")
44 | node := nc.GetStringFromPath(":node")
45 |
46 | var nodeUpdate resource.NodeUpdate
47 | nc.DecodeJSONReq(&nodeUpdate)
48 | nodeUpdate.Cluster = clusterId
49 | if err := nodeUpdate.Verify(); err != nil {
50 | nc.ServeError(common.NewBadRequest().SetCause(err))
51 | return
52 | }
53 |
54 | if err := resource.UpdateNode(clusterId, node, nodeUpdate); err != nil {
55 | nc.ServeError(common.NewInternalServerError().SetCause(err))
56 | return
57 | }
58 | nc.Data["json"] = NewResult(true, nil, "")
59 | nc.ServeJSON()
60 | }
61 |
62 | func (nc *NodeController) NodeDelete() {
63 | clusterId := nc.GetStringFromPath(":cluster")
64 | node := nc.GetStringFromPath(":node")
65 |
66 | if err := resource.DeleteNode(clusterId, node); err != nil {
67 | nc.ServeError(common.NewInternalServerError().SetCause(err))
68 | return
69 | }
70 |
71 | nc.Data["json"] = NewResult(true, nil, "")
72 | nc.ServeJSON()
73 | }
74 |
75 | func (nc *NodeController) NodeFreeze() {
76 | clusterId := nc.GetStringFromPath(":cluster")
77 | node := nc.GetStringFromPath(":node")
78 |
79 | var nodeFreeze resource.NodeFreeze
80 | nc.DecodeJSONReq(&nodeFreeze)
81 |
82 | if err := resource.FreezeNode(clusterId, node, nodeFreeze.DeletePods); err != nil {
83 | nc.ServeError(common.NewInternalServerError().SetCause(err))
84 | return
85 | }
86 | nc.Data["json"] = NewResult(true, nil, "")
87 | nc.ServeJSON()
88 | }
89 |
90 | func (nc *NodeController) NodeUnfreeze() {
91 | clusterId := nc.GetStringFromPath(":cluster")
92 | node := nc.GetStringFromPath(":node")
93 |
94 | if err := resource.UnfreezeNode(clusterId, node); err != nil {
95 | nc.ServeError(common.NewInternalServerError().SetCause(err))
96 | return
97 | }
98 | nc.Data["json"] = NewResult(true, nil, "")
99 | nc.ServeJSON()
100 | }
101 |
102 | func (nc *NodeController) NodePods() {
103 | clusterId := nc.GetStringFromPath(":cluster")
104 | nodeName := nc.GetStringFromPath(":node")
105 |
106 | result, err := resource.GetNodePods(clusterId, nodeName)
107 | if err != nil {
108 | nc.ServeError(common.NewInternalServerError().SetCause(err))
109 | return
110 | }
111 | nc.Data["json"] = NewResult(true, result, "")
112 | nc.ServeJSON()
113 | }
114 |
115 | func (nc *NodeController) NodeEvent() {
116 | filter := nc.GetFilterQuery()
117 | clusterId := nc.GetStringFromPath(":cluster")
118 | nodeName := nc.GetStringFromPath(":node")
119 |
120 | result, err := resource.GetNodeEvent(clusterId, nodeName, filter)
121 | if err != nil {
122 | nc.ServeError(common.NewInternalServerError().SetCause(err))
123 | return
124 | }
125 | nc.Data["json"] = NewResult(true, result, "")
126 | nc.ServeJSON()
127 | }
128 |
--------------------------------------------------------------------------------
/controllers/secret.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "fmt"
5 |
6 | "kubecloud/backend/resource"
7 | "kubecloud/common"
8 | "kubecloud/common/utils"
9 |
10 | "github.com/astaxie/beego"
11 | )
12 |
13 | const MAX_TLS_FILE_SIZE = 10240
14 |
15 | type SecretController struct {
16 | BaseController
17 | }
18 |
19 | type FileSizer interface {
20 | Size() int64
21 | }
22 |
23 | // List get all secret of cluster's namespace
24 | func (sc *SecretController) List() {
25 | clusterId := sc.GetStringFromPath(":cluster")
26 | namespace := sc.GetStringFromPath(":namespace")
27 | filterQuery := sc.GetFilterQuery()
28 |
29 | sHandle, err := resource.NewSecretRes(clusterId, NamespaceListFunc(clusterId, namespace))
30 | if err != nil {
31 | beego.Error("Get secrets failed: "+err.Error(), "cluster: "+clusterId+",", "namespace: "+namespace+".")
32 | sc.ServeError(common.NewInternalServerError().SetCause(err))
33 | return
34 | }
35 |
36 | result, err := sHandle.ListSecret(namespace, filterQuery)
37 | if err != nil {
38 | beego.Error("Get secrets failed: "+err.Error(), "cluster: "+clusterId+",", "namespace: "+namespace+".")
39 | sc.ServeError(common.NewInternalServerError().SetCause(err))
40 | return
41 | }
42 |
43 | sc.ServeResult(NewResult(true, result, ""))
44 | }
45 |
46 | // Create create a secret
47 | func (sc *SecretController) Create() {
48 | clusterId := sc.GetStringFromPath(":cluster")
49 | namespace := sc.GetStringFromPath(":namespace")
50 |
51 | request := new(resource.SecretRequest)
52 | sc.DecodeJSONReq(&request)
53 |
54 | sHandle, err := resource.NewSecretRes(clusterId, nil)
55 | if err != nil {
56 | beego.Error("Create secret failed: "+err.Error(), "cluster: "+clusterId+",", "namespace: "+namespace, "secret name: "+request.Name+".")
57 | sc.ServeError(common.NewInternalServerError().SetCause(err))
58 | return
59 | }
60 | err = sHandle.Validate(request)
61 | if err != nil {
62 | beego.Error("Create secret failed: "+err.Error(), "cluster: "+clusterId+",", "namespace: "+namespace, "secret name: "+request.Name+".")
63 | sc.ServeError(err)
64 | return
65 | }
66 | err = sHandle.CreateSecret(namespace, request)
67 | if err != nil {
68 | beego.Error("Create secret failed: "+err.Error(), "cluster: "+clusterId+",", "namespace: "+namespace+".")
69 | sc.ServeError(err)
70 | return
71 | }
72 |
73 | beego.Info("Create secret successfully:", "cluster: "+clusterId+",", "namespace: "+namespace, "secret name: "+request.Name+".")
74 | sc.Data["json"] = NewResult(true, nil, "")
75 | sc.ServeJSON()
76 | }
77 |
78 | // Update update a secret
79 | func (sc *SecretController) Update() {
80 | clusterId := sc.GetStringFromPath(":cluster")
81 | namespace := sc.GetStringFromPath(":namespace")
82 | name := sc.GetStringFromPath(":secret")
83 |
84 | request := new(resource.SecretRequest)
85 | sc.DecodeJSONReq(request)
86 | request.Name = name
87 |
88 | sHandle, err := resource.NewSecretRes(clusterId, nil)
89 | if err != nil {
90 | beego.Error("Update secret failed: "+err.Error(), "cluster: "+clusterId+",", "namespace: "+namespace, "secret name: "+name+".")
91 | sc.ServeError(common.NewInternalServerError().SetCause(err))
92 | return
93 | }
94 | err = sHandle.Validate(request)
95 | if err != nil {
96 | beego.Error("Update secret failed: "+err.Error(), "cluster: "+clusterId+",", "namespace: "+namespace, "secret name: "+name+".")
97 | sc.ServeError(err)
98 | return
99 | }
100 |
101 | err = sHandle.UpdateSecret(namespace, request)
102 | if err != nil {
103 | beego.Error("Update secret failed: "+err.Error(), "cluster: "+clusterId+",", "namespace: "+namespace, "secret name: "+name+".")
104 | sc.ServeError(err)
105 | return
106 | }
107 |
108 | beego.Info("Update secret successfully:", "cluster: "+clusterId+",", "namespace: "+namespace, "secret name: "+name+".")
109 | sc.Data["json"] = NewResult(true, nil, "")
110 | sc.ServeJSON()
111 | }
112 |
113 | // Delete delete a secret
114 | func (sc *SecretController) Delete() {
115 | clusterId := sc.GetStringFromPath(":cluster")
116 | namespace := sc.GetStringFromPath(":namespace")
117 | name := sc.GetStringFromPath(":secret")
118 | secret, err := resource.NewSecretRes(clusterId, nil)
119 | if err != nil {
120 | sc.ServeError(common.NewInternalServerError().SetCause(err))
121 | beego.Error("Delete secret failed: "+err.Error(), "cluster: "+clusterId+",", "namespace: "+namespace, "secretname: "+name+".")
122 | return
123 | }
124 | ir, err := resource.NewIngressRes(clusterId, nil, NamespaceListFunc(clusterId, namespace))
125 | filterQuery := utils.NewFilterQuery(false).SetFilter("secret_name", name, utils.FilterOperatorEqual)
126 | result, err := ir.ListIngresses(clusterId, namespace, "", filterQuery)
127 | if err != nil {
128 | sc.ServeError(common.NewInternalServerError().SetCause(err))
129 | beego.Error("Delete secret failed: "+err.Error(), "cluster: "+clusterId+",", "namespace: "+namespace, "secretname: "+name+".")
130 | return
131 | }
132 | if list, ok := result.List.([]resource.Ingress); ok && len(list) != 0 {
133 | beego.Error("Delete secret failed: "+"the secret has been used by some ingresses, can not be delete!", "cluster: "+clusterId+",", "namespace: "+namespace, "secretname: "+name+".")
134 | beego.Debug("ingress:", list, "hava used this secret!")
135 | sc.ServeError(common.NewConflict().SetCause(fmt.Errorf(`the secret has been used by some ingress, can not be delete`)))
136 | return
137 | }
138 | err = secret.DeleteSecret(namespace, name)
139 | if err != nil {
140 | sc.ServeError(common.NewInternalServerError().SetCause(err))
141 | beego.Error("Delete secret failed: "+err.Error(), "cluster: "+clusterId+",", "namespace: "+namespace, "secretname: "+name+".")
142 | return
143 | }
144 |
145 | sc.Data["json"] = NewResult(true, nil, "")
146 | sc.ServeJSON()
147 | beego.Info("Delete secret successfully!", "cluster: "+clusterId+",", "namespace: "+namespace+",", "secretname: "+name+"!")
148 | }
149 |
--------------------------------------------------------------------------------
/controllers/services.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/astaxie/beego"
7 |
8 | "kubecloud/backend/resource"
9 | "kubecloud/common"
10 | )
11 |
12 | type ServiceController struct {
13 | BaseController
14 | }
15 |
16 | func (this *ServiceController) List() {
17 | clusterId := this.GetStringFromPath(":cluster")
18 | namespace := this.Ctx.Input.Param(":namespace")
19 | if clusterId == "" || namespace == "" {
20 | beego.Error("cluster: " + clusterId + " or namespace: " + namespace + " is not right, can not be empty")
21 | err := fmt.Errorf("invalid cluster or namespace")
22 | this.ServeError(common.NewBadRequest().SetCause(err))
23 | return
24 | }
25 | srHandle := resource.NewServiceRes(clusterId, NamespaceListFunc(clusterId, namespace))
26 | result, err := srHandle.GetServiceList(namespace)
27 | if err != nil {
28 | this.Data["json"] = NewResult(false, result, err.Error())
29 | } else {
30 | this.Data["json"] = NewResult(true, result, "")
31 | }
32 | this.ServeJSON()
33 | }
34 |
35 | func (this *ServiceController) Inspect() {
36 | clusterId := this.GetStringFromPath(":cluster")
37 | namespace := this.Ctx.Input.Param(":namespace")
38 | name := this.Ctx.Input.Param(":service")
39 | if clusterId == "" || namespace == "" {
40 | beego.Error("cluster: " + clusterId + " or namespace: " + namespace + " is not right, can not be empty")
41 | err := fmt.Errorf("cluster or namespace can not be empty")
42 | this.ServeError(common.NewBadRequest().SetCause(err))
43 | return
44 | }
45 | if name == "" {
46 | beego.Error("service name can not be empty")
47 | err := fmt.Errorf("service name can not be empty")
48 | this.ServeError(common.NewBadRequest().SetCause(err))
49 | return
50 | }
51 | srHandle := resource.NewServiceRes(clusterId, nil)
52 | result, err := srHandle.GetService(namespace, name, "")
53 | if err != nil {
54 | beego.Error("get service"+"("+clusterId, namespace, name+")"+"information failed for", err)
55 | this.Data["json"] = NewResult(false, nil, err.Error())
56 | } else {
57 | this.Data["json"] = NewResult(true, result, "")
58 | }
59 |
60 | this.ServeJSON()
61 | }
62 |
--------------------------------------------------------------------------------
/controllers/version.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | type VersionController struct {
4 | BaseController
5 | }
6 |
7 | type version struct {
8 | Version string `json:"version"`
9 | }
10 |
11 | const curVersion = "V0.1.0"
12 |
13 | func (this *VersionController) Get() {
14 | ver := &version{
15 | Version: curVersion,
16 | }
17 |
18 | this.Data["json"] = NewResult(true, ver, "")
19 | this.ServeJSON()
20 | }
21 |
--------------------------------------------------------------------------------
/deploy/kubecloud.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | name: kubecloud
5 | ---
6 | apiVersion: v1
7 | data:
8 | app.conf: |
9 | appname = kubecloud
10 | httpport = 8080
11 | runmode = dev
12 | copyrequestbody = true
13 |
14 | [k8s]
15 | configPath = ./k8sconfig
16 | domainSuffix =
17 | defaultDepartment =
18 | defaultBizCluster =
19 | timeout = 120
20 | nodeMgrYamlsPath=./yamls
21 | resourcelock_namespace = kubecloud
22 | clusterMode = one
23 | environment = dev
24 | syncResourceDisable=false
25 | syncResourcePeriod=5
26 |
27 | [DB]
28 | databaseUrl = ***:***@tcp(***:3306)/kubecloud?charset=utf8
29 | databaseDebug = false
30 | defaultRowsLimit = 5000
31 |
32 | [log]
33 | logfile = "log/kubecloud.log"
34 | perm = 0640
35 | level = 7
36 | separate = ["error"]
37 | kind: ConfigMap
38 | metadata:
39 | name: kubecloud-config
40 | namespace: kubecloud
41 | ---
42 | apiVersion: apps/v1beta1
43 | kind: Deployment
44 | metadata:
45 | labels:
46 | app: kubecloud
47 | name: kubecloud
48 | namespace: kubecloud
49 | spec:
50 | replicas: 1
51 | selector:
52 | matchLabels:
53 | app: kubecloud
54 | strategy:
55 | rollingUpdate:
56 | maxSurge: 25%
57 | maxUnavailable: 25%
58 | type: RollingUpdate
59 | template:
60 | metadata:
61 | labels:
62 | app: kubecloud
63 | name: kubecloud
64 | namespace: kubecloud
65 | spec:
66 | containers:
67 | - image: kubecloud
68 | imagePullPolicy: Always
69 | livenessProbe:
70 | failureThreshold: 5
71 | initialDelaySeconds: 30
72 | periodSeconds: 60
73 | successThreshold: 1
74 | tcpSocket:
75 | port: 8080
76 | timeoutSeconds: 2
77 | name: kubecloud
78 | readinessProbe:
79 | failureThreshold: 3
80 | httpGet:
81 | path: /health
82 | port: 8080
83 | initialDelaySeconds: 10
84 | periodSeconds: 30
85 | successThreshold: 1
86 | timeoutSeconds: 10
87 | resources:
88 | requests:
89 | cpu: "1"
90 | memory: 1Gi
91 | securityContext:
92 | privileged: false
93 | volumeMounts:
94 | - mountPath: /kubecloud/conf
95 | name: kubecloud-config
96 | dnsPolicy: ClusterFirst
97 | restartPolicy: Always
98 | volumes:
99 | - configMap:
100 | name: kubecloud-config
101 | name: kubecloud-config
102 | status: {}
103 | ---
104 | apiVersion: v1
105 | kind: Service
106 | metadata:
107 | labels:
108 | app: kubecloud
109 | name: svc-kubecloud
110 | namespace: kubecloud
111 | spec:
112 | ports:
113 | - name: http-8080-8080
114 | port: 8080
115 | protocol: TCP
116 | targetPort: 8080
117 | selector:
118 | app: kubecloud
119 | sessionAffinity: ClientIP
120 | type: ClusterIP
121 | status:
122 | loadBalancer: {}
--------------------------------------------------------------------------------
/docs/images/kubecloud-architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhongAnTech/kubecloud/0fc068f5f55a7d10935dcecf52e0813b1a4fa6a3/docs/images/kubecloud-architecture.png
--------------------------------------------------------------------------------
/gitops/git.go:
--------------------------------------------------------------------------------
1 | package gitops
2 |
3 | import (
4 | "fmt"
5 | "path/filepath"
6 | "strings"
7 |
8 | "github.com/golang/glog"
9 |
10 | "kubecloud/common/utils"
11 | )
12 |
13 | type Git struct {
14 | Dir string
15 | Url string
16 | Branch string
17 | Token string
18 | commandName string
19 | }
20 |
21 | func NewGit(dir, url, branch, token string) *Git {
22 | return &Git{
23 | Dir: dir,
24 | Url: url,
25 | Branch: branch,
26 | Token: token,
27 | commandName: "git",
28 | }
29 | }
30 |
31 | func (g *Git) configUserEmail() error {
32 | args := []string{"config", "user.email", `"kubecloud@example.com"`}
33 | out, err := utils.ExecCommand(g.Dir, g.commandName, args...)
34 | if err != nil {
35 | glog.Errorf(`git config user.email "kubecloud@example.com" error: %s`, out)
36 | return fmt.Errorf(out)
37 | }
38 | glog.Infof(`git config user.name "kubecloud"`)
39 | args = []string{"config", "user.name", `"kubecloud"`}
40 | out, err = utils.ExecCommand(g.Dir, "git", args...)
41 | if err != nil {
42 | glog.Errorf(`git config user.name "kubecloud" error: %s`, out)
43 | return fmt.Errorf(out)
44 | }
45 | glog.Infof(`git config user.name "kubecloud"`)
46 | return nil
47 | }
48 |
49 | func (g *Git) Clone() error {
50 | currentDir, _ := filepath.Abs(`.`)
51 | urlWithToken := strings.ReplaceAll(g.Url, "//", fmt.Sprintf("//kubecloud:%s@", g.Token))
52 | args := []string{"clone", urlWithToken, "-b", g.Branch, g.Dir}
53 | out, err := utils.ExecCommand(currentDir, g.commandName, args...)
54 | if err != nil && !strings.Contains(out, "already exists and is not an empty directory") {
55 | glog.Errorf("git clone %s -b %s %s error: %s", g.Url, g.Branch, g.Dir, out)
56 | return fmt.Errorf(out)
57 | }
58 | glog.Infof("git clone %s -b %s", g.Url, g.Branch)
59 | glog.Infoln(out)
60 | if err := g.configUserEmail(); err != nil {
61 | return fmt.Errorf(out)
62 | }
63 | return nil
64 | }
65 |
66 | func (g *Git) Commit(files []string, msg string) error {
67 | for _, f := range files {
68 | args := []string{"add", f}
69 | out, err := utils.ExecCommand(g.Dir, g.commandName, args...)
70 | if err != nil {
71 | glog.Errorf("git add %s error: %s", f, out)
72 | return fmt.Errorf(out)
73 | }
74 | glog.Infof("git add %s", f)
75 | }
76 | args := []string{"commit", "-m", fmt.Sprintf(`"%s"`, msg)}
77 | out, err := utils.ExecCommand(g.Dir, g.commandName, args...)
78 | if err != nil && !strings.Contains(out, "nothing to commit, working tree clean") {
79 | glog.Errorf("git commit -m %s error: %s", fmt.Sprintf(`"%s"`, msg), out)
80 | return fmt.Errorf(out)
81 | }
82 | glog.Infof("git commit -m %s", fmt.Sprintf(`"%s"`, msg))
83 | glog.Infoln(out)
84 | return nil
85 | }
86 |
87 | func (g *Git) Pull() error {
88 | args := []string{"pull", "origin", g.Branch}
89 | out, err := utils.ExecCommand(g.Dir, g.commandName, args...)
90 | if err != nil {
91 | glog.Errorf("git pull origin %s error: %s", g.Branch, out)
92 | return fmt.Errorf(out)
93 | }
94 | glog.Infof("git pull origin %s", g.Branch)
95 | glog.Infoln(out)
96 | return nil
97 | }
98 |
99 | func (g *Git) Push() error {
100 | args := []string{"push", "origin", g.Branch}
101 | out, err := utils.ExecCommand(g.Dir, g.commandName, args...)
102 | if err != nil {
103 | glog.Errorf("git push origin %s error: %s", g.Branch, out)
104 | return fmt.Errorf(out)
105 | }
106 | glog.Infof("git push origin %s", g.Branch)
107 | glog.Infoln(out)
108 | return nil
109 | }
110 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module kubecloud
2 |
3 | go 1.12
4 |
5 | require (
6 | github.com/astaxie/beego v1.12.0
7 | github.com/ghodss/yaml v1.0.0
8 | github.com/go-sql-driver/mysql v1.4.1
9 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
10 | github.com/gorilla/websocket v1.4.1 // indirect
11 | github.com/prometheus/client_golang v1.0.0
12 | github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect
13 | github.com/stretchr/testify v1.4.0
14 | gopkg.in/igm/sockjs-go.v2 v2.0.1
15 | gopkg.in/yaml.v2 v2.2.4
16 | k8s.io/api v0.17.0
17 | k8s.io/apimachinery v0.17.0
18 | k8s.io/apiserver v0.17.0
19 | k8s.io/client-go v0.17.0
20 | k8s.io/component-base v0.17.0
21 | )
22 |
--------------------------------------------------------------------------------