用户指出的三个问题,逐条核实后的结论:
1) 「采集日志路径」对 DaemonSet 确实是多余的 —— 确认后端模板里写死了
`replacement: /var/log/pods/*$1/*.log`(按 pod UID 精确匹配容器标准输出),
表单里填的 logPath 对 DaemonSet 完全不参与渲染。
→ 选择 DaemonSet 时隐藏该字段,提交时强制置空。
2) 「可以指定采集多个命名空间」—— 后端**早就支持**:
models.Collector.TargetNamespaces + promtailNamespaceFilter(),
空数组 = 全集群。只是**前端新建弹框没暴露**(编辑弹框里有)。
→ 新建弹框补上多选 chips(全集群 / 各命名空间可点选),并补齐前后端透传:
dto.CreateCollectorRequest.TargetNamespaces
controller → services.CreateCollectorRequest.TargetNamespaces
models.Collector.TargetNamespaces
之前前端即使传了也会被 Go 静默丢弃(无该字段)。
3) 「命名空间」字段语义已在上一提交补了分类型说明:
- DaemonSet:采集器自身部署在哪个命名空间(不影响采什么)
- Sidecar:目标应用 Deployment 所在命名空间
新增 TestDaemonsetTargetNamespacesFilter:
nil → 不生成 namespaces 白名单(全集群)
["astralx"] → 生成且包含 astralx
["astralx","default"] → 两个都在
=== 实测(d45dfa18ecc5)===
前端文案「只采集这些命名空间」「不选 = 采集整个集群…」均已进包
渲染测试 3 种输入全部符合预期
注:本次 docker push latest 遇到 Docker Hub 502,v0.1 成功而 latest 仍指向旧镜像,
导致首次滚动后 Pod 仍是旧代码;重推 latest + 强制重建 Pod 后确认
Pod digest = d45dfa18ecc5 —— 记录这个坑:push 失败必须核对三方 digest。
159 lines
4.2 KiB
Go
159 lines
4.2 KiB
Go
package controllers
|
||
|
||
import (
|
||
"strconv"
|
||
|
||
"lokistack/dto"
|
||
"lokistack/models"
|
||
"lokistack/services"
|
||
"lokistack/utils"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
type CollectorController struct {
|
||
service *services.CollectorService
|
||
}
|
||
|
||
func NewCollectorController() *CollectorController {
|
||
return &CollectorController{service: services.NewCollectorService()}
|
||
}
|
||
|
||
func (ctrl *CollectorController) Create(c *gin.Context) {
|
||
var req dto.CreateCollectorRequest
|
||
if !utils.BindAndValidate(c, &req) {
|
||
return
|
||
}
|
||
collector, err := ctrl.service.Create(&services.CreateCollectorRequest{
|
||
Name: req.Name, Type: req.Type, LokiDeploymentID: req.LokiDeploymentID,
|
||
HostID: req.HostID, ClusterID: req.ClusterID, TargetDeployment: req.TargetDeployment,
|
||
Namespace: req.Namespace, LogPath: req.LogPath, Labels: req.Labels,
|
||
ScrapeJobs: req.ScrapeJobs, TargetNamespaces: req.TargetNamespaces,
|
||
})
|
||
if err != nil {
|
||
utils.Error(c, 400, err.Error())
|
||
return
|
||
}
|
||
utils.Success(c, models.SanitizedJSON(collector))
|
||
}
|
||
|
||
func (ctrl *CollectorController) Get(c *gin.Context) {
|
||
id, err := utils.IntParam(c, "id")
|
||
if err != nil {
|
||
utils.BadRequest(c, "ID 无效")
|
||
return
|
||
}
|
||
collector, err := ctrl.service.Get(uint(id))
|
||
if err != nil {
|
||
utils.NotFound(c, "采集器不存在")
|
||
return
|
||
}
|
||
utils.Success(c, models.SanitizedJSON(collector))
|
||
}
|
||
|
||
func (ctrl *CollectorController) List(c *gin.Context) {
|
||
page, pageSize := utils.Pagination(c)
|
||
collectors, total, err := ctrl.service.List(page, pageSize)
|
||
if err != nil {
|
||
utils.Error(c, 500, err.Error())
|
||
return
|
||
}
|
||
utils.Success(c, utils.PageData{
|
||
List: models.SanitizedJSON(collectors), Total: total, Page: page, PageSize: pageSize,
|
||
})
|
||
}
|
||
|
||
func (ctrl *CollectorController) Update(c *gin.Context) {
|
||
id, err := utils.IntParam(c, "id")
|
||
if err != nil {
|
||
utils.BadRequest(c, "ID 无效")
|
||
return
|
||
}
|
||
var updates map[string]interface{}
|
||
if !utils.BindAndValidate(c, &updates) {
|
||
return
|
||
}
|
||
if err := ctrl.service.Update(uint(id), updates); err != nil {
|
||
utils.Error(c, 400, err.Error())
|
||
return
|
||
}
|
||
utils.Success(c, gin.H{"message": "更新成功"})
|
||
}
|
||
|
||
func (ctrl *CollectorController) Delete(c *gin.Context) {
|
||
id, err := utils.IntParam(c, "id")
|
||
if err != nil {
|
||
utils.BadRequest(c, "ID 无效")
|
||
return
|
||
}
|
||
if err := ctrl.service.Delete(uint(id)); err != nil {
|
||
utils.Error(c, 400, err.Error())
|
||
return
|
||
}
|
||
utils.Success(c, gin.H{"message": "删除成功"})
|
||
}
|
||
|
||
func (ctrl *CollectorController) Install(c *gin.Context) {
|
||
id, err := utils.IntParam(c, "id")
|
||
if err != nil {
|
||
utils.BadRequest(c, "ID 无效")
|
||
return
|
||
}
|
||
go ctrl.service.Install(uint(id))
|
||
utils.Success(c, gin.H{"message": "已开始部署采集器"})
|
||
}
|
||
|
||
// Preview 渲染「当前配置 + 本次改动」的 promtail.yaml,只预览不落库。
|
||
// 供结构化编辑器里的「预览 YAML」按钮使用:用户能先看到将要写到目标机的配置,
|
||
// 保存前就发现路径/正则写错,而不是点了部署才发现采集起不来。
|
||
func (ctrl *CollectorController) Preview(c *gin.Context) {
|
||
id, err := utils.IntParam(c, "id")
|
||
if err != nil {
|
||
utils.BadRequest(c, "ID 无效")
|
||
return
|
||
}
|
||
var updates map[string]interface{}
|
||
if !utils.BindAndValidate(c, &updates) {
|
||
return
|
||
}
|
||
out, err := ctrl.service.PreviewConfig(uint(id), updates)
|
||
if err != nil {
|
||
utils.Error(c, 400, err.Error())
|
||
return
|
||
}
|
||
utils.Success(c, out)
|
||
}
|
||
|
||
func (ctrl *CollectorController) Uninstall(c *gin.Context) {
|
||
id, err := utils.IntParam(c, "id")
|
||
if err != nil {
|
||
utils.BadRequest(c, "ID 无效")
|
||
return
|
||
}
|
||
go ctrl.service.Uninstall(uint(id))
|
||
utils.Success(c, gin.H{"message": "已开始卸载采集器"})
|
||
}
|
||
|
||
// Logs 返回采集器最近的运行日志(主机:docker logs / journalctl;K8s:Pod 日志)。
|
||
// 主机模式下采集器日志原来只能登机器看 docker logs / journalctl,容器化后这是
|
||
// 顺带拿到的能力,排「采集器起没起来、在报什么错」不用再 SSH。
|
||
func (ctrl *CollectorController) Logs(c *gin.Context) {
|
||
id, err := utils.IntParam(c, "id")
|
||
if err != nil {
|
||
utils.BadRequest(c, "ID 无效")
|
||
return
|
||
}
|
||
tail := 200
|
||
if v := c.Query("tail"); v != "" {
|
||
if n, convErr := strconv.Atoi(v); convErr == nil && n > 0 {
|
||
tail = n
|
||
}
|
||
}
|
||
out, err := ctrl.service.CollectorLogs(uint(id), tail)
|
||
if err != nil {
|
||
utils.Error(c, 400, err.Error())
|
||
return
|
||
}
|
||
utils.Success(c, out)
|
||
}
|