k8s客户端获取从kubeconfig迁移到token
This commit is contained in:
4
go.mod
4
go.mod
@@ -161,10 +161,10 @@ require (
|
|||||||
github.com/google/gnostic-models v0.6.9 // indirect
|
github.com/google/gnostic-models v0.6.9 // indirect
|
||||||
github.com/google/go-cmp v0.7.0 // indirect
|
github.com/google/go-cmp v0.7.0 // indirect
|
||||||
github.com/morikuni/aec v1.0.0 // indirect
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
||||||
golang.org/x/term v0.32.0 // indirect
|
golang.org/x/term v0.32.0 // indirect
|
||||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
k8s.io/klog/v2 v2.130.1 // indirect
|
|
||||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
|
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
|
||||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
||||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||||
@@ -179,9 +179,7 @@ require (
|
|||||||
dario.cat/mergo v1.0.1 // indirect
|
dario.cat/mergo v1.0.1 // indirect
|
||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
|
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
|
||||||
github.com/ArtisanCloud/PowerLibs/v3 v3.3.2
|
|
||||||
github.com/ArtisanCloud/PowerSocialite/v3 v3.0.8 // indirect
|
github.com/ArtisanCloud/PowerSocialite/v3 v3.0.8 // indirect
|
||||||
github.com/ArtisanCloud/PowerWeChat/v3 v3.4.21
|
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
|
||||||
github.com/DataDog/zstd v1.5.7 // indirect
|
github.com/DataDog/zstd v1.5.7 // indirect
|
||||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -871,6 +871,8 @@ golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ug
|
|||||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||||
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||||
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||||
golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
|
golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
|
||||||
golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c=
|
golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c=
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
|||||||
@@ -27,9 +27,11 @@ type UserAppInstance struct {
|
|||||||
MergedApp string `xorm:"TEXT"` // 合并后的完整应用配置JSON
|
MergedApp string `xorm:"TEXT"` // 合并后的完整应用配置JSON
|
||||||
|
|
||||||
// 部署信息
|
// 部署信息
|
||||||
DeployType string `xorm:"NOT NULL"` // 实际部署类型 (kubernetes/docker)
|
DeployType string `xorm:"NOT NULL"` // 实际部署类型 (kubernetes/docker)
|
||||||
Kubeconfig string `xorm:"TEXT"` // Kubeconfig内容(加密存储)
|
|
||||||
KubeconfigContext string // Kubeconfig context
|
// Kubernetes 凭据(URL + Token)
|
||||||
|
K8sURL string `xorm:"TEXT"`
|
||||||
|
K8sToken string `xorm:"TEXT"`
|
||||||
|
|
||||||
// 元数据
|
// 元数据
|
||||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||||
|
|||||||
29
models/migrations/devstar_v1_0/dv5.go
Normal file
29
models/migrations/devstar_v1_0/dv5.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Mengning Software. 2025. All rights reserved.
|
||||||
|
* Authors: DevStar Team, panshuxiao
|
||||||
|
* Create: 2025-11-24
|
||||||
|
* Description: Migration dv5 adding Kubernetes credential columns.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package devstar_v1_0
|
||||||
|
|
||||||
|
import (
|
||||||
|
"xorm.io/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddK8sCredentialColumns adds k8s_url and k8s_token columns to user_app_instance.
|
||||||
|
func AddK8sCredentialColumns(x *xorm.Engine) error {
|
||||||
|
if _, err := x.Exec("ALTER TABLE user_app_instance ADD COLUMN k8s_url TEXT DEFAULT ''"); err != nil {
|
||||||
|
return ErrMigrateDevstarDatabase{
|
||||||
|
Step: "add column 'k8s_url' to user_app_instance",
|
||||||
|
Message: err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err := x.Exec("ALTER TABLE user_app_instance ADD COLUMN k8s_token TEXT DEFAULT ''"); err != nil {
|
||||||
|
return ErrMigrateDevstarDatabase{
|
||||||
|
Step: "add column 'k8s_token' to user_app_instance",
|
||||||
|
Message: err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -92,6 +92,52 @@ func GetKubernetesClient(ctx context.Context, kubeconfig []byte, contextName str
|
|||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetKubernetesClientWithToken 通过用户提供的 k8sURL 和 token 获取动态客户端
|
||||||
|
// 如果 k8sURL 和 token 为空,则优先使用配置文件中的 K8sConfig.Url 和 K8sConfig.Token
|
||||||
|
// 如果配置文件也未配置,则返回错误(不回退到 kubeconfig)
|
||||||
|
func GetKubernetesClientWithToken(ctx context.Context, k8sURL, token string) (dynamicclient.Interface, error) {
|
||||||
|
// 如果未提供 URL 和 token,优先使用配置文件中的设置
|
||||||
|
if k8sURL == "" || token == "" {
|
||||||
|
// 优先使用配置文件中的 K8s 配置
|
||||||
|
if setting.K8sConfig.Enable && setting.K8sConfig.Url != "" && setting.K8sConfig.Token != "" {
|
||||||
|
k8sURL = setting.K8sConfig.Url
|
||||||
|
token = setting.K8sConfig.Token
|
||||||
|
log.Info("使用配置文件中的 K8s 配置: URL=%s", k8sURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果仍然没有 URL 和 token,直接返回错误
|
||||||
|
if k8sURL == "" || token == "" {
|
||||||
|
return nil, fmt.Errorf("k8sURL and token are required, neither provided nor found in config")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 token 认证创建配置
|
||||||
|
config := &clientgorest.Config{
|
||||||
|
Host: k8sURL,
|
||||||
|
BearerToken: token,
|
||||||
|
TLSClientConfig: clientgorest.TLSClientConfig{
|
||||||
|
Insecure: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
applyClientDefaults(config)
|
||||||
|
|
||||||
|
// 强制跳过 TLS 证书校验(与 GetKubernetesClient 保持一致)
|
||||||
|
// 同时清空 CA 配置
|
||||||
|
config.TLSClientConfig.Insecure = true
|
||||||
|
config.TLSClientConfig.CAData = nil
|
||||||
|
config.TLSClientConfig.CAFile = ""
|
||||||
|
|
||||||
|
// 尝试创建客户端,如果TLS验证失败则自动跳过验证
|
||||||
|
client, err := dynamicclient.NewForConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
// 再次兜底:若识别为 TLS 错误,已 Insecure,无需再次设置;否则将错误上抛
|
||||||
|
return nil, fmt.Errorf("failed to create k8s client: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
// restConfigFromKubeconfigBytes 基于 kubeconfig 内容构造 *rest.Config,支持指定 context(为空则使用 current-context)
|
// restConfigFromKubeconfigBytes 基于 kubeconfig 内容构造 *rest.Config,支持指定 context(为空则使用 current-context)
|
||||||
func restConfigFromKubeconfigBytes(kubeconfig []byte, contextName string) (*clientgorest.Config, error) {
|
func restConfigFromKubeconfigBytes(kubeconfig []byte, contextName string) (*clientgorest.Config, error) {
|
||||||
|
|
||||||
|
|||||||
@@ -257,8 +257,8 @@ func AppStoreInstall(ctx *context.Context) {
|
|||||||
appID := ctx.FormString("app_id")
|
appID := ctx.FormString("app_id")
|
||||||
configJSON := ctx.FormString("config")
|
configJSON := ctx.FormString("config")
|
||||||
installTarget := ctx.FormString("install_target")
|
installTarget := ctx.FormString("install_target")
|
||||||
kubeconfig := ctx.FormString("kubeconfig")
|
k8sURL := ctx.FormString("k8s_url")
|
||||||
kubeconfigContext := ctx.FormString("kubeconfig_context")
|
token := ctx.FormString("k8s_token")
|
||||||
|
|
||||||
if appID == "" {
|
if appID == "" {
|
||||||
ctx.JSON(400, map[string]string{"error": "应用ID不能为空"})
|
ctx.JSON(400, map[string]string{"error": "应用ID不能为空"})
|
||||||
@@ -273,7 +273,7 @@ func AppStoreInstall(ctx *context.Context) {
|
|||||||
|
|
||||||
// 创建 manager 并执行安装
|
// 创建 manager 并执行安装
|
||||||
manager := appstore.NewManager(ctx, ctx.Doer.ID)
|
manager := appstore.NewManager(ctx, ctx.Doer.ID)
|
||||||
if err := manager.InstallApp(appID, configJSON, installTarget, kubeconfig, kubeconfigContext); err != nil {
|
if err := manager.InstallApp(appID, configJSON, installTarget, k8sURL, token); err != nil {
|
||||||
// 根据错误类型返回相应的状态码和消息
|
// 根据错误类型返回相应的状态码和消息
|
||||||
if appErr, ok := err.(*appstore.AppStoreError); ok {
|
if appErr, ok := err.(*appstore.AppStoreError); ok {
|
||||||
switch appErr.Code {
|
switch appErr.Code {
|
||||||
@@ -293,7 +293,7 @@ func AppStoreInstall(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 安装成功
|
// 安装成功
|
||||||
if installTarget == "kubeconfig" && kubeconfig != "" {
|
if installTarget == "kubeconfig" && k8sURL != "" && token != "" {
|
||||||
ctx.Flash.Success("应用已成功安装到自定义位置")
|
ctx.Flash.Success("应用已成功安装到自定义位置")
|
||||||
} else {
|
} else {
|
||||||
ctx.Flash.Success("应用已成功安装到默认位置")
|
ctx.Flash.Success("应用已成功安装到默认位置")
|
||||||
@@ -324,8 +324,8 @@ func AppStoreUpdate(ctx *context.Context) {
|
|||||||
appID := ctx.FormString("app_id")
|
appID := ctx.FormString("app_id")
|
||||||
configJSON := ctx.FormString("config")
|
configJSON := ctx.FormString("config")
|
||||||
installTarget := ctx.FormString("install_target")
|
installTarget := ctx.FormString("install_target")
|
||||||
kubeconfig := ctx.FormString("kubeconfig")
|
k8sURL := ctx.FormString("k8s_url")
|
||||||
kubeconfigContext := ctx.FormString("kubeconfig_context")
|
token := ctx.FormString("k8s_token")
|
||||||
|
|
||||||
if appID == "" {
|
if appID == "" {
|
||||||
ctx.JSON(400, map[string]string{"error": "应用ID不能为空"})
|
ctx.JSON(400, map[string]string{"error": "应用ID不能为空"})
|
||||||
@@ -340,7 +340,7 @@ func AppStoreUpdate(ctx *context.Context) {
|
|||||||
|
|
||||||
// 创建 manager 并执行更新
|
// 创建 manager 并执行更新
|
||||||
manager := appstore.NewManager(ctx, ctx.Doer.ID)
|
manager := appstore.NewManager(ctx, ctx.Doer.ID)
|
||||||
if err := manager.UpdateInstalledApp(appID, configJSON, installTarget, kubeconfig, kubeconfigContext); err != nil {
|
if err := manager.UpdateInstalledApp(appID, configJSON, installTarget, k8sURL, token); err != nil {
|
||||||
// 根据错误类型返回相应的状态码和消息
|
// 根据错误类型返回相应的状态码和消息
|
||||||
if appErr, ok := err.(*appstore.AppStoreError); ok {
|
if appErr, ok := err.(*appstore.AppStoreError); ok {
|
||||||
switch appErr.Code {
|
switch appErr.Code {
|
||||||
@@ -360,7 +360,7 @@ func AppStoreUpdate(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 更新成功
|
// 更新成功
|
||||||
if installTarget == "kubeconfig" && kubeconfig != "" {
|
if installTarget == "kubeconfig" && k8sURL != "" && token != "" {
|
||||||
ctx.Flash.Success("应用已成功更新到自定义位置")
|
ctx.Flash.Success("应用已成功更新到自定义位置")
|
||||||
} else {
|
} else {
|
||||||
ctx.Flash.Success("应用已成功更新到默认位置")
|
ctx.Flash.Success("应用已成功更新到默认位置")
|
||||||
@@ -396,7 +396,7 @@ func AppStoreUninstall(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建 manager 并执行卸载
|
// 创建 manager 并执行卸载
|
||||||
// UninstallApp 会自动从数据库读取 kubeconfig 判断是外部集群还是本地集群
|
// UninstallApp 会自动从数据库读取保存的 Kubernetes 凭据判断是外部集群还是本地集群
|
||||||
manager := appstore.NewManager(ctx, ctx.Doer.ID)
|
manager := appstore.NewManager(ctx, ctx.Doer.ID)
|
||||||
if err := manager.UninstallApp(appID); err != nil {
|
if err := manager.UninstallApp(appID); err != nil {
|
||||||
// 根据错误类型返回相应的状态码和消息
|
// 根据错误类型返回相应的状态码和消息
|
||||||
@@ -445,8 +445,8 @@ func AppStoreResume(ctx *context.Context) {
|
|||||||
// 解析表单数据
|
// 解析表单数据
|
||||||
appID := ctx.FormString("app_id")
|
appID := ctx.FormString("app_id")
|
||||||
installTarget := ctx.FormString("install_target")
|
installTarget := ctx.FormString("install_target")
|
||||||
kubeconfig := ctx.FormString("kubeconfig")
|
k8sURL := ctx.FormString("k8s_url")
|
||||||
kubeconfigContext := ctx.FormString("kubeconfig_context")
|
token := ctx.FormString("k8s_token")
|
||||||
|
|
||||||
if appID == "" {
|
if appID == "" {
|
||||||
ctx.JSON(400, map[string]string{"error": "应用ID不能为空"})
|
ctx.JSON(400, map[string]string{"error": "应用ID不能为空"})
|
||||||
@@ -455,7 +455,7 @@ func AppStoreResume(ctx *context.Context) {
|
|||||||
|
|
||||||
// 创建 manager 并执行恢复
|
// 创建 manager 并执行恢复
|
||||||
manager := appstore.NewManager(ctx, ctx.Doer.ID)
|
manager := appstore.NewManager(ctx, ctx.Doer.ID)
|
||||||
if err := manager.ResumeApp(appID, installTarget, []byte(kubeconfig), kubeconfigContext); err != nil {
|
if err := manager.ResumeApp(appID, installTarget, k8sURL, token); err != nil {
|
||||||
// 根据错误类型返回相应的状态码和消息
|
// 根据错误类型返回相应的状态码和消息
|
||||||
if appErr, ok := err.(*appstore.AppStoreError); ok {
|
if appErr, ok := err.(*appstore.AppStoreError); ok {
|
||||||
switch appErr.Code {
|
switch appErr.Code {
|
||||||
@@ -475,7 +475,7 @@ func AppStoreResume(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 恢复成功
|
// 恢复成功
|
||||||
if installTarget == "kubeconfig" && kubeconfig != "" {
|
if installTarget == "kubeconfig" && k8sURL != "" && token != "" {
|
||||||
ctx.Flash.Success("应用已成功在指定位置恢复")
|
ctx.Flash.Success("应用已成功在指定位置恢复")
|
||||||
} else {
|
} else {
|
||||||
ctx.Flash.Success("应用已成功恢复")
|
ctx.Flash.Success("应用已成功恢复")
|
||||||
@@ -506,8 +506,8 @@ func AppStoreStop(ctx *context.Context) {
|
|||||||
// 解析表单数据
|
// 解析表单数据
|
||||||
appID := ctx.FormString("app_id")
|
appID := ctx.FormString("app_id")
|
||||||
installTarget := ctx.FormString("install_target")
|
installTarget := ctx.FormString("install_target")
|
||||||
kubeconfig := ctx.FormString("kubeconfig")
|
k8sURL := ctx.FormString("k8s_url")
|
||||||
kubeconfigContext := ctx.FormString("kubeconfig_context")
|
token := ctx.FormString("k8s_token")
|
||||||
|
|
||||||
if appID == "" {
|
if appID == "" {
|
||||||
ctx.JSON(400, map[string]string{"error": "应用ID不能为空"})
|
ctx.JSON(400, map[string]string{"error": "应用ID不能为空"})
|
||||||
@@ -516,7 +516,7 @@ func AppStoreStop(ctx *context.Context) {
|
|||||||
|
|
||||||
// 创建 manager 并执行暂停
|
// 创建 manager 并执行暂停
|
||||||
manager := appstore.NewManager(ctx, ctx.Doer.ID)
|
manager := appstore.NewManager(ctx, ctx.Doer.ID)
|
||||||
if err := manager.StopApp(appID, installTarget, []byte(kubeconfig), kubeconfigContext); err != nil {
|
if err := manager.StopApp(appID, installTarget, k8sURL, token); err != nil {
|
||||||
// 根据错误类型返回相应的状态码和消息
|
// 根据错误类型返回相应的状态码和消息
|
||||||
if appErr, ok := err.(*appstore.AppStoreError); ok {
|
if appErr, ok := err.(*appstore.AppStoreError); ok {
|
||||||
switch appErr.Code {
|
switch appErr.Code {
|
||||||
@@ -536,7 +536,7 @@ func AppStoreStop(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 暂停成功
|
// 暂停成功
|
||||||
if installTarget == "kubeconfig" && kubeconfig != "" {
|
if installTarget == "kubeconfig" && k8sURL != "" && token != "" {
|
||||||
ctx.Flash.Success("应用已成功在指定位置暂停")
|
ctx.Flash.Success("应用已成功在指定位置暂停")
|
||||||
} else {
|
} else {
|
||||||
ctx.Flash.Success("应用已成功暂停")
|
ctx.Flash.Success("应用已成功暂停")
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"id": "mengningsoftware-2",
|
"id": "mengningsoftware",
|
||||||
"name": "mengningsoftware-2",
|
"name": "mengningsoftware",
|
||||||
"description": "High-performance HTTP server and reverse proxy",
|
"description": "High-performance HTTP server and reverse proxy",
|
||||||
"category": "web-server",
|
"category": "web-server",
|
||||||
"tags": ["web", "proxy", "http", "server"],
|
"tags": ["web", "proxy", "http", "server"],
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/k8s/application"
|
"code.gitea.io/gitea/modules/k8s/application"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
dynamicclient "k8s.io/client-go/dynamic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// K8sManager handles Kubernetes-specific application operations
|
// K8sManager handles Kubernetes-specific application operations
|
||||||
@@ -23,6 +24,12 @@ type K8sManager struct {
|
|||||||
ctx context.Context
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// K8sCredential describes how to reach a Kubernetes cluster.
|
||||||
|
type K8sCredential struct {
|
||||||
|
K8sURL string
|
||||||
|
Token string
|
||||||
|
}
|
||||||
|
|
||||||
// NewK8sManager creates a new Kubernetes manager
|
// NewK8sManager creates a new Kubernetes manager
|
||||||
func NewK8sManager(ctx context.Context) *K8sManager {
|
func NewK8sManager(ctx context.Context) *K8sManager {
|
||||||
return &K8sManager{
|
return &K8sManager{
|
||||||
@@ -30,8 +37,16 @@ func NewK8sManager(ctx context.Context) *K8sManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// buildK8sClient creates a dynamic client from the provided credential.
|
||||||
|
func (km *K8sManager) buildK8sClient(cred *K8sCredential) (dynamicclient.Interface, error) {
|
||||||
|
if cred != nil && cred.K8sURL != "" && cred.Token != "" {
|
||||||
|
return k8s.GetKubernetesClientWithToken(km.ctx, cred.K8sURL, cred.Token)
|
||||||
|
}
|
||||||
|
return k8s.GetKubernetesClientWithToken(km.ctx, "", "")
|
||||||
|
}
|
||||||
|
|
||||||
// InstallAppToKubernetes installs an application to a Kubernetes cluster
|
// InstallAppToKubernetes installs an application to a Kubernetes cluster
|
||||||
func (km *K8sManager) InstallAppToKubernetes(app *App, kubeconfig []byte, contextName string) error {
|
func (km *K8sManager) InstallAppToKubernetes(app *App, cred *K8sCredential) error {
|
||||||
// Validate that the app supports Kubernetes deployment
|
// Validate that the app supports Kubernetes deployment
|
||||||
// 优先检查实际部署类型,如果为空则检查支持的部署类型
|
// 优先检查实际部署类型,如果为空则检查支持的部署类型
|
||||||
if app.Deploy.Type != "" && app.Deploy.Type != "kubernetes" {
|
if app.Deploy.Type != "" && app.Deploy.Type != "kubernetes" {
|
||||||
@@ -48,7 +63,7 @@ func (km *K8sManager) InstallAppToKubernetes(app *App, kubeconfig []byte, contex
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get Kubernetes client
|
// Get Kubernetes client
|
||||||
k8sClient, err := k8s.GetKubernetesClient(km.ctx, kubeconfig, contextName)
|
k8sClient, err := km.buildK8sClient(cred)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &AppStoreError{
|
return &AppStoreError{
|
||||||
Code: "KUBERNETES_CLIENT_ERROR",
|
Code: "KUBERNETES_CLIENT_ERROR",
|
||||||
@@ -81,7 +96,7 @@ func (km *K8sManager) InstallAppToKubernetes(app *App, kubeconfig []byte, contex
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UninstallAppFromKubernetes uninstalls an application from a Kubernetes cluster
|
// UninstallAppFromKubernetes uninstalls an application from a Kubernetes cluster
|
||||||
func (km *K8sManager) UninstallAppFromKubernetes(app *App, kubeconfig []byte, contextName string) error {
|
func (km *K8sManager) UninstallAppFromKubernetes(app *App, cred *K8sCredential) error {
|
||||||
// Validate that the app supports Kubernetes deployment
|
// Validate that the app supports Kubernetes deployment
|
||||||
// 优先检查实际部署类型,如果为空则检查支持的部署类型
|
// 优先检查实际部署类型,如果为空则检查支持的部署类型
|
||||||
if app.Deploy.Type != "" && app.Deploy.Type != "kubernetes" {
|
if app.Deploy.Type != "" && app.Deploy.Type != "kubernetes" {
|
||||||
@@ -98,7 +113,7 @@ func (km *K8sManager) UninstallAppFromKubernetes(app *App, kubeconfig []byte, co
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get Kubernetes client
|
// Get Kubernetes client
|
||||||
k8sClient, err := k8s.GetKubernetesClient(km.ctx, kubeconfig, contextName)
|
k8sClient, err := km.buildK8sClient(cred)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &AppStoreError{
|
return &AppStoreError{
|
||||||
Code: "KUBERNETES_CLIENT_ERROR",
|
Code: "KUBERNETES_CLIENT_ERROR",
|
||||||
@@ -131,7 +146,7 @@ func (km *K8sManager) UninstallAppFromKubernetes(app *App, kubeconfig []byte, co
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAppFromKubernetes gets an application from a Kubernetes cluster
|
// GetAppFromKubernetes gets an application from a Kubernetes cluster
|
||||||
func (km *K8sManager) GetAppFromKubernetes(app *App, kubeconfig []byte, contextName string) (interface{}, error) {
|
func (km *K8sManager) GetAppFromKubernetes(app *App, cred *K8sCredential) (interface{}, error) {
|
||||||
// Validate that the app supports Kubernetes deployment
|
// Validate that the app supports Kubernetes deployment
|
||||||
// 优先检查实际部署类型,如果为空则检查支持的部署类型
|
// 优先检查实际部署类型,如果为空则检查支持的部署类型
|
||||||
if app.Deploy.Type != "" && app.Deploy.Type != "kubernetes" {
|
if app.Deploy.Type != "" && app.Deploy.Type != "kubernetes" {
|
||||||
@@ -148,7 +163,7 @@ func (km *K8sManager) GetAppFromKubernetes(app *App, kubeconfig []byte, contextN
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get Kubernetes client
|
// Get Kubernetes client
|
||||||
k8sClient, err := k8s.GetKubernetesClient(km.ctx, kubeconfig, contextName)
|
k8sClient, err := km.buildK8sClient(cred)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &AppStoreError{
|
return nil, &AppStoreError{
|
||||||
Code: "KUBERNETES_CLIENT_ERROR",
|
Code: "KUBERNETES_CLIENT_ERROR",
|
||||||
@@ -172,7 +187,7 @@ func (km *K8sManager) GetAppFromKubernetes(app *App, kubeconfig []byte, contextN
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListAppsFromKubernetes lists applications from a Kubernetes cluster
|
// ListAppsFromKubernetes lists applications from a Kubernetes cluster
|
||||||
func (km *K8sManager) ListAppsFromKubernetes(app *App, kubeconfig []byte, contextName string) (*applicationv1.ApplicationList, error) {
|
func (km *K8sManager) ListAppsFromKubernetes(app *App, cred *K8sCredential) (*applicationv1.ApplicationList, error) {
|
||||||
// Validate that the app supports Kubernetes deployment
|
// Validate that the app supports Kubernetes deployment
|
||||||
// 优先检查实际部署类型,如果为空则检查支持的部署类型
|
// 优先检查实际部署类型,如果为空则检查支持的部署类型
|
||||||
if app.Deploy.Type != "" && app.Deploy.Type != "kubernetes" {
|
if app.Deploy.Type != "" && app.Deploy.Type != "kubernetes" {
|
||||||
@@ -189,7 +204,7 @@ func (km *K8sManager) ListAppsFromKubernetes(app *App, kubeconfig []byte, contex
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get Kubernetes client
|
// Get Kubernetes client
|
||||||
k8sClient, err := k8s.GetKubernetesClient(km.ctx, kubeconfig, contextName)
|
k8sClient, err := km.buildK8sClient(cred)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &AppStoreError{
|
return nil, &AppStoreError{
|
||||||
Code: "KUBERNETES_CLIENT_ERROR",
|
Code: "KUBERNETES_CLIENT_ERROR",
|
||||||
@@ -213,7 +228,7 @@ func (km *K8sManager) ListAppsFromKubernetes(app *App, kubeconfig []byte, contex
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateAppInKubernetes updates an application in a Kubernetes cluster
|
// UpdateAppInKubernetes updates an application in a Kubernetes cluster
|
||||||
func (km *K8sManager) UpdateAppInKubernetes(app *App, kubeconfig []byte, contextName string) error {
|
func (km *K8sManager) UpdateAppInKubernetes(app *App, cred *K8sCredential) error {
|
||||||
// Validate that the app supports Kubernetes deployment
|
// Validate that the app supports Kubernetes deployment
|
||||||
// 优先检查实际部署类型,如果为空则检查支持的部署类型
|
// 优先检查实际部署类型,如果为空则检查支持的部署类型
|
||||||
if app.Deploy.Type != "" && app.Deploy.Type != "kubernetes" {
|
if app.Deploy.Type != "" && app.Deploy.Type != "kubernetes" {
|
||||||
@@ -230,7 +245,7 @@ func (km *K8sManager) UpdateAppInKubernetes(app *App, kubeconfig []byte, context
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get Kubernetes client
|
// Get Kubernetes client
|
||||||
k8sClient, err := k8s.GetKubernetesClient(km.ctx, kubeconfig, contextName)
|
k8sClient, err := km.buildK8sClient(cred)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &AppStoreError{
|
return &AppStoreError{
|
||||||
Code: "KUBERNETES_CLIENT_ERROR",
|
Code: "KUBERNETES_CLIENT_ERROR",
|
||||||
@@ -240,7 +255,7 @@ func (km *K8sManager) UpdateAppInKubernetes(app *App, kubeconfig []byte, context
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 先获取现有的应用,以保留元数据(特别是 resourceVersion)
|
// 先获取现有的应用,以保留元数据(特别是 resourceVersion)
|
||||||
existingApp, err := km.GetAppFromKubernetes(app, kubeconfig, contextName)
|
existingApp, err := km.GetAppFromKubernetes(app, cred)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 如果应用不存在,尝试创建新应用
|
// 如果应用不存在,尝试创建新应用
|
||||||
log.Warn("Application not found in Kubernetes, attempting to create new application: %v", err)
|
log.Warn("Application not found in Kubernetes, attempting to create new application: %v", err)
|
||||||
|
|||||||
@@ -40,6 +40,29 @@ func NewManager(ctx *gitea_context.Context, userID int64) *Manager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildCredentialFromInput(installTarget, k8sURL, token string) *K8sCredential {
|
||||||
|
if installTarget == "kubeconfig" && k8sURL != "" && token != "" {
|
||||||
|
return &K8sCredential{
|
||||||
|
K8sURL: k8sURL,
|
||||||
|
Token: token,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func credentialFromInstance(instance *user_app_instance.UserAppInstance) *K8sCredential {
|
||||||
|
if instance == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if instance.K8sURL != "" && instance.K8sToken != "" {
|
||||||
|
return &K8sCredential{
|
||||||
|
K8sURL: instance.K8sURL,
|
||||||
|
Token: instance.K8sToken,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ListApps returns all available applications from database
|
// ListApps returns all available applications from database
|
||||||
func (m *Manager) ListApps() ([]App, error) {
|
func (m *Manager) ListApps() ([]App, error) {
|
||||||
appStores, err := appstore_model.ListAppStores(m.ctx, nil)
|
appStores, err := appstore_model.ListAppStores(m.ctx, nil)
|
||||||
@@ -220,7 +243,7 @@ func (m *Manager) SearchApps(query string, category string, tags []string) ([]Ap
|
|||||||
tagMatch := false
|
tagMatch := false
|
||||||
for _, searchTag := range tags {
|
for _, searchTag := range tags {
|
||||||
for _, appTag := range app.Tags {
|
for _, appTag := range app.Tags {
|
||||||
if strings.ToLower(appTag) == strings.ToLower(searchTag) {
|
if strings.EqualFold(appTag, searchTag) {
|
||||||
tagMatch = true
|
tagMatch = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -439,7 +462,7 @@ func (m *Manager) ValidateUserConfig(appID string, userConfig UserConfig) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
// InstallApp installs an application based on the provided parameters
|
// InstallApp installs an application based on the provided parameters
|
||||||
func (m *Manager) InstallApp(appID string, configJSON string, installTarget string, kubeconfig string, kubeconfigContext string) error {
|
func (m *Manager) InstallApp(appID string, configJSON string, installTarget string, k8sURL string, token string) error {
|
||||||
// 获取应用信息
|
// 获取应用信息
|
||||||
app, err := m.GetApp(appID)
|
app, err := m.GetApp(appID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -465,50 +488,51 @@ func (m *Manager) InstallApp(appID string, configJSON string, installTarget stri
|
|||||||
deployType := mergedApp.Deploy.Type
|
deployType := mergedApp.Deploy.Type
|
||||||
if deployType == "" {
|
if deployType == "" {
|
||||||
// 如果 Deploy.Type 为空,根据 DeploymentType 推断
|
// 如果 Deploy.Type 为空,根据 DeploymentType 推断
|
||||||
if mergedApp.DeploymentType == "kubernetes" || mergedApp.DeploymentType == "both" {
|
switch mergedApp.DeploymentType {
|
||||||
|
case "kubernetes", "both":
|
||||||
deployType = "kubernetes"
|
deployType = "kubernetes"
|
||||||
} else if mergedApp.DeploymentType == "docker" {
|
case "docker":
|
||||||
deployType = "docker"
|
deployType = "docker"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据安装目标和应用实际部署类型决定安装方式
|
inputCred := buildCredentialFromInput(installTarget, k8sURL, token)
|
||||||
if installTarget == "kubeconfig" && kubeconfig != "" {
|
if installTarget == "kubeconfig" {
|
||||||
// 安装到外部 Kubernetes 集群
|
if inputCred == nil {
|
||||||
if err := m.InstallAppToKubernetes(mergedApp, []byte(kubeconfig), kubeconfigContext); err != nil {
|
return &AppStoreError{
|
||||||
|
Code: "INVALID_K8S_CREDENTIAL",
|
||||||
|
Message: "自定义 Kubernetes 安装需要提供 API 地址和 Token",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if deployType != "kubernetes" {
|
||||||
|
return &AppStoreError{
|
||||||
|
Code: "DEPLOYMENT_TYPE_ERROR",
|
||||||
|
Message: "该应用不支持 Kubernetes 部署",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据应用实际部署类型决定安装方式
|
||||||
|
log.Info("InstallApp: mergedApp.Deploy.Type = %s", mergedApp.Deploy.Type)
|
||||||
|
switch deployType {
|
||||||
|
case "kubernetes":
|
||||||
|
if err := m.InstallAppToKubernetes(mergedApp, inputCred); err != nil {
|
||||||
return &AppStoreError{
|
return &AppStoreError{
|
||||||
Code: "KUBERNETES_INSTALL_ERROR",
|
Code: "KUBERNETES_INSTALL_ERROR",
|
||||||
Message: "Kubernetes 安装失败",
|
Message: "Kubernetes 安装失败",
|
||||||
Details: err.Error(),
|
Details: err.Error(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
case "docker":
|
||||||
// 根据应用实际部署类型决定本地安装方式
|
// TODO: 实现 Docker 安装逻辑
|
||||||
log.Info("InstallApp: mergedApp.Deploy.Type = %s", mergedApp.Deploy.Type)
|
return &AppStoreError{
|
||||||
switch deployType {
|
Code: "NOT_IMPLEMENTED",
|
||||||
case "kubernetes":
|
Message: "本地 Docker 安装功能开发中",
|
||||||
// 应用要部署到 Kubernetes,安装到本地 K8s 集群
|
}
|
||||||
if err := m.InstallAppToKubernetes(mergedApp, nil, ""); err != nil {
|
default:
|
||||||
return &AppStoreError{
|
return &AppStoreError{
|
||||||
Code: "KUBERNETES_INSTALL_ERROR",
|
Code: "NOT_IMPLEMENTED",
|
||||||
Message: "本地 Kubernetes 安装失败",
|
Message: "本地安装功能开发中",
|
||||||
Details: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "docker":
|
|
||||||
// 应用要部署到 Docker,安装到本地 Docker
|
|
||||||
// TODO: 实现 Docker 安装逻辑
|
|
||||||
return &AppStoreError{
|
|
||||||
Code: "NOT_IMPLEMENTED",
|
|
||||||
Message: "本地 Docker 安装功能开发中",
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// 未知部署类型,默认尝试 Docker
|
|
||||||
// TODO: 实现 Docker 安装逻辑
|
|
||||||
return &AppStoreError{
|
|
||||||
Code: "NOT_IMPLEMENTED",
|
|
||||||
Message: "本地安装功能开发中",
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -524,14 +548,16 @@ func (m *Manager) InstallApp(appID string, configJSON string, installTarget stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
instance := &user_app_instance.UserAppInstance{
|
instance := &user_app_instance.UserAppInstance{
|
||||||
UserID: m.userID,
|
UserID: m.userID,
|
||||||
AppID: appID,
|
AppID: appID,
|
||||||
InstanceName: mergedApp.Name, // 使用应用名称作为实例名称
|
InstanceName: mergedApp.Name,
|
||||||
UserConfig: configJSON,
|
UserConfig: configJSON,
|
||||||
MergedApp: string(mergedAppJSON),
|
MergedApp: string(mergedAppJSON),
|
||||||
DeployType: deployType,
|
DeployType: deployType,
|
||||||
Kubeconfig: kubeconfig,
|
}
|
||||||
KubeconfigContext: kubeconfigContext,
|
if inputCred != nil && deployType == "kubernetes" {
|
||||||
|
instance.K8sURL = inputCred.K8sURL
|
||||||
|
instance.K8sToken = inputCred.Token
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := user_app_instance.CreateUserAppInstance(m.ctx, instance); err != nil {
|
if err := user_app_instance.CreateUserAppInstance(m.ctx, instance); err != nil {
|
||||||
@@ -549,7 +575,7 @@ func (m *Manager) InstallApp(appID string, configJSON string, installTarget stri
|
|||||||
|
|
||||||
// UpdateInstalledApp updates an already installed application with new configuration
|
// UpdateInstalledApp updates an already installed application with new configuration
|
||||||
// The update flow mirrors InstallApp: merge config → choose target → call K8s/Docker updater
|
// The update flow mirrors InstallApp: merge config → choose target → call K8s/Docker updater
|
||||||
func (m *Manager) UpdateInstalledApp(appID string, configJSON string, installTarget string, kubeconfig string, kubeconfigContext string) error {
|
func (m *Manager) UpdateInstalledApp(appID string, configJSON string, installTarget string, k8sURL string, token string) error {
|
||||||
// 获取用户的应用实例
|
// 获取用户的应用实例
|
||||||
instance, err := user_app_instance.GetUserAppInstanceByAppID(m.ctx, m.userID, appID)
|
instance, err := user_app_instance.GetUserAppInstanceByAppID(m.ctx, m.userID, appID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -589,31 +615,38 @@ func (m *Manager) UpdateInstalledApp(appID string, configJSON string, installTar
|
|||||||
// 确定部署类型
|
// 确定部署类型
|
||||||
deployType := mergedApp.Deploy.Type
|
deployType := mergedApp.Deploy.Type
|
||||||
if deployType == "" {
|
if deployType == "" {
|
||||||
deployType = instance.DeployType // 使用实例中保存的部署类型
|
deployType = instance.DeployType
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据安装目标和应用实际部署类型决定更新方式
|
inputCred := buildCredentialFromInput(installTarget, k8sURL, token)
|
||||||
if installTarget == "kubeconfig" && kubeconfig != "" {
|
if installTarget == "kubeconfig" && inputCred == nil {
|
||||||
// 更新外部 Kubernetes 集群中的应用实例
|
return &AppStoreError{
|
||||||
if deployType == "kubernetes" {
|
Code: "INVALID_K8S_CREDENTIAL",
|
||||||
if err := m.UpdateAppInKubernetes(mergedApp, []byte(kubeconfig), kubeconfigContext); err != nil {
|
Message: "自定义 Kubernetes 更新需要提供 API 地址和 Token",
|
||||||
return &AppStoreError{Code: "KUBERNETES_UPDATE_ERROR", Message: "Kubernetes 更新失败", Details: err.Error()}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return &AppStoreError{Code: "NOT_IMPLEMENTED", Message: "外部环境 Docker 更新功能开发中"}
|
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
// 本地更新(依据应用部署类型)
|
if installTarget == "kubeconfig" && deployType != "kubernetes" {
|
||||||
if deployType == "kubernetes" {
|
return &AppStoreError{
|
||||||
if err := m.UpdateAppInKubernetes(mergedApp, nil, ""); err != nil {
|
Code: "DEPLOYMENT_TYPE_ERROR",
|
||||||
return &AppStoreError{Code: "KUBERNETES_UPDATE_ERROR", Message: "本地 Kubernetes 更新失败", Details: err.Error()}
|
Message: "该应用不支持 Kubernetes 部署",
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return &AppStoreError{Code: "NOT_IMPLEMENTED", Message: "本地 Docker 更新功能开发中"}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新成功后,更新用户应用实例
|
// 根据部署类型执行更新
|
||||||
|
switch deployType {
|
||||||
|
case "kubernetes":
|
||||||
|
targetCred := inputCred
|
||||||
|
if targetCred == nil {
|
||||||
|
targetCred = credentialFromInstance(instance)
|
||||||
|
}
|
||||||
|
if err := m.UpdateAppInKubernetes(mergedApp, targetCred); err != nil {
|
||||||
|
return &AppStoreError{Code: "KUBERNETES_UPDATE_ERROR", Message: "Kubernetes 更新失败", Details: err.Error()}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return &AppStoreError{Code: "NOT_IMPLEMENTED", Message: "本地 Docker 更新功能开发中"}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新成功后,写回数据库
|
||||||
mergedAppJSON, err := json.Marshal(mergedApp)
|
mergedAppJSON, err := json.Marshal(mergedApp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to marshal merged app: %v", err)
|
log.Error("Failed to marshal merged app: %v", err)
|
||||||
@@ -627,8 +660,18 @@ func (m *Manager) UpdateInstalledApp(appID string, configJSON string, installTar
|
|||||||
instance.UserConfig = configJSON
|
instance.UserConfig = configJSON
|
||||||
instance.MergedApp = string(mergedAppJSON)
|
instance.MergedApp = string(mergedAppJSON)
|
||||||
instance.DeployType = deployType
|
instance.DeployType = deployType
|
||||||
instance.Kubeconfig = kubeconfig
|
if deployType == "kubernetes" {
|
||||||
instance.KubeconfigContext = kubeconfigContext
|
if inputCred != nil {
|
||||||
|
instance.K8sURL = inputCred.K8sURL
|
||||||
|
instance.K8sToken = inputCred.Token
|
||||||
|
} else if installTarget != "kubeconfig" {
|
||||||
|
instance.K8sURL = ""
|
||||||
|
instance.K8sToken = ""
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
instance.K8sURL = ""
|
||||||
|
instance.K8sToken = ""
|
||||||
|
}
|
||||||
|
|
||||||
if err := user_app_instance.UpdateUserAppInstance(m.ctx, instance); err != nil {
|
if err := user_app_instance.UpdateUserAppInstance(m.ctx, instance); err != nil {
|
||||||
log.Error("Failed to update user app instance: %v", err)
|
log.Error("Failed to update user app instance: %v", err)
|
||||||
@@ -645,7 +688,7 @@ func (m *Manager) UpdateInstalledApp(appID string, configJSON string, installTar
|
|||||||
|
|
||||||
// UninstallApp uninstalls an application
|
// UninstallApp uninstalls an application
|
||||||
// It automatically determines whether to uninstall from external cluster or local cluster
|
// It automatically determines whether to uninstall from external cluster or local cluster
|
||||||
// based on the kubeconfig stored in the database instance
|
// based on the Kubernetes credential stored in the database instance
|
||||||
func (m *Manager) UninstallApp(appID string) error {
|
func (m *Manager) UninstallApp(appID string) error {
|
||||||
// 获取用户的应用实例
|
// 获取用户的应用实例
|
||||||
instance, err := user_app_instance.GetUserAppInstanceByAppID(m.ctx, m.userID, appID)
|
instance, err := user_app_instance.GetUserAppInstanceByAppID(m.ctx, m.userID, appID)
|
||||||
@@ -673,45 +716,28 @@ func (m *Manager) UninstallApp(appID string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据数据库实例中保存的 kubeconfig 判断卸载方式
|
// 根据数据库中保存的凭据判断卸载方式:存在 URL+Token 则视为外部集群,否则使用默认集群
|
||||||
// 如果数据库中有 kubeconfig,说明是安装在外部集群的,使用外部集群卸载
|
cred := credentialFromInstance(instance)
|
||||||
// 如果数据库中没有 kubeconfig,说明是安装在本地集群的,使用本地卸载
|
if instance.DeployType == "kubernetes" {
|
||||||
if instance.Kubeconfig != "" {
|
if cred != nil {
|
||||||
// 从外部 Kubernetes 集群卸载
|
log.Info("UninstallApp: Uninstalling from external cluster, appID=%s, url=%s", appID, cred.K8sURL)
|
||||||
if instance.DeployType == "kubernetes" {
|
|
||||||
log.Info("UninstallApp: Uninstalling from external cluster, appID=%s, kubeconfig length=%d", appID, len(instance.Kubeconfig))
|
|
||||||
if err := m.UninstallAppFromKubernetes(&app, []byte(instance.Kubeconfig), instance.KubeconfigContext); err != nil {
|
|
||||||
return &AppStoreError{
|
|
||||||
Code: "KUBERNETES_UNINSTALL_ERROR",
|
|
||||||
Message: "Kubernetes 卸载失败",
|
|
||||||
Details: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
|
log.Info("UninstallApp: Uninstalling from default cluster, appID=%s", appID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.UninstallAppFromKubernetes(&app, cred); err != nil {
|
||||||
return &AppStoreError{
|
return &AppStoreError{
|
||||||
Code: "NOT_IMPLEMENTED",
|
Code: "KUBERNETES_UNINSTALL_ERROR",
|
||||||
Message: "外部环境 Docker 卸载功能开发中",
|
Message: "Kubernetes 卸载失败",
|
||||||
|
Details: err.Error(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 本地卸载(依据实例中保存的部署类型)
|
return &AppStoreError{
|
||||||
if instance.DeployType == "kubernetes" {
|
Code: "NOT_IMPLEMENTED",
|
||||||
log.Info("UninstallApp: Uninstalling from local cluster, appID=%s", appID)
|
Message: "本地 Docker 卸载功能开发中",
|
||||||
if err := m.UninstallAppFromKubernetes(&app, nil, ""); err != nil {
|
|
||||||
return &AppStoreError{
|
|
||||||
Code: "KUBERNETES_UNINSTALL_ERROR",
|
|
||||||
Message: "本地 Kubernetes 卸载失败",
|
|
||||||
Details: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return &AppStoreError{
|
|
||||||
Code: "NOT_IMPLEMENTED",
|
|
||||||
Message: "本地 Docker 卸载功能开发中",
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 卸载成功后,删除用户应用实例
|
// 卸载成功后,删除用户应用实例
|
||||||
if err := user_app_instance.DeleteUserAppInstanceByAppID(m.ctx, m.userID, appID); err != nil {
|
if err := user_app_instance.DeleteUserAppInstanceByAppID(m.ctx, m.userID, appID); err != nil {
|
||||||
log.Error("Failed to delete user app instance: %v", err)
|
log.Error("Failed to delete user app instance: %v", err)
|
||||||
@@ -725,28 +751,28 @@ func (m *Manager) UninstallApp(appID string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// InstallAppToKubernetes installs an application to a Kubernetes cluster
|
// InstallAppToKubernetes installs an application to a Kubernetes cluster
|
||||||
func (m *Manager) InstallAppToKubernetes(app *App, kubeconfig []byte, contextName string) error {
|
func (m *Manager) InstallAppToKubernetes(app *App, cred *K8sCredential) error {
|
||||||
return m.k8s.InstallAppToKubernetes(app, kubeconfig, contextName)
|
return m.k8s.InstallAppToKubernetes(app, cred)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UninstallAppFromKubernetes uninstalls an application from a Kubernetes cluster
|
// UninstallAppFromKubernetes uninstalls an application from a Kubernetes cluster
|
||||||
func (m *Manager) UninstallAppFromKubernetes(app *App, kubeconfig []byte, contextName string) error {
|
func (m *Manager) UninstallAppFromKubernetes(app *App, cred *K8sCredential) error {
|
||||||
return m.k8s.UninstallAppFromKubernetes(app, kubeconfig, contextName)
|
return m.k8s.UninstallAppFromKubernetes(app, cred)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAppFromKubernetes gets an application from a Kubernetes cluster
|
// GetAppFromKubernetes gets an application from a Kubernetes cluster
|
||||||
func (m *Manager) GetAppFromKubernetes(app *App, kubeconfig []byte, contextName string) (interface{}, error) {
|
func (m *Manager) GetAppFromKubernetes(app *App, cred *K8sCredential) (interface{}, error) {
|
||||||
return m.k8s.GetAppFromKubernetes(app, kubeconfig, contextName)
|
return m.k8s.GetAppFromKubernetes(app, cred)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListAppsFromKubernetes lists applications from a Kubernetes cluster
|
// ListAppsFromKubernetes lists applications from a Kubernetes cluster
|
||||||
func (m *Manager) ListAppsFromKubernetes(app *App, kubeconfig []byte, contextName string) (*applicationv1.ApplicationList, error) {
|
func (m *Manager) ListAppsFromKubernetes(app *App, cred *K8sCredential) (*applicationv1.ApplicationList, error) {
|
||||||
return m.k8s.ListAppsFromKubernetes(app, kubeconfig, contextName)
|
return m.k8s.ListAppsFromKubernetes(app, cred)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateAppInKubernetes updates an application in a Kubernetes cluster
|
// UpdateAppInKubernetes updates an application in a Kubernetes cluster
|
||||||
func (m *Manager) UpdateAppInKubernetes(app *App, kubeconfig []byte, contextName string) error {
|
func (m *Manager) UpdateAppInKubernetes(app *App, cred *K8sCredential) error {
|
||||||
return m.k8s.UpdateAppInKubernetes(app, kubeconfig, contextName)
|
return m.k8s.UpdateAppInKubernetes(app, cred)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsInstalledAppReady 提供给上层的就绪判断入口,避免直接依赖 k8s 层实现
|
// IsInstalledAppReady 提供给上层的就绪判断入口,避免直接依赖 k8s 层实现
|
||||||
@@ -788,14 +814,9 @@ func (m *Manager) GetAppStatus(appID string) (map[string]interface{}, error) {
|
|||||||
// 检查应用是否支持 Kubernetes 部署
|
// 检查应用是否支持 Kubernetes 部署
|
||||||
if instance.DeployType == "kubernetes" {
|
if instance.DeployType == "kubernetes" {
|
||||||
// 尝试从 Kubernetes 获取状态
|
// 尝试从 Kubernetes 获取状态
|
||||||
var kubeconfig []byte
|
cred := credentialFromInstance(instance)
|
||||||
var contextName string
|
|
||||||
if instance.Kubeconfig != "" {
|
|
||||||
kubeconfig = []byte(instance.Kubeconfig)
|
|
||||||
contextName = instance.KubeconfigContext
|
|
||||||
}
|
|
||||||
|
|
||||||
k8sApp, err := m.GetAppFromKubernetes(&app, kubeconfig, contextName)
|
k8sApp, err := m.GetAppFromKubernetes(&app, cred)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 如果应用实例存在于数据库中,但 Kubernetes 中找不到,可能是被暂停了
|
// 如果应用实例存在于数据库中,但 Kubernetes 中找不到,可能是被暂停了
|
||||||
// 返回暂停状态而不是未安装状态
|
// 返回暂停状态而不是未安装状态
|
||||||
@@ -845,7 +866,7 @@ func (m *Manager) GetAppStatus(appID string) (map[string]interface{}, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// StopApp stops an application by setting replicas to 0
|
// StopApp stops an application by setting replicas to 0
|
||||||
func (m *Manager) StopApp(appID string, installTarget string, kubeconfig []byte, kubeconfigContext string) error {
|
func (m *Manager) StopApp(appID string, installTarget string, k8sURL string, token string) error {
|
||||||
// 获取用户的应用实例
|
// 获取用户的应用实例
|
||||||
instance, err := user_app_instance.GetUserAppInstanceByAppID(m.ctx, m.userID, appID)
|
instance, err := user_app_instance.GetUserAppInstanceByAppID(m.ctx, m.userID, appID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -880,8 +901,12 @@ func (m *Manager) StopApp(appID string, installTarget string, kubeconfig []byte,
|
|||||||
stoppedApp.Deploy.Kubernetes.Replicas = 0
|
stoppedApp.Deploy.Kubernetes.Replicas = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调用 k8s 层的 UpdateAppInKubernetes 函数来停止应用
|
cred := buildCredentialFromInput(installTarget, k8sURL, token)
|
||||||
if err := m.k8s.UpdateAppInKubernetes(&stoppedApp, kubeconfig, kubeconfigContext); err != nil {
|
if cred == nil {
|
||||||
|
cred = credentialFromInstance(instance)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.UpdateAppInKubernetes(&stoppedApp, cred); err != nil {
|
||||||
return &AppStoreError{
|
return &AppStoreError{
|
||||||
Code: "KUBERNETES_STOP_ERROR",
|
Code: "KUBERNETES_STOP_ERROR",
|
||||||
Message: "停止应用失败",
|
Message: "停止应用失败",
|
||||||
@@ -899,7 +924,7 @@ func (m *Manager) StopApp(appID string, installTarget string, kubeconfig []byte,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ResumeApp resumes an application by restoring its original replica count
|
// ResumeApp resumes an application by restoring its original replica count
|
||||||
func (m *Manager) ResumeApp(appID string, installTarget string, kubeconfig []byte, kubeconfigContext string) error {
|
func (m *Manager) ResumeApp(appID string, installTarget string, k8sURL string, token string) error {
|
||||||
// 获取用户的应用实例
|
// 获取用户的应用实例
|
||||||
instance, err := user_app_instance.GetUserAppInstanceByAppID(m.ctx, m.userID, appID)
|
instance, err := user_app_instance.GetUserAppInstanceByAppID(m.ctx, m.userID, appID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -937,8 +962,12 @@ func (m *Manager) ResumeApp(appID string, installTarget string, kubeconfig []byt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调用 k8s 层的 UpdateAppInKubernetes 函数来恢复应用
|
cred := buildCredentialFromInput(installTarget, k8sURL, token)
|
||||||
if err := m.k8s.UpdateAppInKubernetes(&resumedApp, kubeconfig, kubeconfigContext); err != nil {
|
if cred == nil {
|
||||||
|
cred = credentialFromInstance(instance)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.UpdateAppInKubernetes(&resumedApp, cred); err != nil {
|
||||||
return &AppStoreError{
|
return &AppStoreError{
|
||||||
Code: "KUBERNETES_RESUME_ERROR",
|
Code: "KUBERNETES_RESUME_ERROR",
|
||||||
Message: "恢复应用失败",
|
Message: "恢复应用失败",
|
||||||
|
|||||||
@@ -274,18 +274,18 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui radio checkbox">
|
<div class="ui radio checkbox">
|
||||||
<input type="radio" name="installTargetRadio" value="kubeconfig" {{if not .IsAdmin}}checked{{end}}>
|
<input type="radio" name="installTargetRadio" value="kubeconfig" {{if not .IsAdmin}}checked{{end}}>
|
||||||
<label>自定义位置(Kubeconfig)</label>
|
<label>自定义位置(Kubernetes URL + Token)</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="kubeconfig-fields" style="display:none;">
|
<div id="k8s-credential-fields" style="display:none;">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Kubeconfig(粘贴内容)</label>
|
<label>Kubernetes API 地址</label>
|
||||||
<textarea id="kubeconfig-content" rows="8" placeholder="粘贴 kubeconfig 内容"></textarea>
|
<input type="text" id="k8s-url" placeholder="例如:https://your-cluster:6443">
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Context 名称(可选)</label>
|
<label>Bearer Token</label>
|
||||||
<input type="text" id="kubeconfig-context" placeholder="不填则使用 current-context">
|
<textarea id="k8s-token" rows="4" placeholder="粘贴访问该集群的 Bearer Token"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -377,10 +377,10 @@ let filteredApps = [];
|
|||||||
let currentApp = null;
|
let currentApp = null;
|
||||||
let currentAppStatus = null; // 存储当前应用的运行状态
|
let currentAppStatus = null; // 存储当前应用的运行状态
|
||||||
let storeSource = 'local'; // local | devstar
|
let storeSource = 'local'; // local | devstar
|
||||||
// 安装位置:local | kubeconfig
|
// 安装位置:local | remote
|
||||||
let installTarget = 'local';
|
let installTarget = 'local';
|
||||||
let installKubeconfigContent = '';
|
let installK8sURL = '';
|
||||||
let installKubeconfigContext = '';
|
let installK8sToken = '';
|
||||||
|
|
||||||
// Initialize the page
|
// Initialize the page
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
@@ -420,7 +420,7 @@ function setupInstallTargetUI() {
|
|||||||
const radioButtons = document.querySelectorAll('#install-modal input[name="installTargetRadio"]');
|
const radioButtons = document.querySelectorAll('#install-modal input[name="installTargetRadio"]');
|
||||||
radioButtons.forEach(radio => {
|
radioButtons.forEach(radio => {
|
||||||
radio.addEventListener('change', function() {
|
radio.addEventListener('change', function() {
|
||||||
const kubeconfigFields = document.querySelector('#install-modal #kubeconfig-fields');
|
const kubeconfigFields = document.querySelector('#install-modal #k8s-credential-fields');
|
||||||
if (kubeconfigFields) {
|
if (kubeconfigFields) {
|
||||||
kubeconfigFields.style.display = (this.value === 'kubeconfig') ? 'block' : 'none';
|
kubeconfigFields.style.display = (this.value === 'kubeconfig') ? 'block' : 'none';
|
||||||
}
|
}
|
||||||
@@ -428,17 +428,17 @@ function setupInstallTargetUI() {
|
|||||||
installTarget = this.value;
|
installTarget = this.value;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// kubeconfig 内容变化时,同步到全局变量
|
// Kubernetes URL/Token 变化时,同步到全局变量
|
||||||
const kcContentEl = document.querySelector('#install-modal #kubeconfig-content');
|
const kcContentEl = document.querySelector('#install-modal #k8s-url');
|
||||||
if (kcContentEl) {
|
if (kcContentEl) {
|
||||||
kcContentEl.addEventListener('input', function() {
|
kcContentEl.addEventListener('input', function() {
|
||||||
installKubeconfigContent = this.value.trim();
|
installK8sURL = this.value.trim();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const kcContextEl = document.querySelector('#install-modal #kubeconfig-context');
|
const kcContextEl = document.querySelector('#install-modal #k8s-token');
|
||||||
if (kcContextEl) {
|
if (kcContextEl) {
|
||||||
kcContextEl.addEventListener('input', function() {
|
kcContextEl.addEventListener('input', function() {
|
||||||
installKubeconfigContext = this.value.trim();
|
installK8sToken = this.value.trim();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -813,14 +813,14 @@ function showInstallModal(appId) {
|
|||||||
// 追加安装位置区块已保留在模板中,这里只同步状态
|
// 追加安装位置区块已保留在模板中,这里只同步状态
|
||||||
const radios = document.querySelectorAll('#install-modal input[name="installTargetRadio"]');
|
const radios = document.querySelectorAll('#install-modal input[name="installTargetRadio"]');
|
||||||
radios.forEach(r => { r.checked = (r.value === installTarget); });
|
radios.forEach(r => { r.checked = (r.value === installTarget); });
|
||||||
const kubeconfigFields = document.querySelector('#install-modal #kubeconfig-fields');
|
const kubeconfigFields = document.querySelector('#install-modal #k8s-credential-fields');
|
||||||
if (kubeconfigFields) {
|
if (kubeconfigFields) {
|
||||||
kubeconfigFields.style.display = (installTarget === 'kubeconfig') ? 'block' : 'none';
|
kubeconfigFields.style.display = (installTarget === 'kubeconfig') ? 'block' : 'none';
|
||||||
}
|
}
|
||||||
const kcContent = document.querySelector('#install-modal #kubeconfig-content');
|
const kcContent = document.querySelector('#install-modal #k8s-url');
|
||||||
const kcContext = document.querySelector('#install-modal #kubeconfig-context');
|
const kcContext = document.querySelector('#install-modal #k8s-token');
|
||||||
if (kcContent) kcContent.value = installKubeconfigContent || '';
|
if (kcContent) kcContent.value = installK8sURL || '';
|
||||||
if (kcContext) kcContext.value = installKubeconfigContext || '';
|
if (kcContext) kcContext.value = installK8sToken || '';
|
||||||
|
|
||||||
const modal = document.getElementById('install-modal');
|
const modal = document.getElementById('install-modal');
|
||||||
if (modal) {
|
if (modal) {
|
||||||
@@ -932,14 +932,14 @@ function showUpdateModal(appId) {
|
|||||||
// 设置安装位置表单
|
// 设置安装位置表单
|
||||||
const radios = document.querySelectorAll('#install-modal input[name="installTargetRadio"]');
|
const radios = document.querySelectorAll('#install-modal input[name="installTargetRadio"]');
|
||||||
radios.forEach(r => { r.checked = (r.value === installTarget); });
|
radios.forEach(r => { r.checked = (r.value === installTarget); });
|
||||||
const kubeconfigFields = document.querySelector('#install-modal #kubeconfig-fields');
|
const kubeconfigFields = document.querySelector('#install-modal #k8s-credential-fields');
|
||||||
if (kubeconfigFields) {
|
if (kubeconfigFields) {
|
||||||
kubeconfigFields.style.display = (installTarget === 'kubeconfig') ? 'block' : 'none';
|
kubeconfigFields.style.display = (installTarget === 'kubeconfig') ? 'block' : 'none';
|
||||||
}
|
}
|
||||||
const kcContent = document.querySelector('#install-modal #kubeconfig-content');
|
const kcContent = document.querySelector('#install-modal #k8s-url');
|
||||||
const kcContext = document.querySelector('#install-modal #kubeconfig-context');
|
const kcContext = document.querySelector('#install-modal #k8s-token');
|
||||||
if (kcContent) kcContent.value = installKubeconfigContent || '';
|
if (kcContent) kcContent.value = installK8sURL || '';
|
||||||
if (kcContext) kcContext.value = installKubeconfigContext || '';
|
if (kcContext) kcContext.value = installK8sToken || '';
|
||||||
|
|
||||||
// 修改安装按钮文字为"更新"
|
// 修改安装按钮文字为"更新"
|
||||||
const installBtn = document.querySelector('#install-modal .ui.primary.button[onclick="installApp()"]');
|
const installBtn = document.querySelector('#install-modal .ui.primary.button[onclick="installApp()"]');
|
||||||
@@ -978,12 +978,12 @@ function updateApp() {
|
|||||||
const selectedTarget = document.querySelector('#install-modal input[name="installTargetRadio"]:checked');
|
const selectedTarget = document.querySelector('#install-modal input[name="installTargetRadio"]:checked');
|
||||||
installTarget = selectedTarget ? selectedTarget.value : 'local';
|
installTarget = selectedTarget ? selectedTarget.value : 'local';
|
||||||
if (installTarget === 'kubeconfig') {
|
if (installTarget === 'kubeconfig') {
|
||||||
const kcContentEl = document.querySelector('#install-modal #kubeconfig-content');
|
const kcContentEl = document.querySelector('#install-modal #k8s-url');
|
||||||
const kcContextEl = document.querySelector('#install-modal #kubeconfig-context');
|
const kcContextEl = document.querySelector('#install-modal #k8s-token');
|
||||||
installKubeconfigContent = kcContentEl ? kcContentEl.value.trim() : '';
|
installK8sURL = kcContentEl ? kcContentEl.value.trim() : '';
|
||||||
installKubeconfigContext = kcContextEl ? kcContextEl.value.trim() : '';
|
installK8sToken = kcContextEl ? kcContextEl.value.trim() : '';
|
||||||
if (!installKubeconfigContent) {
|
if (!installK8sURL || !installK8sToken) {
|
||||||
alert('请输入 kubeconfig 内容');
|
alert('请输入 Kubernetes API 地址和 Token');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
config.deploy = { type: 'kubernetes' };
|
config.deploy = { type: 'kubernetes' };
|
||||||
@@ -1036,14 +1036,14 @@ function updateApp() {
|
|||||||
if (installTarget === 'kubeconfig') {
|
if (installTarget === 'kubeconfig') {
|
||||||
const kcInput = document.createElement('input');
|
const kcInput = document.createElement('input');
|
||||||
kcInput.type = 'hidden';
|
kcInput.type = 'hidden';
|
||||||
kcInput.name = 'kubeconfig';
|
kcInput.name = 'k8s_url';
|
||||||
kcInput.value = installKubeconfigContent;
|
kcInput.value = installK8sURL;
|
||||||
updateForm.appendChild(kcInput);
|
updateForm.appendChild(kcInput);
|
||||||
|
|
||||||
const kctxInput = document.createElement('input');
|
const kctxInput = document.createElement('input');
|
||||||
kctxInput.type = 'hidden';
|
kctxInput.type = 'hidden';
|
||||||
kctxInput.name = 'kubeconfig_context';
|
kctxInput.name = 'k8s_token';
|
||||||
kctxInput.value = installKubeconfigContext;
|
kctxInput.value = installK8sToken;
|
||||||
updateForm.appendChild(kctxInput);
|
updateForm.appendChild(kctxInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1306,12 +1306,12 @@ async function installApp() {
|
|||||||
const selectedTarget = document.querySelector('#install-modal input[name="installTargetRadio"]:checked');
|
const selectedTarget = document.querySelector('#install-modal input[name="installTargetRadio"]:checked');
|
||||||
installTarget = selectedTarget ? selectedTarget.value : 'local';
|
installTarget = selectedTarget ? selectedTarget.value : 'local';
|
||||||
if (installTarget === 'kubeconfig') {
|
if (installTarget === 'kubeconfig') {
|
||||||
const kcContentEl = document.querySelector('#install-modal #kubeconfig-content');
|
const kcContentEl = document.querySelector('#install-modal #k8s-url');
|
||||||
const kcContextEl = document.querySelector('#install-modal #kubeconfig-context');
|
const kcContextEl = document.querySelector('#install-modal #k8s-token');
|
||||||
installKubeconfigContent = kcContentEl ? kcContentEl.value.trim() : '';
|
installK8sURL = kcContentEl ? kcContentEl.value.trim() : '';
|
||||||
installKubeconfigContext = kcContextEl ? kcContextEl.value.trim() : '';
|
installK8sToken = kcContextEl ? kcContextEl.value.trim() : '';
|
||||||
if (!installKubeconfigContent) {
|
if (!installK8sURL || !installK8sToken) {
|
||||||
alert('请输入 kubeconfig 内容');
|
alert('请输入 Kubernetes API 地址和 Token');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
config.deploy = { type: 'kubernetes' };
|
config.deploy = { type: 'kubernetes' };
|
||||||
@@ -1359,13 +1359,13 @@ async function installApp() {
|
|||||||
if (installTarget === 'kubeconfig') {
|
if (installTarget === 'kubeconfig') {
|
||||||
const kcInput = document.createElement('input');
|
const kcInput = document.createElement('input');
|
||||||
kcInput.type = 'hidden';
|
kcInput.type = 'hidden';
|
||||||
kcInput.name = 'kubeconfig';
|
kcInput.name = 'k8s_url';
|
||||||
kcInput.value = installKubeconfigContent;
|
kcInput.value = installK8sURL;
|
||||||
installForm.appendChild(kcInput);
|
installForm.appendChild(kcInput);
|
||||||
const kctxInput = document.createElement('input');
|
const kctxInput = document.createElement('input');
|
||||||
kctxInput.type = 'hidden';
|
kctxInput.type = 'hidden';
|
||||||
kctxInput.name = 'kubeconfig_context';
|
kctxInput.name = 'k8s_token';
|
||||||
kctxInput.value = installKubeconfigContext;
|
kctxInput.value = installK8sToken;
|
||||||
installForm.appendChild(kctxInput);
|
installForm.appendChild(kctxInput);
|
||||||
}
|
}
|
||||||
// 提交安装表单
|
// 提交安装表单
|
||||||
|
|||||||
Reference in New Issue
Block a user