Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0d104755f1 | |||
| 03df2bd5ce | |||
| bde5611b16 | |||
| 295dc3a9ec | |||
| 69e610a5c2 | |||
| 93d2a00a00 | |||
| 3036abf70e | |||
| 2d8fe0f275 | |||
| 7fb8042265 |
@@ -55,7 +55,7 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: golang:1.26.9-bookworm
|
image: golang:1.26.10-bookworm
|
||||||
volumes:
|
volumes:
|
||||||
- go-mod-cache:/go/pkg/mod
|
- go-mod-cache:/go/pkg/mod
|
||||||
- go-build-cache:/root/.cache/go-build
|
- go-build-cache:/root/.cache/go-build
|
||||||
@@ -85,7 +85,7 @@ jobs:
|
|||||||
security:
|
security:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: golang:1.26.9-bookworm
|
image: golang:1.26.10-bookworm
|
||||||
volumes:
|
volumes:
|
||||||
- go-mod-cache:/go/pkg/mod
|
- go-mod-cache:/go/pkg/mod
|
||||||
- go-build-cache:/root/.cache/go-build
|
- go-build-cache:/root/.cache/go-build
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: golang:1.26.9-bookworm
|
image: golang:1.26.10-bookworm
|
||||||
volumes:
|
volumes:
|
||||||
- go-mod-cache:/go/pkg/mod
|
- go-mod-cache:/go/pkg/mod
|
||||||
- go-build-cache:/root/.cache/go-build
|
- go-build-cache:/root/.cache/go-build
|
||||||
|
|||||||
@@ -107,6 +107,10 @@ spec:
|
|||||||
fallbackTopic: ""
|
fallbackTopic: ""
|
||||||
repeatEvery: 15m
|
repeatEvery: 15m
|
||||||
tokenSecretRef: {name: "", key: token}
|
tokenSecretRef: {name: "", key: token}
|
||||||
|
prometheus: # terdut-server v0.49.0+: metrics on incidents; off unless allowedHosts is set
|
||||||
|
allowedHosts: [] # e.g. ["kube-prometheus-stack-prometheus.kube-prometheus-stack:9090"]; wildcards: "*.monitoring.svc"
|
||||||
|
preroll: "" # unset = server default (1h)
|
||||||
|
timeout: "" # unset = server default (10s)
|
||||||
oidc:
|
oidc:
|
||||||
enabled: false
|
enabled: false
|
||||||
issuer: ""
|
issuer: ""
|
||||||
@@ -197,7 +201,7 @@ spec:
|
|||||||
oidc: {memberGroup: terdut-platform-members, ownerGroup: terdut-platform-owners}
|
oidc: {memberGroup: terdut-platform-members, ownerGroup: terdut-platform-owners}
|
||||||
escalation:
|
escalation:
|
||||||
repeatCount: 2
|
repeatCount: 2
|
||||||
fallbackTopic: platform-oncall
|
fallback: {type: ntfy, address: platform-oncall}
|
||||||
levels:
|
levels:
|
||||||
- timeout: 5m
|
- timeout: 5m
|
||||||
targets: [{kind: oncall}, {kind: user, username: alice}]
|
targets: [{kind: oncall}, {kind: user, username: alice}]
|
||||||
|
|||||||
@@ -118,6 +118,32 @@ type NotifySpec struct {
|
|||||||
TokenSecretRef *SecretKeyRef `json:"tokenSecretRef,omitempty"`
|
TokenSecretRef *SecretKeyRef `json:"tokenSecretRef,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrometheusSpec controls the metrics terdut-server shows on an incident,
|
||||||
|
// fetched from the Prometheus named in each alert's generatorURL
|
||||||
|
// (terdut-server v0.49.0+). Off unless allowedHosts is set: the URL comes
|
||||||
|
// out of a webhook payload, so the server queries only hosts listed here.
|
||||||
|
type PrometheusSpec struct {
|
||||||
|
// allowedHosts are the Prometheus servers terdut-server may query,
|
||||||
|
// passed as TERDUT_PROMETHEUS_ALLOWED_HOSTS. Entries are host names
|
||||||
|
// (with "*" as one DNS label and a leading "**" as one or more), IPs or
|
||||||
|
// CIDR ranges, each with an optional ":port", e.g.
|
||||||
|
// "kube-prometheus-stack-prometheus.kube-prometheus-stack:9090" or
|
||||||
|
// "*.monitoring.svc". Empty turns the feature off. The server refuses to
|
||||||
|
// start on a malformed entry or a bare "*".
|
||||||
|
// +optional
|
||||||
|
AllowedHosts []string `json:"allowedHosts,omitempty"`
|
||||||
|
|
||||||
|
// preroll is how far before an alert started its graph begins (a Go
|
||||||
|
// duration, e.g. "1h"). Unset uses terdut-server's default.
|
||||||
|
// +optional
|
||||||
|
Preroll string `json:"preroll,omitempty"`
|
||||||
|
|
||||||
|
// timeout bounds one request to a Prometheus (a Go duration, e.g.
|
||||||
|
// "10s"). Unset uses terdut-server's default.
|
||||||
|
// +optional
|
||||||
|
Timeout string `json:"timeout,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// OIDCSpec controls single sign-on.
|
// OIDCSpec controls single sign-on.
|
||||||
type OIDCSpec struct {
|
type OIDCSpec struct {
|
||||||
// +optional
|
// +optional
|
||||||
@@ -325,6 +351,11 @@ type TerdutServerSpec struct {
|
|||||||
// +optional
|
// +optional
|
||||||
OIDC OIDCSpec `json:"oidc,omitempty"`
|
OIDC OIDCSpec `json:"oidc,omitempty"`
|
||||||
|
|
||||||
|
// prometheus enables the metrics on an incident (terdut-server
|
||||||
|
// v0.49.0+; an older image ignores the env vars it produces).
|
||||||
|
// +optional
|
||||||
|
Prometheus PrometheusSpec `json:"prometheus,omitempty"`
|
||||||
|
|
||||||
// passwordLogin: whether a user may sign in, or sign up, with a
|
// passwordLogin: whether a user may sign in, or sign up, with a
|
||||||
// password.
|
// password.
|
||||||
// +kubebuilder:default=true
|
// +kubebuilder:default=true
|
||||||
|
|||||||
@@ -94,6 +94,24 @@ type EscalationLevel struct {
|
|||||||
Targets []EscalationTarget `json:"targets"`
|
Targets []EscalationTarget `json:"targets"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NotificationTarget is somewhere a page can go without belonging to a person:
|
||||||
|
// a notification provider the server has configured, and an address that
|
||||||
|
// provider understands.
|
||||||
|
type NotificationTarget struct {
|
||||||
|
// type is the notification provider, e.g. "ntfy". The server refuses a
|
||||||
|
// provider it has not been configured with.
|
||||||
|
// +required
|
||||||
|
// +kubebuilder:validation:MinLength=1
|
||||||
|
// +kubebuilder:validation:MaxLength=32
|
||||||
|
Type string `json:"type"`
|
||||||
|
|
||||||
|
// address is where that provider delivers it: an ntfy topic, say.
|
||||||
|
// +required
|
||||||
|
// +kubebuilder:validation:MinLength=1
|
||||||
|
// +kubebuilder:validation:MaxLength=256
|
||||||
|
Address string `json:"address"`
|
||||||
|
}
|
||||||
|
|
||||||
// EscalationSpec is a team's escalation ladder.
|
// EscalationSpec is a team's escalation ladder.
|
||||||
type EscalationSpec struct {
|
type EscalationSpec struct {
|
||||||
// +kubebuilder:validation:Minimum=0
|
// +kubebuilder:validation:Minimum=0
|
||||||
@@ -101,8 +119,10 @@ type EscalationSpec struct {
|
|||||||
// +optional
|
// +optional
|
||||||
RepeatCount int64 `json:"repeatCount,omitempty"`
|
RepeatCount int64 `json:"repeatCount,omitempty"`
|
||||||
|
|
||||||
|
// fallback is paged once when the ladder has run out of levels and repeats.
|
||||||
|
// Omitted, the chain just ends there.
|
||||||
// +optional
|
// +optional
|
||||||
FallbackTopic string `json:"fallbackTopic,omitempty"`
|
Fallback *NotificationTarget `json:"fallback,omitempty"`
|
||||||
|
|
||||||
// +required
|
// +required
|
||||||
// +kubebuilder:validation:MinItems=1
|
// +kubebuilder:validation:MinItems=1
|
||||||
|
|||||||
@@ -110,6 +110,11 @@ func (in *EscalationLevel) DeepCopy() *EscalationLevel {
|
|||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *EscalationSpec) DeepCopyInto(out *EscalationSpec) {
|
func (in *EscalationSpec) DeepCopyInto(out *EscalationSpec) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
if in.Fallback != nil {
|
||||||
|
in, out := &in.Fallback, &out.Fallback
|
||||||
|
*out = new(NotificationTarget)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
if in.Levels != nil {
|
if in.Levels != nil {
|
||||||
in, out := &in.Levels, &out.Levels
|
in, out := &in.Levels, &out.Levels
|
||||||
*out = make([]EscalationLevel, len(*in))
|
*out = make([]EscalationLevel, len(*in))
|
||||||
@@ -189,6 +194,21 @@ func (in *NetworkingSpec) DeepCopy() *NetworkingSpec {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *NotificationTarget) DeepCopyInto(out *NotificationTarget) {
|
||||||
|
*out = *in
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NotificationTarget.
|
||||||
|
func (in *NotificationTarget) DeepCopy() *NotificationTarget {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(NotificationTarget)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *NotifySpec) DeepCopyInto(out *NotifySpec) {
|
func (in *NotifySpec) DeepCopyInto(out *NotifySpec) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@@ -371,6 +391,26 @@ func (in *PostgresClusterRef) DeepCopy() *PostgresClusterRef {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *PrometheusSpec) DeepCopyInto(out *PrometheusSpec) {
|
||||||
|
*out = *in
|
||||||
|
if in.AllowedHosts != nil {
|
||||||
|
in, out := &in.AllowedHosts, &out.AllowedHosts
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrometheusSpec.
|
||||||
|
func (in *PrometheusSpec) DeepCopy() *PrometheusSpec {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(PrometheusSpec)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *SecretKeyRef) DeepCopyInto(out *SecretKeyRef) {
|
func (in *SecretKeyRef) DeepCopyInto(out *SecretKeyRef) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@@ -586,6 +626,7 @@ func (in *TerdutServerSpec) DeepCopyInto(out *TerdutServerSpec) {
|
|||||||
out.Sweeper = in.Sweeper
|
out.Sweeper = in.Sweeper
|
||||||
in.Notify.DeepCopyInto(&out.Notify)
|
in.Notify.DeepCopyInto(&out.Notify)
|
||||||
in.OIDC.DeepCopyInto(&out.OIDC)
|
in.OIDC.DeepCopyInto(&out.OIDC)
|
||||||
|
in.Prometheus.DeepCopyInto(&out.Prometheus)
|
||||||
in.AllowedTeams.DeepCopyInto(&out.AllowedTeams)
|
in.AllowedTeams.DeepCopyInto(&out.AllowedTeams)
|
||||||
in.Pod.DeepCopyInto(&out.Pod)
|
in.Pod.DeepCopyInto(&out.Pod)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ type: application
|
|||||||
# These fields decide nothing: `make helm-package` passes --version and
|
# These fields decide nothing: `make helm-package` passes --version and
|
||||||
# --app-version from the release tag (same reasoning as terdut-server's own
|
# --app-version from the release tag (same reasoning as terdut-server's own
|
||||||
# chart). They're for whoever reads the tree before a tag exists.
|
# chart). They're for whoever reads the tree before a tag exists.
|
||||||
version: 0.5.0
|
version: 0.7.0
|
||||||
appVersion: "v0.5.0"
|
appVersion: "v0.7.0"
|
||||||
|
|
||||||
keywords:
|
keywords:
|
||||||
- kubernetes
|
- kubernetes
|
||||||
|
|||||||
@@ -4301,6 +4301,34 @@ spec:
|
|||||||
type: object
|
type: object
|
||||||
type: array
|
type: array
|
||||||
type: object
|
type: object
|
||||||
|
prometheus:
|
||||||
|
description: |-
|
||||||
|
prometheus enables the metrics on an incident (terdut-server
|
||||||
|
v0.49.0+; an older image ignores the env vars it produces).
|
||||||
|
properties:
|
||||||
|
allowedHosts:
|
||||||
|
description: |-
|
||||||
|
allowedHosts are the Prometheus servers terdut-server may query,
|
||||||
|
passed as TERDUT_PROMETHEUS_ALLOWED_HOSTS. Entries are host names
|
||||||
|
(with "*" as one DNS label and a leading "**" as one or more), IPs or
|
||||||
|
CIDR ranges, each with an optional ":port", e.g.
|
||||||
|
"kube-prometheus-stack-prometheus.kube-prometheus-stack:9090" or
|
||||||
|
"*.monitoring.svc". Empty turns the feature off. The server refuses to
|
||||||
|
start on a malformed entry or a bare "*".
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
preroll:
|
||||||
|
description: |-
|
||||||
|
preroll is how far before an alert started its graph begins (a Go
|
||||||
|
duration, e.g. "1h"). Unset uses terdut-server's default.
|
||||||
|
type: string
|
||||||
|
timeout:
|
||||||
|
description: |-
|
||||||
|
timeout bounds one request to a Prometheus (a Go duration, e.g.
|
||||||
|
"10s"). Unset uses terdut-server's default.
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
replicas:
|
replicas:
|
||||||
default: 2
|
default: 2
|
||||||
description: |-
|
description: |-
|
||||||
|
|||||||
@@ -116,8 +116,28 @@ spec:
|
|||||||
escalation is this team's escalation ladder. Omitted, the team has none
|
escalation is this team's escalation ladder. Omitted, the team has none
|
||||||
(the server's plain reminder behaviour applies).
|
(the server's plain reminder behaviour applies).
|
||||||
properties:
|
properties:
|
||||||
fallbackTopic:
|
fallback:
|
||||||
|
description: |-
|
||||||
|
fallback is paged once when the ladder has run out of levels and repeats.
|
||||||
|
Omitted, the chain just ends there.
|
||||||
|
properties:
|
||||||
|
address:
|
||||||
|
description: 'address is where that provider delivers it:
|
||||||
|
an ntfy topic, say.'
|
||||||
|
maxLength: 256
|
||||||
|
minLength: 1
|
||||||
type: string
|
type: string
|
||||||
|
type:
|
||||||
|
description: |-
|
||||||
|
type is the notification provider, e.g. "ntfy". The server refuses a
|
||||||
|
provider it has not been configured with.
|
||||||
|
maxLength: 32
|
||||||
|
minLength: 1
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- address
|
||||||
|
- type
|
||||||
|
type: object
|
||||||
levels:
|
levels:
|
||||||
items:
|
items:
|
||||||
description: |-
|
description: |-
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ spec:
|
|||||||
oidc:
|
oidc:
|
||||||
{{- toYaml . | nindent 4 }}
|
{{- toYaml . | nindent 4 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- with .Values.terdutServer.prometheus }}
|
||||||
|
prometheus:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
passwordLogin: {{ .Values.terdutServer.passwordLogin }}
|
passwordLogin: {{ .Values.terdutServer.passwordLogin }}
|
||||||
{{- with .Values.terdutServer.allowedTeams }}
|
{{- with .Values.terdutServer.allowedTeams }}
|
||||||
allowedTeams:
|
allowedTeams:
|
||||||
|
|||||||
@@ -269,6 +269,12 @@ terdutServer:
|
|||||||
# notify: {}
|
# notify: {}
|
||||||
# oidc: {}
|
# oidc: {}
|
||||||
|
|
||||||
|
## Metrics on incidents (terdut-server v0.49.0+): hosts it may query for the
|
||||||
|
## Prometheus named in an alert's generatorURL. Empty/unset = off.
|
||||||
|
# prometheus:
|
||||||
|
# allowedHosts:
|
||||||
|
# - kube-prometheus-stack-prometheus.kube-prometheus-stack:9090
|
||||||
|
|
||||||
passwordLogin: true
|
passwordLogin: true
|
||||||
|
|
||||||
# allowedTeams: {}
|
# allowedTeams: {}
|
||||||
|
|||||||
@@ -4298,6 +4298,34 @@ spec:
|
|||||||
type: object
|
type: object
|
||||||
type: array
|
type: array
|
||||||
type: object
|
type: object
|
||||||
|
prometheus:
|
||||||
|
description: |-
|
||||||
|
prometheus enables the metrics on an incident (terdut-server
|
||||||
|
v0.49.0+; an older image ignores the env vars it produces).
|
||||||
|
properties:
|
||||||
|
allowedHosts:
|
||||||
|
description: |-
|
||||||
|
allowedHosts are the Prometheus servers terdut-server may query,
|
||||||
|
passed as TERDUT_PROMETHEUS_ALLOWED_HOSTS. Entries are host names
|
||||||
|
(with "*" as one DNS label and a leading "**" as one or more), IPs or
|
||||||
|
CIDR ranges, each with an optional ":port", e.g.
|
||||||
|
"kube-prometheus-stack-prometheus.kube-prometheus-stack:9090" or
|
||||||
|
"*.monitoring.svc". Empty turns the feature off. The server refuses to
|
||||||
|
start on a malformed entry or a bare "*".
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
preroll:
|
||||||
|
description: |-
|
||||||
|
preroll is how far before an alert started its graph begins (a Go
|
||||||
|
duration, e.g. "1h"). Unset uses terdut-server's default.
|
||||||
|
type: string
|
||||||
|
timeout:
|
||||||
|
description: |-
|
||||||
|
timeout bounds one request to a Prometheus (a Go duration, e.g.
|
||||||
|
"10s"). Unset uses terdut-server's default.
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
replicas:
|
replicas:
|
||||||
default: 2
|
default: 2
|
||||||
description: |-
|
description: |-
|
||||||
|
|||||||
@@ -113,8 +113,28 @@ spec:
|
|||||||
escalation is this team's escalation ladder. Omitted, the team has none
|
escalation is this team's escalation ladder. Omitted, the team has none
|
||||||
(the server's plain reminder behaviour applies).
|
(the server's plain reminder behaviour applies).
|
||||||
properties:
|
properties:
|
||||||
fallbackTopic:
|
fallback:
|
||||||
|
description: |-
|
||||||
|
fallback is paged once when the ladder has run out of levels and repeats.
|
||||||
|
Omitted, the chain just ends there.
|
||||||
|
properties:
|
||||||
|
address:
|
||||||
|
description: 'address is where that provider delivers it:
|
||||||
|
an ntfy topic, say.'
|
||||||
|
maxLength: 256
|
||||||
|
minLength: 1
|
||||||
type: string
|
type: string
|
||||||
|
type:
|
||||||
|
description: |-
|
||||||
|
type is the notification provider, e.g. "ntfy". The server refuses a
|
||||||
|
provider it has not been configured with.
|
||||||
|
maxLength: 32
|
||||||
|
minLength: 1
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- address
|
||||||
|
- type
|
||||||
|
type: object
|
||||||
levels:
|
levels:
|
||||||
items:
|
items:
|
||||||
description: |-
|
description: |-
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ spec:
|
|||||||
image:
|
image:
|
||||||
repository: git.ryuvia.com/niklas/terdut-server
|
repository: git.ryuvia.com/niklas/terdut-server
|
||||||
# v0.36.0 is the floor now that replicas below is 2 (this demo pins
|
# v0.36.0 is the floor now that replicas below is 2 (this demo pins
|
||||||
# the current release, v0.43.0, so it shows the current web UI too): that
|
# the current release, v0.49.1, so it shows the current web UI too): that
|
||||||
# release put the sweeper, the notifier and the migration runner each
|
# release put the sweeper, the notifier and the migration runner each
|
||||||
# behind a Postgres advisory lock, and gave incident creation its own
|
# behind a Postgres advisory lock, and gave incident creation its own
|
||||||
# conflict resolution, which is what makes a second replica safe
|
# conflict resolution, which is what makes a second replica safe
|
||||||
@@ -24,7 +24,7 @@ spec:
|
|||||||
# can adopt/rotate a key on a team-scoped account it didn't just create
|
# can adopt/rotate a key on a team-scoped account it didn't just create
|
||||||
# in the same call -- without which terdutteam-* can wedge permanently
|
# in the same call -- without which terdutteam-* can wedge permanently
|
||||||
# on the crash-window race this demo hit live, niklas/terdut-operator#3.)
|
# on the crash-window race this demo hit live, niklas/terdut-operator#3.)
|
||||||
tag: v0.43.0
|
tag: v0.49.1
|
||||||
# Matches this CRD's own spec.replicas default (v0.4.0) -- stated
|
# Matches this CRD's own spec.replicas default (v0.4.0) -- stated
|
||||||
# explicitly, like every other field in this file, rather than left to
|
# explicitly, like every other field in this file, rather than left to
|
||||||
# the default. RollingUpdate follows automatically; this operator does
|
# the default. RollingUpdate follows automatically; this operator does
|
||||||
@@ -41,6 +41,12 @@ spec:
|
|||||||
sweeper:
|
sweeper:
|
||||||
staleAfter: 6h
|
staleAfter: 6h
|
||||||
archiveAfter: 168h
|
archiveAfter: 168h
|
||||||
|
# No ntfy anywhere in this demo can actually be reached; the URL only has to
|
||||||
|
# exist so the server has an "ntfy" notification provider and accepts the
|
||||||
|
# teams' escalation fallback (02, 03), which names one. A server with no
|
||||||
|
# provider refuses a fallback it could never deliver.
|
||||||
|
notify:
|
||||||
|
ntfyURL: "http://ntfy.invalid"
|
||||||
# No oidc block: password login only, so there's nothing external to
|
# No oidc block: password login only, so there's nothing external to
|
||||||
# register a redirect URI with before this demo can sign in.
|
# register a redirect URI with before this demo can sign in.
|
||||||
passwordLogin: true
|
passwordLogin: true
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ spec:
|
|||||||
# until that person exists -- run-demo.sh creates alice for exactly that.
|
# until that person exists -- run-demo.sh creates alice for exactly that.
|
||||||
escalation:
|
escalation:
|
||||||
repeatCount: 2
|
repeatCount: 2
|
||||||
fallbackTopic: platform-fallback
|
fallback:
|
||||||
|
type: ntfy
|
||||||
|
address: platform-fallback
|
||||||
levels:
|
levels:
|
||||||
- timeout: 5m
|
- timeout: 5m
|
||||||
targets:
|
targets:
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ spec:
|
|||||||
displayName: Payments
|
displayName: Payments
|
||||||
escalation:
|
escalation:
|
||||||
repeatCount: 1
|
repeatCount: 1
|
||||||
fallbackTopic: payments-fallback
|
fallback:
|
||||||
|
type: ntfy
|
||||||
|
address: payments-fallback
|
||||||
levels:
|
levels:
|
||||||
- timeout: 5m
|
- timeout: 5m
|
||||||
targets:
|
targets:
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ var _ = Describe("TerdutServer Controller", func() {
|
|||||||
Expect(env["TERDUT_DB_DSN"].Value).To(Equal(testDSN))
|
Expect(env["TERDUT_DB_DSN"].Value).To(Equal(testDSN))
|
||||||
Expect(env["TERDUT_OPERATOR_MODE"].Value).To(Equal("true"))
|
Expect(env["TERDUT_OPERATOR_MODE"].Value).To(Equal("true"))
|
||||||
Expect(env).NotTo(HaveKey("TERDUT_DEADMAN_MATCHERS"), "dead man's switches are per team now")
|
Expect(env).NotTo(HaveKey("TERDUT_DEADMAN_MATCHERS"), "dead man's switches are per team now")
|
||||||
|
Expect(env).NotTo(HaveKey("TERDUT_PROMETHEUS_ALLOWED_HOSTS"), "metrics stay off unless spec.prometheus asks")
|
||||||
|
|
||||||
var svc corev1.Service
|
var svc corev1.Service
|
||||||
Expect(k8sClient.Get(ctx, objKey, &svc)).To(Succeed())
|
Expect(k8sClient.Get(ctx, objKey, &svc)).To(Succeed())
|
||||||
@@ -252,6 +253,36 @@ var _ = Describe("TerdutServer Controller", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Describe("spec.prometheus", func() {
|
||||||
|
It("passes the allowlist and tuning to the server as env vars", func(ctx SpecContext) {
|
||||||
|
spec := dsnSpec()
|
||||||
|
spec.Prometheus = terdutv1alpha1.PrometheusSpec{
|
||||||
|
AllowedHosts: []string{"kube-prometheus-stack-prometheus.kube-prometheus-stack:9090", "*.monitoring.svc"},
|
||||||
|
Preroll: "2h",
|
||||||
|
Timeout: "5s",
|
||||||
|
}
|
||||||
|
createServer(ctx, spec)
|
||||||
|
reconcileOnce(ctx)
|
||||||
|
|
||||||
|
env := envOf(ctx)
|
||||||
|
Expect(env["TERDUT_PROMETHEUS_ALLOWED_HOSTS"].Value).To(Equal("kube-prometheus-stack-prometheus.kube-prometheus-stack:9090,*.monitoring.svc"))
|
||||||
|
Expect(env["TERDUT_PROMETHEUS_PREROLL"].Value).To(Equal("2h"))
|
||||||
|
Expect(env["TERDUT_PROMETHEUS_TIMEOUT"].Value).To(Equal("5s"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("leaves the tuning to the server's defaults when only hosts are given", func(ctx SpecContext) {
|
||||||
|
spec := dsnSpec()
|
||||||
|
spec.Prometheus = terdutv1alpha1.PrometheusSpec{AllowedHosts: []string{"prometheus.monitoring.svc"}}
|
||||||
|
createServer(ctx, spec)
|
||||||
|
reconcileOnce(ctx)
|
||||||
|
|
||||||
|
env := envOf(ctx)
|
||||||
|
Expect(env).To(HaveKey("TERDUT_PROMETHEUS_ALLOWED_HOSTS"))
|
||||||
|
Expect(env).NotTo(HaveKey("TERDUT_PROMETHEUS_PREROLL"))
|
||||||
|
Expect(env).NotTo(HaveKey("TERDUT_PROMETHEUS_TIMEOUT"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
Describe("spec.pod", func() {
|
Describe("spec.pod", func() {
|
||||||
It("wires pod-level customization onto the right spot on the Deployment", func(ctx SpecContext) {
|
It("wires pod-level customization onto the right spot on the Deployment", func(ctx SpecContext) {
|
||||||
spec := dsnSpec()
|
spec := dsnSpec()
|
||||||
|
|||||||
@@ -216,6 +216,16 @@ func buildEnv(srv *terdutv1alpha1.TerdutServer, dbEnv []corev1.EnvVar) []corev1.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if prom := srv.Spec.Prometheus; len(prom.AllowedHosts) > 0 {
|
||||||
|
env = append(env, corev1.EnvVar{Name: "TERDUT_PROMETHEUS_ALLOWED_HOSTS", Value: strings.Join(prom.AllowedHosts, ",")})
|
||||||
|
if prom.Preroll != "" {
|
||||||
|
env = append(env, corev1.EnvVar{Name: "TERDUT_PROMETHEUS_PREROLL", Value: prom.Preroll})
|
||||||
|
}
|
||||||
|
if prom.Timeout != "" {
|
||||||
|
env = append(env, corev1.EnvVar{Name: "TERDUT_PROMETHEUS_TIMEOUT", Value: prom.Timeout})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
publicURL := fmt.Sprintf("https://%s", srv.Spec.Networking.Hostname)
|
publicURL := fmt.Sprintf("https://%s", srv.Spec.Networking.Hostname)
|
||||||
env = append(env,
|
env = append(env,
|
||||||
corev1.EnvVar{Name: "TERDUT_PUBLIC_URL", Value: publicURL},
|
corev1.EnvVar{Name: "TERDUT_PUBLIC_URL", Value: publicURL},
|
||||||
|
|||||||
@@ -55,9 +55,13 @@ func buildEscalationRequest(spec *terdutv1alpha1.EscalationSpec) (tdclient.SetEs
|
|||||||
Targets: targets,
|
Targets: targets,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var fallback tdclient.NotificationTarget
|
||||||
|
if spec.Fallback != nil {
|
||||||
|
fallback = tdclient.NotificationTarget{Type: spec.Fallback.Type, Address: spec.Fallback.Address}
|
||||||
|
}
|
||||||
return tdclient.SetEscalationRequest{
|
return tdclient.SetEscalationRequest{
|
||||||
RepeatCount: spec.RepeatCount,
|
RepeatCount: spec.RepeatCount,
|
||||||
FallbackTopic: spec.FallbackTopic,
|
Fallback: fallback,
|
||||||
Levels: levels,
|
Levels: levels,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -300,7 +300,7 @@ var _ = Describe("TerdutTeam Controller", func() {
|
|||||||
Describe("spec.escalation", func() {
|
Describe("spec.escalation", func() {
|
||||||
ladder := func() *terdutv1alpha1.EscalationSpec {
|
ladder := func() *terdutv1alpha1.EscalationSpec {
|
||||||
return &terdutv1alpha1.EscalationSpec{
|
return &terdutv1alpha1.EscalationSpec{
|
||||||
RepeatCount: 2, FallbackTopic: "oncall",
|
RepeatCount: 2, Fallback: &terdutv1alpha1.NotificationTarget{Type: "ntfy", Address: "oncall"},
|
||||||
Levels: []terdutv1alpha1.EscalationLevel{
|
Levels: []terdutv1alpha1.EscalationLevel{
|
||||||
{Timeout: "5m", Targets: []terdutv1alpha1.EscalationTarget{{Kind: terdutv1alpha1.EscalationTargetOncall}}},
|
{Timeout: "5m", Targets: []terdutv1alpha1.EscalationTarget{{Kind: terdutv1alpha1.EscalationTargetOncall}}},
|
||||||
{Timeout: "15m", Targets: []terdutv1alpha1.EscalationTarget{{Kind: terdutv1alpha1.EscalationTargetUser, Username: "alice"}}},
|
{Timeout: "15m", Targets: []terdutv1alpha1.EscalationTarget{{Kind: terdutv1alpha1.EscalationTargetUser, Username: "alice"}}},
|
||||||
@@ -320,7 +320,7 @@ var _ = Describe("TerdutTeam Controller", func() {
|
|||||||
|
|
||||||
got := fake.escalation[getTeam(ctx).Status.TeamID]
|
got := fake.escalation[getTeam(ctx).Status.TeamID]
|
||||||
Expect(got.RepeatCount).To(Equal(int64(2)))
|
Expect(got.RepeatCount).To(Equal(int64(2)))
|
||||||
Expect(got.FallbackTopic).To(Equal("oncall"))
|
Expect(got.Fallback).To(Equal(tdclient.NotificationTarget{Type: "ntfy", Address: "oncall"}))
|
||||||
Expect(got.Levels).To(HaveLen(2))
|
Expect(got.Levels).To(HaveLen(2))
|
||||||
Expect(got.Levels[0].TimeoutSeconds).To(Equal(int64(300)))
|
Expect(got.Levels[0].TimeoutSeconds).To(Equal(int64(300)))
|
||||||
Expect(got.Levels[1].Targets[0]).To(Equal(tdclient.EscalationTargetRequest{Kind: "user", Username: "alice"}))
|
Expect(got.Levels[1].Targets[0]).To(Equal(tdclient.EscalationTargetRequest{Kind: "user", Username: "alice"}))
|
||||||
|
|||||||
@@ -208,9 +208,16 @@ type EscalationLevelRequest struct {
|
|||||||
Targets []EscalationTargetRequest `json:"targets"`
|
Targets []EscalationTargetRequest `json:"targets"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NotificationTarget is a notification provider and an address it understands.
|
||||||
|
// Both empty means none.
|
||||||
|
type NotificationTarget struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
}
|
||||||
|
|
||||||
type SetEscalationRequest struct {
|
type SetEscalationRequest struct {
|
||||||
RepeatCount int64 `json:"repeat_count"`
|
RepeatCount int64 `json:"repeat_count"`
|
||||||
FallbackTopic string `json:"fallback_topic"`
|
Fallback NotificationTarget `json:"fallback"`
|
||||||
Levels []EscalationLevelRequest `json:"levels"`
|
Levels []EscalationLevelRequest `json:"levels"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user