create deployment

This commit is contained in:
w-mj 2023-02-15 22:37:10 +08:00
parent ef982d7a05
commit 04dae7da4e
No known key found for this signature in database
GPG Key ID: 3A2CB5BE2F835897
10 changed files with 177 additions and 23 deletions

View File

@ -14,3 +14,7 @@ install: docker
uninstall: uninstall:
kubectl delete -f install.yaml kubectl delete -f install.yaml
restart: docker
kubectl delete -f install.yaml
kubectl apply -f install.yaml

16
go.mod
View File

@ -3,7 +3,14 @@ module k8s-manager
go 1.19 go 1.19
require ( require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.8.2 github.com/gin-gonic/gin v1.8.2
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/swag v0.19.14 // indirect
github.com/golang-jwt/jwt/v4 v4.3.0 github.com/golang-jwt/jwt/v4 v4.3.0
github.com/sirupsen/logrus v1.9.0 github.com/sirupsen/logrus v1.9.0
gorm.io/driver/mysql v1.4.6 gorm.io/driver/mysql v1.4.6
@ -11,17 +18,9 @@ require (
k8s.io/api v0.26.1 k8s.io/api v0.26.1
k8s.io/apimachinery v0.26.1 k8s.io/apimachinery v0.26.1
k8s.io/client-go v0.26.1 k8s.io/client-go v0.26.1
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/swag v0.19.14 // indirect
) )
require ( require (
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.11.2 // indirect github.com/go-playground/validator/v10 v10.11.2 // indirect
@ -44,6 +43,7 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/thoas/go-funk v0.9.3 // indirect
github.com/ugorji/go/codec v1.2.9 // indirect github.com/ugorji/go/codec v1.2.9 // indirect
golang.org/x/crypto v0.6.0 // indirect golang.org/x/crypto v0.6.0 // indirect
golang.org/x/net v0.6.0 // indirect golang.org/x/net v0.6.0 // indirect

2
go.sum
View File

@ -194,6 +194,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw=
github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU= github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU=
github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=

View File

@ -20,6 +20,13 @@ spec:
image: docker.educg.net/k8s/manager:dev image: docker.educg.net/k8s/manager:dev
ports: ports:
- containerPort: 8080 - containerPort: 8080
env:
- name: DB_HOST
value: 192.168.131.191
- name: DB_USER
value: app
- name: DB_PASS
value: 1PQlNf9*9Ulo
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
@ -35,4 +42,4 @@ spec:
protocol: TCP protocol: TCP
nodePort: 30001 nodePort: 30001
selector: selector:
app: k8s-manager-deployment app: k8s-manager

View File

@ -14,7 +14,8 @@ import (
var f embed.FS var f embed.FS
func main() { func main() {
go m.ListPods() m.InitClient()
r := gin.Default() r := gin.Default()
r.SetFuncMap(template.FuncMap{ r.SetFuncMap(template.FuncMap{
@ -35,6 +36,10 @@ func main() {
r.POST("/app", m.JWTAuthMiddleware, m.CreateUpdateDeleteApp) r.POST("/app", m.JWTAuthMiddleware, m.CreateUpdateDeleteApp)
r.PUT("/app", m.JWTAuthMiddleware, m.CreateUpdateDeleteApp) r.PUT("/app", m.JWTAuthMiddleware, m.CreateUpdateDeleteApp)
r.DELETE("/app", m.JWTAuthMiddleware, m.CreateUpdateDeleteApp) r.DELETE("/app", m.JWTAuthMiddleware, m.CreateUpdateDeleteApp)
r.POST("/dep", m.JWTAuthMiddleware, m.CreateDeleteDeployment)
r.DELETE("/dep", m.JWTAuthMiddleware, m.CreateDeleteDeployment)
r.GET("/login/:username", Test) r.GET("/login/:username", Test)
err := r.Run() err := r.Run()
if err != nil { if err != nil {

View File

@ -28,6 +28,12 @@ func GetApps(username string) []AppModel {
return ans return ans
} }
func (a *AppModel) Fill() error {
db := DB()
res := db.First(a, a.Id)
return res.Error
}
func (a *AppModel) Update() error { func (a *AppModel) Update() error {
db := DB() db := DB()
db.Save(a) db.Save(a)
@ -59,6 +65,9 @@ func (a *AppModel) ContainerName() string {
} }
func (a *AppModel) ServiceName() string { func (a *AppModel) ServiceName() string {
if strings.TrimSpace(a.SvcName) != "" {
return a.SvcName
}
return fmt.Sprintf("%s-service", a.NamePrefix()) return fmt.Sprintf("%s-service", a.NamePrefix())
} }

View File

@ -3,28 +3,71 @@ package k8s_manager
import ( import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/thoas/go-funk"
v1 "k8s.io/api/apps/v1"
"net/http" "net/http"
"strings"
) )
type AppViewModel struct { type AppViewModel struct {
*AppModel
Status string
}
type AppsViewModel struct {
Username string Username string
Apps []AppModel Apps []AppViewModel
}
func statusString(status *v1.DeploymentStatus, err error) string {
if err != nil {
if strings.Contains(err.Error(), "not found") {
return "Stopped"
}
return err.Error()
} else {
if status.AvailableReplicas > 0 {
return "Running"
} else {
return "Error"
}
}
} }
func AppPage(c *gin.Context) { func AppPage(c *gin.Context) {
username := c.MustGet("username").(string) username := c.MustGet("username").(string)
vm := &AppViewModel{ appsvm := funk.Map(GetApps(username), func(a AppModel) AppViewModel { return AppViewModel{AppModel: &a} }).([]AppViewModel)
for i := range appsvm {
a := &appsvm[i]
d, e := K.GetAppStatus(a.AppModel)
a.Status = statusString(d, e)
}
vm := &AppsViewModel{
Username: username, Username: username,
Apps: GetApps(username), Apps: appsvm,
} }
c.HTML(http.StatusOK, "app_view.html", vm) c.HTML(http.StatusOK, "app_view.html", vm)
} }
func AppMiddleware(c *gin.Context) {
app := &AppModel{}
err := c.ShouldBindJSON(&app)
if err == nil {
username := c.MustGet("username")
if app.UserName != username {
c.JSON(http.StatusUnauthorized, gin.H{"msg": "user error."})
c.Abort()
}
c.Set("app", app)
}
}
func CreateUpdateDeleteApp(c *gin.Context) { func CreateUpdateDeleteApp(c *gin.Context) {
app := &AppModel{} app := &AppModel{}
err := c.BindJSON(app) err := c.BindJSON(app)
if err != nil { if err != nil {
log.WithError(err).Error("AddApp") log.WithError(err).Error("CreateUpdateDeleteApp")
c.JSON(http.StatusBadRequest, gin.H{"msg": "Cannot get data."}) c.JSON(http.StatusBadRequest, gin.H{"msg": "Cannot get data."})
} else { } else {
username := c.MustGet("username").(string) username := c.MustGet("username").(string)

51
src/deployment_manager.go Normal file
View File

@ -0,0 +1,51 @@
package k8s_manager
import (
"github.com/gin-gonic/gin"
"k8s.io/api/apps/v1"
"net/http"
"strconv"
"strings"
)
var K *K8sClient = nil
func InitClient() {
K = CreateClient()
}
type DeployStatusObject struct {
E string `json:"e"`
S *v1.DeploymentStatus `json:"s"`
}
func CreateDeleteDeployment(c *gin.Context) {
tokenUsername := c.MustGet("username").(string)
id := c.Query("id")
if strings.TrimSpace(id) == "" {
c.JSON(http.StatusBadRequest, gin.H{"msg": "no app id"})
return
}
appId, err := strconv.Atoi(id)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"msg": "wrong id"})
return
}
app := AppModel{Id: appId}
if app.Fill() != nil {
c.JSON(http.StatusBadRequest, gin.H{"msg": "wrong id"})
return
}
if app.UserName != tokenUsername {
c.JSON(http.StatusUnauthorized, gin.H{"msg": "user error."})
return
}
if c.Request.Method == http.MethodPost {
K.CreateApp(&app)
} else if c.Request.Method == http.MethodDelete {
K.DeleteApp(&app)
} else {
c.JSON(http.StatusMethodNotAllowed, gin.H{"msg": "unsupported method"})
}
c.JSON(http.StatusOK, gin.H{"msg": "ok"})
}

View File

@ -3,8 +3,9 @@ package k8s_manager
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/goccy/go-json"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"k8s.io/api/apps/v1beta1" appv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
@ -16,7 +17,7 @@ type K8sClient struct {
Namespace string Namespace string
} }
func InitClient() *K8sClient { func CreateClient() *K8sClient {
var err error var err error
config, err := rest.InClusterConfig() config, err := rest.InClusterConfig()
if err != nil { if err != nil {
@ -40,17 +41,22 @@ func (c *K8sClient) ListPods() {
} }
} }
func (c *K8sClient) CreateApp(app *AppModel) { func (c *K8sClient) GetAppStatus(app *AppModel) (*appv1.DeploymentStatus, error) {
get, err := c.AppsV1().Deployments(c.Namespace).Get(context.Background(), app.DeploymentName(), metav1.GetOptions{})
// log.WithError(err).WithField("dep", get).WithField("name", app.DeploymentName()).Info("GetAppStatus")
return &get.Status, err
}
func (c *K8sClient) CreateApp(app *AppModel) {
var rep int32 = 1 var rep int32 = 1
labelKey, labelValue := app.GetLabel() labelKey, labelValue := app.GetLabel()
labels := map[string]string{labelKey: labelValue} labels := map[string]string{labelKey: labelValue}
deployment := &v1beta1.Deployment{ deployment := &appv1.Deployment{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: app.DeploymentName(), Name: app.DeploymentName(),
Labels: labels, Labels: labels,
}, },
Spec: v1beta1.DeploymentSpec{ Spec: appv1.DeploymentSpec{
Replicas: &rep, Replicas: &rep,
Selector: &metav1.LabelSelector{ Selector: &metav1.LabelSelector{
MatchLabels: labels, MatchLabels: labels,
@ -71,8 +77,10 @@ func (c *K8sClient) CreateApp(app *AppModel) {
}, },
}, },
} }
deployment, err := c.AppsV1beta1().Deployments(c.Namespace).Create(context.Background(), deployment, metav1.CreateOptions{}) deployment, err := c.AppsV1().Deployments(c.Namespace).Create(context.Background(), deployment, metav1.CreateOptions{})
if err != nil { if err != nil {
d, _ := json.Marshal(deployment)
fmt.Println(string(d))
log.WithError(err).Error("Create Deployment error.") log.WithError(err).Error("Create Deployment error.")
} }
@ -82,6 +90,7 @@ func (c *K8sClient) CreateApp(app *AppModel) {
}, },
Spec: v1.ServiceSpec{ Spec: v1.ServiceSpec{
Type: v1.ServiceTypeClusterIP, Type: v1.ServiceTypeClusterIP,
ClusterIP: "None",
Selector: labels, Selector: labels,
}, },
} }
@ -91,3 +100,11 @@ func (c *K8sClient) CreateApp(app *AppModel) {
log.WithError(err).Error("Create Service ") log.WithError(err).Error("Create Service ")
} }
} }
func (c *K8sClient) DeleteApp(app *AppModel) {
err := c.AppsV1().Deployments(c.Namespace).Delete(context.Background(), app.DeploymentName(), metav1.DeleteOptions{})
if err != nil {
log.WithError(err).Error("Delete error ", app.DeploymentName())
return
}
}

View File

@ -33,6 +33,7 @@
<th>镜像名</th> <th>镜像名</th>
<th>启动命令</th> <th>启动命令</th>
<th>环境变量</th> <th>环境变量</th>
<th>状态</th>
<th>操作</th> <th>操作</th>
</tr> </tr>
</thead> </thead>
@ -46,8 +47,11 @@
<td class="app-field-image"> {{ $v.Image }}</td> <td class="app-field-image"> {{ $v.Image }}</td>
<td class="app-field-cmd"> {{ $v.Command }}</td> <td class="app-field-cmd"> {{ $v.Command }}</td>
<td class="app-field-env"> {{ $v.Env }}</td> <td class="app-field-env"> {{ $v.Env }}</td>
<td class="app-field-status" id="app-status-{{ $v.Id }}"> {{ $v.Status }} </td>
<td> <td>
<div class="btn-group"> <div class="btn-group">
<button class="btn btn-success" onclick="startApp('{{$v.Id}}')">启动</button>
<button class="btn btn-dark" onclick="stopApp('{{$v.Id}}')">停止</button>
<button class="btn btn-primary" onclick="showModal('app-list-{{ $v.Id }}')">修改</button> <button class="btn btn-primary" onclick="showModal('app-list-{{ $v.Id }}')">修改</button>
<button class="btn btn-danger" onclick="deleteApp('{{ $v.Id }}')">删除</button> <button class="btn btn-danger" onclick="deleteApp('{{ $v.Id }}')">删除</button>
</div> </div>
@ -202,5 +206,17 @@
}) })
} }
function startApp(id) {
axios.post("dep?id=" + id).then(e=>{
console.log(e.data)
})
}
function stopApp(id) {
axios.delete("dep?id=" + id).then(e=>{
console.log(e.data)
})
}
</script> </script>
</html> </html>