mirror of
				https://github.com/1Panel-dev/KubePi.git
				synced 2025-10-31 18:42:41 +08:00 
			
		
		
		
	feat(cluster): 增加集群导入
This commit is contained in:
		
							
								
								
									
										101
									
								
								internal/api/v1/cluster/cluster.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								internal/api/v1/cluster/cluster.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | |||||||
|  | package cluster | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	kContext "context" | ||||||
|  | 	"fmt" | ||||||
|  | 	v1 "github.com/KubeOperator/ekko/internal/model/v1" | ||||||
|  | 	v1Cluster "github.com/KubeOperator/ekko/internal/model/v1/cluster" | ||||||
|  | 	"github.com/KubeOperator/ekko/internal/service/v1/cluster" | ||||||
|  | 	"github.com/KubeOperator/ekko/internal/util/kubernetes" | ||||||
|  | 	"github.com/asdine/storm/v3" | ||||||
|  | 	"github.com/google/uuid" | ||||||
|  | 	"github.com/kataras/iris/v12" | ||||||
|  | 	"github.com/kataras/iris/v12/context" | ||||||
|  | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type Handler struct { | ||||||
|  | 	clusterService cluster.Cluster | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewHandler() *Handler { | ||||||
|  | 	return &Handler{ | ||||||
|  | 		clusterService: cluster.NewClusterService(), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (h *Handler) Create() iris.Handler { | ||||||
|  | 	return func(ctx *context.Context) { | ||||||
|  | 		var clusterImport Import | ||||||
|  | 		if err := ctx.ReadJSON(&clusterImport); err != nil { | ||||||
|  | 			ctx.StatusCode(iris.StatusBadRequest) | ||||||
|  | 			ctx.Values().Set("message", err.Error()) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		c, err := h.clusterService.Get(clusterImport.Name) | ||||||
|  | 		if err != nil && err != storm.ErrNotFound { | ||||||
|  | 			ctx.StatusCode(iris.StatusBadRequest) | ||||||
|  | 			ctx.Values().Set("message", fmt.Sprintf("error: %s", err.Error())) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		if c != nil && c.Name != "" { | ||||||
|  | 			ctx.StatusCode(iris.StatusBadRequest) | ||||||
|  | 			ctx.Values().Set("message", fmt.Sprintf("cluster name %s is alerady exist", clusterImport.Name)) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		client, err := kubernetes.NewKubernetesClient(&kubernetes.Config{ | ||||||
|  | 			Host:  clusterImport.ApiServer, | ||||||
|  | 			Token: clusterImport.Token, | ||||||
|  | 		}) | ||||||
|  | 		if err != nil { | ||||||
|  | 			ctx.StatusCode(iris.StatusBadRequest) | ||||||
|  | 			ctx.Values().Set("message", fmt.Sprintf("import cluster filed: %s", err.Error())) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		_, err = client.CoreV1().Namespaces().List(kContext.TODO(), metav1.ListOptions{}) | ||||||
|  | 		if err != nil { | ||||||
|  | 			ctx.StatusCode(iris.StatusBadRequest) | ||||||
|  | 			ctx.Values().Set("message", fmt.Sprintf("import cluster failed: %s", err.Error())) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		cs := v1Cluster.Cluster{ | ||||||
|  | 			Metadata: v1.Metadata{ | ||||||
|  | 				Name: clusterImport.Name, | ||||||
|  | 				UUID: uuid.New().String(), | ||||||
|  | 			}, | ||||||
|  | 			Token:     clusterImport.Token, | ||||||
|  | 			Router:    clusterImport.Router, | ||||||
|  | 			ApiServer: clusterImport.ApiServer, | ||||||
|  | 			Status:    "Running", | ||||||
|  | 		} | ||||||
|  | 		err = h.clusterService.Create(&cs) | ||||||
|  | 		if err != nil { | ||||||
|  | 			ctx.StatusCode(iris.StatusBadRequest) | ||||||
|  | 			ctx.Values().Set("message", fmt.Sprintf("create cluster failed: %s", err.Error())) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		ctx.StatusCode(iris.StatusOK) | ||||||
|  | 		ctx.Values().Set("data", cs) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (h *Handler) ListAll() iris.Handler { | ||||||
|  | 	return func(ctx *context.Context) { | ||||||
|  | 		var clusters []v1Cluster.Cluster | ||||||
|  | 		clusters, err := h.clusterService.All() | ||||||
|  | 		if err != nil { | ||||||
|  | 			ctx.StatusCode(iris.StatusBadRequest) | ||||||
|  | 			ctx.Values().Set("message", fmt.Sprintf("get clusters failed: %s", err.Error())) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		ctx.StatusCode(iris.StatusOK) | ||||||
|  | 		ctx.Values().Set("data", clusters) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Install(parent iris.Party) { | ||||||
|  | 	handler := NewHandler() | ||||||
|  | 	sp := parent.Party("/clusters") | ||||||
|  | 	sp.Post("", handler.Create()) | ||||||
|  | 	sp.Get("", handler.ListAll()) | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								internal/api/v1/cluster/types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								internal/api/v1/cluster/types.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | package cluster | ||||||
|  |  | ||||||
|  | type Import struct { | ||||||
|  | 	Name      string `json:"name"` | ||||||
|  | 	ApiServer string `json:"apiServer"` | ||||||
|  | 	Router    string `json:"router"` | ||||||
|  | 	Token     string `json:"token"` | ||||||
|  | } | ||||||
| @@ -1,6 +1,7 @@ | |||||||
| package v1 | package v1 | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"github.com/KubeOperator/ekko/internal/api/v1/cluster" | ||||||
| 	"github.com/KubeOperator/ekko/internal/api/v1/session" | 	"github.com/KubeOperator/ekko/internal/api/v1/session" | ||||||
| 	"github.com/kataras/iris/v12" | 	"github.com/kataras/iris/v12" | ||||||
| ) | ) | ||||||
| @@ -8,4 +9,5 @@ import ( | |||||||
| func AddV1Route(app iris.Party) { | func AddV1Route(app iris.Party) { | ||||||
| 	v1Party := app.Party("/v1") | 	v1Party := app.Party("/v1") | ||||||
| 	session.Install(v1Party) | 	session.Install(v1Party) | ||||||
|  | 	cluster.Install(v1Party) | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								internal/model/v1/cluster/cluster.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								internal/model/v1/cluster/cluster.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | package cluster | ||||||
|  |  | ||||||
|  | import v1 "github.com/KubeOperator/ekko/internal/model/v1" | ||||||
|  |  | ||||||
|  | type Cluster struct { | ||||||
|  | 	v1.BaseModel `storm:"inline"` | ||||||
|  | 	v1.Metadata  `storm:"inline"` | ||||||
|  | 	ApiServer    string `json:"apiServer"` | ||||||
|  | 	Router       string `json:"router"` | ||||||
|  | 	Token        string `json:"token"` | ||||||
|  | 	Status       string `json:"status"` | ||||||
|  | } | ||||||
							
								
								
									
										42
									
								
								internal/service/v1/cluster/cluster.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								internal/service/v1/cluster/cluster.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | package cluster | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	v1Cluster "github.com/KubeOperator/ekko/internal/model/v1/cluster" | ||||||
|  | 	"github.com/KubeOperator/ekko/internal/server" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type Cluster interface { | ||||||
|  | 	Create(cluster *v1Cluster.Cluster) error | ||||||
|  | 	Get(name string) (*v1Cluster.Cluster, error) | ||||||
|  | 	All() ([]v1Cluster.Cluster, error) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewClusterService() Cluster { | ||||||
|  | 	return &cluster{} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type cluster struct { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *cluster) Create(cluster *v1Cluster.Cluster) error { | ||||||
|  | 	db := server.DB() | ||||||
|  | 	return db.Save(cluster) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *cluster) Get(name string) (*v1Cluster.Cluster, error) { | ||||||
|  | 	db := server.DB() | ||||||
|  | 	var cluster v1Cluster.Cluster | ||||||
|  | 	if err := db.One("Name", name, &cluster); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return &cluster, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *cluster) All() ([]v1Cluster.Cluster, error) { | ||||||
|  | 	db := server.DB() | ||||||
|  | 	var clusters []v1Cluster.Cluster | ||||||
|  | 	if err := db.All(&clusters); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return clusters, nil | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								internal/util/kubernetes/client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								internal/util/kubernetes/client.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | package kubernetes | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"k8s.io/client-go/kubernetes" | ||||||
|  | 	"k8s.io/client-go/rest" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type Config struct { | ||||||
|  | 	Host  string | ||||||
|  | 	Token string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewKubernetesClient(c *Config) (*kubernetes.Clientset, error) { | ||||||
|  | 	kubeConf := &rest.Config{ | ||||||
|  | 		Host:        c.Host, | ||||||
|  | 		BearerToken: c.Token, | ||||||
|  | 		TLSClientConfig: rest.TLSClientConfig{ | ||||||
|  | 			Insecure: true, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	return kubernetes.NewForConfig(kubeConf) | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								internal/util/kubernetes/client_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								internal/util/kubernetes/client_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | package kubernetes | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"fmt" | ||||||
|  | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestNewKubernetesClient(t *testing.T) { | ||||||
|  | 	c, err := NewKubernetesClient(&Config{ | ||||||
|  | 		Host:  "172.16.10.125:8443", | ||||||
|  | 		Token: "eyJhbGciOiJSUzI1NiIsImtpZCI6Ii1odW5XaTdXY1ZTZmdRWVJFLTN4aUhLemttN21mOV83TG9jbmp5dzJtR1kifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJrby1hZG1pbi10b2tlbi05c21kMiIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJrby1hZG1pbiIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjhjYTNlZjExLWU3YTMtNGU2Zi1iNzljLTI2NzMzZDY1MmQ5ZiIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTprby1hZG1pbiJ9.ntgCFo_UL8Hah69Nba__RwJbUGEvznUFAWX2XRXII5kexC_av5isAp3rJyv-Lqq_qGd9mJcDGm0TodMo4sP46qsMuKQ-LM6-lvCK77-y5wnGuqyXjT9C6-h7nOfP55gCaOX9mNY40l2fgjVVLcaaTklB9-QceRFgOzfjRZGwcExyL-JNepw78yMnRM_g1fV4w1VduBqniOgoM5uzACJPO-viyoZPmedCadMlnKhJnETshAe_zNZT9yZ90ujwprI9znrJLt6szLmQgK24EbcG4KF3T9PLyIBO_K0clG_U36Q14DowCLRenKZw9HOsU-AbAzwIGMjj2x8QohGDMZglRw", | ||||||
|  | 	}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Println(err) | ||||||
|  | 	} | ||||||
|  | 	l, err := c.CoreV1().ServiceAccounts("default").List(context.TODO(), metav1.ListOptions{}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Println(err) | ||||||
|  | 	} | ||||||
|  | 	fmt.Println(l) | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								ui/src/api/clusters.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								ui/src/api/clusters.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | import {post,get} from "@/plugins/request" | ||||||
|  |  | ||||||
|  | const authUrl = "/api/v1/clusters" | ||||||
|  |  | ||||||
|  | export function create(data) { | ||||||
|  |   return post(authUrl, data) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function listAll(){ | ||||||
|  |   return get(authUrl) | ||||||
|  | } | ||||||
| @@ -1,18 +1,32 @@ | |||||||
| <template> | <template> | ||||||
|   <layout-content :header="$t('business.cluster.import')"  :back-to="{ name: 'ClusterList' }"> |   <layout-content :header="$t('business.cluster.import')" :back-to="{ name: 'ClusterList' }"> | ||||||
|     <el-row> |     <el-row> | ||||||
|       <el-col :span="4"><br/></el-col> |       <el-col :span="4"><br/></el-col> | ||||||
|       <el-col :span="10"> |       <el-col :span="10"> | ||||||
|         <div class="grid-content bg-purple-light"> |         <div class="grid-content bg-purple-light"> | ||||||
|             <el-form :model="form" label-position="left" :rules="rules" label-width="120px"> |           <el-form :model="form" label-position="left" :rules="rules" label-width="120px"> | ||||||
|               <el-form-item  :label="$t('commons.table.name')" prop="name"> |             <el-form-item :label="$t('commons.table.name')" prop="name"> | ||||||
|                   <el-input style="width: 100%" v-model="form.name" clearable></el-input> |               <el-input v-model="form.name" clearable></el-input> | ||||||
|               </el-form-item> |             </el-form-item> | ||||||
|               <el-form-item style="float: right"> |             <el-form-item label="apiServer" prop="apiServer"> | ||||||
|                 <el-button @click="onCancel()">{{ $t("commons.button.cancel") }}</el-button> |               <el-input v-model="form.apiServer" :placeholder="$t('business.cluster.api_server_help')" | ||||||
|                 <el-button v-loading="loading" @click="onSubmit" type="primary">{{ $t("commons.button.create") }}</el-button> |                         clearable></el-input> | ||||||
|               </el-form-item> |             </el-form-item> | ||||||
|             </el-form> |             <el-form-item label="router" prop="router"> | ||||||
|  |               <el-input v-model="form.router" :placeholder="$t('business.cluster.router_help')" clearable></el-input> | ||||||
|  |             </el-form-item> | ||||||
|  |             <el-form-item label="token" prop="token"> | ||||||
|  |               <el-input type="textarea" :autosize="{ minRows: 2}" style="width: 100%" v-model="form.token" | ||||||
|  |                         clearable></el-input> | ||||||
|  |             </el-form-item> | ||||||
|  |             <el-form-item style="float: right"> | ||||||
|  |               <el-button @click="onCancel()">{{ $t("commons.button.cancel") }}</el-button> | ||||||
|  |               <el-button v-loading="loading" @click="onSubmit" type="primary">{{ | ||||||
|  |                   $t("commons.button.create") | ||||||
|  |                 }} | ||||||
|  |               </el-button> | ||||||
|  |             </el-form-item> | ||||||
|  |           </el-form> | ||||||
|         </div> |         </div> | ||||||
|       </el-col> |       </el-col> | ||||||
|     </el-row> |     </el-row> | ||||||
| @@ -21,26 +35,35 @@ | |||||||
|  |  | ||||||
| <script> | <script> | ||||||
| import LayoutContent from "@/components/layout/LayoutContent" | import LayoutContent from "@/components/layout/LayoutContent" | ||||||
|  | import {create} from "@/api/clusters" | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|   name: "ClusterImport", |   name: "ClusterImport", | ||||||
|   components: { LayoutContent }, |   components: { LayoutContent }, | ||||||
|   data() { |   data () { | ||||||
|     return { |     return { | ||||||
|       form: { |       form: { | ||||||
|         name: "" |         name: "", | ||||||
|  |         apiServer: "", | ||||||
|  |         router: "", | ||||||
|  |         token: "" | ||||||
|       }, |       }, | ||||||
|       rules: {}, |       rules: {}, | ||||||
|       loading: false |       loading: false | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|     onCancel() { |     onCancel () { | ||||||
|       this.$router.push({ name: "ClusterList" }) |       this.$router.push({ name: "ClusterList" }) | ||||||
|  |  | ||||||
|     }, |     }, | ||||||
|     onSubmit() { |     onSubmit () { | ||||||
|  |       create(this.form).then(() => { | ||||||
|  |         this.$message({ | ||||||
|  |           type: "success", | ||||||
|  |           message: this.$t("创建成功") | ||||||
|  |         }) | ||||||
|  |       }) | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
| } | } | ||||||
|   | |||||||
| @@ -25,6 +25,7 @@ | |||||||
| <script> | <script> | ||||||
| import LayoutContent from "@/components/layout/LayoutContent" | import LayoutContent from "@/components/layout/LayoutContent" | ||||||
| import ComplexTable from "@/components/complex-table" | import ComplexTable from "@/components/complex-table" | ||||||
|  | import {listAll} from "@/api/clusters" | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|   name: "ClusterList", |   name: "ClusterList", | ||||||
| @@ -50,8 +51,6 @@ export default { | |||||||
|         ], |         ], | ||||||
|       }, |       }, | ||||||
|       data: [ |       data: [ | ||||||
|         { name: "测试" }, |  | ||||||
|         { name: "测试2" } |  | ||||||
|       ], |       ], | ||||||
|       selects: [] |       selects: [] | ||||||
|     } |     } | ||||||
| @@ -60,6 +59,14 @@ export default { | |||||||
|     onImport () { |     onImport () { | ||||||
|       this.$router.push({ name: "ClusterImport" }) |       this.$router.push({ name: "ClusterImport" }) | ||||||
|     }, |     }, | ||||||
|  |     list() { | ||||||
|  |       listAll().then(res => { | ||||||
|  |         this.data =res.data | ||||||
|  |       }) | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   created () { | ||||||
|  |     this.list() | ||||||
|   } |   } | ||||||
| } | } | ||||||
| </script> | </script> | ||||||
|   | |||||||
| @@ -58,7 +58,9 @@ const message = { | |||||||
|     cluster: { |     cluster: { | ||||||
|       cluster: "集群", |       cluster: "集群", | ||||||
|       list: "集群列表", |       list: "集群列表", | ||||||
|       import: "导入集群" |       import: "导入集群", | ||||||
|  |       api_server_help: "例如: https://172.16.10.100:8443", | ||||||
|  |       router_help: "装有 kube-proxy 的任意节点的且可以被访问到的 IP 地址", | ||||||
|     }, |     }, | ||||||
|     workload: { |     workload: { | ||||||
|       workload: "工作量" |       workload: "工作量" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 wangzhengkun
					wangzhengkun