// Copyright (c) 2023 Volvo Car Corporation
// SPDX-License-Identifier: Apache-2.0

// Code generated by lingon. EDIT AS MUCH AS YOU LIKE.

package vmk8s

import (
	_ "embed"
	"fmt"

	vmo "github.com/VictoriaMetrics/operator/api/operator/v1"
	"github.com/golingon/lingon/pkg/kube"
	ku "github.com/golingon/lingon/pkg/kubeutil"
	"github.com/golingon/lingoneks/meta"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var AM = &meta.Metadata{
	Name:      "alert-manager",
	Namespace: namespace,
	Instance:  "alert-manager-" + namespace,
	Component: "alert",
	PartOf:    appName,
	Version:   AlertManagerVersion,
	ManagedBy: "lingon",
}

const AlertManagerVersion = "v0.25.0"

type VMAlertManager struct {
	kube.App

	AlertManager        *v1beta1.VMAlertmanager
	Alert               *v1beta1.VMAlert
	SlackMonzoTplCM     *corev1.ConfigMap
	OverviewDashboardCM *corev1.ConfigMap
	Rules               *v1beta1.VMRule
	Secrets             *corev1.Secret
	DashboardCM         *corev1.ConfigMap
}

func NewVMAlertManager() *VMAlertManager {
	return &VMAlertManager{
		AlertManager:        AlertManager,
		SlackMonzoTplCM:     SlackMonzoTplCM,
		OverviewDashboardCM: AlertManagerOverviewDashCM,
		Rules:               AlertManagerRules,
		Secrets:             AlertManagerSecrets,
		DashboardCM:         AlertManagerDashboardCM,
		Alert:               Alert,
	}
}

var AlertManager = &v1beta1.VMAlertmanager{
	ObjectMeta: AM.ObjectMeta(),
	Spec: v1beta1.VMAlertmanagerSpec{
		ConfigSecret:       AlertManagerSecrets.Name,
		Image:              v1beta1.Image{Tag: AlertManagerVersion},
		RoutePrefix:        "/",
		SelectAllByDefault: true,
		Resources:          ku.Resources("500m", "512Mi", "500m", "512Mi"),
		Templates: []v1beta1.ConfigMapKeyReference{
			{
				Key:                  "monzo.tmpl",
				LocalObjectReference: corev1.LocalObjectReference{Name: SlackMonzoTplCM.Name},
			},
		},
	},
	TypeMeta: metav1.TypeMeta{
		APIVersion: "operator.victoriametrics.com/v1beta1",
		Kind:       "VMAlertmanager",
	},
}

var Alert = &v1beta1.VMAlert{
	ObjectMeta: AM.ObjectMeta(),
	Spec: v1beta1.VMAlertSpec{
		Datasource: v1beta1.VMAlertDatasourceSpec{
			URL: fmt.Sprintf(
				"http://%s.%s.svc:%s/",
				VMDB.PrefixedName(), VMDB.Namespace, VMDB.Spec.Port,
			),
		},
		EvaluationInterval: "15s",
		ExternalLabels:     map[string]string{},
		ExtraArgs:          map[string]string{"remoteWrite.disablePathAppend": "true"},
		Image:              v1beta1.Image{Tag: "v" + Single.Version},
		Notifiers: []v1beta1.VMAlertNotifierSpec{
			{
				URL: fmt.Sprintf(
					"http://%s.%s.svc:%d",
					AlertManager.PrefixedName(),
					AlertManager.Namespace,
					VMAlertManagerPort,
				),
			},
		},
		Resources: ku.Resources("200m", "128Mi", "200m", "128Mi"),
		RemoteRead: &v1beta1.VMAlertRemoteReadSpec{
			URL: fmt.Sprintf(
				"http://%s.%s.svc:%s/",
				VMDB.PrefixedName(), VMDB.Namespace, VMDB.Spec.Port,
			),
		},
		RemoteWrite: &v1beta1.VMAlertRemoteWriteSpec{
			URL: fmt.Sprintf(
				"http://%s.%s.svc:%s/api/v1/write",
				VMDB.PrefixedName(), VMDB.Namespace, VMDB.Spec.Port,
			),
		},
		SelectAllByDefault: true,
	},
	TypeMeta: metav1.TypeMeta{
		APIVersion: "operator.victoriametrics.com/v1beta1",
		Kind:       "VMAlert",
	},
}

var AlertManagerSecrets = &corev1.Secret{
	ObjectMeta: AM.ObjectMeta(),
	StringData: map[string]string{
		"alertmanager.yaml": `
global:
  resolve_timeout: 5m
  slack_api_url: http://slack:30500/
inhibit_rules:
- equal:
  - cluster
  - namespace
  - alertname
  source_matchers:
  - severity=critical
  target_matchers:
  - severity=~"warning|info"
- equal:
  - cluster
  - namespace
  - alertname
  source_matchers:
  - severity=warning
  target_matchers:
  - severity=info
- equal:
  - cluster
  - namespace
  source_matchers:
  - alertname=InfoInhibitor
  target_matchers:
  - severity=info
receivers:
- name: slack-monitoring
  slack_configs:
  - actions:
    - text: 'Runbook :green_book:'
      type: button
      url: '{{ (index .Alerts 0).Annotations.runbook_url }}'
    - text: 'Query :mag:'
      type: button
      url: '{{ (index .Alerts 0).GeneratorURL }}'
    - text: 'Dashboard :grafana:'
      type: button
      url: '{{ (index .Alerts 0).Annotations.dashboard }}'
    - text: 'Silence :no_bell:'
      type: button
      url: '{{ template "__alert_silence_link" . }}'
    - text: '{{ template "slack.monzo.link_button_text" . }}'
      type: button
      url: '{{ .CommonAnnotations.link_url }}'
    channel: '#channel'
    color: '{{ template "slack.monzo.color" . }}'
    icon_emoji: '{{ template "slack.monzo.icon_emoji" . }}'
    send_resolved: true
    text: '{{ template "slack.monzo.text" . }}'
    title: '{{ template "slack.monzo.title" . }}'
- name: slack-code-owners
  slack_configs:
  - actions:
    - text: 'Runbook :green_book:'
      type: button
      url: '{{ (index .Alerts 0).Annotations.runbook }}'
    - text: 'Query :mag:'
      type: button
      url: '{{ (index .Alerts 0).GeneratorURL }}'
    - text: 'Dashboard :grafana:'
      type: button
      url: '{{ (index .Alerts 0).Annotations.dashboard }}'
    - text: 'Silence :no_bell:'
      type: button
      url: '{{ template "__alert_silence_link" . }}'
    - text: '{{ template "slack.monzo.link_button_text" . }}'
      type: button
      url: '{{ .CommonAnnotations.link_url }}'
    channel: '#{{ .CommonLabels.code_owner_channel }}'
    color: '{{ template "slack.monzo.color" . }}'
    icon_emoji: '{{ template "slack.monzo.icon_emoji" . }}'
    send_resolved: true
    text: '{{ template "slack.monzo.text" . }}'
    title: '{{ template "slack.monzo.title" . }}'
route:
  group_by:
  - alertgroup
  - job
  group_interval: 5m
  group_wait: 30s
  receiver: slack-monitoring
  repeat_interval: 12h
  routes:
  - group_by:
    - code_owner_channel
    - alertgroup
    - job
    matchers:
    - code_owner_channel!=""
    - severity=~"info|warning|critical"
    receiver: slack-code-owners
  - continue: true
    matchers:
    - severity=~"info|warning|critical"
    receiver: slack-monitoring
templates:
- /etc/vm/configs/**/*.tmpl
`,
	},
	TypeMeta: ku.TypeSecretV1,
} // TODO: SECRETS SHOULD BE STORED ELSEWHERE THAN IN THE CODE!!!!

var AlertManagerRules = &v1beta1.VMRule{
	ObjectMeta: AM.ObjectMetaNameSuffix("rules"),
	Spec: v1beta1.VMRuleSpec{
		Groups: []v1beta1.RuleGroup{
			{
				Name: "alertmanager.rules",
				Rules: []v1beta1.Rule{
					{
						Alert: "AlertmanagerFailedReload",
						Annotations: map[string]string{
							"description": "Configuration has failed to load for {{ $labels.namespace }}/{{ $labels.pod}}.",
							"runbook_url": "https://runbooks.prometheus-operator.dev/runbooks/alertmanager/alertmanagerfailedreload",
							"summary":     "Reloading an Alertmanager configuration has failed.",
						},
						Expr: `
# Without max_over_time, failed scrapes could create false negatives, see
# https://www.robustperception.io/alerting-on-gauges-in-prometheus-2-0 for details.
max_over_time(alertmanager_config_last_reload_successful{job="vmalertmanager-vmk8s-victoria-metrics-k8s-stack",namespace="monitoring"}[5m]) == 0
`,
						For:    "10m",
						Labels: map[string]string{"severity": "critical"},
					}, {
						Alert: "AlertmanagerMembersInconsistent",
						Annotations: map[string]string{
							"description": "Alertmanager {{ $labels.namespace }}/{{ $labels.pod}} has only found {{ $value }} members of the {{$labels.job}} cluster.",
							"runbook_url": "https://runbooks.prometheus-operator.dev/runbooks/alertmanager/alertmanagermembersinconsistent",
							"summary":     "A member of an Alertmanager cluster has not found all other cluster members.",
						},
						Expr: `
# Without max_over_time, failed scrapes could create false negatives, see
# https://www.robustperception.io/alerting-on-gauges-in-prometheus-2-0 for details.
  max_over_time(alertmanager_cluster_members{job="vmalertmanager-vmk8s-victoria-metrics-k8s-stack",namespace="monitoring"}[5m])
< on (namespace,service) group_left
  count by (namespace,service) (max_over_time(alertmanager_cluster_members{job="vmalertmanager-vmk8s-victoria-metrics-k8s-stack",namespace="monitoring"}[5m]))
`,
						For:    "15m",
						Labels: map[string]string{"severity": "critical"},
					}, {
						Alert: "AlertmanagerFailedToSendAlerts",
						Annotations: map[string]string{
							"description": "Alertmanager {{ $labels.namespace }}/{{ $labels.pod}} failed to send {{ $value | humanizePercentage }} of notifications to {{ $labels.integration }}.",
							"runbook_url": "https://runbooks.prometheus-operator.dev/runbooks/alertmanager/alertmanagerfailedtosendalerts",
							"summary":     "An Alertmanager instance failed to send notifications.",
						},
						Expr: `
(
  rate(alertmanager_notifications_failed_total{job="vmalertmanager-vmk8s-victoria-metrics-k8s-stack",namespace="monitoring"}[5m])
/
  rate(alertmanager_notifications_total{job="vmalertmanager-vmk8s-victoria-metrics-k8s-stack",namespace="monitoring"}[5m])
)
> 0.01
`,
						For:    "5m",
						Labels: map[string]string{"severity": "warning"},
					}, {
						Alert: "AlertmanagerClusterFailedToSendAlerts",
						Annotations: map[string]string{
							"description": "The minimum notification failure rate to {{ $labels.integration }} sent from any instance in the {{$labels.job}} cluster is {{ $value | humanizePercentage }}.",
							"runbook_url": "https://runbooks.prometheus-operator.dev/runbooks/alertmanager/alertmanagerclusterfailedtosendalerts",
							"summary":     "All Alertmanager instances in a cluster failed to send notifications to a critical integration.",
						},
						Expr:   "min by (namespace,service, integration) (\n  rate(alertmanager_notifications_failed_total{job=\"vmalertmanager-vmk8s-victoria-metrics-k8s-stack\",namespace=\"monitoring\", integration=~`.*`}[5m])\n/\n  rate(alertmanager_notifications_total{job=\"vmalertmanager-vmk8s-victoria-metrics-k8s-stack\",namespace=\"monitoring\", integration=~`.*`}[5m])\n)\n> 0.01",
						For:    "5m",
						Labels: map[string]string{"severity": "critical"},
					}, {
						Alert: "AlertmanagerClusterFailedToSendAlerts",
						Annotations: map[string]string{
							"description": "The minimum notification failure rate to {{ $labels.integration }} sent from any instance in the {{$labels.job}} cluster is {{ $value | humanizePercentage }}.",
							"runbook_url": "https://runbooks.prometheus-operator.dev/runbooks/alertmanager/alertmanagerclusterfailedtosendalerts",
							"summary":     "All Alertmanager instances in a cluster failed to send notifications to a non-critical integration.",
						},
						Expr:   "min by (namespace,service, integration) (\n  rate(alertmanager_notifications_failed_total{job=\"vmalertmanager-vmk8s-victoria-metrics-k8s-stack\",namespace=\"monitoring\", integration!~`.*`}[5m])\n/\n  rate(alertmanager_notifications_total{job=\"vmalertmanager-vmk8s-victoria-metrics-k8s-stack\",namespace=\"monitoring\", integration!~`.*`}[5m])\n)\n> 0.01",
						For:    "5m",
						Labels: map[string]string{"severity": "warning"},
					}, {
						Alert: "AlertmanagerConfigInconsistent",
						Annotations: map[string]string{
							"description": "Alertmanager instances within the {{$labels.job}} cluster have different configurations.",
							"runbook_url": "https://runbooks.prometheus-operator.dev/runbooks/alertmanager/alertmanagerconfiginconsistent",
							"summary":     "Alertmanager instances within the same cluster have different configurations.",
						},
						Expr: `
count by (namespace,service) (
  count_values by (namespace,service) ("config_hash", alertmanager_config_hash{job="vmalertmanager-vmk8s-victoria-metrics-k8s-stack",namespace="monitoring"})
)
!= 1
`,
						For:    "20m",
						Labels: map[string]string{"severity": "critical"},
					}, {
						Alert: "AlertmanagerClusterDown",
						Annotations: map[string]string{
							"description": "{{ $value | humanizePercentage }} of Alertmanager instances within the {{$labels.job}} cluster have been up for less than half of the last 5m.",
							"runbook_url": "https://runbooks.prometheus-operator.dev/runbooks/alertmanager/alertmanagerclusterdown",
							"summary":     "Half or more of the Alertmanager instances within the same cluster are down.",
						},
						Expr: `
(
  count by (namespace,service) (
    avg_over_time(up{job="vmalertmanager-vmk8s-victoria-metrics-k8s-stack",namespace="monitoring"}[5m]) < 0.5
  )
/
  count by (namespace,service) (
    up{job="vmalertmanager-vmk8s-victoria-metrics-k8s-stack",namespace="monitoring"}
  )
)
>= 0.5
`,
						For:    "5m",
						Labels: map[string]string{"severity": "critical"},
					}, {
						Alert: "AlertmanagerClusterCrashlooping",
						Annotations: map[string]string{
							"description": "{{ $value | humanizePercentage }} of Alertmanager instances within the {{$labels.job}} cluster have restarted at least 5 times in the last 10m.",
							"runbook_url": "https://runbooks.prometheus-operator.dev/runbooks/alertmanager/alertmanagerclustercrashlooping",
							"summary":     "Half or more of the Alertmanager instances within the same cluster are crashlooping.",
						},
						Expr: `
(
  count by (namespace,service) (
    changes(process_start_time_seconds{job="vmalertmanager-vmk8s-victoria-metrics-k8s-stack",namespace="monitoring"}[10m]) > 4
  )
/
  count by (namespace,service) (
    up{job="vmalertmanager-vmk8s-victoria-metrics-k8s-stack",namespace="monitoring"}
  )
)
>= 0.5
`,
						For:    "5m",
						Labels: map[string]string{"severity": "critical"},
					},
				},
			},
		},
	},
	TypeMeta: metav1.TypeMeta{
		APIVersion: "operator.victoriametrics.com/v1beta1",
		Kind:       "VMRule",
	},
}

var SlackMonzoTplCM = &corev1.ConfigMap{
	Data: map[string]string{
		"monzo.tmpl": `
# This builds the silence URL.  We exclude the alertname in the range
# to avoid the issue of having trailing comma separator (%2C) at the end
# of the generated URL
{{ define "__alert_silence_link" -}}
    {{ .ExternalURL }}/#/silences/new?filter=%7B
    {{- range .CommonLabels.SortedPairs -}}
        {{- if ne .Name "alertname" -}}
            {{- .Name }}%3D"{{- .Value | urlquery -}}"%2C%20
        {{- end -}}
    {{- end -}}
    alertname%3D"{{ .CommonLabels.alertname }}"%7D
{{- end }}
{{ define "__alert_severity_prefix" -}}
    {{ if ne .Status "firing" -}}
    :lgtm:
    {{- else if eq .Labels.severity "critical" -}}
    :fire:
    {{- else if eq .Labels.severity "warning" -}}
    :warning:
    {{- else -}}
    :question:
    {{- end }}
{{- end }}
{{ define "__alert_severity_prefix_title" -}}
    {{ if ne .Status "firing" -}}
    :lgtm:
    {{- else if eq .CommonLabels.severity "critical" -}}
    :fire:
    {{- else if eq .CommonLabels.severity "warning" -}}
    :warning:
    {{- else if eq .CommonLabels.severity "info" -}}
    :information_source:
    {{- else -}}
    :question:
    {{- end }}
{{- end }}
{{/* First line of Slack alerts */}}
{{ define "slack.monzo.title" -}}
    [{{ .Status | toUpper -}}
    {{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{- end -}}
    ] {{ template "__alert_severity_prefix_title" . }} {{ .CommonLabels.alertname }}
{{- end }}
{{/* Color of Slack attachment (appears as line next to alert )*/}}
{{ define "slack.monzo.color" -}}
    {{ if eq .Status "firing" -}}
        {{ if eq .CommonLabels.severity "warning" -}}
            warning
        {{- else if eq .CommonLabels.severity "critical" -}}
            danger
        {{- else -}}
            #439FE0
        {{- end -}}
    {{ else -}}
    good
    {{- end }}
{{- end }}
{{/* Emoji to display as user icon (custom emoji supported!) */}}
{{ define "slack.monzo.icon_emoji" }}:waitwhat:{{ end }}
{{/* The test to display in the alert */}}
{{ define "slack.monzo.text" -}}
    {{ range .Alerts }}
        {{- if .Annotations.message }}
 • {{ .Annotations.message }}
        {{- end }}
        {{- if .Annotations.description }}
 • {{ .Annotations.description }}
        {{- end }}
    {{- end }}
{{- end }}
{{ define "slack.monzo.link_button_text" -}}
    {{- if .CommonAnnotations.link_text -}}
        {{- .CommonAnnotations.link_text -}}
    {{- else -}}
        Link
    {{- end }} :link:
{{- end }}
`,
	},
	TypeMeta:   ku.TypeConfigMapV1,
	ObjectMeta: AM.ObjectMetaNameSuffix("-monzo-tpl"),
}
