package services import ( "fmt" "io" "regexp" "strconv" "strings" "gopkg.in/yaml.v3" ) var retentionPeriodPattern = regexp.MustCompile(`^([1-9][0-9]*)(d|h)$`) // parseRetentionYAML 仅接收 limits_config.retention_period,避免把任意配置或凭证写入部署。 func parseRetentionYAML(source string) (int, error) { var fragment struct { LimitsConfig *struct { RetentionPeriod string `yaml:"retention_period"` } `yaml:"limits_config"` } decoder := yaml.NewDecoder(strings.NewReader(source)) decoder.KnownFields(true) if err := decoder.Decode(&fragment); err != nil { return 0, fmt.Errorf("YAML 格式无效: %w", err) } var extra interface{} if err := decoder.Decode(&extra); err != io.EOF { return 0, fmt.Errorf("只允许一段 limits_config YAML") } if fragment.LimitsConfig == nil { return 0, fmt.Errorf("缺少 limits_config.retention_period") } match := retentionPeriodPattern.FindStringSubmatch(fragment.LimitsConfig.RetentionPeriod) if match == nil { return 0, fmt.Errorf("retention_period 须使用正整数天数,如 90d;也支持 2160h") } value, err := strconv.Atoi(match[1]) if err != nil { return 0, fmt.Errorf("保留时长超出范围") } if match[2] == "h" { if value%24 != 0 { return 0, fmt.Errorf("小时数必须是 24 的倍数") } value /= 24 } if value < 1 || value > 36500 { return 0, fmt.Errorf("保留天数须在 1 到 36500 之间") } return value, nil } // UpdateRetention 保存 Loki 的保留时长;apply 为 true 时触发当前部署重新生成并应用配置。 func (s *DeploymentService) UpdateRetention(id uint, source string, apply bool) (int, error) { days, err := parseRetentionYAML(source) if err != nil { return 0, err } deployment, err := s.Get(id) if err != nil { return 0, err } if deployment.Status == "deploying" { return 0, fmt.Errorf("部署正在进行中,请稍后编辑") } if apply && deployment.Status != "running" { return 0, fmt.Errorf("当前部署未运行,请仅保存后再部署") } if err := s.Update(id, map[string]interface{}{"retentionDays": float64(days)}); err != nil { return 0, err } if apply { if err := s.StartDeploy(id); err != nil { return days, fmt.Errorf("保留配置已保存,但重新部署失败: %w", err) } } return days, nil }