Merge branch 'master' of https://gitlab.educg.net/wangmingjian/k8s-manager
Conflicts: install.yaml
This commit is contained in:
commit
93a7210db3
10
install.yaml
10
install.yaml
|
|
@ -33,11 +33,15 @@ spec:
|
|||
value: "!bd832W0@CG"
|
||||
- name: ASE_KEY
|
||||
value: A5es$&!0GeoEast
|
||||
- name: NFS_HOST
|
||||
value: 192.168.252.250
|
||||
- name: NFS_PATH
|
||||
value: /mnt/CGdata/ms
|
||||
volumeMounts:
|
||||
- name: persistent-storage
|
||||
mountPath: /nfs
|
||||
- name: k8s-manager-nfs-volume
|
||||
mountPath: /mnt/CGdata/ms
|
||||
volumes:
|
||||
- name: persistent-storage
|
||||
- name: k8s-manager-nfs-volume
|
||||
nfs:
|
||||
server: 192.168.252.250
|
||||
path: /mnt/CGdata/ms
|
||||
|
|
|
|||
61
main.go
61
main.go
|
|
@ -21,21 +21,10 @@ import (
|
|||
//go:embed template/* static/*
|
||||
var f embed.FS
|
||||
|
||||
var jwtKey string
|
||||
var aseKey string
|
||||
|
||||
func main() {
|
||||
m.InitConfig()
|
||||
m.InitClient()
|
||||
|
||||
jwtKey = os.Getenv("JWT_KEY")
|
||||
aseKey = os.Getenv("ASE_KEY")
|
||||
if jwtKey == "" {
|
||||
panic("No jwt key")
|
||||
}
|
||||
if aseKey == "" {
|
||||
panic("No ase key")
|
||||
}
|
||||
|
||||
r := gin.Default()
|
||||
|
||||
fp, _ := fs.Sub(f, "static")
|
||||
|
|
@ -52,7 +41,9 @@ func main() {
|
|||
r.POST("/dep", m.JWTAuthMiddleware, m.CreateDeleteDeployment)
|
||||
r.DELETE("/dep", m.JWTAuthMiddleware, m.CreateDeleteDeployment)
|
||||
|
||||
r.GET("/login", Test)
|
||||
r.GET("/pod", m.JWTAuthMiddleware, m.GetPodStatus)
|
||||
|
||||
r.GET("/login", LoginToken)
|
||||
r.Any("/test_token", TestToken)
|
||||
r.GET("/username", m.JWTAuthMiddleware, m.GetUsername)
|
||||
err := r.Run(":8000")
|
||||
|
|
@ -76,10 +67,10 @@ func ParseCGToken(token string) (string, error) {
|
|||
return "", err
|
||||
}
|
||||
|
||||
jwtString := AesDecryptECB(aesEncrypt, []byte(aseKey))
|
||||
jwtString := AesDecryptECB(aesEncrypt, []byte(m.GetConfig().AseKey))
|
||||
|
||||
jwtResult, err := jwt.ParseWithClaims(string(jwtString), &CGJWTClaim{}, func(token *jwt.Token) (i interface{}, err error) {
|
||||
return []byte(jwtKey), nil
|
||||
return []byte(m.GetConfig().JwtKey), nil
|
||||
// return hmac.New(sha256.New, []byte(jwtKey)), nil
|
||||
})
|
||||
if err != nil {
|
||||
|
|
@ -146,7 +137,7 @@ func AesDecrypt(data []byte, key []byte) ([]byte, error) {
|
|||
return crypted, nil
|
||||
}
|
||||
|
||||
func Test(c *gin.Context) {
|
||||
func LoginToken(c *gin.Context) {
|
||||
username := c.Request.URL.Query().Get("username")
|
||||
var err error
|
||||
if username == "" {
|
||||
|
|
@ -158,26 +149,30 @@ func Test(c *gin.Context) {
|
|||
"msg": "user name error",
|
||||
})
|
||||
} else {
|
||||
token, err := m.GenerateToken(username)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
log.WithField("user name", username).Info("Login success.")
|
||||
c.SetCookie("Authorization", "Bearer "+token, 0, "", "", false, false)
|
||||
c.Header("Authorization", "Bearer "+token)
|
||||
c.Header("Cache-Control", "no-store")
|
||||
redirectUrl := ""
|
||||
if os.Getenv("URL_PREFIX") == "" {
|
||||
redirectUrl = "/index"
|
||||
} else {
|
||||
redirectUrl = fmt.Sprintf("/%s/index", os.Getenv("URL_PREFIX"))
|
||||
}
|
||||
c.Redirect(http.StatusTemporaryRedirect, redirectUrl)
|
||||
Login(c, username)
|
||||
}
|
||||
}
|
||||
|
||||
func Login(c *gin.Context, username string) {
|
||||
token, err := m.GenerateToken(username)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
log.WithField("user name", username).Info("Login success.")
|
||||
c.SetCookie("Authorization", "Bearer "+token, 0, "", "", false, false)
|
||||
c.Header("Authorization", "Bearer "+token)
|
||||
c.Header("Cache-Control", "no-store")
|
||||
redirectUrl := ""
|
||||
if os.Getenv("URL_PREFIX") == "" {
|
||||
redirectUrl = "/index"
|
||||
} else {
|
||||
redirectUrl = fmt.Sprintf("/%s/index", os.Getenv("URL_PREFIX"))
|
||||
}
|
||||
c.Redirect(http.StatusTemporaryRedirect, redirectUrl)
|
||||
}
|
||||
|
||||
func TestToken(c *gin.Context) {
|
||||
token := c.Request.URL.Query().Get("cgtoken")
|
||||
if token == "" {
|
||||
|
|
|
|||
|
|
@ -37,21 +37,25 @@ func (a *AppModel) Fill() error {
|
|||
return res.Error
|
||||
}
|
||||
|
||||
func (a *AppModel) Update() error {
|
||||
func (a *AppModel) fillDefaults() {
|
||||
if a.SvcName == "" {
|
||||
a.SvcName = "svc"
|
||||
}
|
||||
a.SvcName = strings.TrimPrefix(a.SvcName, a.NamePrefix()+"-")
|
||||
if a.PersistentPath == "" {
|
||||
a.PersistentPath = "/data/"
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AppModel) Update() error {
|
||||
a.fillDefaults()
|
||||
db := DB()
|
||||
db.Save(a)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AppModel) Create() error {
|
||||
if a.SvcName == "" {
|
||||
a.SvcName = "svc"
|
||||
}
|
||||
a.SvcName = strings.TrimPrefix(a.SvcName, a.NamePrefix()+"-")
|
||||
a.fillDefaults()
|
||||
db := DB()
|
||||
db.Create(a)
|
||||
return nil
|
||||
|
|
@ -156,7 +160,7 @@ func (a *AppModel) PersistentVolumeName() string {
|
|||
}
|
||||
|
||||
func (a *AppModel) NFSPath() string {
|
||||
return "/data/" + a.UserName
|
||||
return GetConfig().NFSPath + "/" + a.UserName
|
||||
}
|
||||
|
||||
func (a *AppModel) PersistentVolumeClaimName() string {
|
||||
|
|
@ -166,3 +170,7 @@ func (a *AppModel) PersistentVolumeClaimName() string {
|
|||
func (a *AppModel) StorageClassName() string {
|
||||
return a.NamePrefix() + "-storage-class"
|
||||
}
|
||||
|
||||
func (a *AppModel) PersistentVolumeMountName() string {
|
||||
return a.NamePrefix() + "-pvm"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
package k8s_manager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
JwtKey string
|
||||
AseKey string
|
||||
DBHost string
|
||||
DBUser string
|
||||
DBPassword string
|
||||
NFSHost string
|
||||
NFSPath string
|
||||
}
|
||||
|
||||
var config *Config
|
||||
|
||||
func getField(v *Config, field string) string {
|
||||
r := reflect.ValueOf(v)
|
||||
f := reflect.Indirect(r).FieldByName(field)
|
||||
return f.String()
|
||||
}
|
||||
|
||||
func setField(v *Config, field string, value string) {
|
||||
r := reflect.ValueOf(v)
|
||||
f := reflect.Indirect(r).FieldByName(field)
|
||||
f.SetString(value)
|
||||
}
|
||||
|
||||
func InitConfig() {
|
||||
config = &Config{}
|
||||
config.JwtKey = os.Getenv("JWT_KEY")
|
||||
config.AseKey = os.Getenv("ASE_KEY")
|
||||
config.DBHost = os.Getenv("DB_HOST")
|
||||
config.DBUser = os.Getenv("DB_USER")
|
||||
config.DBPassword = os.Getenv("DB_PASS")
|
||||
config.NFSHost = os.Getenv("NFS_HOST")
|
||||
config.NFSPath = os.Getenv("NFS_PATH")
|
||||
|
||||
mustFields := []string{"JwtKey", "AseKey", "DBHost", "DBUser", "DBPassword", "NFSHost", "NFSPath"}
|
||||
optionalFields := map[string]string{}
|
||||
for _, f := range mustFields {
|
||||
if getField(config, f) == "" {
|
||||
panic(fmt.Sprintf("field %s must be set.", f))
|
||||
}
|
||||
}
|
||||
for f, v := range optionalFields {
|
||||
if getField(config, f) == "" {
|
||||
setField(config, f, v)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func GetConfig() *Config {
|
||||
return config
|
||||
}
|
||||
|
|
@ -49,3 +49,20 @@ func CreateDeleteDeployment(c *gin.Context) {
|
|||
}
|
||||
c.JSON(http.StatusOK, gin.H{"msg": "ok"})
|
||||
}
|
||||
|
||||
func GetPodStatus(c *gin.Context) {
|
||||
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}
|
||||
app.Fill()
|
||||
pod := K.GetPodFromDeployment(app.DeploymentName())
|
||||
c.JSON(http.StatusOK, pod.Status)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,20 +72,27 @@ func (c *K8sClient) DeploymentExists(deploymentName string) bool {
|
|||
}
|
||||
|
||||
func (c *K8sClient) UpdateDeployment(app *AppModel) {
|
||||
var rep int32 = 1
|
||||
labelKey, labelValue := app.GetLabel()
|
||||
l := map[string]string{labelKey: labelValue}
|
||||
if _, err := os.Stat(app.NFSPath()); os.IsNotExist(err) {
|
||||
os.MkdirAll(app.NFSPath(), os.ModeDir)
|
||||
}
|
||||
podLabels := map[string]string{
|
||||
labelKey: labelValue,
|
||||
"cg-deployment": app.DeploymentName(),
|
||||
}
|
||||
containerPorts := funk.Map(app.GetPorts(), func(port int32) v1.ContainerPort {
|
||||
return v1.ContainerPort{
|
||||
ContainerPort: port,
|
||||
}
|
||||
}).([]v1.ContainerPort)
|
||||
deployment := &appv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: app.DeploymentName(),
|
||||
Labels: l,
|
||||
},
|
||||
Spec: appv1.DeploymentSpec{
|
||||
Replicas: &rep,
|
||||
Replicas: UnnamedPointer(int32(1)),
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: l,
|
||||
},
|
||||
|
|
@ -102,14 +109,30 @@ func (c *K8sClient) UpdateDeployment(app *AppModel) {
|
|||
Name: "URL_PREFIX",
|
||||
Value: app.ServiceName(),
|
||||
}),
|
||||
VolumeMounts: nil,
|
||||
Ports: []v1.ContainerPort{
|
||||
{ContainerPort: 80},
|
||||
},
|
||||
//VolumeMounts: []v1.VolumeMount{
|
||||
// {
|
||||
// Name: app.PersistentVolumeMountName(),
|
||||
// ReadOnly: false,
|
||||
// MountPath: app.PersistentPath,
|
||||
// },
|
||||
//},
|
||||
Ports: containerPorts,
|
||||
SecurityContext: &v1.SecurityContext{
|
||||
RunAsUser: UnnamedPointer(int64(0)),
|
||||
},
|
||||
}},
|
||||
//Volumes: []v1.Volume{
|
||||
// {
|
||||
// Name: app.PersistentVolumeMountName(),
|
||||
// VolumeSource: v1.VolumeSource{
|
||||
// NFS: &v1.NFSVolumeSource{
|
||||
// Server: config.NFSHost,
|
||||
// Path: app.NFSPath(),
|
||||
// ReadOnly: false,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
//},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -362,7 +385,7 @@ func (c *K8sClient) CreatePersistentVolume(app *AppModel) {
|
|||
StorageClassName: app.StorageClassName(),
|
||||
PersistentVolumeSource: v1.PersistentVolumeSource{
|
||||
NFS: &v1.NFSVolumeSource{
|
||||
Server: "192.168.1.1",
|
||||
Server: GetConfig().NFSHost,
|
||||
Path: app.NFSPath(),
|
||||
ReadOnly: false,
|
||||
},
|
||||
|
|
|
|||
19
src/orm.go
19
src/orm.go
|
|
@ -5,7 +5,6 @@ import (
|
|||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
|
@ -16,18 +15,9 @@ func opendb() *gorm.DB {
|
|||
if instance == nil {
|
||||
lock.Lock()
|
||||
if instance == nil {
|
||||
db_host, ok := os.LookupEnv("DB_HOST")
|
||||
if !ok {
|
||||
panic("DB_HOST NOT SET")
|
||||
}
|
||||
db_user, ok := os.LookupEnv("DB_USER")
|
||||
if !ok {
|
||||
panic("DB_USER NOT SET")
|
||||
}
|
||||
db_pass, ok := os.LookupEnv("DB_PASS")
|
||||
if !ok {
|
||||
panic("DB_PASS NOT SET")
|
||||
}
|
||||
db_host := GetConfig().DBHost
|
||||
db_user := GetConfig().DBUser
|
||||
db_pass := GetConfig().DBPassword
|
||||
dsn := fmt.Sprintf("%s:%s@tcp(%s:3306)/k8s?charset=utf8mb4&parseTime=True&loc=Local", db_user, db_pass, db_host)
|
||||
var err error
|
||||
instance, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||
|
|
@ -43,6 +33,9 @@ func opendb() *gorm.DB {
|
|||
|
||||
func checkdb(cnt int) *gorm.DB {
|
||||
ans := opendb()
|
||||
if ans == nil {
|
||||
return nil
|
||||
}
|
||||
db, err := ans.DB()
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("Cannot get *sql.DB.")
|
||||
|
|
|
|||
|
|
@ -48,8 +48,15 @@
|
|||
<td> {{ item.Image }}</td>
|
||||
<td> {{ item.Command }}</td>
|
||||
<td> {{ item.Env }}</td>
|
||||
<td> {{ item.Status }}</td>
|
||||
<td> <a :href="'/' + item.ServiceDisplayName + '/'" target="_blank">{{ item.ServiceDisplayName }}</a></td>
|
||||
<td>
|
||||
<span v-if="item.Status == 'Halt'">
|
||||
{{ item.Status }}
|
||||
</span>
|
||||
<span v-else>
|
||||
<a href="javascript:void(0)" @click="showPodStatus(item)">{{ item.Status }}</a>
|
||||
</span>
|
||||
</td>
|
||||
<td><a :href="'/' + item.ServiceDisplayName + '/'" target="_blank">{{ item.ServiceDisplayName }}</a></td>
|
||||
<td> {{ item.Ports }}</td>
|
||||
<td>
|
||||
<div class="btn-group">
|
||||
|
|
@ -112,7 +119,14 @@
|
|||
<div class="row">
|
||||
<label for="app-ports" class="form-label">端口号</label>
|
||||
<input type="text" id="app-ports" class="form-control" v-model="selectApp.Ports"
|
||||
placeholder="端口号,多个端口以逗号分隔"/>
|
||||
placeholder="端口号"/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="app-ports" class="form-label">持久化路径</label>
|
||||
<input type="text" id="app-persistent-path" class="form-control"
|
||||
v-model="selectApp.PersistentPath"
|
||||
placeholder="持久化路径"/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
|
@ -136,13 +150,27 @@
|
|||
<strong class="me-auto">请求失败</strong>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="toast-body">
|
||||
{{ toastText }}
|
||||
<div class="toast-body" v-html="toastText">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal" tabindex="-1" id="pod-status-modal">
|
||||
<div class="modal-dialog modal-dialog-scrollable">
|
||||
<div class="modal-header">
|
||||
Pod详情
|
||||
</div>
|
||||
<div class="modal-content">{{ podStatus }}</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
|
|
@ -165,8 +193,11 @@
|
|||
Comment: "",
|
||||
Env: "",
|
||||
SvcName: "",
|
||||
PersistentPath: "",
|
||||
Ports: "",
|
||||
}
|
||||
},
|
||||
timer: null,
|
||||
podStatus: ""
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -216,11 +247,22 @@
|
|||
this.username = r.data.Username
|
||||
this.apps = r.data.Apps
|
||||
})
|
||||
},
|
||||
showPodStatus(app) {
|
||||
axios.get("pod?id=" + app.Id).then(r => {
|
||||
this.podStatus = JSON.stringify(r.data, null, 2);
|
||||
(new bootstrap.Modal("#pod-status-modal")).show();
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.blankApp = JSON.parse(JSON.stringify(this.selectApp))
|
||||
this.refresh()
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer)
|
||||
this.timer = null
|
||||
}
|
||||
setInterval(this.timer, this.refresh, 5000)
|
||||
}
|
||||
}).mount("#app")
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue