feat: K8s 部署清单、Token 接入加固与日志/设置页优化
- kubernetes/: 30Gi PVC、单副本 Recreate、Secret/Service/NodePort/Ingress 清单与部署文档 - 数据安全: kubeconfig/token 统一加密存储,启动迁移历史明文,DB 0600 - Token 接入: 命名空间级最小权限提示、长期 Token、自签证书 TLS 兼容 - 部署: 命名空间级 Token 下跳过 Namespace patch/枚举,节点信息容错 - 日志页: namespace → app → pod 选择顺序与作用域收窄 - 设置页: 拆分为通用/告警/LDAP/镜像源四个 tab - 弹框: 统一 max-h 滚动与长命令换行,修复内容溢出
This commit is contained in:
1 parent
2d73661dd1
commit
9f7c522157
50 files changed
+1346
-149
No files matched your search
@@ -3,6 +3,7 @@ package services
|
||||
// 镜像仓库源 — 全局设置(参照 kuboard 的国内/海外镜像选择):
|
||||
// overseas = docker.io 官方源;cn = daocloud 国内镜像;custom = 用户自填 registry 前缀。
|
||||
// 影响平台拉取的全部镜像:Loki / Grafana / Promtail / Fluent Bit。
|
||||
// 每个组件可单独配置仓库与 tag(Loki/Grafana 的 tag 留空则跟随部署版本)。
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -20,15 +21,23 @@ const (
|
||||
daocloudPrefix = "docker.m.daocloud.io"
|
||||
)
|
||||
|
||||
// ComponentImage 单个组件的镜像配置
|
||||
type ComponentImage struct {
|
||||
Repository string `json:"repository"` // 如 grafana/loki
|
||||
Tag string `json:"tag"` // 留空表示跟随部署版本 / 使用默认
|
||||
}
|
||||
|
||||
// ImageRegistryConfig 镜像源配置
|
||||
type ImageRegistryConfig struct {
|
||||
Mode string `json:"mode"` // overseas | cn | custom
|
||||
CustomHost string `json:"customHost"` // custom 模式下的仓库前缀,如 swr.cn-east-2.myhuaweicloud.com/xxx
|
||||
Mode string `json:"mode"` // overseas | cn | custom
|
||||
CustomHost string `json:"customHost"` // custom 模式下的仓库前缀
|
||||
Images map[string]ComponentImage `json:"images"` // loki/grafana/promtail/fluentbit
|
||||
}
|
||||
|
||||
type ImageRegistryPublic struct {
|
||||
Mode string `json:"mode"`
|
||||
CustomHost string `json:"customHost"`
|
||||
Mode string `json:"mode"`
|
||||
CustomHost string `json:"customHost"`
|
||||
Images map[string]ComponentImage `json:"images"`
|
||||
// 示例预览(前端展示生效镜像)
|
||||
SampleLoki string `json:"sampleLoki"`
|
||||
SampleGrafana string `json:"sampleGrafana"`
|
||||
@@ -36,31 +45,55 @@ type ImageRegistryPublic struct {
|
||||
SampleFluentBit string `json:"sampleFluentBit"`
|
||||
}
|
||||
|
||||
var defaultComponentImages = map[string]ComponentImage{
|
||||
"loki": {Repository: "grafana/loki", Tag: ""}, // 跟随部署版本
|
||||
"grafana": {Repository: "grafana/grafana", Tag: ""}, // 跟随部署版本
|
||||
"promtail": {Repository: "grafana/promtail", Tag: "3.4.0"},
|
||||
"fluentbit": {Repository: "fluent/fluent-bit", Tag: "5.1.5"},
|
||||
}
|
||||
|
||||
func (c *ImageRegistryConfig) fillDefaults() {
|
||||
if c.Images == nil {
|
||||
c.Images = map[string]ComponentImage{}
|
||||
}
|
||||
for name, def := range defaultComponentImages {
|
||||
img, ok := c.Images[name]
|
||||
if !ok || (img.Repository == "" && img.Tag == "") {
|
||||
c.Images[name] = def
|
||||
continue
|
||||
}
|
||||
if img.Repository == "" {
|
||||
img.Repository = def.Repository
|
||||
}
|
||||
c.Images[name] = img
|
||||
}
|
||||
}
|
||||
|
||||
func GetImageRegistry() *ImageRegistryConfig {
|
||||
db := repository.GetDB()
|
||||
if db == nil {
|
||||
return &ImageRegistryConfig{Mode: RegistryOverseas}
|
||||
}
|
||||
db.RLock()
|
||||
var raw string
|
||||
for i := range db.Settings {
|
||||
if db.Settings[i].Key == imageRegistrySettingKey {
|
||||
raw = db.Settings[i].Value
|
||||
break
|
||||
}
|
||||
}
|
||||
db.RUnlock()
|
||||
cfg := &ImageRegistryConfig{Mode: RegistryOverseas}
|
||||
if raw != "" {
|
||||
_ = json.Unmarshal([]byte(raw), cfg)
|
||||
if db != nil {
|
||||
db.RLock()
|
||||
var raw string
|
||||
for i := range db.Settings {
|
||||
if db.Settings[i].Key == imageRegistrySettingKey {
|
||||
raw = db.Settings[i].Value
|
||||
break
|
||||
}
|
||||
}
|
||||
db.RUnlock()
|
||||
if raw != "" {
|
||||
_ = json.Unmarshal([]byte(raw), cfg)
|
||||
}
|
||||
}
|
||||
if cfg.Mode == "" {
|
||||
cfg.Mode = RegistryOverseas
|
||||
}
|
||||
cfg.fillDefaults()
|
||||
return cfg
|
||||
}
|
||||
|
||||
func SaveImageRegistry(mode, customHost string) error {
|
||||
func SaveImageRegistry(mode, customHost string, images map[string]ComponentImage) error {
|
||||
if mode != RegistryOverseas && mode != RegistryCN && mode != RegistryCustom {
|
||||
return fmt.Errorf("无效的镜像源模式")
|
||||
}
|
||||
@@ -70,7 +103,15 @@ func SaveImageRegistry(mode, customHost string) error {
|
||||
return fmt.Errorf("自定义镜像仓库地址无效(需含域名,如 swr.cn-east-2.myhuaweicloud.com/your-ns)")
|
||||
}
|
||||
}
|
||||
data, _ := json.Marshal(&ImageRegistryConfig{Mode: mode, CustomHost: customHost})
|
||||
cfg := &ImageRegistryConfig{Mode: mode, CustomHost: customHost, Images: images}
|
||||
cfg.fillDefaults()
|
||||
// 清理空白输入
|
||||
clean := map[string]ComponentImage{}
|
||||
for name, img := range cfg.Images {
|
||||
clean[name] = ComponentImage{Repository: strings.TrimSpace(img.Repository), Tag: strings.TrimSpace(img.Tag)}
|
||||
}
|
||||
cfg.Images = clean
|
||||
data, _ := json.Marshal(cfg)
|
||||
db := repository.GetDB()
|
||||
newID := db.NextID("settings")
|
||||
db.Lock()
|
||||
@@ -97,7 +138,6 @@ func ResolveImage(official string) string {
|
||||
return daocloudPrefix + "/" + strings.TrimPrefix(official, "docker.io/")
|
||||
}
|
||||
if !strings.Contains(strings.SplitN(official, "/", 2)[0], ".") {
|
||||
// 无仓库前缀的官方镜像(grafana/loki:tag)
|
||||
return daocloudPrefix + "/" + official
|
||||
}
|
||||
return official
|
||||
@@ -110,22 +150,41 @@ func ResolveImage(official string) string {
|
||||
}
|
||||
return official
|
||||
default:
|
||||
if strings.HasPrefix(official, "docker.io/") {
|
||||
return official // 官方模式保留全限定名
|
||||
}
|
||||
return official
|
||||
}
|
||||
}
|
||||
|
||||
// ResolveImageFor 按组件配置解析镜像;组件 tag 为空时使用 fallbackTag(通常是部署版本)。
|
||||
func ResolveImageFor(component, fallbackRepo, fallbackTag string) string {
|
||||
cfg := GetImageRegistry()
|
||||
img, ok := cfg.Images[component]
|
||||
repo := fallbackRepo
|
||||
tag := fallbackTag
|
||||
if ok {
|
||||
if strings.TrimSpace(img.Repository) != "" {
|
||||
repo = strings.TrimSpace(img.Repository)
|
||||
}
|
||||
if strings.TrimSpace(img.Tag) != "" {
|
||||
tag = strings.TrimSpace(img.Tag)
|
||||
}
|
||||
}
|
||||
official := repo
|
||||
if !strings.Contains(repo, ":") && tag != "" {
|
||||
official = repo + ":" + tag
|
||||
}
|
||||
return ResolveImage(official)
|
||||
}
|
||||
|
||||
// ImageRegistryPublicConfig 对外输出(含示例预览)
|
||||
func ImageRegistryPublicConfig() *ImageRegistryPublic {
|
||||
cfg := GetImageRegistry()
|
||||
return &ImageRegistryPublic{
|
||||
Mode: cfg.Mode,
|
||||
CustomHost: cfg.CustomHost,
|
||||
SampleLoki: ResolveImage("docker.io/grafana/loki:3.4.0"),
|
||||
SampleGrafana: ResolveImage("docker.io/grafana/grafana:11.3.0"),
|
||||
SamplePromtail: ResolveImage("docker.io/grafana/promtail:3.4.0"),
|
||||
SampleFluentBit: ResolveImage("docker.io/fluent/fluent-bit:5.1.5"),
|
||||
Images: cfg.Images,
|
||||
SampleLoki: ResolveImageFor("loki", "grafana/loki", "3.4.0"),
|
||||
SampleGrafana: ResolveImageFor("grafana", "grafana/grafana", "11.3.0"),
|
||||
SamplePromtail: ResolveImageFor("promtail", "grafana/promtail", "3.4.0"),
|
||||
SampleFluentBit: ResolveImageFor("fluentbit", "fluent/fluent-bit", "5.1.5"),
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user