feat: 新增「部署客户端」日志采集器模块
三种采集器(host-promtail / k8s-daemonset / k8s-fluentbit-sidecar),每种关联已部署 Loki: - host-promtail:SSH 安装 Promtail(复用 deployBinary) - k8s-daemonset:helm 安装 Promtail DaemonSet(复用 deployHelm) - k8s-fluentbit-sidecar:kubectl patch 注入 Fluent Bit sidecar 含 Collector 数据模型、三个部署器、配置模板、CRUD API、前端页面。 附带 UI 优化:ErrorBoundary、部署卡片浏览器访问按钮、菜单名称调整。 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
1 parent
2a93de600d
commit
673b2c60c8
17 files changed
+1442
-4
No files matched your search
@@ -0,0 +1,66 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
// Collector 日志采集器(部署客户端),关联一个已部署的 Loki 作为推送目标
|
||||
type Collector struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"` // host-promtail | k8s-daemonset | k8s-fluentbit-sidecar
|
||||
LokiDeploymentID uint `json:"lokiDeploymentID"`
|
||||
LokiDeployment *LokiDeployment `json:"lokiDeployment,omitempty"`
|
||||
RefType string `json:"refType"` // host | cluster
|
||||
HostID *uint `json:"hostID,omitempty"`
|
||||
Host *Host `json:"host,omitempty"`
|
||||
ClusterID *uint `json:"clusterID,omitempty"`
|
||||
Cluster *Cluster `json:"cluster,omitempty"`
|
||||
TargetDeployment string `json:"targetDeployment"` // sidecar 专用:目标 deployment 名
|
||||
Namespace string `json:"namespace"` // k8s 命名空间(默认 logging)
|
||||
LogPath string `json:"logPath"` // 采集日志路径
|
||||
Labels string `json:"labels"` // 额外标签(JSON)
|
||||
Status string `json:"status"` // pending | active | error
|
||||
StatusMessage string `json:"statusMessage"`
|
||||
Config string `json:"config"` // 生成的配置快照
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
func (c *Collector) Sanitized() map[string]interface{} {
|
||||
var loki map[string]interface{}
|
||||
if c.LokiDeployment != nil {
|
||||
loki = map[string]interface{}{
|
||||
"id": c.LokiDeployment.ID,
|
||||
"name": c.LokiDeployment.Name,
|
||||
"endpoint": c.LokiDeployment.Endpoint,
|
||||
"status": c.LokiDeployment.Status,
|
||||
}
|
||||
}
|
||||
var host map[string]interface{}
|
||||
if c.Host != nil {
|
||||
host = c.Host.Sanitized()
|
||||
}
|
||||
var cluster map[string]interface{}
|
||||
if c.Cluster != nil {
|
||||
cluster = c.Cluster.Sanitized()
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"id": c.ID,
|
||||
"name": c.Name,
|
||||
"type": c.Type,
|
||||
"lokiDeploymentID": c.LokiDeploymentID,
|
||||
"lokiDeployment": loki,
|
||||
"refType": c.RefType,
|
||||
"hostID": c.HostID,
|
||||
"host": host,
|
||||
"clusterID": c.ClusterID,
|
||||
"cluster": cluster,
|
||||
"targetDeployment": c.TargetDeployment,
|
||||
"namespace": c.Namespace,
|
||||
"logPath": c.LogPath,
|
||||
"labels": c.Labels,
|
||||
"status": c.Status,
|
||||
"statusMessage": c.StatusMessage,
|
||||
"createdAt": c.CreatedAt,
|
||||
"updatedAt": c.UpdatedAt,
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ type JsonDB struct {
|
||||
Hosts []Host `json:"hosts"`
|
||||
Storages []StorageConfig `json:"storages"`
|
||||
Deployments []LokiDeployment `json:"deployments"`
|
||||
Collectors []Collector `json:"collectors"`
|
||||
Logs []DeploymentLog `json:"logs"`
|
||||
Settings []SystemSetting `json:"settings"`
|
||||
sequences map[string]uint
|
||||
@@ -37,6 +38,7 @@ func InitDB() error {
|
||||
Hosts: []Host{},
|
||||
Storages: []StorageConfig{},
|
||||
Deployments: []LokiDeployment{},
|
||||
Collectors: []Collector{},
|
||||
Logs: []DeploymentLog{},
|
||||
Settings: []SystemSetting{},
|
||||
sequences: map[string]uint{
|
||||
@@ -45,6 +47,7 @@ func InitDB() error {
|
||||
"hosts": 0,
|
||||
"storages": 0,
|
||||
"deployments": 0,
|
||||
"collectors": 0,
|
||||
"logs": 0,
|
||||
},
|
||||
}
|
||||
@@ -57,6 +60,7 @@ func InitDB() error {
|
||||
for _, h := range d.Hosts { if h.ID > d.sequences["hosts"] { d.sequences["hosts"] = h.ID } }
|
||||
for _, s := range d.Storages { if s.ID > d.sequences["storages"] { d.sequences["storages"] = s.ID } }
|
||||
for _, de := range d.Deployments { if de.ID > d.sequences["deployments"] { d.sequences["deployments"] = de.ID } }
|
||||
for _, col := range d.Collectors { if col.ID > d.sequences["collectors"] { d.sequences["collectors"] = col.ID } }
|
||||
for _, l := range d.Logs { if l.ID > d.sequences["logs"] { d.sequences["logs"] = l.ID } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,13 @@ func SanitizedJSON(v any) any {
|
||||
return nil
|
||||
}
|
||||
return val.Sanitized()
|
||||
case Collector:
|
||||
return val.Sanitized()
|
||||
case *Collector:
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
return val.Sanitized()
|
||||
// Slices
|
||||
case []User:
|
||||
result := make([]map[string]interface{}, len(val))
|
||||
@@ -62,6 +69,12 @@ func SanitizedJSON(v any) any {
|
||||
result[i] = item.Sanitized()
|
||||
}
|
||||
return result
|
||||
case []Collector:
|
||||
result := make([]map[string]interface{}, len(val))
|
||||
for i, item := range val {
|
||||
result[i] = item.Sanitized()
|
||||
}
|
||||
return result
|
||||
default:
|
||||
return v
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user