|
| 1 | +// Package main dumps operator DefaultHealthRules as TypeScript for the console plugin. |
| 2 | +// Run via scripts/generate-health-rule-defaults.sh (do not invoke directly unless go.mod replace is set). |
| 3 | +package main |
| 4 | + |
| 5 | +import ( |
| 6 | + "fmt" |
| 7 | + "os" |
| 8 | + "regexp" |
| 9 | + "strings" |
| 10 | + "time" |
| 11 | + |
| 12 | + flowsv1beta2 "github.com/netobserv/netobserv-operator/api/flowcollector/v1beta2" |
| 13 | + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| 14 | +) |
| 15 | + |
| 16 | +var regTrim = regexp.MustCompile(`([a-zA-Z])(0[a-zA-Z])+`) |
| 17 | + |
| 18 | +func durationTrimmed(d *metav1.Duration) string { |
| 19 | + if d == nil { |
| 20 | + return "" |
| 21 | + } |
| 22 | + return regTrim.ReplaceAllString(d.Duration.String(), "$1") |
| 23 | +} |
| 24 | + |
| 25 | +func modeOrDefault(m flowsv1beta2.HealthRuleMode) string { |
| 26 | + if m == "" { |
| 27 | + return string(flowsv1beta2.ModeAlert) |
| 28 | + } |
| 29 | + return string(m) |
| 30 | +} |
| 31 | + |
| 32 | +func tsString(s string) string { |
| 33 | + return fmt.Sprintf("%q", s) |
| 34 | +} |
| 35 | + |
| 36 | +func writeThresholdFields(b *strings.Builder, indent string, t flowsv1beta2.HealthRuleThresholds) { |
| 37 | + if t.Info != "" { |
| 38 | + fmt.Fprintf(b, "%sinfo: %s,\n", indent, tsString(t.Info)) |
| 39 | + } |
| 40 | + if t.Warning != "" { |
| 41 | + fmt.Fprintf(b, "%swarning: %s,\n", indent, tsString(t.Warning)) |
| 42 | + } |
| 43 | + if t.Critical != "" { |
| 44 | + fmt.Fprintf(b, "%scritical: %s,\n", indent, tsString(t.Critical)) |
| 45 | + } |
| 46 | +} |
| 47 | + |
| 48 | +func formatThresholdsInline(t flowsv1beta2.HealthRuleThresholds) string { |
| 49 | + parts := []string{} |
| 50 | + if t.Info != "" { |
| 51 | + parts = append(parts, fmt.Sprintf("info: %s", tsString(t.Info))) |
| 52 | + } |
| 53 | + if t.Warning != "" { |
| 54 | + parts = append(parts, fmt.Sprintf("warning: %s", tsString(t.Warning))) |
| 55 | + } |
| 56 | + if t.Critical != "" { |
| 57 | + parts = append(parts, fmt.Sprintf("critical: %s", tsString(t.Critical))) |
| 58 | + } |
| 59 | + return "{ " + strings.Join(parts, ", ") + " }" |
| 60 | +} |
| 61 | + |
| 62 | +func writeVariant(b *strings.Builder, indent string, v flowsv1beta2.HealthRuleVariant) { |
| 63 | + fmt.Fprintf(b, "%s{\n", indent) |
| 64 | + fmt.Fprintf(b, "%s thresholds: %s", indent, formatThresholdsInline(v.Thresholds)) |
| 65 | + hasExtra := v.LowVolumeThreshold != "" || v.GroupBy != "" || v.TrendOffset != nil || v.TrendDuration != nil || v.Mode != nil |
| 66 | + if hasExtra { |
| 67 | + b.WriteString(",\n") |
| 68 | + if v.LowVolumeThreshold != "" { |
| 69 | + fmt.Fprintf(b, "%s lowVolumeThreshold: %s,\n", indent, tsString(v.LowVolumeThreshold)) |
| 70 | + } |
| 71 | + if v.GroupBy != "" { |
| 72 | + fmt.Fprintf(b, "%s groupBy: %s,\n", indent, tsString(string(v.GroupBy))) |
| 73 | + } |
| 74 | + if off := durationTrimmed(v.TrendOffset); off != "" { |
| 75 | + fmt.Fprintf(b, "%s trendOffset: %s,\n", indent, tsString(off)) |
| 76 | + } |
| 77 | + if dur := durationTrimmed(v.TrendDuration); dur != "" { |
| 78 | + fmt.Fprintf(b, "%s trendDuration: %s,\n", indent, tsString(dur)) |
| 79 | + } |
| 80 | + if v.Mode != nil { |
| 81 | + fmt.Fprintf(b, "%s mode: %s,\n", indent, tsString(string(*v.Mode))) |
| 82 | + } |
| 83 | + fmt.Fprintf(b, "%s}", indent) |
| 84 | + } else { |
| 85 | + b.WriteString(fmt.Sprintf("\n%s}", indent)) |
| 86 | + } |
| 87 | +} |
| 88 | + |
| 89 | +func main() { |
| 90 | + date := time.Now().Format(time.RFC1123) |
| 91 | + var b strings.Builder |
| 92 | + |
| 93 | + fmt.Fprintf(&b, "// Auto-generated on %s by scripts/generate-health-rule-defaults.sh ; DO NOT EDIT.\n", date) |
| 94 | + b.WriteString("// Source: network-observability-operator api/flowcollector/v1beta2.DefaultHealthRules\n") |
| 95 | + b.WriteString("// Re-run: OPERATOR_PATH=../network-observability-operator ./scripts/generate-health-rule-defaults.sh\n\n") |
| 96 | + |
| 97 | + b.WriteString("export type VariantFieldPlaceholders = {\n") |
| 98 | + b.WriteString(" critical?: string;\n") |
| 99 | + b.WriteString(" warning?: string;\n") |
| 100 | + b.WriteString(" info?: string;\n") |
| 101 | + b.WriteString(" lowVolumeThreshold?: string;\n") |
| 102 | + b.WriteString(" trendOffset?: string;\n") |
| 103 | + b.WriteString(" trendDuration?: string;\n") |
| 104 | + b.WriteString("};\n\n") |
| 105 | + |
| 106 | + b.WriteString("export type HealthRuleDefaultVariant = {\n") |
| 107 | + b.WriteString(" thresholds: { info?: string; warning?: string; critical?: string };\n") |
| 108 | + b.WriteString(" lowVolumeThreshold?: string;\n") |
| 109 | + b.WriteString(" groupBy?: string;\n") |
| 110 | + b.WriteString(" trendOffset?: string;\n") |
| 111 | + b.WriteString(" trendDuration?: string;\n") |
| 112 | + b.WriteString(" mode?: 'Alert' | 'Recording';\n") |
| 113 | + b.WriteString("};\n\n") |
| 114 | + |
| 115 | + b.WriteString("export type HealthRuleDefaultSummary = {\n") |
| 116 | + b.WriteString(" template: string;\n") |
| 117 | + b.WriteString(" /** Effective rule mode (Alert when unset in operator defaults). */\n") |
| 118 | + b.WriteString(" mode: 'Alert' | 'Recording';\n") |
| 119 | + b.WriteString(" /** All default variants for this template (used when customizing). */\n") |
| 120 | + b.WriteString(" variants: HealthRuleDefaultVariant[];\n") |
| 121 | + b.WriteString("};\n\n") |
| 122 | + |
| 123 | + b.WriteString("/** Default health-rule templates from the operator (name, mode, variants). */\n") |
| 124 | + b.WriteString("export const HEALTH_RULE_DEFAULTS: HealthRuleDefaultSummary[] = [\n") |
| 125 | + |
| 126 | + for _, rule := range flowsv1beta2.DefaultHealthRules { |
| 127 | + if len(rule.Variants) == 0 { |
| 128 | + fmt.Fprintf(os.Stderr, "default health rule %s has no variants\n", rule.Template) |
| 129 | + os.Exit(1) |
| 130 | + } |
| 131 | + mode := modeOrDefault(rule.Mode) |
| 132 | + |
| 133 | + fmt.Fprintf(&b, " {\n") |
| 134 | + fmt.Fprintf(&b, " template: %s,\n", tsString(string(rule.Template))) |
| 135 | + fmt.Fprintf(&b, " mode: %s,\n", tsString(mode)) |
| 136 | + b.WriteString(" variants: [\n") |
| 137 | + for i, v := range rule.Variants { |
| 138 | + writeVariant(&b, " ", v) |
| 139 | + if i < len(rule.Variants)-1 { |
| 140 | + b.WriteString(",\n") |
| 141 | + } else { |
| 142 | + b.WriteString("\n") |
| 143 | + } |
| 144 | + } |
| 145 | + b.WriteString(" ]\n") |
| 146 | + b.WriteString(" },\n") |
| 147 | + } |
| 148 | + b.WriteString("];\n\n") |
| 149 | + |
| 150 | + b.WriteString("/** Primary (first) variant defaults per health-rule template (form placeholders). */\n") |
| 151 | + b.WriteString("export const VARIANT_DEFAULT_PLACEHOLDERS: Record<string, VariantFieldPlaceholders> = {\n") |
| 152 | + for _, rule := range flowsv1beta2.DefaultHealthRules { |
| 153 | + v := rule.Variants[0] |
| 154 | + fmt.Fprintf(&b, " %s: {\n", rule.Template) |
| 155 | + writeThresholdFields(&b, " ", v.Thresholds) |
| 156 | + if v.LowVolumeThreshold != "" { |
| 157 | + fmt.Fprintf(&b, " lowVolumeThreshold: %s,\n", tsString(v.LowVolumeThreshold)) |
| 158 | + } |
| 159 | + if off := durationTrimmed(v.TrendOffset); off != "" { |
| 160 | + fmt.Fprintf(&b, " trendOffset: %s,\n", tsString(off)) |
| 161 | + } |
| 162 | + if dur := durationTrimmed(v.TrendDuration); dur != "" { |
| 163 | + fmt.Fprintf(&b, " trendDuration: %s,\n", tsString(dur)) |
| 164 | + } |
| 165 | + b.WriteString(" },\n") |
| 166 | + } |
| 167 | + b.WriteString("};\n\n") |
| 168 | + |
| 169 | + b.WriteString("export const getVariantPlaceholders = (template?: string): VariantFieldPlaceholders => {\n") |
| 170 | + b.WriteString(" if (!template) {\n") |
| 171 | + b.WriteString(" return {};\n") |
| 172 | + b.WriteString(" }\n") |
| 173 | + b.WriteString(" return VARIANT_DEFAULT_PLACEHOLDERS[template] ?? {};\n") |
| 174 | + b.WriteString("};\n\n") |
| 175 | + |
| 176 | + b.WriteString("export const getHealthRuleDefault = (template?: string): HealthRuleDefaultSummary | undefined =>\n") |
| 177 | + b.WriteString(" template ? HEALTH_RULE_DEFAULTS.find(d => d.template === template) : undefined;\n\n") |
| 178 | + |
| 179 | + b.WriteString("/** Convert an operator default into a FlowCollector healthRules entry. */\n") |
| 180 | + b.WriteString("export const healthRuleDefaultToFLP = (def: HealthRuleDefaultSummary): {\n") |
| 181 | + b.WriteString(" template: string;\n") |
| 182 | + b.WriteString(" mode: 'Alert' | 'Recording';\n") |
| 183 | + b.WriteString(" variants: HealthRuleDefaultVariant[];\n") |
| 184 | + b.WriteString("} => ({\n") |
| 185 | + b.WriteString(" template: def.template,\n") |
| 186 | + b.WriteString(" mode: def.mode,\n") |
| 187 | + b.WriteString(" variants: def.variants.map(v => ({\n") |
| 188 | + b.WriteString(" thresholds: { ...v.thresholds },\n") |
| 189 | + b.WriteString(" ...(v.lowVolumeThreshold ? { lowVolumeThreshold: v.lowVolumeThreshold } : {}),\n") |
| 190 | + b.WriteString(" ...(v.groupBy ? { groupBy: v.groupBy } : {}),\n") |
| 191 | + b.WriteString(" ...(v.trendOffset ? { trendOffset: v.trendOffset } : {}),\n") |
| 192 | + b.WriteString(" ...(v.trendDuration ? { trendDuration: v.trendDuration } : {}),\n") |
| 193 | + b.WriteString(" ...(v.mode ? { mode: v.mode } : {})\n") |
| 194 | + b.WriteString(" }))\n") |
| 195 | + b.WriteString("});\n\n") |
| 196 | + |
| 197 | + b.WriteString("/** Format threshold map for compact UI display (e.g. Manage rules drawer). */\n") |
| 198 | + b.WriteString("export const formatThresholdsSummary = (thresholds?: {\n") |
| 199 | + b.WriteString(" info?: string | number;\n") |
| 200 | + b.WriteString(" warning?: string | number;\n") |
| 201 | + b.WriteString(" critical?: string | number;\n") |
| 202 | + b.WriteString("}): string => {\n") |
| 203 | + b.WriteString(" if (!thresholds) {\n") |
| 204 | + b.WriteString(" return '';\n") |
| 205 | + b.WriteString(" }\n") |
| 206 | + b.WriteString(" const parts: string[] = [];\n") |
| 207 | + b.WriteString(" if (thresholds.info != null && thresholds.info !== '') {\n") |
| 208 | + b.WriteString(" parts.push(`info: ${thresholds.info}`);\n") |
| 209 | + b.WriteString(" }\n") |
| 210 | + b.WriteString(" if (thresholds.warning != null && thresholds.warning !== '') {\n") |
| 211 | + b.WriteString(" parts.push(`warning: ${thresholds.warning}`);\n") |
| 212 | + b.WriteString(" }\n") |
| 213 | + b.WriteString(" if (thresholds.critical != null && thresholds.critical !== '') {\n") |
| 214 | + b.WriteString(" parts.push(`critical: ${thresholds.critical}`);\n") |
| 215 | + b.WriteString(" }\n") |
| 216 | + b.WriteString(" return parts.join(', ');\n") |
| 217 | + b.WriteString("};\n") |
| 218 | + |
| 219 | + os.Stdout.WriteString(b.String()) |
| 220 | +} |
0 commit comments