1
0
Fork 0
mirror of https://github.com/prometheus-operator/prometheus-operator.git synced 2025-04-21 19:49:46 +00:00

*: add v1beta1 for AlertmanagerConfig CRD ()

* *: add v1beta1 for AlertmanagerConfig CRD

Signed-off-by: Simon Pasquier <spasquie@redhat.com>

* *: implement conversion webhook for AlertmanagerConfig

Signed-off-by: Simon Pasquier <spasquie@redhat.com>

* *: add jsonnet support for CRD conversion webhook

Signed-off-by: Simon Pasquier <spasquie@redhat.com>

* test: configure conversion webhook for AlertmanagerConfig

Signed-off-by: Simon Pasquier <spasquie@redhat.com>

* test/e2e: add test for v1alpha1<->v1beta1 conversion

Signed-off-by: Simon Pasquier <spasquie@redhat.com>

* Documentation: update webhook documentation

Signed-off-by: Simon Pasquier <spasquie@redhat.com>

* pkg/apis/monitoring/v1beta1: remove Regex field from Matcher type

Signed-off-by: Simon Pasquier <spasquie@redhat.com>

* *: rename muteTimeIntervals field to timeIntervals (v1beta1)

Signed-off-by: Simon Pasquier <spasquie@redhat.com>

* *: restore short name for AlertmanagerConfig

Signed-off-by: Simon Pasquier <spasquie@redhat.com>

* pkg/admission: add unit test for conversion webhook

Signed-off-by: Simon Pasquier <spasquie@redhat.com>

* pkg/apis/monitoring/v1beta1: replace v1.SecretKeySelector by SecretKeySelector

v1.SecretKeySelector has an `Optional` field which doesn't make sense
in the context of the AlertmanagerConfig CRD. Not depending on an
external type also means that we can enforce that key and name values
are not empty.

Signed-off-by: Simon Pasquier <spasquie@redhat.com>

* *: regenerate

Signed-off-by: Simon Pasquier <spasquie@redhat.com>

* *: make AlertmanagerConfig v1beta1 an opt-in choice

Enabling by default AlertmanagerConfig v1beta1 by default means that
users would have to configure the conversion webhook and it must be
performed in advance or at the same time users upgrade to the latest
operator version. To offer a smoother transition, we offer
AlertmanagerConfig v1beta1 as an opt-in feature: it's neither included
in the bundle.yaml file nor in the example/prometheus-operator-crd/
manifests.

People that want to enable v1beta1 should use the
example/prometheus-operator-crd-full manifests. For jsonnet users, the
Prometheus operator jsonnet library has a new
`enableAlertmanagerConfigV1beta1` configuration option that can be set
to `true`.

Signed-off-by: Simon Pasquier <spasquie@redhat.com>

* *: add Telegram support in v1beta1

Signed-off-by: Simon Pasquier <spasquie@redhat.com>

* *: synchronize with latest main changes

Signed-off-by: Simon Pasquier <spasquie@redhat.com>

* Documentation/user-guides/webhook.md: clarify mutation webhook

Signed-off-by: Simon Pasquier <spasquie@redhat.com>

* example: regenerate

Signed-off-by: Simon Pasquier <spasquie@redhat.com>
This commit is contained in:
Simon Pasquier 2022-05-31 11:11:00 +02:00 committed by GitHub
parent b3e71e5e99
commit 1829e379cd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
63 changed files with 42344 additions and 1071 deletions
Documentation/user-guides
Makefile
example
go.modgo.sum
jsonnet/prometheus-operator
pkg
scripts/generate
test

View file

@ -1,50 +1,89 @@
# Admission webhooks
This document describes how to set up an admission webhook to validate
PrometheusRules, and thus preventing Prometheus from loading invalid
configuration.
This document describes how to deploy the Prometheus operator's admission webhook service.
The admission webhook can be used to:
* Ensure that all annotations of PrometheusRule objects are coerced into string values.
* Check that PrometheusRule objects are semantically valid.
* Check that AlertmanagerConfig objects are semantically valid.
* Convert AlertmanagerConfig objects between v1alpha1 and v1beta1 versions.
## Prerequisites
This guide assumes that you have already [deployed the Prometheus
Operator](getting-started.md) and that [admission
controllers are
Operator](getting-started.md) and that [admission controllers are
enabled](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#how-do-i-turn-on-an-admission-controller)
on your cluster.
Admission webhooks require TLS, and as such this guide also assumes that you
have a TLS certificate and key ready.
The Kubernetes API server expects admission webhooks to communicate over HTTPS
so this guide also assumes the following:
1. A valid TLS certificate and key has been provisioned for the admission webhook service.
2. A secret holding the TLS certificate and key has been created.
## Preparing the Operator
If you don't want to manually provision the TLS materials,
[cert-manager](https://cert-manager.io/) is a good option.
A secret needs to be created from the TLS certificate and key, assuming the
certificate is in `tls.crt` and the key in `tls.key`:
## Admission webhook deployment
```bash
kubectl create secret tls prometheus-operator-certs --cert=tls.crt --key=tls.key
Assuming that the following secret exists and contains the base64-encoded
[PEM](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail) certificate
(`tls.crt`) and key (`tls.key`) for the admission webhook service.
```yaml
apiVersion: v1
data:
tls.crt: LS0tLS...LS0tCg==
tls.key: LS0tLS...LS0tCg==
kind: Secret
metadata:
name: admission-webhook-certs
namespace: default
```
The Prometheus Operator will serve the admission webhook. However, to do so, it
requires being available over TLS, and not only plain HTTP. Thus the following
flags need to be added to the Prometheus Operator deployment:
The admission webhook's pod template should be modified to mount the secret as a
volume and the container arguments should be modified to include the
certificate and key:
* `--web.enable-tls=true` to enable the Prometheus Operator to serve its API
over TLS,
* `--web.cert-file` to load the TLS certificate to use,
* `--web.key-file` to load the associate key.
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus-operator-admission-webhook
namespace: default
spec:
template:
spec:
containers:
- name: prometheus-operator-admission-webhook
args:
- --web.enable-tls=true
- --web.cert-file=/etc/tls/private/tls.crt
- --web.key-file=/etc/tls/private/tls.key
volumeMounts:
- mountPath: /etc/tls/private
name: tls-certificates
readOnly: true
volumes:
- name: tls-certificates
- secret:
secretName: admission-webhook-certs
```
## Webhook endpoints
### caBundle note
The `caBundle` contains the base64-encoded CA certificate used to sign the
webhook's certificate.
The `caBundle` field contains the base64-encoded CA certificate used to sign the
webhook's certificate. It is used by the Kubernetes API server to validate the
certificate of the webhook service.
Certificate managers like [cert-manager](https://cert-manager.io/) supports CA
injection into webhook configurations and custom resource definitions.
### `/admission-prometheusrules/validate`
The endpoint `/admission-prometheusrules/validate` rejects rules that are not valid prometheus config.
The endpoint `/admission-prometheusrules/validate` rejects `PrometheusRule`
objects that are not valid.
The following example deploys the validating admission webhook:
@ -55,7 +94,7 @@ metadata:
name: prometheus-operator-rulesvalidation
webhooks:
- clientConfig:
caBundle: SOMECABASE64ENCODED==
caBundle: LS0tLS...LS0tCg==
service:
name: prometheus-operator
namespace: default
@ -79,7 +118,9 @@ webhooks:
### `/admission-prometheusrules/mutate`
The endpoint `/admission-prometheusrules/mutate` ensures that integers and boolean yaml data elements are cooerced into strings.
The endpoint `/admission-prometheusrules/mutate` mutates `PrometheusRule`
objects so that integers and boolean yaml data elements are coerced into
strings.
The following example deploys the mutating admission webhook:
@ -90,7 +131,7 @@ metadata:
name: prometheus-operator-rulesmutation
webhooks:
- clientConfig:
caBundle: SOMECABASE64ENCODED==
caBundle: LS0tLS...LS0tCg==
service:
name: prometheus-operator
namespace: default
@ -114,7 +155,8 @@ webhooks:
### `/admission-alertmanagerconfigs/validate`
The endpoint `/admission-alertmanagerconfigs/validate` rejects alertmanagerconfigs that are not valid alertmanager config.
The endpoint `/admission-alertmanagerconfigs/validate` rejects
`AlertmanagerConfig` objects that are not valid.
The following example deploys the validating admission webhook:
@ -125,7 +167,7 @@ metadata:
name: prometheus-operator-alertmanager-config-validation
webhooks:
- clientConfig:
caBundle: SOMECABASE64ENCODED==
caBundle: LS0tLS...LS0tCg==
service:
name: prometheus-operator
namespace: default
@ -146,3 +188,42 @@ webhooks:
admissionReviewVersions: ["v1", "v1beta1"]
sideEffects: None
```
### `/convert`
The endpoint `/convert` convert `Alertmanagerconfig` objects between `v1alpha1`
and `v1beta1` versions.
The following example is a patch for the
`alertmanagerconfigs.monitoring.coreos.com` CRD to configure the conversion
webhook.
```json
{
"apiVersion": "apiextensions.k8s.io/v1",
"kind": "CustomResourceDefinition",
"metadata": {
"name": "alertmanagerconfigs.monitoring.coreos.com"
},
"spec": {
"conversion": {
"strategy": "Webhook",
"webhook": {
"clientConfig": {
"service": {
"name": "prometheus-operator-admission-webhook",
"namespace": "default",
"path": "/convert",
"port": 8443
},
"caBundle": "LS0tLS...LS0tCg=="
},
"conversionReviewVersions": [
"v1beta1",
"v1alpha1"
]
}
}
}
}
```

View file

@ -19,6 +19,7 @@ TYPES_V1_TARGET := pkg/apis/monitoring/v1/types.go
TYPES_V1_TARGET += pkg/apis/monitoring/v1/thanos_types.go
TYPES_V1ALPHA1_TARGET := pkg/apis/monitoring/v1alpha1/alertmanager_config_types.go
TYPES_V1BETA1_TARGET := pkg/apis/monitoring/v1beta1/alertmanager_config_types.go
TOOLS_BIN_DIR ?= $(shell pwd)/tmp/bin
export PATH := $(TOOLS_BIN_DIR):$(PATH)
@ -42,6 +43,7 @@ K8S_GEN_ARGS:=--go-header-file $(shell pwd)/.header --v=1 --logtostderr
K8S_GEN_DEPS:=.header
K8S_GEN_DEPS+=$(TYPES_V1_TARGET)
K8S_GEN_DEPS+=$(TYPES_V1ALPHA1_TARGET)
K8S_GEN_DEPS+=$(TYPES_V1BETA1_TARGET)
K8S_GEN_DEPS+=$(foreach bin,$(K8S_GEN_BINARIES),$(TOOLS_BIN_DIR)/$(bin))
BUILD_DATE=$(shell date +"%Y%m%d-%T")
@ -109,12 +111,14 @@ admission-webhook:
po-lint:
$(GO_BUILD_RECIPE) -o po-lint cmd/po-lint/main.go
DEEPCOPY_TARGETS := pkg/apis/monitoring/v1/zz_generated.deepcopy.go pkg/apis/monitoring/v1alpha1/zz_generated.deepcopy.go
DEEPCOPY_TARGETS := pkg/apis/monitoring/v1/zz_generated.deepcopy.go pkg/apis/monitoring/v1alpha1/zz_generated.deepcopy.go pkg/apis/monitoring/v1beta1/zz_generated.deepcopy.go
$(DEEPCOPY_TARGETS): $(CONTROLLER_GEN_BINARY)
cd ./pkg/apis/monitoring/v1 && $(CONTROLLER_GEN_BINARY) object:headerFile=$(CURDIR)/.header \
paths=.
cd ./pkg/apis/monitoring/v1alpha1 && $(CONTROLLER_GEN_BINARY) object:headerFile=$(CURDIR)/.header \
paths=.
cd ./pkg/apis/monitoring/v1beta1 && $(CONTROLLER_GEN_BINARY) object:headerFile=$(CURDIR)/.header \
paths=.
CLIENT_TARGET := pkg/client/versioned/clientset.go
$(CLIENT_TARGET): $(K8S_GEN_DEPS)
@ -122,23 +126,23 @@ $(CLIENT_TARGET): $(K8S_GEN_DEPS)
$(K8S_GEN_ARGS) \
--input-base "" \
--clientset-name "versioned" \
--input "$(GO_PKG)/pkg/apis/monitoring/v1,$(GO_PKG)/pkg/apis/monitoring/v1alpha1" \
--input "$(GO_PKG)/pkg/apis/monitoring/v1,$(GO_PKG)/pkg/apis/monitoring/v1alpha1,$(GO_PKG)/pkg/apis/monitoring/v1beta1" \
--output-package "$(GO_PKG)/pkg/client"
LISTER_TARGETS := pkg/client/listers/monitoring/v1/prometheus.go pkg/client/listers/monitoring/v1alpha1/prometheus.go
LISTER_TARGETS := pkg/client/listers/monitoring/v1/interface.go pkg/client/listers/monitoring/v1alpha1/interface.go pkg/client/listers/monitoring/v1beta1/interface.go
$(LISTER_TARGETS): $(K8S_GEN_DEPS)
$(LISTER_GEN_BINARY) \
$(K8S_GEN_ARGS) \
--input-dirs "$(GO_PKG)/pkg/apis/monitoring/v1,$(GO_PKG)/pkg/apis/monitoring/v1alpha1" \
--input-dirs "$(GO_PKG)/pkg/apis/monitoring/v1,$(GO_PKG)/pkg/apis/monitoring/v1alpha1,$(GO_PKG)/pkg/apis/monitoring/v1beta1" \
--output-package "$(GO_PKG)/pkg/client/listers"
INFORMER_TARGETS := pkg/client/informers/externalversions/monitoring/v1/prometheus.go pkg/client/informers/externalversions/monitoring/v1alpha1/prometheus.go
INFORMER_TARGETS := pkg/client/informers/externalversions/monitoring/v1/interface.go pkg/client/informers/externalversions/monitoring/v1alpha1/interface.go pkg/client/informers/externalversions/monitoring/v1beta1/interface.go
$(INFORMER_TARGETS): $(K8S_GEN_DEPS) $(LISTER_TARGETS) $(CLIENT_TARGET)
$(INFORMER_GEN_BINARY) \
$(K8S_GEN_ARGS) \
--versioned-clientset-package "$(GO_PKG)/pkg/client/versioned" \
--listers-package "$(GO_PKG)/pkg/client/listers" \
--input-dirs "$(GO_PKG)/pkg/apis/monitoring/v1,$(GO_PKG)/pkg/apis/monitoring/v1alpha1" \
--input-dirs "$(GO_PKG)/pkg/apis/monitoring/v1,$(GO_PKG)/pkg/apis/monitoring/v1alpha1,$(GO_PKG)/pkg/apis/monitoring/v1beta1" \
--output-package "$(GO_PKG)/pkg/client/informers"
.PHONY: k8s-gen
@ -195,13 +199,19 @@ tidy:
cd scripts && go mod tidy -v -modfile=go.mod
.PHONY: generate
generate: $(DEEPCOPY_TARGETS) generate-crds bundle.yaml example/mixin/alerts.yaml example/thanos/thanos.yaml example/admission-webhook generate-docs
generate: $(DEEPCOPY_TARGETS) generate-crds bundle.yaml example/mixin/alerts.yaml example/thanos/thanos.yaml example/admission-webhook example/alertmanager-crd-conversion generate-docs
# For now, the v1beta1 CRDs aren't part of the default bundle because they
# require to deploy/run the conversion webhook.
# They are provided in a separate directory
# (example/prometheus-operator-crd-full) and we generate jsonnet code that can
# be used to patch the "default" jsonnet CRD.
.PHONY: generate-crds
generate-crds: $(CONTROLLER_GEN_BINARY) $(GOJSONTOYAML_BINARY) $(TYPES_V1_TARGET) $(TYPES_V1ALPHA1_TARGET)
cd pkg/apis/monitoring && $(CONTROLLER_GEN_BINARY) crd:crdVersions=v1 paths=./... output:crd:dir=$(PWD)/example/prometheus-operator-crd
generate-crds: $(CONTROLLER_GEN_BINARY) $(GOJSONTOYAML_BINARY) $(TYPES_V1_TARGET) $(TYPES_V1ALPHA1_TARGET) $(TYPES_V1BETA1_TARGET)
cd pkg/apis/monitoring && $(CONTROLLER_GEN_BINARY) crd:crdVersions=v1 paths=./v1/. paths=./v1alpha1/. output:crd:dir=$(PWD)/example/prometheus-operator-crd/
find example/prometheus-operator-crd/ -name '*.yaml' -print0 | xargs -0 -I{} sh -c '$(GOJSONTOYAML_BINARY) -yamltojson < "$$1" | jq > "$(PWD)/jsonnet/prometheus-operator/$$(basename $$1 | cut -d'_' -f2 | cut -d. -f1)-crd.json"' -- {}
cd pkg/apis/monitoring && $(CONTROLLER_GEN_BINARY) crd:crdVersions=v1 paths=./... output:crd:dir=$(PWD)/example/prometheus-operator-crd-full
echo "{spec+: {versions+: $$($(GOJSONTOYAML_BINARY) -yamltojson < example/prometheus-operator-crd-full/monitoring.coreos.com_alertmanagerconfigs.yaml | jq '.spec.versions | map(select(.name == "v1beta1"))')}}" | $(JSONNETFMT_BINARY) - > $(PWD)/jsonnet/prometheus-operator/alertmanagerconfigs-v1beta1-crd.libsonnet
.PHONY: generate-remote-write-certs
generate-remote-write-certs:
@ -234,12 +244,18 @@ example/thanos/thanos.yaml: scripts/generate/vendor scripts/generate/thanos.json
example/admission-webhook: scripts/generate/vendor scripts/generate/admission-webhook.jsonnet $(shell find jsonnet -type f)
scripts/generate/build-admission-webhook-example.sh
example/alertmanager-crd-conversion: scripts/generate/vendor scripts/generate/conversion-webhook-patch-for-alermanagerconfig-crd.jsonnet $(shell find jsonnet -type f)
scripts/generate/build-conversion-webhook-patch-for-alermanagerconfig-crd.sh
FULLY_GENERATED_DOCS = Documentation/api.md Documentation/compatibility.md Documentation/operator.md
TO_BE_EXTENDED_DOCS = $(filter-out $(FULLY_GENERATED_DOCS), $(shell find Documentation -type f))
Documentation/operator.md: operator
$(MDOX_BINARY) fmt $@
# For now, the v1beta1 CRDs aren't part of the default bundle because they
# require to deploy/run the conversion webhook. As a consequence, they are not
# yet included in the API documentation.
Documentation/api.md: $(PO_DOCGEN_BINARY) $(TYPES_V1_TARGET) $(TYPES_V1ALPHA1_TARGET)
$(PO_DOCGEN_BINARY) api $(TYPES_V1_TARGET) $(TYPES_V1ALPHA1_TARGET) > $@

View file

@ -7,7 +7,6 @@ metadata:
name: prometheus-operator-admission-webhook
namespace: default
spec:
clusterIP: None
ports:
- name: https
port: 8443

View file

@ -0,0 +1,30 @@
{
"apiVersion": "apiextensions.k8s.io/v1",
"kind": "CustomResourceDefinition",
"metadata": {
"annotations": {
"controller-gen.kubebuilder.io/version": "v0.8.0"
},
"creationTimestamp": null,
"name": "alertmanagerconfigs.monitoring.coreos.com"
},
"spec": {
"conversion": {
"strategy": "Webhook",
"webhook": {
"clientConfig": {
"service": {
"name": "prometheus-operator-admission-webhook",
"namespace": "default",
"path": "/convert",
"port": 8443
}
},
"conversionReviewVersions": [
"v1beta1",
"v1alpha1"
]
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,627 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.8.0
creationTimestamp: null
name: podmonitors.monitoring.coreos.com
spec:
group: monitoring.coreos.com
names:
categories:
- prometheus-operator
kind: PodMonitor
listKind: PodMonitorList
plural: podmonitors
shortNames:
- pmon
singular: podmonitor
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: PodMonitor defines monitoring for a set of pods.
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: Specification of desired Pod selection for target discovery
by Prometheus.
properties:
attachMetadata:
description: 'Attaches node metadata to discovered targets. Only valid
for role: pod. Only valid in Prometheus versions 2.35.0 and newer.'
properties:
node:
description: When set to true, Prometheus must have permissions
to get Nodes.
type: boolean
type: object
jobLabel:
description: The label to use to retrieve the job name from.
type: string
labelLimit:
description: Per-scrape limit on number of labels that will be accepted
for a sample. Only valid in Prometheus versions 2.27.0 and newer.
format: int64
type: integer
labelNameLengthLimit:
description: Per-scrape limit on length of labels name that will be
accepted for a sample. Only valid in Prometheus versions 2.27.0
and newer.
format: int64
type: integer
labelValueLengthLimit:
description: Per-scrape limit on length of labels value that will
be accepted for a sample. Only valid in Prometheus versions 2.27.0
and newer.
format: int64
type: integer
namespaceSelector:
description: Selector to select which namespaces the Endpoints objects
are discovered from.
properties:
any:
description: Boolean describing whether all namespaces are selected
in contrast to a list restricting them.
type: boolean
matchNames:
description: List of namespace names to select from.
items:
type: string
type: array
type: object
podMetricsEndpoints:
description: A list of endpoints allowed as part of this PodMonitor.
items:
description: PodMetricsEndpoint defines a scrapeable endpoint of
a Kubernetes Pod serving Prometheus metrics.
properties:
authorization:
description: Authorization section for this endpoint
properties:
credentials:
description: The secret's key that contains the credentials
of the request
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
type:
description: Set the authentication type. Defaults to Bearer,
Basic will cause an error
type: string
type: object
basicAuth:
description: 'BasicAuth allow an endpoint to authenticate over
basic authentication. More info: https://prometheus.io/docs/operating/configuration/#endpoint'
properties:
password:
description: The secret in the service monitor namespace
that contains the password for authentication.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
username:
description: The secret in the service monitor namespace
that contains the username for authentication.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
type: object
bearerTokenSecret:
description: Secret to mount to read bearer token for scraping
targets. The secret needs to be in the same namespace as the
pod monitor and accessible by the Prometheus Operator.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
followRedirects:
description: FollowRedirects configures whether scrape requests
follow HTTP 3xx redirects.
type: boolean
honorLabels:
description: HonorLabels chooses the metric's labels on collisions
with target labels.
type: boolean
honorTimestamps:
description: HonorTimestamps controls whether Prometheus respects
the timestamps present in scraped data.
type: boolean
interval:
description: Interval at which metrics should be scraped If
not specified Prometheus' global scrape interval is used.
pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$
type: string
metricRelabelings:
description: MetricRelabelConfigs to apply to samples before
ingestion.
items:
description: 'RelabelConfig allows dynamic rewriting of the
label set, being applied to samples before ingestion. It
defines `<metric_relabel_configs>`-section of Prometheus
configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs'
properties:
action:
default: replace
description: Action to perform based on regex matching.
Default is 'replace'
enum:
- replace
- keep
- drop
- hashmod
- labelmap
- labeldrop
- labelkeep
type: string
modulus:
description: Modulus to take of the hash of the source
label values.
format: int64
type: integer
regex:
description: Regular expression against which the extracted
value is matched. Default is '(.*)'
type: string
replacement:
description: Replacement value against which a regex replace
is performed if the regular expression matches. Regex
capture groups are available. Default is '$1'
type: string
separator:
description: Separator placed between concatenated source
label values. default is ';'.
type: string
sourceLabels:
description: The source labels select values from existing
labels. Their content is concatenated using the configured
separator and matched against the configured regular
expression for the replace, keep, and drop actions.
items:
description: LabelName is a valid Prometheus label name
which may only contain ASCII letters, numbers, as
well as underscores.
pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$
type: string
type: array
targetLabel:
description: Label to which the resulting value is written
in a replace action. It is mandatory for replace actions.
Regex capture groups are available.
type: string
type: object
type: array
oauth2:
description: OAuth2 for the URL. Only valid in Prometheus versions
2.27.0 and newer.
properties:
clientId:
description: The secret or configmap containing the OAuth2
client id
properties:
configMap:
description: ConfigMap containing data to use for the
targets.
properties:
key:
description: The key to select.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the ConfigMap or its
key must be defined
type: boolean
required:
- key
type: object
secret:
description: Secret containing data to use for the targets.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the Secret or its key
must be defined
type: boolean
required:
- key
type: object
type: object
clientSecret:
description: The secret containing the OAuth2 client secret
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
endpointParams:
additionalProperties:
type: string
description: Parameters to append to the token URL
type: object
scopes:
description: OAuth2 scopes used for the token request
items:
type: string
type: array
tokenUrl:
description: The URL to fetch the token from
minLength: 1
type: string
required:
- clientId
- clientSecret
- tokenUrl
type: object
params:
additionalProperties:
items:
type: string
type: array
description: Optional HTTP URL parameters
type: object
path:
description: HTTP path to scrape for metrics.
type: string
port:
description: Name of the pod port this endpoint refers to. Mutually
exclusive with targetPort.
type: string
proxyUrl:
description: ProxyURL eg http://proxyserver:2195 Directs scrapes
to proxy through this endpoint.
type: string
relabelings:
description: 'RelabelConfigs to apply to samples before scraping.
Prometheus Operator automatically adds relabelings for a few
standard Kubernetes fields. The original scrape job''s name
is available via the `__tmp_prometheus_job_name` label. More
info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config'
items:
description: 'RelabelConfig allows dynamic rewriting of the
label set, being applied to samples before ingestion. It
defines `<metric_relabel_configs>`-section of Prometheus
configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs'
properties:
action:
default: replace
description: Action to perform based on regex matching.
Default is 'replace'
enum:
- replace
- keep
- drop
- hashmod
- labelmap
- labeldrop
- labelkeep
type: string
modulus:
description: Modulus to take of the hash of the source
label values.
format: int64
type: integer
regex:
description: Regular expression against which the extracted
value is matched. Default is '(.*)'
type: string
replacement:
description: Replacement value against which a regex replace
is performed if the regular expression matches. Regex
capture groups are available. Default is '$1'
type: string
separator:
description: Separator placed between concatenated source
label values. default is ';'.
type: string
sourceLabels:
description: The source labels select values from existing
labels. Their content is concatenated using the configured
separator and matched against the configured regular
expression for the replace, keep, and drop actions.
items:
description: LabelName is a valid Prometheus label name
which may only contain ASCII letters, numbers, as
well as underscores.
pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$
type: string
type: array
targetLabel:
description: Label to which the resulting value is written
in a replace action. It is mandatory for replace actions.
Regex capture groups are available.
type: string
type: object
type: array
scheme:
description: HTTP scheme to use for scraping.
type: string
scrapeTimeout:
description: Timeout after which the scrape is ended If not
specified, the Prometheus global scrape interval is used.
pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$
type: string
targetPort:
anyOf:
- type: integer
- type: string
description: 'Deprecated: Use ''port'' instead.'
x-kubernetes-int-or-string: true
tlsConfig:
description: TLS configuration to use when scraping the endpoint.
properties:
ca:
description: Struct containing the CA cert to use for the
targets.
properties:
configMap:
description: ConfigMap containing data to use for the
targets.
properties:
key:
description: The key to select.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the ConfigMap or its
key must be defined
type: boolean
required:
- key
type: object
secret:
description: Secret containing data to use for the targets.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the Secret or its key
must be defined
type: boolean
required:
- key
type: object
type: object
cert:
description: Struct containing the client cert file for
the targets.
properties:
configMap:
description: ConfigMap containing data to use for the
targets.
properties:
key:
description: The key to select.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the ConfigMap or its
key must be defined
type: boolean
required:
- key
type: object
secret:
description: Secret containing data to use for the targets.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the Secret or its key
must be defined
type: boolean
required:
- key
type: object
type: object
insecureSkipVerify:
description: Disable target certificate validation.
type: boolean
keySecret:
description: Secret containing the client key file for the
targets.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
serverName:
description: Used to verify the hostname for the targets.
type: string
type: object
type: object
type: array
podTargetLabels:
description: PodTargetLabels transfers labels on the Kubernetes Pod
onto the target.
items:
type: string
type: array
sampleLimit:
description: SampleLimit defines per-scrape limit on number of scraped
samples that will be accepted.
format: int64
type: integer
selector:
description: Selector to select Pod objects.
properties:
matchExpressions:
description: matchExpressions is a list of label selector requirements.
The requirements are ANDed.
items:
description: A label selector requirement is a selector that
contains values, a key, and an operator that relates the key
and values.
properties:
key:
description: key is the label key that the selector applies
to.
type: string
operator:
description: operator represents a key's relationship to
a set of values. Valid operators are In, NotIn, Exists
and DoesNotExist.
type: string
values:
description: values is an array of string values. If the
operator is In or NotIn, the values array must be non-empty.
If the operator is Exists or DoesNotExist, the values
array must be empty. This array is replaced during a strategic
merge patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs. A single
{key,value} in the matchLabels map is equivalent to an element
of matchExpressions, whose key field is "key", the operator
is "In", and the values array contains only "value". The requirements
are ANDed.
type: object
type: object
targetLimit:
description: TargetLimit defines a limit on the number of scraped
targets that will be accepted.
format: int64
type: integer
required:
- podMetricsEndpoints
- selector
type: object
required:
- spec
type: object
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View file

@ -0,0 +1,659 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.8.0
creationTimestamp: null
name: probes.monitoring.coreos.com
spec:
group: monitoring.coreos.com
names:
categories:
- prometheus-operator
kind: Probe
listKind: ProbeList
plural: probes
shortNames:
- prb
singular: probe
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: Probe defines monitoring for a set of static targets or ingresses.
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: Specification of desired Ingress selection for target discovery
by Prometheus.
properties:
authorization:
description: Authorization section for this endpoint
properties:
credentials:
description: The secret's key that contains the credentials of
the request
properties:
key:
description: The key of the secret to select from. Must be
a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must be
defined
type: boolean
required:
- key
type: object
type:
description: Set the authentication type. Defaults to Bearer,
Basic will cause an error
type: string
type: object
basicAuth:
description: 'BasicAuth allow an endpoint to authenticate over basic
authentication. More info: https://prometheus.io/docs/operating/configuration/#endpoint'
properties:
password:
description: The secret in the service monitor namespace that
contains the password for authentication.
properties:
key:
description: The key of the secret to select from. Must be
a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must be
defined
type: boolean
required:
- key
type: object
username:
description: The secret in the service monitor namespace that
contains the username for authentication.
properties:
key:
description: The key of the secret to select from. Must be
a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must be
defined
type: boolean
required:
- key
type: object
type: object
bearerTokenSecret:
description: Secret to mount to read bearer token for scraping targets.
The secret needs to be in the same namespace as the probe and accessible
by the Prometheus Operator.
properties:
key:
description: The key of the secret to select from. Must be a
valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must be defined
type: boolean
required:
- key
type: object
interval:
description: Interval at which targets are probed using the configured
prober. If not specified Prometheus' global scrape interval is used.
pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$
type: string
jobName:
description: The job name assigned to scraped metrics by default.
type: string
labelLimit:
description: Per-scrape limit on number of labels that will be accepted
for a sample. Only valid in Prometheus versions 2.27.0 and newer.
format: int64
type: integer
labelNameLengthLimit:
description: Per-scrape limit on length of labels name that will be
accepted for a sample. Only valid in Prometheus versions 2.27.0
and newer.
format: int64
type: integer
labelValueLengthLimit:
description: Per-scrape limit on length of labels value that will
be accepted for a sample. Only valid in Prometheus versions 2.27.0
and newer.
format: int64
type: integer
metricRelabelings:
description: MetricRelabelConfigs to apply to samples before ingestion.
items:
description: 'RelabelConfig allows dynamic rewriting of the label
set, being applied to samples before ingestion. It defines `<metric_relabel_configs>`-section
of Prometheus configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs'
properties:
action:
default: replace
description: Action to perform based on regex matching. Default
is 'replace'
enum:
- replace
- keep
- drop
- hashmod
- labelmap
- labeldrop
- labelkeep
type: string
modulus:
description: Modulus to take of the hash of the source label
values.
format: int64
type: integer
regex:
description: Regular expression against which the extracted
value is matched. Default is '(.*)'
type: string
replacement:
description: Replacement value against which a regex replace
is performed if the regular expression matches. Regex capture
groups are available. Default is '$1'
type: string
separator:
description: Separator placed between concatenated source label
values. default is ';'.
type: string
sourceLabels:
description: The source labels select values from existing labels.
Their content is concatenated using the configured separator
and matched against the configured regular expression for
the replace, keep, and drop actions.
items:
description: LabelName is a valid Prometheus label name which
may only contain ASCII letters, numbers, as well as underscores.
pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$
type: string
type: array
targetLabel:
description: Label to which the resulting value is written in
a replace action. It is mandatory for replace actions. Regex
capture groups are available.
type: string
type: object
type: array
module:
description: 'The module to use for probing specifying how to probe
the target. Example module configuring in the blackbox exporter:
https://github.com/prometheus/blackbox_exporter/blob/master/example.yml'
type: string
oauth2:
description: OAuth2 for the URL. Only valid in Prometheus versions
2.27.0 and newer.
properties:
clientId:
description: The secret or configmap containing the OAuth2 client
id
properties:
configMap:
description: ConfigMap containing data to use for the targets.
properties:
key:
description: The key to select.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the ConfigMap or its key
must be defined
type: boolean
required:
- key
type: object
secret:
description: Secret containing data to use for the targets.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
type: object
clientSecret:
description: The secret containing the OAuth2 client secret
properties:
key:
description: The key of the secret to select from. Must be
a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must be
defined
type: boolean
required:
- key
type: object
endpointParams:
additionalProperties:
type: string
description: Parameters to append to the token URL
type: object
scopes:
description: OAuth2 scopes used for the token request
items:
type: string
type: array
tokenUrl:
description: The URL to fetch the token from
minLength: 1
type: string
required:
- clientId
- clientSecret
- tokenUrl
type: object
prober:
description: Specification for the prober to use for probing targets.
The prober.URL parameter is required. Targets cannot be probed if
left empty.
properties:
path:
description: Path to collect metrics from. Defaults to `/probe`.
type: string
proxyUrl:
description: Optional ProxyURL.
type: string
scheme:
description: HTTP scheme to use for scraping. Defaults to `http`.
type: string
url:
description: Mandatory URL of the prober.
type: string
required:
- url
type: object
sampleLimit:
description: SampleLimit defines per-scrape limit on number of scraped
samples that will be accepted.
format: int64
type: integer
scrapeTimeout:
description: Timeout for scraping metrics from the Prometheus exporter.
If not specified, the Prometheus global scrape interval is used.
pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$
type: string
targetLimit:
description: TargetLimit defines a limit on the number of scraped
targets that will be accepted.
format: int64
type: integer
targets:
description: Targets defines a set of static or dynamically discovered
targets to probe.
properties:
ingress:
description: ingress defines the Ingress objects to probe and
the relabeling configuration. If `staticConfig` is also defined,
`staticConfig` takes precedence.
properties:
namespaceSelector:
description: From which namespaces to select Ingress objects.
properties:
any:
description: Boolean describing whether all namespaces
are selected in contrast to a list restricting them.
type: boolean
matchNames:
description: List of namespace names to select from.
items:
type: string
type: array
type: object
relabelingConfigs:
description: 'RelabelConfigs to apply to the label set of
the target before it gets scraped. The original ingress
address is available via the `__tmp_prometheus_ingress_address`
label. It can be used to customize the probed URL. The original
scrape job''s name is available via the `__tmp_prometheus_job_name`
label. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config'
items:
description: 'RelabelConfig allows dynamic rewriting of
the label set, being applied to samples before ingestion.
It defines `<metric_relabel_configs>`-section of Prometheus
configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs'
properties:
action:
default: replace
description: Action to perform based on regex matching.
Default is 'replace'
enum:
- replace
- keep
- drop
- hashmod
- labelmap
- labeldrop
- labelkeep
type: string
modulus:
description: Modulus to take of the hash of the source
label values.
format: int64
type: integer
regex:
description: Regular expression against which the extracted
value is matched. Default is '(.*)'
type: string
replacement:
description: Replacement value against which a regex
replace is performed if the regular expression matches.
Regex capture groups are available. Default is '$1'
type: string
separator:
description: Separator placed between concatenated source
label values. default is ';'.
type: string
sourceLabels:
description: The source labels select values from existing
labels. Their content is concatenated using the configured
separator and matched against the configured regular
expression for the replace, keep, and drop actions.
items:
description: LabelName is a valid Prometheus label
name which may only contain ASCII letters, numbers,
as well as underscores.
pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$
type: string
type: array
targetLabel:
description: Label to which the resulting value is written
in a replace action. It is mandatory for replace actions.
Regex capture groups are available.
type: string
type: object
type: array
selector:
description: Selector to select the Ingress objects.
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: A label selector requirement is a selector
that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: operator represents a key's relationship
to a set of values. Valid operators are In, NotIn,
Exists and DoesNotExist.
type: string
values:
description: values is an array of string values.
If the operator is In or NotIn, the values array
must be non-empty. If the operator is Exists or
DoesNotExist, the values array must be empty.
This array is replaced during a strategic merge
patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs.
A single {key,value} in the matchLabels map is equivalent
to an element of matchExpressions, whose key field is
"key", the operator is "In", and the values array contains
only "value". The requirements are ANDed.
type: object
type: object
type: object
staticConfig:
description: 'staticConfig defines the static list of targets
to probe and the relabeling configuration. If `ingress` is also
defined, `staticConfig` takes precedence. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#static_config.'
properties:
labels:
additionalProperties:
type: string
description: Labels assigned to all metrics scraped from the
targets.
type: object
relabelingConfigs:
description: 'RelabelConfigs to apply to the label set of
the targets before it gets scraped. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config'
items:
description: 'RelabelConfig allows dynamic rewriting of
the label set, being applied to samples before ingestion.
It defines `<metric_relabel_configs>`-section of Prometheus
configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs'
properties:
action:
default: replace
description: Action to perform based on regex matching.
Default is 'replace'
enum:
- replace
- keep
- drop
- hashmod
- labelmap
- labeldrop
- labelkeep
type: string
modulus:
description: Modulus to take of the hash of the source
label values.
format: int64
type: integer
regex:
description: Regular expression against which the extracted
value is matched. Default is '(.*)'
type: string
replacement:
description: Replacement value against which a regex
replace is performed if the regular expression matches.
Regex capture groups are available. Default is '$1'
type: string
separator:
description: Separator placed between concatenated source
label values. default is ';'.
type: string
sourceLabels:
description: The source labels select values from existing
labels. Their content is concatenated using the configured
separator and matched against the configured regular
expression for the replace, keep, and drop actions.
items:
description: LabelName is a valid Prometheus label
name which may only contain ASCII letters, numbers,
as well as underscores.
pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$
type: string
type: array
targetLabel:
description: Label to which the resulting value is written
in a replace action. It is mandatory for replace actions.
Regex capture groups are available.
type: string
type: object
type: array
static:
description: The list of hosts to probe.
items:
type: string
type: array
type: object
type: object
tlsConfig:
description: TLS configuration to use when scraping the endpoint.
properties:
ca:
description: Struct containing the CA cert to use for the targets.
properties:
configMap:
description: ConfigMap containing data to use for the targets.
properties:
key:
description: The key to select.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the ConfigMap or its key
must be defined
type: boolean
required:
- key
type: object
secret:
description: Secret containing data to use for the targets.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
type: object
cert:
description: Struct containing the client cert file for the targets.
properties:
configMap:
description: ConfigMap containing data to use for the targets.
properties:
key:
description: The key to select.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the ConfigMap or its key
must be defined
type: boolean
required:
- key
type: object
secret:
description: Secret containing data to use for the targets.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
type: object
insecureSkipVerify:
description: Disable target certificate validation.
type: boolean
keySecret:
description: Secret containing the client key file for the targets.
properties:
key:
description: The key of the secret to select from. Must be
a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must be
defined
type: boolean
required:
- key
type: object
serverName:
description: Used to verify the hostname for the targets.
type: string
type: object
type: object
required:
- spec
type: object
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,103 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.8.0
creationTimestamp: null
name: prometheusrules.monitoring.coreos.com
spec:
group: monitoring.coreos.com
names:
categories:
- prometheus-operator
kind: PrometheusRule
listKind: PrometheusRuleList
plural: prometheusrules
shortNames:
- promrule
singular: prometheusrule
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: PrometheusRule defines recording and alerting rules for a Prometheus
instance
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: Specification of desired alerting rule definitions for Prometheus.
properties:
groups:
description: Content of Prometheus rule file
items:
description: 'RuleGroup is a list of sequentially evaluated recording
and alerting rules. Note: PartialResponseStrategy is only used
by ThanosRuler and will be ignored by Prometheus instances. Valid
values for this field are ''warn'' or ''abort''. More info: https://github.com/thanos-io/thanos/blob/main/docs/components/rule.md#partial-response'
properties:
interval:
type: string
name:
type: string
partial_response_strategy:
type: string
rules:
items:
description: 'Rule describes an alerting or recording rule
See Prometheus documentation: [alerting](https://www.prometheus.io/docs/prometheus/latest/configuration/alerting_rules/)
or [recording](https://www.prometheus.io/docs/prometheus/latest/configuration/recording_rules/#recording-rules)
rule'
properties:
alert:
type: string
annotations:
additionalProperties:
type: string
type: object
expr:
anyOf:
- type: integer
- type: string
x-kubernetes-int-or-string: true
for:
type: string
labels:
additionalProperties:
type: string
type: object
record:
type: string
required:
- expr
type: object
type: array
required:
- name
- rules
type: object
type: array
type: object
required:
- spec
type: object
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View file

@ -0,0 +1,645 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.8.0
creationTimestamp: null
name: servicemonitors.monitoring.coreos.com
spec:
group: monitoring.coreos.com
names:
categories:
- prometheus-operator
kind: ServiceMonitor
listKind: ServiceMonitorList
plural: servicemonitors
shortNames:
- smon
singular: servicemonitor
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: ServiceMonitor defines monitoring for a set of services.
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: Specification of desired Service selection for target discovery
by Prometheus.
properties:
endpoints:
description: A list of endpoints allowed as part of this ServiceMonitor.
items:
description: Endpoint defines a scrapeable endpoint serving Prometheus
metrics.
properties:
authorization:
description: Authorization section for this endpoint
properties:
credentials:
description: The secret's key that contains the credentials
of the request
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
type:
description: Set the authentication type. Defaults to Bearer,
Basic will cause an error
type: string
type: object
basicAuth:
description: 'BasicAuth allow an endpoint to authenticate over
basic authentication More info: https://prometheus.io/docs/operating/configuration/#endpoints'
properties:
password:
description: The secret in the service monitor namespace
that contains the password for authentication.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
username:
description: The secret in the service monitor namespace
that contains the username for authentication.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
type: object
bearerTokenFile:
description: File to read bearer token for scraping targets.
type: string
bearerTokenSecret:
description: Secret to mount to read bearer token for scraping
targets. The secret needs to be in the same namespace as the
service monitor and accessible by the Prometheus Operator.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
followRedirects:
description: FollowRedirects configures whether scrape requests
follow HTTP 3xx redirects.
type: boolean
honorLabels:
description: HonorLabels chooses the metric's labels on collisions
with target labels.
type: boolean
honorTimestamps:
description: HonorTimestamps controls whether Prometheus respects
the timestamps present in scraped data.
type: boolean
interval:
description: Interval at which metrics should be scraped If
not specified Prometheus' global scrape interval is used.
pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$
type: string
metricRelabelings:
description: MetricRelabelConfigs to apply to samples before
ingestion.
items:
description: 'RelabelConfig allows dynamic rewriting of the
label set, being applied to samples before ingestion. It
defines `<metric_relabel_configs>`-section of Prometheus
configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs'
properties:
action:
default: replace
description: Action to perform based on regex matching.
Default is 'replace'
enum:
- replace
- keep
- drop
- hashmod
- labelmap
- labeldrop
- labelkeep
type: string
modulus:
description: Modulus to take of the hash of the source
label values.
format: int64
type: integer
regex:
description: Regular expression against which the extracted
value is matched. Default is '(.*)'
type: string
replacement:
description: Replacement value against which a regex replace
is performed if the regular expression matches. Regex
capture groups are available. Default is '$1'
type: string
separator:
description: Separator placed between concatenated source
label values. default is ';'.
type: string
sourceLabels:
description: The source labels select values from existing
labels. Their content is concatenated using the configured
separator and matched against the configured regular
expression for the replace, keep, and drop actions.
items:
description: LabelName is a valid Prometheus label name
which may only contain ASCII letters, numbers, as
well as underscores.
pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$
type: string
type: array
targetLabel:
description: Label to which the resulting value is written
in a replace action. It is mandatory for replace actions.
Regex capture groups are available.
type: string
type: object
type: array
oauth2:
description: OAuth2 for the URL. Only valid in Prometheus versions
2.27.0 and newer.
properties:
clientId:
description: The secret or configmap containing the OAuth2
client id
properties:
configMap:
description: ConfigMap containing data to use for the
targets.
properties:
key:
description: The key to select.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the ConfigMap or its
key must be defined
type: boolean
required:
- key
type: object
secret:
description: Secret containing data to use for the targets.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the Secret or its key
must be defined
type: boolean
required:
- key
type: object
type: object
clientSecret:
description: The secret containing the OAuth2 client secret
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
endpointParams:
additionalProperties:
type: string
description: Parameters to append to the token URL
type: object
scopes:
description: OAuth2 scopes used for the token request
items:
type: string
type: array
tokenUrl:
description: The URL to fetch the token from
minLength: 1
type: string
required:
- clientId
- clientSecret
- tokenUrl
type: object
params:
additionalProperties:
items:
type: string
type: array
description: Optional HTTP URL parameters
type: object
path:
description: HTTP path to scrape for metrics.
type: string
port:
description: Name of the service port this endpoint refers to.
Mutually exclusive with targetPort.
type: string
proxyUrl:
description: ProxyURL eg http://proxyserver:2195 Directs scrapes
to proxy through this endpoint.
type: string
relabelings:
description: 'RelabelConfigs to apply to samples before scraping.
Prometheus Operator automatically adds relabelings for a few
standard Kubernetes fields. The original scrape job''s name
is available via the `__tmp_prometheus_job_name` label. More
info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config'
items:
description: 'RelabelConfig allows dynamic rewriting of the
label set, being applied to samples before ingestion. It
defines `<metric_relabel_configs>`-section of Prometheus
configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs'
properties:
action:
default: replace
description: Action to perform based on regex matching.
Default is 'replace'
enum:
- replace
- keep
- drop
- hashmod
- labelmap
- labeldrop
- labelkeep
type: string
modulus:
description: Modulus to take of the hash of the source
label values.
format: int64
type: integer
regex:
description: Regular expression against which the extracted
value is matched. Default is '(.*)'
type: string
replacement:
description: Replacement value against which a regex replace
is performed if the regular expression matches. Regex
capture groups are available. Default is '$1'
type: string
separator:
description: Separator placed between concatenated source
label values. default is ';'.
type: string
sourceLabels:
description: The source labels select values from existing
labels. Their content is concatenated using the configured
separator and matched against the configured regular
expression for the replace, keep, and drop actions.
items:
description: LabelName is a valid Prometheus label name
which may only contain ASCII letters, numbers, as
well as underscores.
pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$
type: string
type: array
targetLabel:
description: Label to which the resulting value is written
in a replace action. It is mandatory for replace actions.
Regex capture groups are available.
type: string
type: object
type: array
scheme:
description: HTTP scheme to use for scraping.
type: string
scrapeTimeout:
description: Timeout after which the scrape is ended If not
specified, the Prometheus global scrape timeout is used unless
it is less than `Interval` in which the latter is used.
pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$
type: string
targetPort:
anyOf:
- type: integer
- type: string
description: Name or number of the target port of the Pod behind
the Service, the port must be specified with container port
property. Mutually exclusive with port.
x-kubernetes-int-or-string: true
tlsConfig:
description: TLS configuration to use when scraping the endpoint
properties:
ca:
description: Struct containing the CA cert to use for the
targets.
properties:
configMap:
description: ConfigMap containing data to use for the
targets.
properties:
key:
description: The key to select.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the ConfigMap or its
key must be defined
type: boolean
required:
- key
type: object
secret:
description: Secret containing data to use for the targets.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the Secret or its key
must be defined
type: boolean
required:
- key
type: object
type: object
caFile:
description: Path to the CA cert in the Prometheus container
to use for the targets.
type: string
cert:
description: Struct containing the client cert file for
the targets.
properties:
configMap:
description: ConfigMap containing data to use for the
targets.
properties:
key:
description: The key to select.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the ConfigMap or its
key must be defined
type: boolean
required:
- key
type: object
secret:
description: Secret containing data to use for the targets.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the Secret or its key
must be defined
type: boolean
required:
- key
type: object
type: object
certFile:
description: Path to the client cert file in the Prometheus
container for the targets.
type: string
insecureSkipVerify:
description: Disable target certificate validation.
type: boolean
keyFile:
description: Path to the client key file in the Prometheus
container for the targets.
type: string
keySecret:
description: Secret containing the client key file for the
targets.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
serverName:
description: Used to verify the hostname for the targets.
type: string
type: object
type: object
type: array
jobLabel:
description: "Chooses the label of the Kubernetes `Endpoints`. Its
value will be used for the `job`-label's value of the created metrics.
\n Default & fallback value: the name of the respective Kubernetes
`Endpoint`."
type: string
labelLimit:
description: Per-scrape limit on number of labels that will be accepted
for a sample. Only valid in Prometheus versions 2.27.0 and newer.
format: int64
type: integer
labelNameLengthLimit:
description: Per-scrape limit on length of labels name that will be
accepted for a sample. Only valid in Prometheus versions 2.27.0
and newer.
format: int64
type: integer
labelValueLengthLimit:
description: Per-scrape limit on length of labels value that will
be accepted for a sample. Only valid in Prometheus versions 2.27.0
and newer.
format: int64
type: integer
namespaceSelector:
description: Selector to select which namespaces the Kubernetes Endpoints
objects are discovered from.
properties:
any:
description: Boolean describing whether all namespaces are selected
in contrast to a list restricting them.
type: boolean
matchNames:
description: List of namespace names to select from.
items:
type: string
type: array
type: object
podTargetLabels:
description: PodTargetLabels transfers labels on the Kubernetes `Pod`
onto the created metrics.
items:
type: string
type: array
sampleLimit:
description: SampleLimit defines per-scrape limit on number of scraped
samples that will be accepted.
format: int64
type: integer
selector:
description: Selector to select Endpoints objects.
properties:
matchExpressions:
description: matchExpressions is a list of label selector requirements.
The requirements are ANDed.
items:
description: A label selector requirement is a selector that
contains values, a key, and an operator that relates the key
and values.
properties:
key:
description: key is the label key that the selector applies
to.
type: string
operator:
description: operator represents a key's relationship to
a set of values. Valid operators are In, NotIn, Exists
and DoesNotExist.
type: string
values:
description: values is an array of string values. If the
operator is In or NotIn, the values array must be non-empty.
If the operator is Exists or DoesNotExist, the values
array must be empty. This array is replaced during a strategic
merge patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs. A single
{key,value} in the matchLabels map is equivalent to an element
of matchExpressions, whose key field is "key", the operator
is "In", and the values array contains only "value". The requirements
are ANDed.
type: object
type: object
targetLabels:
description: TargetLabels transfers labels from the Kubernetes `Service`
onto the created metrics.
items:
type: string
type: array
targetLimit:
description: TargetLimit defines a limit on the number of scraped
targets that will be accepted.
format: int64
type: integer
required:
- endpoints
- selector
type: object
required:
- spec
type: object
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

File diff suppressed because it is too large Load diff

6
go.mod
View file

@ -34,12 +34,13 @@ require (
gopkg.in/alecthomas/kingpin.v2 v2.2.6
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.24.1
k8s.io/apiextensions-apiserver v0.23.3
k8s.io/apiextensions-apiserver v0.23.5
k8s.io/apimachinery v0.24.1
k8s.io/client-go v0.24.1
k8s.io/component-base v0.24.1
k8s.io/klog/v2 v2.60.1
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
sigs.k8s.io/controller-runtime v0.11.2
)
require (
@ -93,7 +94,6 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/onsi/gomega v1.16.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
@ -121,7 +121,7 @@ require (
k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
sigs.k8s.io/yaml v1.2.0 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
replace (

741
go.sum

File diff suppressed because it is too large Load diff

View file

@ -21,7 +21,6 @@ local defaults = {
},
};
function(params) {
local aw = self,
_config:: defaults + params,
@ -47,7 +46,6 @@ function(params) {
{ name: 'https', targetPort: 'https', port: aw._config.port },
],
selector: aw._config.selectorLabels,
clusterIP: 'None',
},
},

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,39 @@
local defaults = {
local defaults = self,
name: error 'must provide the name of the webhook service',
namespace: error 'must provide the namespace of the webhook service',
annotations: {},
port: 8443,
path: '/convert',
versions: ['v1beta1', 'v1alpha1'],
caBundle: '',
};
function(params) {
local c = self,
_config:: defaults + params,
metadata+: {
annotations+: c._config.annotations,
},
spec+: {
conversion: {
strategy: 'Webhook',
webhook: {
conversionReviewVersions: c._config.versions,
clientConfig: {
service: {
namespace: c._config.namespace,
name: c._config.name,
path: c._config.path,
port: c._config.port,
} + if c._config.caBundle != '' then
{ caBundle: c._config.caBundle }
else
{},
},
},
},
},
}

View file

@ -20,6 +20,7 @@ local defaults = {
for labelName in std.objectFields(defaults.commonLabels)
if !std.setMember(labelName, ['app.kubernetes.io/version'])
},
enableAlertmanagerConfigV1beta1: false,
};
function(params) {
@ -28,7 +29,10 @@ function(params) {
// Prefixing with 0 to ensure these manifests are listed and therefore created first.
'0alertmanagerCustomResourceDefinition': import 'alertmanagers-crd.json',
'0alertmanagerConfigCustomResourceDefinition': import 'alertmanagerconfigs-crd.json',
'0alertmanagerConfigCustomResourceDefinition': (import 'alertmanagerconfigs-crd.json') +
if po.config.enableAlertmanagerConfigV1beta1 then
(import 'alertmanagerconfigs-v1beta1-crd.libsonnet')
else {},
'0prometheusCustomResourceDefinition': import 'prometheuses-crd.json',
'0servicemonitorCustomResourceDefinition': import 'servicemonitors-crd.json',
'0podmonitorCustomResourceDefinition': import 'podmonitors-crd.json',

View file

@ -23,15 +23,19 @@ import (
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-operator/prometheus-operator/pkg/alertmanager"
validationv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/alertmanager/validation/v1alpha1"
validationv1beta1 "github.com/prometheus-operator/prometheus-operator/pkg/alertmanager/validation/v1beta1"
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
monitoringv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1"
monitoringv1beta1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1beta1"
promoperator "github.com/prometheus-operator/prometheus-operator/pkg/prometheus"
"github.com/prometheus/client_golang/prometheus"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/webhook/conversion"
v1 "k8s.io/api/admission/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/scheme"
kscheme "k8s.io/client-go/kubernetes/scheme"
)
const (
@ -45,21 +49,24 @@ const (
prometheusRuleResource = monitoringv1.PrometheusRuleName
prometheusRuleVersion = monitoringv1.Version
alertManagerConfigResource = monitoringv1alpha1.AlertmanagerConfigName
alertManagerConfigCurrentVersion = monitoringv1alpha1.Version
alertManagerConfigKind = monitoringv1alpha1.AlertmanagerConfigKind
alertManagerConfigResource = monitoringv1beta1.AlertmanagerConfigName
alertManagerConfigKind = monitoringv1beta1.AlertmanagerConfigKind
prometheusRuleValidatePath = "/admission-prometheusrules/validate"
prometheusRuleMutatePath = "/admission-prometheusrules/mutate"
alertmanagerConfigValidatePath = "/admission-alertmanagerconfigs/validate"
convertPath = "/convert"
)
var (
deserializer = scheme.Codecs.UniversalDeserializer()
deserializer = kscheme.Codecs.UniversalDeserializer()
prometheusRuleGVR = metav1.GroupVersionResource{
Group: group,
Version: prometheusRuleVersion,
Resource: prometheusRuleResource,
}
alertManagerConfigGVR = metav1.GroupVersionResource{
alertManagerConfigGR = metav1.GroupResource{
Group: group,
Version: alertManagerConfigCurrentVersion,
Resource: alertManagerConfigResource,
}
)
@ -73,16 +80,36 @@ type Admission struct {
amConfValidationErrorsCounter prometheus.Counter
amConfValidationTriggeredCounter prometheus.Counter
logger log.Logger
wh *conversion.Webhook
}
func New(logger log.Logger) *Admission {
return &Admission{logger: logger}
scheme := runtime.NewScheme()
if err := monitoringv1alpha1.AddToScheme(scheme); err != nil {
panic(err)
}
if err := monitoringv1beta1.AddToScheme(scheme); err != nil {
panic(err)
}
wh := &conversion.Webhook{}
if err := wh.InjectScheme(scheme); err != nil {
panic(fmt.Sprintf("failed to inject scheme into the webhook handler: %s", err))
}
return &Admission{
logger: logger,
wh: wh,
}
}
func (a *Admission) Register(mux *http.ServeMux) {
mux.HandleFunc("/admission-prometheusrules/validate", a.servePrometheusRulesValidate)
mux.HandleFunc("/admission-prometheusrules/mutate", a.servePrometheusRulesMutate)
mux.HandleFunc("/admission-alertmanagerconfigs/validate", a.serveAlertManagerConfigValidate)
mux.HandleFunc(prometheusRuleValidatePath, a.servePrometheusRulesValidate)
mux.HandleFunc(prometheusRuleMutatePath, a.servePrometheusRulesMutate)
mux.HandleFunc(alertmanagerConfigValidatePath, a.serveAlertmanagerConfigValidate)
mux.HandleFunc(convertPath, a.serveConvert)
}
func (a *Admission) RegisterMetrics(
@ -107,8 +134,12 @@ func (a *Admission) servePrometheusRulesValidate(w http.ResponseWriter, r *http.
a.serveAdmission(w, r, a.validatePrometheusRules)
}
func (a *Admission) serveAlertManagerConfigValidate(w http.ResponseWriter, r *http.Request) {
a.serveAdmission(w, r, a.validateAlertManagerConfig)
func (a *Admission) serveAlertmanagerConfigValidate(w http.ResponseWriter, r *http.Request) {
a.serveAdmission(w, r, a.validateAlertmanagerConfig)
}
func (a *Admission) serveConvert(w http.ResponseWriter, r *http.Request) {
a.wh.ServeHTTP(w, r)
}
func toAdmissionResponseFailure(message, resource string, errors []error) *v1.AdmissionResponse {
@ -248,30 +279,51 @@ func (a *Admission) validatePrometheusRules(ar v1.AdmissionReview) *v1.Admission
return &v1.AdmissionResponse{Allowed: true}
}
func (a *Admission) validateAlertManagerConfig(ar v1.AdmissionReview) *v1.AdmissionResponse {
func (a *Admission) validateAlertmanagerConfig(ar v1.AdmissionReview) *v1.AdmissionResponse {
a.incrementCounter(a.amConfValidationTriggeredCounter)
level.Debug(a.logger).Log("msg", "Validating alertmanagerconfigs")
if ar.Request.Resource != alertManagerConfigGVR {
gr := metav1.GroupResource{Group: ar.Request.Resource.Group, Resource: ar.Request.Resource.Resource}
if gr != alertManagerConfigGR {
err := fmt.Errorf("expected resource to be %v, but received %v", alertManagerConfigResource, ar.Request.Resource)
level.Warn(a.logger).Log("err", err)
a.incrementCounter(a.amConfValidationErrorsCounter)
return toAdmissionResponseFailure("Unexpected resource kind", alertManagerConfigResource, []error{err})
}
amConf := &monitoringv1alpha1.AlertmanagerConfig{}
var amConf interface{}
switch ar.Request.Resource.Version {
case monitoringv1alpha1.Version:
amConf = &monitoringv1alpha1.AlertmanagerConfig{}
case monitoringv1beta1.Version:
amConf = &monitoringv1beta1.AlertmanagerConfig{}
default:
err := fmt.Errorf("expected resource version to be 'v1alpha1' or 'v1beta1', but received %v", ar.Request.Resource.Version)
return toAdmissionResponseFailure("Unexpected resource version", alertManagerConfigResource, []error{err})
}
if err := json.Unmarshal(ar.Request.Object.Raw, amConf); err != nil {
level.Info(a.logger).Log("msg", errUnmarshalConfig, "err", err)
a.incrementCounter(a.amConfValidationErrorsCounter)
return toAdmissionResponseFailure(errUnmarshalConfig, alertManagerConfigResource, []error{err})
}
if err := alertmanager.ValidateAlertmanagerConfig(amConf); err != nil {
var (
err error
)
switch ar.Request.Resource.Version {
case monitoringv1alpha1.Version:
err = validationv1alpha1.ValidateAlertmanagerConfig(amConf.(*monitoringv1alpha1.AlertmanagerConfig))
case monitoringv1beta1.Version:
err = validationv1beta1.ValidateAlertmanagerConfig(amConf.(*monitoringv1beta1.AlertmanagerConfig))
}
if err != nil {
msg := "invalid config"
level.Debug(a.logger).Log("msg", msg, "content", amConf.Spec)
level.Debug(a.logger).Log("msg", msg, "content", string(ar.Request.Object.Raw))
level.Info(a.logger).Log("msg", msg, "err", err)
a.incrementCounter(a.amConfValidationErrorsCounter)
return toAdmissionResponseFailure("AlertManagerConfig is invalid", alertManagerConfigResource, []error{err})
return toAdmissionResponseFailure("AlertmanagerConfig is invalid", alertManagerConfigResource, []error{err})
}
return &v1.AdmissionResponse{Allowed: true}
}

View file

@ -30,13 +30,17 @@ import (
"github.com/go-kit/log/level"
"github.com/prometheus/client_golang/prometheus"
v1 "k8s.io/api/admission/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1"
"github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1beta1"
)
func TestMutateRule(t *testing.T) {
ts := server(api().servePrometheusRulesMutate)
defer ts.Close()
resp := send(t, ts, goodRulesWithAnnotations)
resp := sendAdmissionReview(t, ts, goodRulesWithAnnotations)
if len(resp.Response.Patch) == 0 {
t.Errorf("Expected a patch to be applied but found none")
@ -47,7 +51,7 @@ func TestMutateRuleNoAnnotations(t *testing.T) {
ts := server(api().servePrometheusRulesMutate)
defer ts.Close()
resp := send(t, ts, badRulesNoAnnotations)
resp := sendAdmissionReview(t, ts, badRulesNoAnnotations)
if len(resp.Response.Patch) == 0 {
t.Errorf("Expected a patch to be applied but found none")
@ -58,7 +62,7 @@ func TestAdmitGoodRule(t *testing.T) {
ts := server(api().servePrometheusRulesValidate)
defer ts.Close()
resp := send(t, ts, goodRulesWithAnnotations)
resp := sendAdmissionReview(t, ts, goodRulesWithAnnotations)
if !resp.Response.Allowed {
t.Errorf("Expected admission to be allowed but it was not")
@ -69,7 +73,7 @@ func TestAdmitGoodRuleExternalLabels(t *testing.T) {
ts := server(api().servePrometheusRulesValidate)
defer ts.Close()
resp := send(t, ts, goodRulesWithExternalLabelsInAnnotations)
resp := sendAdmissionReview(t, ts, goodRulesWithExternalLabelsInAnnotations)
if !resp.Response.Allowed {
t.Errorf("Expected admission to be allowed but it was not")
@ -80,7 +84,7 @@ func TestAdmitBadRule(t *testing.T) {
ts := server(api().servePrometheusRulesValidate)
defer ts.Close()
resp := send(t, ts, badRulesNoAnnotations)
resp := sendAdmissionReview(t, ts, badRulesNoAnnotations)
if resp.Response.Allowed {
t.Errorf("Expected admission to not be allowed but it was")
@ -111,7 +115,7 @@ func TestAdmitBadRuleWithBooleanInAnnotations(t *testing.T) {
ts := server(api().servePrometheusRulesValidate)
defer ts.Close()
resp := send(t, ts, badRulesWithBooleanInAnnotations)
resp := sendAdmissionReview(t, ts, badRulesWithBooleanInAnnotations)
if resp.Response.Allowed {
t.Errorf("Expected admission to not be allowed but it was")
@ -127,7 +131,7 @@ func TestAdmitBadRuleWithBooleanInAnnotations(t *testing.T) {
func TestMutateNonStringsToStrings(t *testing.T) {
request := nonStringsInLabelsAnnotations
ts := server(api().servePrometheusRulesMutate)
resp := send(t, ts, request)
resp := sendAdmissionReview(t, ts, request)
if len(resp.Response.Patch) == 0 {
t.Errorf("Expected a patch to be applied but found none")
}
@ -148,30 +152,32 @@ func TestMutateNonStringsToStrings(t *testing.T) {
// Sent patched request to validation endpoint
ts.Close()
ts = server(api().servePrometheusRulesValidate)
ts = server(api().servePrometheusRulesMutate)
defer ts.Close()
resp = send(t, ts, request)
resp = sendAdmissionReview(t, ts, request)
if !resp.Response.Allowed {
t.Errorf("Expected admission to be allowed but it was not")
}
}
// TestAlertManagerConfigAdmission tests the admission controller
// validation of the AlertManagerConfig but does not aim to cover
// TestAlertmanagerConfigAdmission tests the admission controller
// validation of the AlertmanagerConfig but does not aim to cover
// all the edge cases of the Validate function in pkg/alertmanager
func TestAlertManagerConfigAdmission(t *testing.T) {
ts := server(api().serveAlertManagerConfigValidate)
func TestAlertmanagerConfigAdmission(t *testing.T) {
ts := server(api().serveAlertmanagerConfigValidate)
t.Cleanup(ts.Close)
testCases := []struct {
name string
send string
apiVersion string
spec string
expectAdmissionAllowed bool
}{
{
name: "Test reject on duplicate receiver",
send: `{
name: "Test reject on duplicate receiver",
apiVersion: "v1alpha1",
spec: `{
"route": {
"groupBy": [
"job"
@ -193,8 +199,33 @@ func TestAlertManagerConfigAdmission(t *testing.T) {
expectAdmissionAllowed: false,
},
{
name: "Test reject on invalid receiver",
send: `{
name: "Test reject on duplicate receiver",
apiVersion: "v1beta1",
spec: `{
"route": {
"groupBy": [
"job"
],
"groupWait": "30s",
"groupInterval": "5m",
"repeatInterval": "12h",
"receiver": "wechat-example"
},
"receivers": [
{
"name": "wechat-example"
},
{
"name": "wechat-example"
}
]
}`,
expectAdmissionAllowed: false,
},
{
name: "Test reject on invalid receiver",
apiVersion: "v1alpha1",
spec: `{
"route": {
"groupBy": [
"job"
@ -223,8 +254,40 @@ func TestAlertManagerConfigAdmission(t *testing.T) {
expectAdmissionAllowed: false,
},
{
name: "Test reject on invalid mute time intervals",
send: `{
name: "Test reject on invalid receiver",
apiVersion: "v1beta1",
spec: `{
"route": {
"groupBy": [
"job"
],
"groupWait": "30s",
"groupInterval": "5m",
"repeatInterval": "12h",
"receiver": "wechat-example"
},
"receivers": [
{
"name": "wechat-example",
"wechatConfigs": [
{
"apiURL": "https://%<>wechatserver:8080/",
"corpID": "wechat-corpid",
"apiSecret": {
"name": "wechat-config",
"key": "apiSecret"
}
}
]
}
]
}`,
expectAdmissionAllowed: false,
},
{
name: "Test reject on invalid mute time intervals",
apiVersion: "v1alpha1",
spec: `{
"route": {
"groupBy": [
"job"
@ -278,8 +341,65 @@ func TestAlertManagerConfigAdmission(t *testing.T) {
expectAdmissionAllowed: false,
},
{
name: "Test happy path",
send: `{
name: "Test reject on invalid time intervals",
apiVersion: "v1beta1",
spec: `{
"route": {
"groupBy": [
"job"
],
"groupWait": "30s",
"groupInterval": "5m",
"repeatInterval": "12h",
"receiver": "wechat-example"
},
"receivers": [
{
"name": "wechat-example",
"wechatConfigs": [
{
"apiURL": "https://wechatserver:8080",
"corpID": "wechat-corpid",
"apiSecret": {
"name": "wechat-config",
"key": "apiSecret"
}
}
]
}
],
"timeIntervals": [
{
"name": "out-of-business-hours",
"timeIntervals": [
{
"weekdays": [
"Xaturday",
"Sunday"
]
},
{
"times": [
{
"startTime": "50:00",
"endTime": "08:00"
},
{
"startTime": "18:00",
"endTime": "24:00"
}
]
}
]
}
]
}`,
expectAdmissionAllowed: false,
},
{
name: "Test happy path",
apiVersion: "v1alpha1",
spec: `{
"route": {
"groupBy": [
"job"
@ -329,14 +449,70 @@ func TestAlertManagerConfigAdmission(t *testing.T) {
]
}
]
}`,
expectAdmissionAllowed: true,
},
{
name: "Test happy path",
apiVersion: "v1beta1",
spec: `{
"route": {
"groupBy": [
"job"
],
"groupWait": "30s",
"groupInterval": "5m",
"repeatInterval": "12h",
"receiver": "wechat-example"
},
"receivers": [
{
"name": "wechat-example",
"wechatConfigs": [
{
"apiURL": "http://wechatserver:8080/",
"corpID": "wechat-corpid",
"apiSecret": {
"name": "wechat-config",
"key": "apiSecret"
}
}
]
}
],
"timeIntervals": [
{
"name": "out-of-business-hours",
"timeIntervals": [
{
"weekdays": [
"Saturday",
"Sunday"
]
},
{
"times": [
{
"startTime": "00:00",
"endTime": "08:00"
},
{
"startTime": "18:00",
"endTime": "24:00"
}
]
}
]
}
]
}`,
expectAdmissionAllowed: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
resp := send(t, ts, buildAlertManagerConfigFromSpec(t, tc.send))
t.Run(tc.name+","+tc.apiVersion, func(t *testing.T) {
resp := sendAdmissionReview(t, ts, buildAdmissionReviewFromAlertmanagerConfigSpec(t, tc.apiVersion, tc.spec))
if resp.Response.Allowed != tc.expectAdmissionAllowed {
t.Errorf(
"Unexpected admission result, wanted %v but got %v - (warnings=%v) - (details=%v)",
@ -346,6 +522,194 @@ func TestAlertManagerConfigAdmission(t *testing.T) {
}
}
func TestAlertmanagerConfigConversion(t *testing.T) {
ts := server(api().serveConvert)
t.Cleanup(ts.Close)
for _, tc := range []struct {
name string
from string
to string
spec string
checkFn func(converted []byte) error
}{
{
name: "happy path",
from: "v1alpha1",
to: "v1beta1",
spec: `{
"route": {
"groupBy": [
"job"
],
"groupWait": "30s",
"groupInterval": "5m",
"repeatInterval": "12h",
"receiver": "wechat-example"
},
"receivers": [
{
"name": "wechat-example",
"wechatConfigs": [
{
"apiURL": "http://wechatserver:8080/",
"corpID": "wechat-corpid",
"apiSecret": {
"name": "wechat-config",
"key": "apiSecret"
}
}
]
}
],
"muteTimeIntervals": [
{
"name": "out-of-business-hours",
"timeIntervals": [
{
"weekdays": [
"Saturday",
"Sunday"
]
},
{
"times": [
{
"startTime": "00:00",
"endTime": "08:00"
},
{
"startTime": "18:00",
"endTime": "24:00"
}
]
}
]
}
]
}`,
checkFn: func(converted []byte) error {
o := v1beta1.AlertmanagerConfig{}
err := json.Unmarshal(converted, &o)
if err != nil {
return err
}
if len(o.Spec.TimeIntervals) != 1 {
return fmt.Errorf("expecting 1 item in spec.timeIntervals, got %d", len(o.Spec.TimeIntervals))
}
if o.Spec.TimeIntervals[0].Name != "out-of-business-hours" {
return fmt.Errorf("expecting spec.timeIntervals[0].name to be %q, got %q", "out-of-business-hours", o.Spec.TimeIntervals[0].Name)
}
return nil
},
},
{
name: "happy path",
from: "v1beta1",
to: "v1alpha1",
spec: `{
"route": {
"groupBy": [
"job"
],
"groupWait": "30s",
"groupInterval": "5m",
"repeatInterval": "12h",
"receiver": "wechat-example"
},
"receivers": [
{
"name": "wechat-example",
"wechatConfigs": [
{
"apiURL": "http://wechatserver:8080/",
"corpID": "wechat-corpid",
"apiSecret": {
"name": "wechat-config",
"key": "apiSecret"
}
}
]
}
],
"timeIntervals": [
{
"name": "out-of-business-hours",
"timeIntervals": [
{
"weekdays": [
"Saturday",
"Sunday"
]
},
{
"times": [
{
"startTime": "00:00",
"endTime": "08:00"
},
{
"startTime": "18:00",
"endTime": "24:00"
}
]
}
]
}
]
}`,
checkFn: func(converted []byte) error {
o := v1alpha1.AlertmanagerConfig{}
err := json.Unmarshal(converted, &o)
if err != nil {
return err
}
if len(o.Spec.MuteTimeIntervals) != 1 {
return fmt.Errorf("expecting 1 item in spec.muteTimeIntervals, got %d", len(o.Spec.MuteTimeIntervals))
}
if o.Spec.MuteTimeIntervals[0].Name != "out-of-business-hours" {
return fmt.Errorf("expecting spec.muteTimeIntervals[0].name to be %q, got %q", "out-of-business-hours", o.Spec.MuteTimeIntervals[0].Name)
}
return nil
},
},
} {
t.Run(tc.name+","+tc.from+">"+tc.to, func(t *testing.T) {
resp := sendConversionReview(t, ts, buildConversionReviewFromAlertmanagerConfigSpec(t, tc.from, tc.to, tc.spec))
if resp.Response.Result.Status != "Success" {
t.Fatalf(
"Unexpected conversion result, wanted 'Success' but got %v - (result=%v)",
resp.Response.Result.Status,
resp.Response.Result)
}
if len(resp.Response.ConvertedObjects) != 1 {
t.Fatalf("expected 1 converted object, got %d", len(resp.Response.ConvertedObjects))
}
if tc.checkFn == nil {
return
}
err := tc.checkFn(resp.Response.ConvertedObjects[0].Raw)
if err != nil {
t.Fatalf("unexpected error while checking converted object: %v", err)
}
})
}
}
func api() *Admission {
validationTriggered := prometheus.NewCounter(prometheus.CounterOpts{
Name: "prometheus_operator_rule_validation_triggered_total",
@ -368,29 +732,48 @@ func api() *Admission {
a := New(level.NewFilter(log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout)), level.AllowNone()))
a.RegisterMetrics(validationTriggered, validationErrors, alertManagerConfigValidationTriggered, alertManagerConfigValidationError)
return a
}
type serveFunc func(w http.ResponseWriter, r *http.Request)
func server(s serveFunc) *httptest.Server {
return httptest.NewServer(http.HandlerFunc(s))
func server(h http.HandlerFunc) *httptest.Server {
return httptest.NewServer(h)
}
func send(t *testing.T, ts *httptest.Server, rules []byte) *v1.AdmissionReview {
resp, err := http.Post(ts.URL, "application/json", bytes.NewReader(rules))
func sendAdmissionReview(t *testing.T, ts *httptest.Server, b []byte) *v1.AdmissionReview {
resp, err := http.Post(ts.URL, "application/json", bytes.NewReader(b))
if err != nil {
t.Errorf("Publish() returned an error: %s", err)
t.Fatalf("POST request returned an error: %s", err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Errorf("ioutil.ReadAll(resp.Body) returned an error: %s", err)
t.Fatalf("ioutil.ReadAll(resp.Body) returned an error: %s", err)
}
rev := &v1.AdmissionReview{}
if err := json.Unmarshal(body, rev); err != nil {
t.Errorf("unable to parse webhook response: %s", err)
t.Fatalf("unable to parse webhook response: %s", err)
}
return rev
}
func sendConversionReview(t *testing.T, ts *httptest.Server, b []byte) *apiextensionsv1.ConversionReview {
t.Helper()
resp, err := http.Post(ts.URL, "application/json", bytes.NewReader(b))
if err != nil {
t.Fatalf("POST request returned an error: %s", err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("ioutil.ReadAll(resp.Body) returned an error: %s", err)
}
rev := &apiextensionsv1.ConversionReview{}
if err := json.Unmarshal(body, rev); err != nil {
t.Fatalf("unable to parse webhook response: %s (%q)", err, string(body))
}
return rev
@ -731,7 +1114,7 @@ var nonStringsInLabelsAnnotations = []byte(`
}
}`)
func buildAlertManagerConfigFromSpec(t *testing.T, spec string) []byte {
func buildAdmissionReviewFromAlertmanagerConfigSpec(t *testing.T, version, spec string) []byte {
t.Helper()
tmpl := fmt.Sprintf(`
{
@ -776,11 +1159,42 @@ func buildAlertManagerConfigFromSpec(t *testing.T, spec string) []byte {
}
`,
group,
alertManagerConfigCurrentVersion,
version,
alertManagerConfigKind,
alertManagerConfigCurrentVersion,
version,
alertManagerConfigResource,
alertManagerConfigCurrentVersion,
version,
alertManagerConfigKind,
spec)
return []byte(tmpl)
}
func buildConversionReviewFromAlertmanagerConfigSpec(t *testing.T, from, to, spec string) []byte {
t.Helper()
tmpl := fmt.Sprintf(`
{
"kind": "ConversionReview",
"apiVersion": "apiextensions.k8s.io/v1",
"request": {
"uid": "87c5df7f-5090-11e9-b9b4-02425473f309",
"desiredAPIVersion": "monitoring.coreos.com/%s",
"objects": [{
"apiVersion": "monitoring.coreos.com/%s",
"kind": "%s",
"metadata": {
"creationTimestamp": "2019-03-27T13:02:09Z",
"generation": 1,
"name": "test",
"namespace": "monitoring",
"uid": "87c5d31d-5090-11e9-b9b4-02425473f309"
},
"spec": %s
}]
}
}
`,
to,
from,
alertManagerConfigKind,
spec)
return []byte(tmpl)

View file

@ -27,6 +27,7 @@ import (
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/pkg/errors"
"github.com/prometheus-operator/prometheus-operator/pkg/alertmanager/validation"
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
monitoringv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1"
"github.com/prometheus-operator/prometheus-operator/pkg/assets"
@ -318,7 +319,7 @@ func (cb *configBuilder) getValidURLFromSecret(ctx context.Context, namespace st
}
url = strings.TrimSpace(url)
if _, err := ValidateURL(url); err != nil {
if _, err := validation.ValidateURL(url); err != nil {
return url, errors.Wrapf(err, "invalid URL %q in key %q from secret %q", url, selector.Key, selector.Name)
}
return url, nil
@ -534,7 +535,7 @@ func (cb *configBuilder) convertWebhookConfig(ctx context.Context, in monitoring
}
out.URL = url
} else if in.URL != nil {
url, err := ValidateURL(*in.URL)
url, err := validation.ValidateURL(*in.URL)
if err != nil {
return nil, err
}

View file

@ -23,6 +23,8 @@ import (
"time"
"github.com/blang/semver/v4"
"github.com/prometheus-operator/prometheus-operator/pkg/alertmanager/validation"
validationv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/alertmanager/validation/v1alpha1"
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
monitoringv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1"
"github.com/prometheus-operator/prometheus-operator/pkg/assets"
@ -904,7 +906,7 @@ func (c *Operator) loadConfigurationFromSecret(ctx context.Context, am *monitori
func (c *Operator) provisionAlertmanagerConfiguration(ctx context.Context, am *monitoringv1.Alertmanager, store *assets.Store) error {
namespacedLogger := log.With(c.logger, "alertmanager", am.Name, "namespace", am.Namespace)
if err := ValidateAlertmanager(am); err != nil {
if err := validation.ValidateAlertmanager(am); err != nil {
return err
}
@ -1111,7 +1113,7 @@ func (c *Operator) selectAlertmanagerConfigs(ctx context.Context, am *monitoring
// checkAlertmanagerConfigResource verifies that an AlertmanagerConfig object is valid
// for the given Alertmanager version and has no missing references to other objects.
func checkAlertmanagerConfigResource(ctx context.Context, amc *monitoringv1alpha1.AlertmanagerConfig, amVersion semver.Version, store *assets.Store) error {
if err := ValidateAlertmanagerConfig(amc); err != nil {
if err := validationv1alpha1.ValidateAlertmanagerConfig(amc); err != nil {
return err
}
@ -1353,7 +1355,7 @@ func checkWebhookConfigs(
if err != nil {
return err
}
if _, err := ValidateURL(strings.TrimSpace(url)); err != nil {
if _, err := validation.ValidateURL(strings.TrimSpace(url)); err != nil {
return errors.Wrapf(err, "webhook 'url' %s invalid", url)
}
}

View file

@ -12,10 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package alertmanager
package v1alpha1
import (
"encoding/json"
"fmt"
"net"
"regexp"
@ -23,44 +22,12 @@ import (
"github.com/pkg/errors"
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
"github.com/prometheus-operator/prometheus-operator/pkg/alertmanager/validation"
monitoringv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1"
"github.com/prometheus-operator/prometheus-operator/pkg/operator"
"github.com/prometheus/alertmanager/config"
)
var durationRe = regexp.MustCompile(`^(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?$`)
// ValidateAlertmanager runs extra validation on the AlertManager fields which
// can't be done at the CRD schema validation level.
func ValidateAlertmanager(am *monitoringv1.Alertmanager) error {
if am.Spec.Retention != "" {
if err := operator.ValidateDurationField(am.Spec.Retention); err != nil {
return errors.Wrap(err, "invalid retention value specified")
}
}
if am.Spec.ClusterGossipInterval != "" {
if err := operator.ValidateDurationField(am.Spec.ClusterGossipInterval); err != nil {
return errors.Wrap(err, "invalid clusterGossipInterval value specified")
}
}
if am.Spec.ClusterPushpullInterval != "" {
if err := operator.ValidateDurationField(am.Spec.ClusterPushpullInterval); err != nil {
return errors.Wrap(err, "invalid clusterPushpullInterval value specified")
}
}
if am.Spec.ClusterPeerTimeout != "" {
if err := operator.ValidateDurationField(am.Spec.ClusterPeerTimeout); err != nil {
return errors.Wrap(err, "invalid clusterPeerTimeout value specified")
}
}
return nil
}
// ValidateAlertmanagerConfig checks that the given resource complies with the
// semantics of the Alertmanager configuration.
// In particular, it verifies things that can't be modelized with the OpenAPI
@ -79,19 +46,6 @@ func ValidateAlertmanagerConfig(amc *monitoringv1alpha1.AlertmanagerConfig) erro
return validateAlertManagerRoutes(amc.Spec.Route, receivers, muteTimeIntervals, true)
}
// ValidateURL against the config.URL
// This could potentially become a regex and be validated via OpenAPI
// but right now, since we know we need to unmarshal into an upstream type
// after conversion, we validate we don't error when doing so
func ValidateURL(url string) (*config.URL, error) {
var u config.URL
err := json.Unmarshal([]byte(fmt.Sprintf(`"%s"`, url)), &u)
if err != nil {
return nil, fmt.Errorf("validate url from string failed for %s: %w", url, err)
}
return &u, nil
}
func validateReceivers(receivers []monitoringv1alpha1.Receiver) (map[string]struct{}, error) {
var err error
receiverNames := make(map[string]struct{})
@ -150,7 +104,7 @@ func validateReceivers(receivers []monitoringv1alpha1.Receiver) (map[string]stru
func validatePagerDutyConfigs(configs []monitoringv1alpha1.PagerDutyConfig) error {
for _, conf := range configs {
if conf.URL != "" {
if _, err := ValidateURL(conf.URL); err != nil {
if _, err := validation.ValidateURL(conf.URL); err != nil {
return errors.Wrap(err, "pagerduty validation failed for 'url'")
}
}
@ -171,7 +125,7 @@ func validateOpsGenieConfigs(configs []monitoringv1alpha1.OpsGenieConfig) error
return err
}
if config.APIURL != "" {
if _, err := ValidateURL(config.APIURL); err != nil {
if _, err := validation.ValidateURL(config.APIURL); err != nil {
return errors.Wrap(err, "invalid 'apiURL'")
}
}
@ -202,7 +156,7 @@ func validateWebhookConfigs(configs []monitoringv1alpha1.WebhookConfig) error {
return errors.New("one of 'url' or 'urlSecret' must be specified")
}
if config.URL != nil {
if _, err := ValidateURL(*config.URL); err != nil {
if _, err := validation.ValidateURL(*config.URL); err != nil {
return errors.Wrapf(err, "invalid 'url'")
}
}
@ -217,7 +171,7 @@ func validateWebhookConfigs(configs []monitoringv1alpha1.WebhookConfig) error {
func validateWechatConfigs(configs []monitoringv1alpha1.WeChatConfig) error {
for _, config := range configs {
if config.APIURL != "" {
if _, err := ValidateURL(config.APIURL); err != nil {
if _, err := validation.ValidateURL(config.APIURL); err != nil {
return errors.Wrap(err, "invalid 'apiURL'")
}
}
@ -284,7 +238,7 @@ func validateVictorOpsConfigs(configs []monitoringv1alpha1.VictorOpsConfig) erro
}
if config.APIURL != "" {
if _, err := ValidateURL(config.APIURL); err != nil {
if _, err := validation.ValidateURL(config.APIURL); err != nil {
return errors.Wrapf(err, "'apiURL' %s invalid", config.APIURL)
}
}

View file

@ -0,0 +1,353 @@
// Copyright 2021 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package v1beta1
import (
"fmt"
"net"
"regexp"
"strings"
"github.com/pkg/errors"
"github.com/prometheus-operator/prometheus-operator/pkg/alertmanager/validation"
monitoringv1beta1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1beta1"
)
var durationRe = regexp.MustCompile(`^(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?$`)
// ValidateAlertmanagerConfig checks that the given resource complies with the
// semantics of the Alertmanager configuration.
// In particular, it verifies things that can't be modelized with the OpenAPI
// specification such as routes should refer to an existing receiver.
func ValidateAlertmanagerConfig(amc *monitoringv1beta1.AlertmanagerConfig) error {
receivers, err := validateReceivers(amc.Spec.Receivers)
if err != nil {
return err
}
timeIntervals, err := validateTimeIntervals(amc.Spec.TimeIntervals)
if err != nil {
return err
}
return validateAlertManagerRoutes(amc.Spec.Route, receivers, timeIntervals, true)
}
func validateReceivers(receivers []monitoringv1beta1.Receiver) (map[string]struct{}, error) {
var err error
receiverNames := make(map[string]struct{})
for _, receiver := range receivers {
if _, found := receiverNames[receiver.Name]; found {
return nil, errors.Errorf("%q receiver is not unique", receiver.Name)
}
receiverNames[receiver.Name] = struct{}{}
if err = validatePagerDutyConfigs(receiver.PagerDutyConfigs); err != nil {
return nil, errors.Wrapf(err, "failed to validate 'pagerDutyConfig' - receiver %s", receiver.Name)
}
if err := validateOpsGenieConfigs(receiver.OpsGenieConfigs); err != nil {
return nil, errors.Wrapf(err, "failed to validate 'opsGenieConfig' - receiver %s", receiver.Name)
}
if err := validateSlackConfigs(receiver.SlackConfigs); err != nil {
return nil, errors.Wrapf(err, "failed to validate 'slackConfig' - receiver %s", receiver.Name)
}
if err := validateWebhookConfigs(receiver.WebhookConfigs); err != nil {
return nil, errors.Wrapf(err, "failed to validate 'webhookConfig' - receiver %s", receiver.Name)
}
if err := validateWechatConfigs(receiver.WeChatConfigs); err != nil {
return nil, errors.Wrapf(err, "failed to validate 'weChatConfig' - receiver %s", receiver.Name)
}
if err := validateEmailConfig(receiver.EmailConfigs); err != nil {
return nil, errors.Wrapf(err, "failed to validate 'emailConfig' - receiver %s", receiver.Name)
}
if err := validateVictorOpsConfigs(receiver.VictorOpsConfigs); err != nil {
return nil, errors.Wrapf(err, "failed to validate 'victorOpsConfig' - receiver %s", receiver.Name)
}
if err := validatePushoverConfigs(receiver.PushoverConfigs); err != nil {
return nil, errors.Wrapf(err, "failed to validate 'pushOverConfig' - receiver %s", receiver.Name)
}
if err := validateSnsConfigs(receiver.SNSConfigs); err != nil {
return nil, errors.Wrapf(err, "failed to validate 'snsConfig' - receiver %s", receiver.Name)
}
}
return receiverNames, nil
}
func validatePagerDutyConfigs(configs []monitoringv1beta1.PagerDutyConfig) error {
for _, conf := range configs {
if conf.URL != "" {
if _, err := validation.ValidateURL(conf.URL); err != nil {
return errors.Wrap(err, "pagerduty validation failed for 'url'")
}
}
if conf.RoutingKey == nil && conf.ServiceKey == nil {
return errors.New("one of 'routingKey' or 'serviceKey' is required")
}
if err := conf.HTTPConfig.Validate(); err != nil {
return err
}
}
return nil
}
func validateOpsGenieConfigs(configs []monitoringv1beta1.OpsGenieConfig) error {
for _, config := range configs {
if err := config.Validate(); err != nil {
return err
}
if config.APIURL != "" {
if _, err := validation.ValidateURL(config.APIURL); err != nil {
return errors.Wrap(err, "invalid 'apiURL'")
}
}
if err := config.HTTPConfig.Validate(); err != nil {
return err
}
}
return nil
}
func validateSlackConfigs(configs []monitoringv1beta1.SlackConfig) error {
for _, config := range configs {
if err := config.Validate(); err != nil {
return err
}
if err := config.HTTPConfig.Validate(); err != nil {
return err
}
}
return nil
}
func validateWebhookConfigs(configs []monitoringv1beta1.WebhookConfig) error {
for _, config := range configs {
if config.URL == nil && config.URLSecret == nil {
return errors.New("one of 'url' or 'urlSecret' must be specified")
}
if config.URL != nil {
if _, err := validation.ValidateURL(*config.URL); err != nil {
return errors.Wrapf(err, "invalid 'url'")
}
}
if err := config.HTTPConfig.Validate(); err != nil {
return err
}
}
return nil
}
func validateWechatConfigs(configs []monitoringv1beta1.WeChatConfig) error {
for _, config := range configs {
if config.APIURL != "" {
if _, err := validation.ValidateURL(config.APIURL); err != nil {
return errors.Wrap(err, "invalid 'apiURL'")
}
}
if err := config.HTTPConfig.Validate(); err != nil {
return err
}
}
return nil
}
func validateEmailConfig(configs []monitoringv1beta1.EmailConfig) error {
for _, config := range configs {
if config.To == "" {
return errors.New("missing 'to' address")
}
if config.Smarthost != "" {
_, _, err := net.SplitHostPort(config.Smarthost)
if err != nil {
return errors.Wrapf(err, "invalid field 'smarthost': %s", config.Smarthost)
}
}
if config.Headers != nil {
// Header names are case-insensitive, check for collisions.
normalizedHeaders := map[string]struct{}{}
for _, v := range config.Headers {
normalized := strings.Title(v.Key)
if _, ok := normalizedHeaders[normalized]; ok {
return fmt.Errorf("duplicate header %q", normalized)
}
normalizedHeaders[normalized] = struct{}{}
}
}
}
return nil
}
func validateVictorOpsConfigs(configs []monitoringv1beta1.VictorOpsConfig) error {
for _, config := range configs {
// from https://github.com/prometheus/alertmanager/blob/a7f9fdadbecbb7e692d2cd8d3334e3d6de1602e1/config/notifiers.go#L497
reservedFields := map[string]struct{}{
"routing_key": {},
"message_type": {},
"state_message": {},
"entity_display_name": {},
"monitoring_tool": {},
"entity_id": {},
"entity_state": {},
}
if len(config.CustomFields) > 0 {
for _, v := range config.CustomFields {
if _, ok := reservedFields[v.Key]; ok {
return fmt.Errorf("usage of reserved word %q is not allowed in custom fields", v.Key)
}
}
}
if config.RoutingKey == "" {
return errors.New("missing 'routingKey' key")
}
if config.APIURL != "" {
if _, err := validation.ValidateURL(config.APIURL); err != nil {
return errors.Wrapf(err, "'apiURL' %s invalid", config.APIURL)
}
}
if err := config.HTTPConfig.Validate(); err != nil {
return err
}
}
return nil
}
func validatePushoverConfigs(configs []monitoringv1beta1.PushoverConfig) error {
for _, config := range configs {
if config.UserKey == nil {
return errors.Errorf("mandatory field %q is empty", "userKey")
}
if config.Token == nil {
return errors.Errorf("mandatory field %q is empty", "token")
}
if err := config.HTTPConfig.Validate(); err != nil {
return err
}
}
return nil
}
func validateSnsConfigs(configs []monitoringv1beta1.SNSConfig) error {
for _, config := range configs {
if (config.TargetARN == "") != (config.TopicARN == "") != (config.PhoneNumber == "") {
return fmt.Errorf("must provide either a Target ARN, Topic ARN, or Phone Number for SNS config")
}
if err := config.HTTPConfig.Validate(); err != nil {
return err
}
}
return nil
}
// validateAlertManagerRoutes verifies that the given route and all its children are semantically valid.
// because of the self-referential issues mentioned in https://github.com/kubernetes/kubernetes/issues/62872
// it is not currently possible to apply OpenAPI validation to a v1beta1.Route
func validateAlertManagerRoutes(r *monitoringv1beta1.Route, receivers, timeIntervals map[string]struct{}, topLevelRoute bool) error {
if r == nil {
return nil
}
if r.Receiver == "" {
if topLevelRoute {
return errors.Errorf("root route must define a receiver")
}
} else {
if _, found := receivers[r.Receiver]; !found {
return errors.Errorf("receiver %q not found", r.Receiver)
}
}
if groupLen := len(r.GroupBy); groupLen > 0 {
groupedBy := make(map[string]struct{}, groupLen)
for _, str := range r.GroupBy {
if _, found := groupedBy[str]; found {
return errors.Errorf("duplicate values not permitted in route 'groupBy': %v", r.GroupBy)
}
groupedBy[str] = struct{}{}
}
if _, found := groupedBy["..."]; found && groupLen > 1 {
return errors.Errorf("'...' must be a sole value in route 'groupBy': %v", r.GroupBy)
}
}
for _, namedTimeInterval := range r.MuteTimeIntervals {
if _, found := timeIntervals[namedTimeInterval]; !found {
return errors.Errorf("time interval %q not found", namedTimeInterval)
}
}
// validate that if defaults are set, they match regex
if r.GroupInterval != "" && !durationRe.MatchString(r.GroupInterval) {
return errors.Errorf("groupInterval %s does not match required regex: %s", r.GroupInterval, durationRe.String())
}
if r.GroupWait != "" && !durationRe.MatchString(r.GroupWait) {
return errors.Errorf("groupWait %s does not match required regex: %s", r.GroupWait, durationRe.String())
}
if r.RepeatInterval != "" && !durationRe.MatchString(r.RepeatInterval) {
return errors.Errorf("repeatInterval %s does not match required regex: %s", r.RepeatInterval, durationRe.String())
}
children, err := r.ChildRoutes()
if err != nil {
return err
}
for i := range children {
if err := validateAlertManagerRoutes(&children[i], receivers, timeIntervals, false); err != nil {
return errors.Wrapf(err, "route[%d]", i)
}
}
return nil
}
func validateTimeIntervals(timeIntervals []monitoringv1beta1.TimeInterval) (map[string]struct{}, error) {
timeIntervalNames := make(map[string]struct{}, len(timeIntervals))
for i, ti := range timeIntervals {
if err := ti.Validate(); err != nil {
return nil, errors.Wrapf(err, "time interval[%d] is invalid", i)
}
timeIntervalNames[ti.Name] = struct{}{}
}
return timeIntervalNames, nil
}

View file

@ -12,30 +12,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package alertmanager
package v1beta1
import (
"net/url"
"reflect"
"testing"
v1 "k8s.io/api/core/v1"
monitoringv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1"
"github.com/prometheus/alertmanager/config"
monitoringv1beta1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1beta1"
)
func TestValidateAlertmanagerConfig(t *testing.T) {
testCases := []struct {
name string
in *monitoringv1alpha1.AlertmanagerConfig
in *monitoringv1beta1.AlertmanagerConfig
expectErr bool
}{
{
name: "Test fail to validate on duplicate receiver",
in: &monitoringv1alpha1.AlertmanagerConfig{
Spec: monitoringv1alpha1.AlertmanagerConfigSpec{
Receivers: []monitoringv1alpha1.Receiver{
in: &monitoringv1beta1.AlertmanagerConfig{
Spec: monitoringv1beta1.AlertmanagerConfigSpec{
Receivers: []monitoringv1beta1.Receiver{
{
Name: "same",
},
@ -49,17 +44,17 @@ func TestValidateAlertmanagerConfig(t *testing.T) {
},
{
name: "Test fail to validate on opsgenie config - missing required fields",
in: &monitoringv1alpha1.AlertmanagerConfig{
Spec: monitoringv1alpha1.AlertmanagerConfigSpec{
Receivers: []monitoringv1alpha1.Receiver{
in: &monitoringv1beta1.AlertmanagerConfig{
Spec: monitoringv1beta1.AlertmanagerConfigSpec{
Receivers: []monitoringv1beta1.Receiver{
{
Name: "same",
},
{
Name: "different",
OpsGenieConfigs: []monitoringv1alpha1.OpsGenieConfig{
OpsGenieConfigs: []monitoringv1beta1.OpsGenieConfig{
{
Responders: []monitoringv1alpha1.OpsGenieConfigResponder{
Responders: []monitoringv1beta1.OpsGenieConfigResponder{
{},
},
},
@ -72,28 +67,28 @@ func TestValidateAlertmanagerConfig(t *testing.T) {
},
{
name: "Test fail to validate on slack config - valid action fields - invalid fields field",
in: &monitoringv1alpha1.AlertmanagerConfig{
Spec: monitoringv1alpha1.AlertmanagerConfigSpec{
Receivers: []monitoringv1alpha1.Receiver{
in: &monitoringv1beta1.AlertmanagerConfig{
Spec: monitoringv1beta1.AlertmanagerConfigSpec{
Receivers: []monitoringv1beta1.Receiver{
{
Name: "same",
},
{
Name: "different",
SlackConfigs: []monitoringv1alpha1.SlackConfig{
SlackConfigs: []monitoringv1beta1.SlackConfig{
{
Actions: []monitoringv1alpha1.SlackAction{
Actions: []monitoringv1beta1.SlackAction{
{
Type: "a",
Text: "b",
URL: "www.test.com",
Name: "c",
ConfirmField: &monitoringv1alpha1.SlackConfirmationField{
ConfirmField: &monitoringv1beta1.SlackConfirmationField{
Text: "d",
},
},
},
Fields: []monitoringv1alpha1.SlackField{
Fields: []monitoringv1beta1.SlackField{
{},
},
},
@ -106,15 +101,15 @@ func TestValidateAlertmanagerConfig(t *testing.T) {
},
{
name: "Test fail to validate webhook config - missing required fields",
in: &monitoringv1alpha1.AlertmanagerConfig{
Spec: monitoringv1alpha1.AlertmanagerConfigSpec{
Receivers: []monitoringv1alpha1.Receiver{
in: &monitoringv1beta1.AlertmanagerConfig{
Spec: monitoringv1beta1.AlertmanagerConfigSpec{
Receivers: []monitoringv1beta1.Receiver{
{
Name: "same",
},
{
Name: "different",
WebhookConfigs: []monitoringv1alpha1.WebhookConfig{
WebhookConfigs: []monitoringv1beta1.WebhookConfig{
{},
},
},
@ -125,15 +120,15 @@ func TestValidateAlertmanagerConfig(t *testing.T) {
},
{
name: "Test fail to validate wechat config - invalid URL",
in: &monitoringv1alpha1.AlertmanagerConfig{
Spec: monitoringv1alpha1.AlertmanagerConfigSpec{
Receivers: []monitoringv1alpha1.Receiver{
in: &monitoringv1beta1.AlertmanagerConfig{
Spec: monitoringv1beta1.AlertmanagerConfigSpec{
Receivers: []monitoringv1beta1.Receiver{
{
Name: "same",
},
{
Name: "different",
WeChatConfigs: []monitoringv1alpha1.WeChatConfig{
WeChatConfigs: []monitoringv1beta1.WeChatConfig{
{
APIURL: "http://%><invalid.com",
},
@ -146,15 +141,15 @@ func TestValidateAlertmanagerConfig(t *testing.T) {
},
{
name: "Test fail to validate email config - missing to field",
in: &monitoringv1alpha1.AlertmanagerConfig{
Spec: monitoringv1alpha1.AlertmanagerConfigSpec{
Receivers: []monitoringv1alpha1.Receiver{
in: &monitoringv1beta1.AlertmanagerConfig{
Spec: monitoringv1beta1.AlertmanagerConfigSpec{
Receivers: []monitoringv1beta1.Receiver{
{
Name: "same",
},
{
Name: "different",
EmailConfigs: []monitoringv1alpha1.EmailConfig{
EmailConfigs: []monitoringv1beta1.EmailConfig{
{},
},
},
@ -165,15 +160,15 @@ func TestValidateAlertmanagerConfig(t *testing.T) {
},
{
name: "Test fail to validate email config - invalid smarthost",
in: &monitoringv1alpha1.AlertmanagerConfig{
Spec: monitoringv1alpha1.AlertmanagerConfigSpec{
Receivers: []monitoringv1alpha1.Receiver{
in: &monitoringv1beta1.AlertmanagerConfig{
Spec: monitoringv1beta1.AlertmanagerConfigSpec{
Receivers: []monitoringv1beta1.Receiver{
{
Name: "same",
},
{
Name: "different",
EmailConfigs: []monitoringv1alpha1.EmailConfig{
EmailConfigs: []monitoringv1beta1.EmailConfig{
{
To: "a",
Smarthost: "invalid",
@ -187,15 +182,15 @@ func TestValidateAlertmanagerConfig(t *testing.T) {
},
{
name: "Test fail to validate VictorOpsConfigs - missing routing key",
in: &monitoringv1alpha1.AlertmanagerConfig{
Spec: monitoringv1alpha1.AlertmanagerConfigSpec{
Receivers: []monitoringv1alpha1.Receiver{
in: &monitoringv1beta1.AlertmanagerConfig{
Spec: monitoringv1beta1.AlertmanagerConfigSpec{
Receivers: []monitoringv1beta1.Receiver{
{
Name: "same",
},
{
Name: "different",
VictorOpsConfigs: []monitoringv1alpha1.VictorOpsConfig{
VictorOpsConfigs: []monitoringv1beta1.VictorOpsConfig{
{},
},
},
@ -206,18 +201,18 @@ func TestValidateAlertmanagerConfig(t *testing.T) {
},
{
name: "Test fail to validate VictorOpsConfigs - reservedFields",
in: &monitoringv1alpha1.AlertmanagerConfig{
Spec: monitoringv1alpha1.AlertmanagerConfigSpec{
Receivers: []monitoringv1alpha1.Receiver{
in: &monitoringv1beta1.AlertmanagerConfig{
Spec: monitoringv1beta1.AlertmanagerConfigSpec{
Receivers: []monitoringv1beta1.Receiver{
{
Name: "same",
},
{
Name: "different",
VictorOpsConfigs: []monitoringv1alpha1.VictorOpsConfig{
VictorOpsConfigs: []monitoringv1beta1.VictorOpsConfig{
{
RoutingKey: "a",
CustomFields: []monitoringv1alpha1.KeyValue{
CustomFields: []monitoringv1beta1.KeyValue{
{
Key: "routing_key",
Value: "routing_key",
@ -233,15 +228,15 @@ func TestValidateAlertmanagerConfig(t *testing.T) {
},
{
name: "Test fail to validate PushoverConfigs - missing user key",
in: &monitoringv1alpha1.AlertmanagerConfig{
Spec: monitoringv1alpha1.AlertmanagerConfigSpec{
Receivers: []monitoringv1alpha1.Receiver{
in: &monitoringv1beta1.AlertmanagerConfig{
Spec: monitoringv1beta1.AlertmanagerConfigSpec{
Receivers: []monitoringv1beta1.Receiver{
{
Name: "same",
},
{
Name: "different",
PushoverConfigs: []monitoringv1alpha1.PushoverConfig{
PushoverConfigs: []monitoringv1beta1.PushoverConfig{
{},
},
},
@ -252,17 +247,20 @@ func TestValidateAlertmanagerConfig(t *testing.T) {
},
{
name: "Test fail to validate PushoverConfigs - missing token",
in: &monitoringv1alpha1.AlertmanagerConfig{
Spec: monitoringv1alpha1.AlertmanagerConfigSpec{
Receivers: []monitoringv1alpha1.Receiver{
in: &monitoringv1beta1.AlertmanagerConfig{
Spec: monitoringv1beta1.AlertmanagerConfigSpec{
Receivers: []monitoringv1beta1.Receiver{
{
Name: "same",
},
{
Name: "different",
PushoverConfigs: []monitoringv1alpha1.PushoverConfig{
PushoverConfigs: []monitoringv1beta1.PushoverConfig{
{
UserKey: &v1.SecretKeySelector{},
UserKey: &monitoringv1beta1.SecretKeySelector{
Name: "creds",
Key: "user",
},
},
},
},
@ -273,14 +271,14 @@ func TestValidateAlertmanagerConfig(t *testing.T) {
},
{
name: "Test fail to validate routes - parent route has no receiver",
in: &monitoringv1alpha1.AlertmanagerConfig{
Spec: monitoringv1alpha1.AlertmanagerConfigSpec{
Receivers: []monitoringv1alpha1.Receiver{
in: &monitoringv1beta1.AlertmanagerConfig{
Spec: monitoringv1beta1.AlertmanagerConfigSpec{
Receivers: []monitoringv1beta1.Receiver{
{
Name: "same",
},
},
Route: &monitoringv1alpha1.Route{
Route: &monitoringv1beta1.Route{
Receiver: "will-not-be-found",
},
},
@ -289,14 +287,14 @@ func TestValidateAlertmanagerConfig(t *testing.T) {
},
{
name: "Test fail to validate routes with duplicate groupBy",
in: &monitoringv1alpha1.AlertmanagerConfig{
Spec: monitoringv1alpha1.AlertmanagerConfigSpec{
Receivers: []monitoringv1alpha1.Receiver{
in: &monitoringv1beta1.AlertmanagerConfig{
Spec: monitoringv1beta1.AlertmanagerConfigSpec{
Receivers: []monitoringv1beta1.Receiver{
{
Name: "same",
},
},
Route: &monitoringv1alpha1.Route{
Route: &monitoringv1beta1.Route{
Receiver: "same",
GroupBy: []string{"job", "job"},
},
@ -306,14 +304,14 @@ func TestValidateAlertmanagerConfig(t *testing.T) {
},
{
name: "Test fail to validate routes with exclusive value and other in groupBy",
in: &monitoringv1alpha1.AlertmanagerConfig{
Spec: monitoringv1alpha1.AlertmanagerConfigSpec{
Receivers: []monitoringv1alpha1.Receiver{
in: &monitoringv1beta1.AlertmanagerConfig{
Spec: monitoringv1beta1.AlertmanagerConfigSpec{
Receivers: []monitoringv1beta1.Receiver{
{
Name: "same",
},
},
Route: &monitoringv1alpha1.Route{
Route: &monitoringv1beta1.Route{
Receiver: "same",
GroupBy: []string{"job", "..."},
},
@ -323,14 +321,14 @@ func TestValidateAlertmanagerConfig(t *testing.T) {
},
{
name: "Test fail to validate routes - named mute time interval does not exist",
in: &monitoringv1alpha1.AlertmanagerConfig{
Spec: monitoringv1alpha1.AlertmanagerConfigSpec{
Receivers: []monitoringv1alpha1.Receiver{
in: &monitoringv1beta1.AlertmanagerConfig{
Spec: monitoringv1beta1.AlertmanagerConfigSpec{
Receivers: []monitoringv1beta1.Receiver{
{
Name: "same",
},
},
Route: &monitoringv1alpha1.Route{
Route: &monitoringv1beta1.Route{
Receiver: "same",
MuteTimeIntervals: []string{"awol"},
},
@ -340,17 +338,17 @@ func TestValidateAlertmanagerConfig(t *testing.T) {
},
{
name: "Test happy path",
in: &monitoringv1alpha1.AlertmanagerConfig{
Spec: monitoringv1alpha1.AlertmanagerConfigSpec{
Receivers: []monitoringv1alpha1.Receiver{
in: &monitoringv1beta1.AlertmanagerConfig{
Spec: monitoringv1beta1.AlertmanagerConfigSpec{
Receivers: []monitoringv1beta1.Receiver{
{
Name: "same",
},
{
Name: "different",
OpsGenieConfigs: []monitoringv1alpha1.OpsGenieConfig{
OpsGenieConfigs: []monitoringv1beta1.OpsGenieConfig{
{
Responders: []monitoringv1alpha1.OpsGenieConfigResponder{
Responders: []monitoringv1beta1.OpsGenieConfigResponder{
{
ID: "a",
Name: "b",
@ -359,20 +357,20 @@ func TestValidateAlertmanagerConfig(t *testing.T) {
},
},
},
SlackConfigs: []monitoringv1alpha1.SlackConfig{
SlackConfigs: []monitoringv1beta1.SlackConfig{
{
Actions: []monitoringv1alpha1.SlackAction{
Actions: []monitoringv1beta1.SlackAction{
{
Type: "a",
Text: "b",
URL: "https://www.test.com",
Name: "c",
ConfirmField: &monitoringv1alpha1.SlackConfirmationField{
ConfirmField: &monitoringv1beta1.SlackConfirmationField{
Text: "d",
},
},
},
Fields: []monitoringv1alpha1.SlackField{
Fields: []monitoringv1beta1.SlackField{
{
Title: "a",
Value: "b",
@ -380,22 +378,25 @@ func TestValidateAlertmanagerConfig(t *testing.T) {
},
},
},
WebhookConfigs: []monitoringv1alpha1.WebhookConfig{
WebhookConfigs: []monitoringv1beta1.WebhookConfig{
{
URL: strToPtr("https://www.test.com"),
URLSecret: &v1.SecretKeySelector{},
URL: strToPtr("https://www.test.com"),
URLSecret: &monitoringv1beta1.SecretKeySelector{
Name: "creds",
Key: "url",
},
},
},
WeChatConfigs: []monitoringv1alpha1.WeChatConfig{
WeChatConfigs: []monitoringv1beta1.WeChatConfig{
{
APIURL: "https://test.com",
},
},
EmailConfigs: []monitoringv1alpha1.EmailConfig{
EmailConfigs: []monitoringv1beta1.EmailConfig{
{
To: "a",
Smarthost: "b:8080",
Headers: []monitoringv1alpha1.KeyValue{
Headers: []monitoringv1beta1.KeyValue{
{
Key: "c",
Value: "d",
@ -403,10 +404,10 @@ func TestValidateAlertmanagerConfig(t *testing.T) {
},
},
},
VictorOpsConfigs: []monitoringv1alpha1.VictorOpsConfig{
VictorOpsConfigs: []monitoringv1beta1.VictorOpsConfig{
{
RoutingKey: "a",
CustomFields: []monitoringv1alpha1.KeyValue{
CustomFields: []monitoringv1beta1.KeyValue{
{
Key: "b",
Value: "c",
@ -414,29 +415,35 @@ func TestValidateAlertmanagerConfig(t *testing.T) {
},
},
},
PushoverConfigs: []monitoringv1alpha1.PushoverConfig{
PushoverConfigs: []monitoringv1beta1.PushoverConfig{
{
UserKey: &v1.SecretKeySelector{},
Token: &v1.SecretKeySelector{},
Retry: "10m",
Expire: "5m",
UserKey: &monitoringv1beta1.SecretKeySelector{
Name: "creds",
Key: "user",
},
Token: &monitoringv1beta1.SecretKeySelector{
Name: "creds",
Key: "token",
},
Retry: "10m",
Expire: "5m",
},
},
},
},
Route: &monitoringv1alpha1.Route{
Route: &monitoringv1beta1.Route{
Receiver: "same",
GroupBy: []string{"..."},
MuteTimeIntervals: []string{"weekdays-only"},
},
MuteTimeIntervals: []monitoringv1alpha1.MuteTimeInterval{
TimeIntervals: []monitoringv1beta1.TimeInterval{
{
Name: "weekdays-only",
TimeIntervals: []monitoringv1alpha1.TimeInterval{
TimeIntervals: []monitoringv1beta1.TimePeriod{
{
Weekdays: []monitoringv1alpha1.WeekdayRange{
monitoringv1alpha1.WeekdayRange("Saturday"),
monitoringv1alpha1.WeekdayRange("Sunday"),
Weekdays: []monitoringv1beta1.WeekdayRange{
monitoringv1beta1.WeekdayRange("Saturday"),
monitoringv1beta1.WeekdayRange("Sunday"),
},
},
},
@ -465,54 +472,6 @@ func TestValidateAlertmanagerConfig(t *testing.T) {
}
}
func TestValidateUrl(t *testing.T) {
tests := []struct {
name string
in string
expectErr bool
expectResult func() *config.URL
}{
{
name: "Test invalid url returns error",
in: "https://!^invalid.com",
expectErr: true,
},
{
name: "Test missing scheme returns error",
in: "is.normally.valid",
expectErr: true,
},
{
name: "Test happy path",
in: "https://u:p@is.compliant.with.upstream.unmarshal",
expectResult: func() *config.URL {
u, _ := url.Parse("https://u:p@is.compliant.with.upstream.unmarshal")
return &config.URL{URL: u}
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
u, err := ValidateURL(tc.in)
if tc.expectErr {
if err == nil {
t.Fatal("expected error but got none")
}
return
}
if err != nil {
t.Fatal(err)
}
res := tc.expectResult()
if !reflect.DeepEqual(u, res) {
t.Fatalf("wanted %v but got %v", res, u)
}
})
}
}
func strToPtr(s string) *string {
return &s
}

View file

@ -0,0 +1,70 @@
// Copyright 2022 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package validation
import (
"encoding/json"
"fmt"
"github.com/pkg/errors"
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
"github.com/prometheus-operator/prometheus-operator/pkg/operator"
"github.com/prometheus/alertmanager/config"
)
// ValidateAlertmanager runs extra validation on the AlertManager fields which
// can't be done at the CRD schema validation level.
func ValidateAlertmanager(am *monitoringv1.Alertmanager) error {
if am.Spec.Retention != "" {
if err := operator.ValidateDurationField(am.Spec.Retention); err != nil {
return errors.Wrap(err, "invalid retention value specified")
}
}
if am.Spec.ClusterGossipInterval != "" {
if err := operator.ValidateDurationField(am.Spec.ClusterGossipInterval); err != nil {
return errors.Wrap(err, "invalid clusterGossipInterval value specified")
}
}
if am.Spec.ClusterPushpullInterval != "" {
if err := operator.ValidateDurationField(am.Spec.ClusterPushpullInterval); err != nil {
return errors.Wrap(err, "invalid clusterPushpullInterval value specified")
}
}
if am.Spec.ClusterPeerTimeout != "" {
if err := operator.ValidateDurationField(am.Spec.ClusterPeerTimeout); err != nil {
return errors.Wrap(err, "invalid clusterPeerTimeout value specified")
}
}
return nil
}
// ValidateAlertmanagerConfig checks that the given resource complies with the
// ValidateURL against the config.URL
// This could potentially become a regex and be validated via OpenAPI
// but right now, since we know we need to unmarshal into an upstream type
// after conversion, we validate we don't error when doing so
func ValidateURL(url string) (*config.URL, error) {
var u config.URL
err := json.Unmarshal([]byte(fmt.Sprintf(`"%s"`, url)), &u)
if err != nil {
return nil, fmt.Errorf("validate url from string failed for %s: %w", url, err)
}
return &u, nil
}

View file

@ -0,0 +1,71 @@
// Copyright 2021 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package validation
import (
"net/url"
"reflect"
"testing"
"github.com/prometheus/alertmanager/config"
)
func TestValidateUrl(t *testing.T) {
tests := []struct {
name string
in string
expectErr bool
expectResult func() *config.URL
}{
{
name: "Test invalid url returns error",
in: "https://!^invalid.com",
expectErr: true,
},
{
name: "Test missing scheme returns error",
in: "is.normally.valid",
expectErr: true,
},
{
name: "Test happy path",
in: "https://u:p@is.compliant.with.upstream.unmarshal",
expectResult: func() *config.URL {
u, _ := url.Parse("https://u:p@is.compliant.with.upstream.unmarshal")
return &config.URL{URL: u}
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
u, err := ValidateURL(tc.in)
if tc.expectErr {
if err == nil {
t.Fatal("expected error but got none")
}
return
}
if err != nil {
t.Fatal(err)
}
res := tc.expectResult()
if !reflect.DeepEqual(u, res) {
t.Fatalf("wanted %v but got %v", res, u)
}
})
}
}

View file

@ -3,9 +3,10 @@ module github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring
go 1.17
require (
k8s.io/api v0.23.0
k8s.io/apiextensions-apiserver v0.23.0
k8s.io/apimachinery v0.23.0
k8s.io/api v0.23.5
k8s.io/apiextensions-apiserver v0.23.5
k8s.io/apimachinery v0.23.5
sigs.k8s.io/controller-runtime v0.11.2
)
require (
@ -16,12 +17,12 @@ require (
github.com/json-iterator/go v1.1.12 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
golang.org/x/net v0.0.0-20210825183410-e898025ed96a // indirect
golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect
golang.org/x/text v0.3.7 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/klog/v2 v2.30.0 // indirect
k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b // indirect
k8s.io/utils v0.0.0-20211116205334-6203023598ed // indirect
sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
)

View file

@ -117,6 +117,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
@ -124,6 +125,7 @@ github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoD
github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@ -148,6 +150,7 @@ github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
@ -259,7 +262,9 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
@ -324,14 +329,18 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
@ -448,11 +457,14 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@ -541,10 +553,12 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210825183410-e898025ed96a h1:bRuuGXV8wwSdGTB+CtJf+FjgO1APK1CoO39T4BN/XBw=
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY=
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -617,6 +631,7 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -630,8 +645,10 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -701,6 +718,7 @@ golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82u
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
@ -712,6 +730,7 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@ -860,16 +879,16 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.23.0 h1:WrL1gb73VSC8obi8cuYETJGXEoFNEh3LU0Pt+Sokgro=
k8s.io/api v0.23.0/go.mod h1:8wmDdLBHBNxtOIytwLstXt5E9PddnZb0GaMcqsvDBpg=
k8s.io/apiextensions-apiserver v0.23.0 h1:uii8BYmHYiT2ZTAJxmvc3X8UhNYMxl2A0z0Xq3Pm+WY=
k8s.io/apiextensions-apiserver v0.23.0/go.mod h1:xIFAEEDlAZgpVBl/1VSjGDmLoXAWRG40+GsWhKhAxY4=
k8s.io/apimachinery v0.23.0 h1:mIfWRMjBuMdolAWJ3Fd+aPTMv3X9z+waiARMpvvb0HQ=
k8s.io/apimachinery v0.23.0/go.mod h1:fFCTTBKvKcwTPFzjlcxp91uPFZr+JA0FubU4fLzzFYc=
k8s.io/apiserver v0.23.0/go.mod h1:Cec35u/9zAepDPPFyT+UMrgqOCjgJ5qtfVJDxjZYmt4=
k8s.io/client-go v0.23.0/go.mod h1:hrDnpnK1mSr65lHHcUuIZIXDgEbzc7/683c6hyG4jTA=
k8s.io/code-generator v0.23.0/go.mod h1:vQvOhDXhuzqiVfM/YHp+dmg10WDZCchJVObc9MvowsE=
k8s.io/component-base v0.23.0/go.mod h1:DHH5uiFvLC1edCpvcTDV++NKULdYYU6pR9Tt3HIKMKI=
k8s.io/api v0.23.5 h1:zno3LUiMubxD/V1Zw3ijyKO3wxrhbUF1Ck+VjBvfaoA=
k8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8=
k8s.io/apiextensions-apiserver v0.23.5 h1:5SKzdXyvIJKu+zbfPc3kCbWpbxi+O+zdmAJBm26UJqI=
k8s.io/apiextensions-apiserver v0.23.5/go.mod h1:ntcPWNXS8ZPKN+zTXuzYMeg731CP0heCTl6gYBxLcuQ=
k8s.io/apimachinery v0.23.5 h1:Va7dwhp8wgkUPWsEXk6XglXWU4IKYLKNlv8VkX7SDM0=
k8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM=
k8s.io/apiserver v0.23.5/go.mod h1:7wvMtGJ42VRxzgVI7jkbKvMbuCbVbgsWFT7RyXiRNTw=
k8s.io/client-go v0.23.5/go.mod h1:flkeinTO1CirYgzMPRWxUCnV0G4Fbu2vLhYCObnt/r4=
k8s.io/code-generator v0.23.5/go.mod h1:S0Q1JVA+kSzTI1oUvbKAxZY/DYbA/ZUb4Uknog12ETk=
k8s.io/component-base v0.23.5/go.mod h1:c5Nq44KZyt1aLl0IpHX82fhsn84Sb0jjzwjpcA42bY0=
k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
@ -877,16 +896,19 @@ k8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw=
k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk=
k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b h1:wxEMGetGMur3J1xuGLQY7GEQYg9bZxKn3tKo5k/eYcs=
k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20211116205334-6203023598ed h1:ck1fRPWPJWsMd8ZRFsWc6mh/zHp5fZ/shhbrgPUxDAE=
k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.25/go.mod h1:Mlj9PNLmG9bZ6BHFwFKDo5afkpWyUISkb9Me0GnK66I=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30/go.mod h1:fEO7lRTdivWO2qYVCVG7dEADOMo/MLDCVr8So2g88Uw=
sigs.k8s.io/controller-runtime v0.11.2 h1:H5GTxQl0Mc9UjRJhORusqfJCIjBO8UtUxGggCwL1rLA=
sigs.k8s.io/controller-runtime v0.11.2/go.mod h1:P6QCzrEjLaZGqHsfd+os7JQ+WFZhvB8MRFsn4dWF7O4=
sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s=
sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno=
sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y=
sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=

View file

@ -0,0 +1,18 @@
// Copyright 2022 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package v1alpha1
// Hub marks this type as a conversion hub.
func (*AlertmanagerConfig) Hub() {}

View file

@ -41,6 +41,7 @@ const (
// +genclient
// +k8s:openapi-gen=true
// +kubebuilder:resource:categories="prometheus-operator",shortName="amcfg"
// +kubebuilder:storageversion
type AlertmanagerConfig struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,544 @@
// Copyright 2022 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package v1beta1
import (
"encoding/json"
"fmt"
v1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"sigs.k8s.io/controller-runtime/pkg/conversion"
"github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1"
)
func convertRouteFrom(in *v1alpha1.Route) (*Route, error) {
if in == nil {
return nil, nil
}
out := &Route{
Receiver: in.Receiver,
GroupBy: in.GroupBy,
GroupWait: in.GroupWait,
GroupInterval: in.GroupInterval,
RepeatInterval: in.RepeatInterval,
Matchers: convertMatchersFrom(in.Matchers),
MuteTimeIntervals: in.MuteTimeIntervals,
}
// Deserialize child routes to convert them to v1alpha1 and serialize back.
crs, err := in.ChildRoutes()
if err != nil {
return nil, err
}
out.Routes = make([]apiextensionsv1.JSON, 0, len(in.Routes))
for i := range crs {
cr, err := convertRouteFrom(&crs[i])
if err != nil {
return nil, fmt.Errorf("route[%d]: %w", i, err)
}
b, err := json.Marshal(cr)
if err != nil {
return nil, fmt.Errorf("route[%d]: %w", i, err)
}
out.Routes = append(out.Routes, apiextensionsv1.JSON{Raw: b})
}
return out, nil
}
func convertMatchersFrom(in []v1alpha1.Matcher) []Matcher {
out := make([]Matcher, 0, len(in))
for _, m := range in {
mt := m.MatchType
if mt == "" {
mt = "="
if m.Regex {
mt = "=~"
}
}
out = append(
out,
Matcher{
Name: m.Name,
Value: m.Value,
MatchType: MatchType(mt),
},
)
}
return out
}
func convertTimeIntervalsFrom(in []v1alpha1.TimeInterval) []TimePeriod {
out := make([]TimePeriod, 0, len(in))
for _, ti := range in {
var (
trs = make([]TimeRange, 0, len(ti.Times))
wds = make([]WeekdayRange, 0, len(ti.Weekdays))
doms = make([]DayOfMonthRange, 0, len(ti.DaysOfMonth))
mrs = make([]MonthRange, 0, len(ti.Months))
yrs = make([]YearRange, 0, len(ti.Years))
)
for _, tr := range ti.Times {
trs = append(trs, TimeRange{StartTime: Time(tr.StartTime), EndTime: Time(tr.EndTime)})
}
for _, wd := range ti.Weekdays {
wds = append(wds, WeekdayRange(wd))
}
for _, dm := range ti.DaysOfMonth {
doms = append(doms, DayOfMonthRange{Start: dm.Start, End: dm.End})
}
for _, mr := range ti.Months {
mrs = append(mrs, MonthRange(mr))
}
for _, yr := range ti.Years {
yrs = append(yrs, YearRange(yr))
}
out = append(
out,
TimePeriod{
Times: trs,
Weekdays: wds,
DaysOfMonth: doms,
Months: mrs,
Years: yrs,
},
)
}
return out
}
func convertHTTPConfigFrom(in *v1alpha1.HTTPConfig) *HTTPConfig {
if in == nil {
return nil
}
return &HTTPConfig{
Authorization: in.Authorization,
BasicAuth: in.BasicAuth,
OAuth2: in.OAuth2,
BearerTokenSecret: convertSecretKeySelectorFrom(in.BearerTokenSecret),
TLSConfig: in.TLSConfig,
ProxyURL: in.ProxyURL,
}
}
func convertKeyValuesFrom(in []v1alpha1.KeyValue) []KeyValue {
out := make([]KeyValue, len(in))
for i := range in {
out[i] = KeyValue{
Key: in[i].Key,
Value: in[i].Value,
}
}
return out
}
func convertSecretKeySelectorFrom(in *v1.SecretKeySelector) *SecretKeySelector {
if in == nil {
return nil
}
return &SecretKeySelector{
Name: in.Name,
Key: in.Key,
}
}
func convertOpsGenieConfigRespondersFrom(in []v1alpha1.OpsGenieConfigResponder) []OpsGenieConfigResponder {
out := make([]OpsGenieConfigResponder, len(in))
for i := range in {
out[i] = OpsGenieConfigResponder{
ID: in[i].ID,
Name: in[i].Name,
Username: in[i].Username,
Type: in[i].Type,
}
}
return out
}
func convertOpsGenieConfigFrom(in v1alpha1.OpsGenieConfig) OpsGenieConfig {
return OpsGenieConfig{
SendResolved: in.SendResolved,
APIKey: convertSecretKeySelectorFrom(in.APIKey),
APIURL: in.APIURL,
Message: in.Message,
Description: in.Description,
Source: in.Source,
Tags: in.Tags,
Note: in.Note,
Priority: in.Priority,
Details: convertKeyValuesFrom(in.Details),
Responders: convertOpsGenieConfigRespondersFrom(in.Responders),
HTTPConfig: convertHTTPConfigFrom(in.HTTPConfig),
Entity: in.Entity,
Actions: in.Actions,
}
}
func convertPagerDutyImageConfigsFrom(in []v1alpha1.PagerDutyImageConfig) []PagerDutyImageConfig {
out := make([]PagerDutyImageConfig, len(in))
for i := range in {
out[i] = PagerDutyImageConfig{
Src: in[i].Src,
Href: in[i].Href,
Alt: in[i].Alt,
}
}
return out
}
func convertPagerDutyLinkConfigsFrom(in []v1alpha1.PagerDutyLinkConfig) []PagerDutyLinkConfig {
out := make([]PagerDutyLinkConfig, len(in))
for i := range in {
out[i] = PagerDutyLinkConfig{
Href: in[i].Href,
Text: in[i].Text,
}
}
return out
}
func convertPagerDutyConfigFrom(in v1alpha1.PagerDutyConfig) PagerDutyConfig {
return PagerDutyConfig{
SendResolved: in.SendResolved,
RoutingKey: convertSecretKeySelectorFrom(in.RoutingKey),
ServiceKey: convertSecretKeySelectorFrom(in.ServiceKey),
URL: in.URL,
Client: in.Client,
ClientURL: in.ClientURL,
Description: in.Description,
Severity: in.Severity,
Class: in.Class,
Group: in.Group,
Component: in.Component,
Details: convertKeyValuesFrom(in.Details),
PagerDutyImageConfigs: convertPagerDutyImageConfigsFrom(in.PagerDutyImageConfigs),
PagerDutyLinkConfigs: convertPagerDutyLinkConfigsFrom(in.PagerDutyLinkConfigs),
HTTPConfig: convertHTTPConfigFrom(in.HTTPConfig),
}
}
func convertSlackFieldsFrom(in []v1alpha1.SlackField) []SlackField {
out := make([]SlackField, len(in))
for i := range in {
out[i] = SlackField{
Title: in[i].Title,
Value: in[i].Value,
Short: in[i].Short,
}
}
return out
}
func convertSlackActionsFrom(in []v1alpha1.SlackAction) []SlackAction {
out := make([]SlackAction, len(in))
for i := range in {
out[i] = SlackAction{
Type: in[i].Type,
Text: in[i].Text,
URL: in[i].URL,
Style: in[i].Style,
Name: in[i].Name,
Value: in[i].Value,
}
if in[i].ConfirmField != nil {
out[i].ConfirmField = &SlackConfirmationField{
Text: in[i].ConfirmField.Text,
Title: in[i].ConfirmField.Title,
OkText: in[i].ConfirmField.OkText,
DismissText: in[i].ConfirmField.DismissText,
}
}
}
return out
}
func convertSlackConfigFrom(in v1alpha1.SlackConfig) SlackConfig {
return SlackConfig{
SendResolved: in.SendResolved,
APIURL: convertSecretKeySelectorFrom(in.APIURL),
Channel: in.Channel,
Username: in.Username,
Color: in.Color,
Title: in.Title,
TitleLink: in.TitleLink,
Pretext: in.Pretext,
Text: in.Text,
Fields: convertSlackFieldsFrom(in.Fields),
ShortFields: in.ShortFields,
Footer: in.Footer,
Fallback: in.Fallback,
CallbackID: in.CallbackID,
IconEmoji: in.IconEmoji,
IconURL: in.IconURL,
ImageURL: in.ImageURL,
ThumbURL: in.ThumbURL,
LinkNames: in.LinkNames,
MrkdwnIn: in.MrkdwnIn,
Actions: convertSlackActionsFrom(in.Actions),
HTTPConfig: convertHTTPConfigFrom(in.HTTPConfig),
}
}
func convertWebhookConfigFrom(in v1alpha1.WebhookConfig) WebhookConfig {
return WebhookConfig{
SendResolved: in.SendResolved,
URL: in.URL,
URLSecret: convertSecretKeySelectorFrom(in.URLSecret),
HTTPConfig: convertHTTPConfigFrom(in.HTTPConfig),
MaxAlerts: in.MaxAlerts,
}
}
func convertWeChatConfigFrom(in v1alpha1.WeChatConfig) WeChatConfig {
return WeChatConfig{
SendResolved: in.SendResolved,
APISecret: convertSecretKeySelectorFrom(in.APISecret),
APIURL: in.APIURL,
CorpID: in.CorpID,
AgentID: in.AgentID,
ToUser: in.ToUser,
ToParty: in.ToParty,
ToTag: in.ToTag,
Message: in.Message,
MessageType: in.MessageType,
HTTPConfig: convertHTTPConfigFrom(in.HTTPConfig),
}
}
func convertEmailConfigFrom(in v1alpha1.EmailConfig) EmailConfig {
return EmailConfig{
SendResolved: in.SendResolved,
To: in.To,
From: in.From,
Hello: in.Hello,
Smarthost: in.Smarthost,
AuthUsername: in.AuthUsername,
AuthPassword: convertSecretKeySelectorFrom(in.AuthPassword),
AuthSecret: convertSecretKeySelectorFrom(in.AuthSecret),
AuthIdentity: in.AuthIdentity,
Headers: convertKeyValuesFrom(in.Headers),
HTML: in.HTML,
Text: in.Text,
RequireTLS: in.RequireTLS,
TLSConfig: in.TLSConfig,
}
}
func convertVictorOpsConfigFrom(in v1alpha1.VictorOpsConfig) VictorOpsConfig {
return VictorOpsConfig{
SendResolved: in.SendResolved,
APIKey: convertSecretKeySelectorFrom(in.APIKey),
APIURL: in.APIURL,
RoutingKey: in.RoutingKey,
MessageType: in.MessageType,
EntityDisplayName: in.EntityDisplayName,
StateMessage: in.StateMessage,
MonitoringTool: in.MonitoringTool,
CustomFields: convertKeyValuesFrom(in.CustomFields),
HTTPConfig: convertHTTPConfigFrom(in.HTTPConfig),
}
}
func convertPushoverConfigFrom(in v1alpha1.PushoverConfig) PushoverConfig {
return PushoverConfig{
SendResolved: in.SendResolved,
UserKey: convertSecretKeySelectorFrom(in.UserKey),
Token: convertSecretKeySelectorFrom(in.Token),
Title: in.Title,
Message: in.Message,
URL: in.URL,
URLTitle: in.URLTitle,
Sound: in.Sound,
Priority: in.Priority,
Retry: in.Retry,
Expire: in.Expire,
HTML: in.HTML,
HTTPConfig: convertHTTPConfigFrom(in.HTTPConfig),
}
}
func convertSNSConfigFrom(in v1alpha1.SNSConfig) SNSConfig {
return SNSConfig{
SendResolved: in.SendResolved,
ApiURL: in.ApiURL,
Sigv4: in.Sigv4,
TopicARN: in.TopicARN,
Subject: in.Subject,
PhoneNumber: in.PhoneNumber,
TargetARN: in.TargetARN,
Message: in.Message,
Attributes: in.Attributes,
HTTPConfig: convertHTTPConfigFrom(in.HTTPConfig),
}
}
func convertTelegramConfigFrom(in v1alpha1.TelegramConfig) TelegramConfig {
return TelegramConfig{
SendResolved: in.SendResolved,
APIURL: in.APIURL,
BotToken: convertSecretKeySelectorFrom(in.BotToken),
ChatID: in.ChatID,
Message: in.Message,
DisableNotifications: in.DisableNotifications,
ParseMode: in.ParseMode,
HTTPConfig: convertHTTPConfigFrom(in.HTTPConfig),
}
}
// ConvertFrom converts from the Hub version (v1alpha1) to this version (v1beta1).
func (dst *AlertmanagerConfig) ConvertFrom(srcRaw conversion.Hub) error {
src := srcRaw.(*v1alpha1.AlertmanagerConfig)
dst.ObjectMeta = src.ObjectMeta
for _, in := range src.Spec.Receivers {
out := Receiver{
Name: in.Name,
}
for _, in := range in.OpsGenieConfigs {
out.OpsGenieConfigs = append(
out.OpsGenieConfigs,
convertOpsGenieConfigFrom(in),
)
}
for _, in := range in.PagerDutyConfigs {
out.PagerDutyConfigs = append(
out.PagerDutyConfigs,
convertPagerDutyConfigFrom(in),
)
}
for _, in := range in.SlackConfigs {
out.SlackConfigs = append(
out.SlackConfigs,
convertSlackConfigFrom(in),
)
}
for _, in := range in.WebhookConfigs {
out.WebhookConfigs = append(
out.WebhookConfigs,
convertWebhookConfigFrom(in),
)
}
for _, in := range in.WeChatConfigs {
out.WeChatConfigs = append(
out.WeChatConfigs,
convertWeChatConfigFrom(in),
)
}
for _, in := range in.EmailConfigs {
out.EmailConfigs = append(
out.EmailConfigs,
convertEmailConfigFrom(in),
)
}
for _, in := range in.VictorOpsConfigs {
out.VictorOpsConfigs = append(
out.VictorOpsConfigs,
convertVictorOpsConfigFrom(in),
)
}
for _, in := range in.PushoverConfigs {
out.PushoverConfigs = append(
out.PushoverConfigs,
convertPushoverConfigFrom(in),
)
}
for _, in := range in.SNSConfigs {
out.SNSConfigs = append(
out.SNSConfigs,
convertSNSConfigFrom(in),
)
}
for _, in := range in.TelegramConfigs {
out.TelegramConfigs = append(
out.TelegramConfigs,
convertTelegramConfigFrom(in),
)
}
dst.Spec.Receivers = append(dst.Spec.Receivers, out)
}
for _, in := range src.Spec.InhibitRules {
dst.Spec.InhibitRules = append(
dst.Spec.InhibitRules,
InhibitRule{
TargetMatch: convertMatchersFrom(in.TargetMatch),
SourceMatch: convertMatchersFrom(in.SourceMatch),
Equal: in.Equal,
},
)
}
for _, in := range src.Spec.MuteTimeIntervals {
dst.Spec.TimeIntervals = append(
dst.Spec.TimeIntervals,
TimeInterval{
Name: in.Name,
TimeIntervals: convertTimeIntervalsFrom(in.TimeIntervals),
},
)
}
r, err := convertRouteFrom(src.Spec.Route)
if err != nil {
return err
}
dst.Spec.Route = r
return nil
}

View file

@ -0,0 +1,539 @@
// Copyright 2022 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package v1beta1
import (
"encoding/json"
"fmt"
v1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"sigs.k8s.io/controller-runtime/pkg/conversion"
"github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1"
)
func convertRouteTo(in *Route) (*v1alpha1.Route, error) {
if in == nil {
return nil, nil
}
out := &v1alpha1.Route{
Receiver: in.Receiver,
GroupBy: in.GroupBy,
GroupWait: in.GroupWait,
GroupInterval: in.GroupInterval,
RepeatInterval: in.RepeatInterval,
Matchers: convertMatchersTo(in.Matchers),
MuteTimeIntervals: in.MuteTimeIntervals,
}
// Deserialize child routes to convert them to v1alpha1 and serialize back.
crs, err := in.ChildRoutes()
if err != nil {
return nil, err
}
out.Routes = make([]apiextensionsv1.JSON, 0, len(in.Routes))
for i := range crs {
cr, err := convertRouteTo(&crs[i])
if err != nil {
return nil, fmt.Errorf("route[%d]: %w", i, err)
}
b, err := json.Marshal(cr)
if err != nil {
return nil, fmt.Errorf("route[%d]: %w", i, err)
}
out.Routes = append(out.Routes, apiextensionsv1.JSON{Raw: b})
}
return out, nil
}
func convertMatchersTo(in []Matcher) []v1alpha1.Matcher {
out := make([]v1alpha1.Matcher, 0, len(in))
for _, m := range in {
out = append(
out,
v1alpha1.Matcher{
Name: m.Name,
Value: m.Value,
MatchType: v1alpha1.MatchType(m.MatchType),
},
)
}
return out
}
func convertTimeIntervalsTo(in []TimePeriod) []v1alpha1.TimeInterval {
out := make([]v1alpha1.TimeInterval, 0, len(in))
for _, ti := range in {
var (
trs = make([]v1alpha1.TimeRange, 0, len(ti.Times))
wds = make([]v1alpha1.WeekdayRange, 0, len(ti.Weekdays))
doms = make([]v1alpha1.DayOfMonthRange, 0, len(ti.DaysOfMonth))
mrs = make([]v1alpha1.MonthRange, 0, len(ti.Months))
yrs = make([]v1alpha1.YearRange, 0, len(ti.Years))
)
for _, tr := range ti.Times {
trs = append(trs, v1alpha1.TimeRange{StartTime: v1alpha1.Time(tr.StartTime), EndTime: v1alpha1.Time(tr.EndTime)})
}
for _, wd := range ti.Weekdays {
wds = append(wds, v1alpha1.WeekdayRange(wd))
}
for _, dm := range ti.DaysOfMonth {
doms = append(doms, v1alpha1.DayOfMonthRange{Start: dm.Start, End: dm.End})
}
for _, mr := range ti.Months {
mrs = append(mrs, v1alpha1.MonthRange(mr))
}
for _, yr := range ti.Years {
yrs = append(yrs, v1alpha1.YearRange(yr))
}
out = append(
out,
v1alpha1.TimeInterval{
Times: trs,
Weekdays: wds,
DaysOfMonth: doms,
Months: mrs,
Years: yrs,
},
)
}
return out
}
func convertHTTPConfigTo(in *HTTPConfig) *v1alpha1.HTTPConfig {
if in == nil {
return nil
}
return &v1alpha1.HTTPConfig{
Authorization: in.Authorization,
BasicAuth: in.BasicAuth,
OAuth2: in.OAuth2,
BearerTokenSecret: convertSecretKeySelectorTo(in.BearerTokenSecret),
TLSConfig: in.TLSConfig,
ProxyURL: in.ProxyURL,
}
}
func convertKeyValuesTo(in []KeyValue) []v1alpha1.KeyValue {
out := make([]v1alpha1.KeyValue, len(in))
for i := range in {
out[i] = v1alpha1.KeyValue{
Key: in[i].Key,
Value: in[i].Value,
}
}
return out
}
func convertSecretKeySelectorTo(in *SecretKeySelector) *v1.SecretKeySelector {
if in == nil {
return nil
}
return &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: in.Name,
},
Key: in.Key,
}
}
func convertOpsGenieConfigRespondersTo(in []OpsGenieConfigResponder) []v1alpha1.OpsGenieConfigResponder {
out := make([]v1alpha1.OpsGenieConfigResponder, len(in))
for i := range in {
out[i] = v1alpha1.OpsGenieConfigResponder{
ID: in[i].ID,
Name: in[i].Name,
Username: in[i].Username,
Type: in[i].Type,
}
}
return out
}
func convertOpsGenieConfigTo(in OpsGenieConfig) v1alpha1.OpsGenieConfig {
return v1alpha1.OpsGenieConfig{
SendResolved: in.SendResolved,
APIKey: convertSecretKeySelectorTo(in.APIKey),
APIURL: in.APIURL,
Message: in.Message,
Description: in.Description,
Source: in.Source,
Tags: in.Tags,
Note: in.Note,
Priority: in.Priority,
Details: convertKeyValuesTo(in.Details),
Responders: convertOpsGenieConfigRespondersTo(in.Responders),
HTTPConfig: convertHTTPConfigTo(in.HTTPConfig),
Entity: in.Entity,
Actions: in.Actions,
}
}
func convertPagerDutyImageConfigsTo(in []PagerDutyImageConfig) []v1alpha1.PagerDutyImageConfig {
out := make([]v1alpha1.PagerDutyImageConfig, len(in))
for i := range in {
out[i] = v1alpha1.PagerDutyImageConfig{
Src: in[i].Src,
Href: in[i].Href,
Alt: in[i].Alt,
}
}
return out
}
func convertPagerDutyLinkConfigsTo(in []PagerDutyLinkConfig) []v1alpha1.PagerDutyLinkConfig {
out := make([]v1alpha1.PagerDutyLinkConfig, len(in))
for i := range in {
out[i] = v1alpha1.PagerDutyLinkConfig{
Href: in[i].Href,
Text: in[i].Text,
}
}
return out
}
func convertPagerDutyConfigTo(in PagerDutyConfig) v1alpha1.PagerDutyConfig {
return v1alpha1.PagerDutyConfig{
SendResolved: in.SendResolved,
RoutingKey: convertSecretKeySelectorTo(in.RoutingKey),
ServiceKey: convertSecretKeySelectorTo(in.ServiceKey),
URL: in.URL,
Client: in.Client,
ClientURL: in.ClientURL,
Description: in.Description,
Severity: in.Severity,
Class: in.Class,
Group: in.Group,
Component: in.Component,
Details: convertKeyValuesTo(in.Details),
PagerDutyImageConfigs: convertPagerDutyImageConfigsTo(in.PagerDutyImageConfigs),
PagerDutyLinkConfigs: convertPagerDutyLinkConfigsTo(in.PagerDutyLinkConfigs),
HTTPConfig: convertHTTPConfigTo(in.HTTPConfig),
}
}
func convertSlackFieldsTo(in []SlackField) []v1alpha1.SlackField {
out := make([]v1alpha1.SlackField, len(in))
for i := range in {
out[i] = v1alpha1.SlackField{
Title: in[i].Title,
Value: in[i].Value,
Short: in[i].Short,
}
}
return out
}
func convertSlackActionsTo(in []SlackAction) []v1alpha1.SlackAction {
out := make([]v1alpha1.SlackAction, len(in))
for i := range in {
out[i] = v1alpha1.SlackAction{
Type: in[i].Type,
Text: in[i].Text,
URL: in[i].URL,
Style: in[i].Style,
Name: in[i].Name,
Value: in[i].Value,
}
if in[i].ConfirmField != nil {
out[i].ConfirmField = &v1alpha1.SlackConfirmationField{
Text: in[i].ConfirmField.Text,
Title: in[i].ConfirmField.Title,
OkText: in[i].ConfirmField.OkText,
DismissText: in[i].ConfirmField.DismissText,
}
}
}
return out
}
func convertSlackConfigTo(in SlackConfig) v1alpha1.SlackConfig {
return v1alpha1.SlackConfig{
SendResolved: in.SendResolved,
APIURL: convertSecretKeySelectorTo(in.APIURL),
Channel: in.Channel,
Username: in.Username,
Color: in.Color,
Title: in.Title,
TitleLink: in.TitleLink,
Pretext: in.Pretext,
Text: in.Text,
Fields: convertSlackFieldsTo(in.Fields),
ShortFields: in.ShortFields,
Footer: in.Footer,
Fallback: in.Fallback,
CallbackID: in.CallbackID,
IconEmoji: in.IconEmoji,
IconURL: in.IconURL,
ImageURL: in.ImageURL,
ThumbURL: in.ThumbURL,
LinkNames: in.LinkNames,
MrkdwnIn: in.MrkdwnIn,
Actions: convertSlackActionsTo(in.Actions),
HTTPConfig: convertHTTPConfigTo(in.HTTPConfig),
}
}
func convertWebhookConfigTo(in WebhookConfig) v1alpha1.WebhookConfig {
return v1alpha1.WebhookConfig{
SendResolved: in.SendResolved,
URL: in.URL,
URLSecret: convertSecretKeySelectorTo(in.URLSecret),
HTTPConfig: convertHTTPConfigTo(in.HTTPConfig),
MaxAlerts: in.MaxAlerts,
}
}
func convertWeChatConfigTo(in WeChatConfig) v1alpha1.WeChatConfig {
return v1alpha1.WeChatConfig{
SendResolved: in.SendResolved,
APISecret: convertSecretKeySelectorTo(in.APISecret),
APIURL: in.APIURL,
CorpID: in.CorpID,
AgentID: in.AgentID,
ToUser: in.ToUser,
ToParty: in.ToParty,
ToTag: in.ToTag,
Message: in.Message,
MessageType: in.MessageType,
HTTPConfig: convertHTTPConfigTo(in.HTTPConfig),
}
}
func convertEmailConfigTo(in EmailConfig) v1alpha1.EmailConfig {
return v1alpha1.EmailConfig{
SendResolved: in.SendResolved,
To: in.To,
From: in.From,
Hello: in.Hello,
Smarthost: in.Smarthost,
AuthUsername: in.AuthUsername,
AuthPassword: convertSecretKeySelectorTo(in.AuthPassword),
AuthSecret: convertSecretKeySelectorTo(in.AuthSecret),
AuthIdentity: in.AuthIdentity,
Headers: convertKeyValuesTo(in.Headers),
HTML: in.HTML,
Text: in.Text,
RequireTLS: in.RequireTLS,
TLSConfig: in.TLSConfig,
}
}
func convertVictorOpsConfigTo(in VictorOpsConfig) v1alpha1.VictorOpsConfig {
return v1alpha1.VictorOpsConfig{
SendResolved: in.SendResolved,
APIKey: convertSecretKeySelectorTo(in.APIKey),
APIURL: in.APIURL,
RoutingKey: in.RoutingKey,
MessageType: in.MessageType,
EntityDisplayName: in.EntityDisplayName,
StateMessage: in.StateMessage,
MonitoringTool: in.MonitoringTool,
CustomFields: convertKeyValuesTo(in.CustomFields),
HTTPConfig: convertHTTPConfigTo(in.HTTPConfig),
}
}
func convertPushoverConfigTo(in PushoverConfig) v1alpha1.PushoverConfig {
return v1alpha1.PushoverConfig{
SendResolved: in.SendResolved,
UserKey: convertSecretKeySelectorTo(in.UserKey),
Token: convertSecretKeySelectorTo(in.Token),
Title: in.Title,
Message: in.Message,
URL: in.URL,
URLTitle: in.URLTitle,
Sound: in.Sound,
Priority: in.Priority,
Retry: in.Retry,
Expire: in.Expire,
HTML: in.HTML,
HTTPConfig: convertHTTPConfigTo(in.HTTPConfig),
}
}
func convertSNSConfigTo(in SNSConfig) v1alpha1.SNSConfig {
return v1alpha1.SNSConfig{
SendResolved: in.SendResolved,
ApiURL: in.ApiURL,
Sigv4: in.Sigv4,
TopicARN: in.TopicARN,
Subject: in.Subject,
PhoneNumber: in.PhoneNumber,
TargetARN: in.TargetARN,
Message: in.Message,
Attributes: in.Attributes,
HTTPConfig: convertHTTPConfigTo(in.HTTPConfig),
}
}
func convertTelegramConfigTo(in TelegramConfig) v1alpha1.TelegramConfig {
return v1alpha1.TelegramConfig{
SendResolved: in.SendResolved,
APIURL: in.APIURL,
BotToken: convertSecretKeySelectorTo(in.BotToken),
ChatID: in.ChatID,
Message: in.Message,
DisableNotifications: in.DisableNotifications,
ParseMode: in.ParseMode,
HTTPConfig: convertHTTPConfigTo(in.HTTPConfig),
}
}
// ConvertTo converts from this version (v1beta1) to the Hub version (v1alpha1).
func (src *AlertmanagerConfig) ConvertTo(dstRaw conversion.Hub) error {
dst := dstRaw.(*v1alpha1.AlertmanagerConfig)
dst.ObjectMeta = src.ObjectMeta
for _, in := range src.Spec.Receivers {
out := v1alpha1.Receiver{
Name: in.Name,
}
for _, in := range in.OpsGenieConfigs {
out.OpsGenieConfigs = append(
out.OpsGenieConfigs,
convertOpsGenieConfigTo(in),
)
}
for _, in := range in.PagerDutyConfigs {
out.PagerDutyConfigs = append(
out.PagerDutyConfigs,
convertPagerDutyConfigTo(in),
)
}
for _, in := range in.SlackConfigs {
out.SlackConfigs = append(
out.SlackConfigs,
convertSlackConfigTo(in),
)
}
for _, in := range in.WebhookConfigs {
out.WebhookConfigs = append(
out.WebhookConfigs,
convertWebhookConfigTo(in),
)
}
for _, in := range in.WeChatConfigs {
out.WeChatConfigs = append(
out.WeChatConfigs,
convertWeChatConfigTo(in),
)
}
for _, in := range in.EmailConfigs {
out.EmailConfigs = append(
out.EmailConfigs,
convertEmailConfigTo(in),
)
}
for _, in := range in.VictorOpsConfigs {
out.VictorOpsConfigs = append(
out.VictorOpsConfigs,
convertVictorOpsConfigTo(in),
)
}
for _, in := range in.PushoverConfigs {
out.PushoverConfigs = append(
out.PushoverConfigs,
convertPushoverConfigTo(in),
)
}
for _, in := range in.SNSConfigs {
out.SNSConfigs = append(
out.SNSConfigs,
convertSNSConfigTo(in),
)
}
for _, in := range in.TelegramConfigs {
out.TelegramConfigs = append(
out.TelegramConfigs,
convertTelegramConfigTo(in),
)
}
dst.Spec.Receivers = append(dst.Spec.Receivers, out)
}
for _, in := range src.Spec.InhibitRules {
dst.Spec.InhibitRules = append(
dst.Spec.InhibitRules,
v1alpha1.InhibitRule{
TargetMatch: convertMatchersTo(in.TargetMatch),
SourceMatch: convertMatchersTo(in.SourceMatch),
Equal: in.Equal,
},
)
}
for _, in := range src.Spec.TimeIntervals {
dst.Spec.MuteTimeIntervals = append(
dst.Spec.MuteTimeIntervals,
v1alpha1.MuteTimeInterval{
Name: in.Name,
TimeIntervals: convertTimeIntervalsTo(in.TimeIntervals),
},
)
}
r, err := convertRouteTo(src.Spec.Route)
if err != nil {
return err
}
dst.Spec.Route = r
return nil
}

View file

@ -0,0 +1,18 @@
// Copyright 2020 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +k8s:deepcopy-gen=package
// +groupName=monitoring.coreos.com
package v1beta1

View file

@ -0,0 +1,55 @@
// Copyright 2020 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package v1beta1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring"
)
// SchemeGroupVersion is the group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: monitoring.GroupName, Version: Version}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.
SchemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &SchemeBuilder
AddToScheme = localSchemeBuilder.AddToScheme
)
func init() {
// We only register manually written functions here. The registration of the
// generated functions takes place in the generated files. The separation
// makes the code compile even when the generated files are missing.
localSchemeBuilder.Register(addKnownTypes)
}
// Adds the list of known types to api.Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&AlertmanagerConfig{},
&AlertmanagerConfigList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}

View file

@ -0,0 +1,348 @@
// Copyright 2021 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package v1beta1
import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
)
func (hc *HTTPConfig) Validate() error {
if hc == nil {
return nil
}
if (hc.BasicAuth != nil || hc.OAuth2 != nil) && (hc.BearerTokenSecret != nil) {
return fmt.Errorf("at most one of basicAuth, oauth2, bearerTokenSecret must be configured")
}
if hc.Authorization != nil {
if hc.BearerTokenSecret != nil {
return fmt.Errorf("authorization is not compatible with bearerTokenSecret")
}
if hc.BasicAuth != nil || hc.OAuth2 != nil {
return fmt.Errorf("at most one of basicAuth, oauth2 & authorization must be configured")
}
if err := hc.Authorization.Validate(); err != nil {
return err
}
}
if hc.OAuth2 != nil {
if hc.BasicAuth != nil {
return fmt.Errorf("at most one of basicAuth, oauth2 & authorization must be configured")
}
if err := hc.OAuth2.Validate(); err != nil {
return err
}
}
if hc.TLSConfig != nil {
if err := hc.TLSConfig.Validate(); err != nil {
return err
}
}
return nil
}
// Validate the TimeInterval
func (ti TimeInterval) Validate() error {
if ti.Name == "" {
return errors.New("empty name field for time interval")
}
for i, ti := range ti.TimeIntervals {
for _, time := range ti.Times {
if err := time.Validate(); err != nil {
return fmt.Errorf("time range at %d is invalid: %w", i, err)
}
}
for _, weekday := range ti.Weekdays {
if err := weekday.Validate(); err != nil {
return fmt.Errorf("weekday range at %d is invalid: %w", i, err)
}
}
for _, dom := range ti.DaysOfMonth {
if err := dom.Validate(); err != nil {
return fmt.Errorf("day of month range at %d is invalid: %w", i, err)
}
}
for _, month := range ti.Months {
if err := month.Validate(); err != nil {
return fmt.Errorf("month range at %d is invalid: %w", i, err)
}
}
for _, year := range ti.Years {
if err := year.Validate(); err != nil {
return fmt.Errorf("year range at %d is invalid: %w", i, err)
}
}
}
return nil
}
// Validate the TimeRange
func (tr TimeRange) Validate() error {
_, err := tr.Parse()
return err
}
// Parse returns a ParsedRange on valid input or an error if the fields cannot be parsed
// End of the day is represented as 1440.
func (tr TimeRange) Parse() (*ParsedRange, error) {
if tr.StartTime == "" || tr.EndTime == "" {
return nil, fmt.Errorf("start and end are required")
}
start, err := parseTime(string(tr.StartTime))
if err != nil {
return nil, fmt.Errorf("start time invalid: %w", err)
}
end, err := parseTime(string(tr.EndTime))
if err != nil {
return nil, fmt.Errorf("end time invalid: %w", err)
}
if start >= end {
return nil, fmt.Errorf("start time %d cannot be equal or greater than end time %d", start, end)
}
return &ParsedRange{
Start: start,
End: end,
}, nil
}
// Validate the WeekdayRange
func (wr WeekdayRange) Validate() error {
_, err := wr.Parse()
return err
}
// Parse returns a ParsedRange on valid input or an error if the fields cannot be parsed
// The week starts on Sunday -> 0
func (wr WeekdayRange) Parse() (*ParsedRange, error) {
startStr, endStr, err := parseRange(string(wr))
if err != nil {
return nil, err
}
start, err := Weekday(startStr).Int()
if err != nil {
return nil, fmt.Errorf("failed to parse start day from weekday range: %w", err)
}
end, err := Weekday(endStr).Int()
if err != nil {
return nil, fmt.Errorf("failed to parse end day from weekday range: %w", err)
}
if start > end {
return nil, errors.New("start day cannot be before end day")
}
if start < 0 || start > 6 {
return nil, fmt.Errorf("%s is not a valid day of the week: out of range", startStr)
}
if end < 0 || end > 6 {
return nil, fmt.Errorf("%s is not a valid day of the week: out of range", endStr)
}
return &ParsedRange{Start: start, End: end}, nil
}
// Validate the YearRange
func (yr YearRange) Validate() error {
_, err := yr.Parse()
return err
}
// Parse returns a ParsedRange on valid input or an error if the fields cannot be parsed
func (yr YearRange) Parse() (*ParsedRange, error) {
startStr, endStr, err := parseRange(string(yr))
if err != nil {
return nil, err
}
start, err := strconv.Atoi(startStr)
if err != nil {
fmt.Errorf("start year cannot be %s parsed: %w", startStr, err)
}
end, err := strconv.Atoi(endStr)
if err != nil {
fmt.Errorf("end year cannot be %s parsed: %w", endStr, err)
}
if start > end {
return nil, fmt.Errorf("end year %d is before start year %d", end, start)
}
return &ParsedRange{Start: start, End: end}, nil
}
// Int returns an integer, which is the canonical representation
// of the Weekday in upstream types.
// Returns an error if the Weekday is invalid
func (w Weekday) Int() (int, error) {
normaliseWeekday := Weekday(strings.ToLower(string(w)))
day, found := daysOfWeek[normaliseWeekday]
if !found {
i, err := strconv.Atoi(string(normaliseWeekday))
if err != nil {
return day, fmt.Errorf("%s is an invalid weekday", w)
}
day = i
}
return day, nil
}
// Int validates the Month and returns an integer, which is the canonical representation
// of the Month in upstream types.
// Returns an error if the Month is invalid
func (m Month) Int() (int, error) {
normaliseMonth := Month(strings.ToLower(string(m)))
day, found := months[normaliseMonth]
if !found {
i, err := strconv.Atoi(string(normaliseMonth))
if err != nil {
return day, fmt.Errorf("%s is an invalid month", m)
}
day = i
}
return day, nil
}
// Validate the DayOfMonthRange
func (r DayOfMonthRange) Validate() error {
// Note: Validation is copied from UnmarshalYAML for DayOfMonthRange in alertmanager repo
// Check beginning <= end accounting for negatives day of month indices as well.
// Months != 31 days can't be addressed here and are clamped, but at least we can catch blatant errors.
if r.Start == 0 || r.Start < -31 || r.Start > 31 {
return fmt.Errorf("%d is not a valid day of the month: out of range", r.Start)
}
if r.End == 0 || r.End < -31 || r.End > 31 {
return fmt.Errorf("%d is not a valid day of the month: out of range", r.End)
}
// Restricting here prevents errors where begin > end in longer months but not shorter months.
if r.Start < 0 && r.End > 0 {
return fmt.Errorf("end day must be negative if start day is negative")
}
// Check begin <= end. We can't know this for sure when using negative indices,
// but we can prevent cases where its always invalid (using 28 day minimum length).
checkBegin := r.Start
checkEnd := r.End
if r.Start < 0 {
checkBegin = 28 + r.Start
}
if r.End < 0 {
checkEnd = 28 + r.End
}
if checkBegin > checkEnd {
return fmt.Errorf("end day %d is always before start day %d", r.End, r.Start)
}
return nil
}
// Validate the month range
func (mr MonthRange) Validate() error {
_, err := mr.Parse()
return err
}
// Parse returns a ParsedMonthRange or error on invalid input
func (mr MonthRange) Parse() (*ParsedRange, error) {
startStr, endStr, err := parseRange(string(mr))
if err != nil {
return nil, err
}
start, err := Month(startStr).Int()
if err != nil {
return nil, fmt.Errorf("failed to parse start month from month range: %w", err)
}
end, err := Month(endStr).Int()
if err != nil {
return nil, fmt.Errorf("failed to parse start month from month range: %w", err)
}
if start > end {
return nil, fmt.Errorf("end month %s is before start month %s", endStr, startStr)
}
return &ParsedRange{
Start: start,
End: end,
}, nil
}
// ParsedRange is an integer representation of a range
// +kubebuilder:object:generate:=false
type ParsedRange struct {
// Start is the beginning of the range
Start int `json:"start,omitempty"`
// End of the range
End int `json:"end,omitempty"`
}
var validTime = "^((([01][0-9])|(2[0-3])):[0-5][0-9])$|(^24:00$)"
var validTimeRE = regexp.MustCompile(validTime)
// Converts a string of the form "HH:MM" into the number of minutes elapsed in the day.
func parseTime(in string) (mins int, err error) {
if !validTimeRE.MatchString(in) {
return 0, fmt.Errorf("couldn't parse timestamp %s, invalid format", in)
}
timestampComponents := strings.Split(in, ":")
if len(timestampComponents) != 2 {
return 0, fmt.Errorf("invalid timestamp format: %s", in)
}
timeStampHours, err := strconv.Atoi(timestampComponents[0])
if err != nil {
return 0, err
}
timeStampMinutes, err := strconv.Atoi(timestampComponents[1])
if err != nil {
return 0, err
}
if timeStampHours < 0 || timeStampHours > 24 || timeStampMinutes < 0 || timeStampMinutes > 60 {
return 0, fmt.Errorf("timestamp %s out of range", in)
}
// Timestamps are stored as minutes elapsed in the day, so multiply hours by 60.
mins = timeStampHours*60 + timeStampMinutes
return mins, nil
}
// parseRange parses a valid range string into parts
func parseRange(in string) (start, end string, err error) {
if !strings.ContainsRune(in, ':') {
return in, in, nil
}
parts := strings.Split(string(in), ":")
if len(parts) != 2 {
return start, end, fmt.Errorf("invalid range provided %s", in)
}
return parts[0], parts[1], nil
}

View file

@ -0,0 +1,345 @@
// Copyright 2021 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package v1beta1
import (
"reflect"
"testing"
)
func TestTimeRange_Parse(t *testing.T) {
testCases := []struct {
name string
in TimeRange
expectErr bool
expectResult *ParsedRange
}{
{
name: "Test invalid time string produces error",
in: TimeRange{
StartTime: "16:00",
EndTime: "25:00",
},
expectErr: true,
},
{
name: "Test invalid negative string produces error",
in: TimeRange{
StartTime: "-16:00",
EndTime: "24:00",
},
expectErr: true,
},
{
name: "Test end time earlier than start time is invalid",
in: TimeRange{
StartTime: "16:00",
EndTime: "14:00",
},
expectErr: true,
},
{
name: "Test happy path",
in: TimeRange{
StartTime: "12:00",
EndTime: "24:00",
},
expectResult: &ParsedRange{
Start: 720,
End: 1440,
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got, err := tc.in.Parse()
if tc.expectErr {
if err == nil {
t.Fatal("expected err but got none")
}
return
}
if err != nil {
t.Fatalf("expected no error but got %v", err)
}
if !reflect.DeepEqual(got, tc.expectResult) {
t.Fatalf("wanted %v, but got %v", tc.expectResult, got)
}
})
}
}
func TestMonthRange_Parse(t *testing.T) {
testCases := []struct {
name string
in MonthRange
expectErr bool
expectResult *ParsedRange
}{
{
name: "Test invalid range - more than two months returns an error",
in: MonthRange("january:march:december"),
expectErr: true,
},
{
name: "Test invalid months returns error",
in: MonthRange("januarE"),
expectErr: true,
},
{
name: "Test invalid months in range returns error",
in: MonthRange("january:Merch"),
expectErr: true,
},
{
name: "Test invalid range - end before start returns error",
in: MonthRange("march:january"),
expectErr: true,
},
{
name: "Test happy path",
in: MonthRange("january"),
expectResult: &ParsedRange{
Start: 1,
End: 1,
},
},
{
name: "Test happy path range",
in: MonthRange("january:march"),
expectResult: &ParsedRange{
Start: 1,
End: 3,
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got, err := tc.in.Parse()
if tc.expectErr {
if err == nil {
t.Fatal("expected err but got none")
}
return
}
if err != nil {
t.Fatalf("expected no error but got %v", err)
}
if !reflect.DeepEqual(got, tc.expectResult) {
t.Fatalf("wanted %v, but got %v", tc.expectResult, got)
}
})
}
}
func TestWeekdayRange_Parse(t *testing.T) {
testCases := []struct {
name string
in WeekdayRange
expectErr bool
expectResult *ParsedRange
}{
{
name: "Test invalid range - more than two days returns an error",
in: WeekdayRange("monday:wednesday:friday"),
expectErr: true,
},
{
name: "Test invalid day returns error",
in: WeekdayRange("onday"),
expectErr: true,
},
{
name: "Test invalid days in range returns error",
in: WeekdayRange("monday:friyay"),
expectErr: true,
},
{
name: "Test invalid range - end before start returns error",
in: WeekdayRange("friday:monday"),
expectErr: true,
},
{
name: "Test happy path",
in: WeekdayRange("monday"),
expectResult: &ParsedRange{
Start: 1,
End: 1,
},
},
{
name: "Test happy path range",
in: WeekdayRange("monday:wednesday"),
expectResult: &ParsedRange{
Start: 1,
End: 3,
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got, err := tc.in.Parse()
if tc.expectErr {
if err == nil {
t.Fatal("expected err but got none")
}
return
}
if err != nil {
t.Fatalf("expected no error but got %v", err)
}
if !reflect.DeepEqual(got, tc.expectResult) {
t.Fatalf("wanted %v, but got %v", tc.expectResult, got)
}
})
}
}
func TestDayOfMonthRange_Validate(t *testing.T) {
testCases := []struct {
name string
in DayOfMonthRange
expectErr bool
}{
{
name: "Test zero value returns error",
in: DayOfMonthRange{
Start: 0,
End: 0,
},
expectErr: true,
},
{
name: "Test out of range returns error",
in: DayOfMonthRange{
Start: -50,
End: -20,
},
expectErr: true,
},
{
name: "Test out of range returns error",
in: DayOfMonthRange{
Start: 20,
End: 50,
},
expectErr: true,
},
{
name: "Test invalid input - negative start day with positive end day",
in: DayOfMonthRange{
Start: -20,
End: 5,
},
expectErr: true,
},
{
name: "Test invalid range - end before start returns error",
in: DayOfMonthRange{
Start: 10,
End: -25,
},
expectErr: true,
},
{
name: "Test happy path",
in: DayOfMonthRange{
Start: 1,
End: 31,
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := tc.in.Validate()
if tc.expectErr {
if err == nil {
t.Fatal("expected err but got none")
}
return
}
if err != nil {
t.Fatalf("expected no error but got %v", err)
}
})
}
}
func TestYearRange_Parse(t *testing.T) {
testCases := []struct {
name string
in YearRange
expectErr bool
expectResult *ParsedRange
}{
{
name: "Test invalid range - more than two years returns an error",
in: YearRange("2019:2029:2039"),
expectErr: true,
},
{
name: "Test invalid range - end before start returns error",
in: YearRange("2020:2010"),
expectErr: true,
},
{
name: "Test happy path",
in: YearRange("2030"),
expectResult: &ParsedRange{
Start: 2030,
End: 2030,
},
},
{
name: "Test happy path range",
in: YearRange("2030:2050"),
expectResult: &ParsedRange{
Start: 2030,
End: 2050,
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got, err := tc.in.Parse()
if tc.expectErr {
if err == nil {
t.Fatal("expected err but got none")
}
return
}
if err != nil {
t.Fatalf("expected no error but got %v", err)
}
if !reflect.DeepEqual(got, tc.expectResult) {
t.Fatalf("wanted %v, but got %v", tc.expectResult, got)
}
})
}
}

View file

@ -0,0 +1,937 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
// Copyright The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by controller-gen. DO NOT EDIT.
package v1beta1
import (
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AlertmanagerConfig) DeepCopyInto(out *AlertmanagerConfig) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AlertmanagerConfig.
func (in *AlertmanagerConfig) DeepCopy() *AlertmanagerConfig {
if in == nil {
return nil
}
out := new(AlertmanagerConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AlertmanagerConfigList) DeepCopyInto(out *AlertmanagerConfigList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]*AlertmanagerConfig, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(AlertmanagerConfig)
(*in).DeepCopyInto(*out)
}
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AlertmanagerConfigList.
func (in *AlertmanagerConfigList) DeepCopy() *AlertmanagerConfigList {
if in == nil {
return nil
}
out := new(AlertmanagerConfigList)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AlertmanagerConfigSpec) DeepCopyInto(out *AlertmanagerConfigSpec) {
*out = *in
if in.Route != nil {
in, out := &in.Route, &out.Route
*out = new(Route)
(*in).DeepCopyInto(*out)
}
if in.Receivers != nil {
in, out := &in.Receivers, &out.Receivers
*out = make([]Receiver, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.InhibitRules != nil {
in, out := &in.InhibitRules, &out.InhibitRules
*out = make([]InhibitRule, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.TimeIntervals != nil {
in, out := &in.TimeIntervals, &out.TimeIntervals
*out = make([]TimeInterval, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AlertmanagerConfigSpec.
func (in *AlertmanagerConfigSpec) DeepCopy() *AlertmanagerConfigSpec {
if in == nil {
return nil
}
out := new(AlertmanagerConfigSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DayOfMonthRange) DeepCopyInto(out *DayOfMonthRange) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DayOfMonthRange.
func (in *DayOfMonthRange) DeepCopy() *DayOfMonthRange {
if in == nil {
return nil
}
out := new(DayOfMonthRange)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EmailConfig) DeepCopyInto(out *EmailConfig) {
*out = *in
if in.SendResolved != nil {
in, out := &in.SendResolved, &out.SendResolved
*out = new(bool)
**out = **in
}
if in.AuthPassword != nil {
in, out := &in.AuthPassword, &out.AuthPassword
*out = new(SecretKeySelector)
**out = **in
}
if in.AuthSecret != nil {
in, out := &in.AuthSecret, &out.AuthSecret
*out = new(SecretKeySelector)
**out = **in
}
if in.Headers != nil {
in, out := &in.Headers, &out.Headers
*out = make([]KeyValue, len(*in))
copy(*out, *in)
}
if in.RequireTLS != nil {
in, out := &in.RequireTLS, &out.RequireTLS
*out = new(bool)
**out = **in
}
if in.TLSConfig != nil {
in, out := &in.TLSConfig, &out.TLSConfig
*out = new(monitoringv1.SafeTLSConfig)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EmailConfig.
func (in *EmailConfig) DeepCopy() *EmailConfig {
if in == nil {
return nil
}
out := new(EmailConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HTTPConfig) DeepCopyInto(out *HTTPConfig) {
*out = *in
if in.Authorization != nil {
in, out := &in.Authorization, &out.Authorization
*out = new(monitoringv1.SafeAuthorization)
(*in).DeepCopyInto(*out)
}
if in.BasicAuth != nil {
in, out := &in.BasicAuth, &out.BasicAuth
*out = new(monitoringv1.BasicAuth)
(*in).DeepCopyInto(*out)
}
if in.OAuth2 != nil {
in, out := &in.OAuth2, &out.OAuth2
*out = new(monitoringv1.OAuth2)
(*in).DeepCopyInto(*out)
}
if in.BearerTokenSecret != nil {
in, out := &in.BearerTokenSecret, &out.BearerTokenSecret
*out = new(SecretKeySelector)
**out = **in
}
if in.TLSConfig != nil {
in, out := &in.TLSConfig, &out.TLSConfig
*out = new(monitoringv1.SafeTLSConfig)
(*in).DeepCopyInto(*out)
}
if in.FollowRedirects != nil {
in, out := &in.FollowRedirects, &out.FollowRedirects
*out = new(bool)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPConfig.
func (in *HTTPConfig) DeepCopy() *HTTPConfig {
if in == nil {
return nil
}
out := new(HTTPConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *InhibitRule) DeepCopyInto(out *InhibitRule) {
*out = *in
if in.TargetMatch != nil {
in, out := &in.TargetMatch, &out.TargetMatch
*out = make([]Matcher, len(*in))
copy(*out, *in)
}
if in.SourceMatch != nil {
in, out := &in.SourceMatch, &out.SourceMatch
*out = make([]Matcher, len(*in))
copy(*out, *in)
}
if in.Equal != nil {
in, out := &in.Equal, &out.Equal
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InhibitRule.
func (in *InhibitRule) DeepCopy() *InhibitRule {
if in == nil {
return nil
}
out := new(InhibitRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KeyValue) DeepCopyInto(out *KeyValue) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeyValue.
func (in *KeyValue) DeepCopy() *KeyValue {
if in == nil {
return nil
}
out := new(KeyValue)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Matcher) DeepCopyInto(out *Matcher) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Matcher.
func (in *Matcher) DeepCopy() *Matcher {
if in == nil {
return nil
}
out := new(Matcher)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OpsGenieConfig) DeepCopyInto(out *OpsGenieConfig) {
*out = *in
if in.SendResolved != nil {
in, out := &in.SendResolved, &out.SendResolved
*out = new(bool)
**out = **in
}
if in.APIKey != nil {
in, out := &in.APIKey, &out.APIKey
*out = new(SecretKeySelector)
**out = **in
}
if in.Details != nil {
in, out := &in.Details, &out.Details
*out = make([]KeyValue, len(*in))
copy(*out, *in)
}
if in.Responders != nil {
in, out := &in.Responders, &out.Responders
*out = make([]OpsGenieConfigResponder, len(*in))
copy(*out, *in)
}
if in.HTTPConfig != nil {
in, out := &in.HTTPConfig, &out.HTTPConfig
*out = new(HTTPConfig)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpsGenieConfig.
func (in *OpsGenieConfig) DeepCopy() *OpsGenieConfig {
if in == nil {
return nil
}
out := new(OpsGenieConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OpsGenieConfigResponder) DeepCopyInto(out *OpsGenieConfigResponder) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpsGenieConfigResponder.
func (in *OpsGenieConfigResponder) DeepCopy() *OpsGenieConfigResponder {
if in == nil {
return nil
}
out := new(OpsGenieConfigResponder)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PagerDutyConfig) DeepCopyInto(out *PagerDutyConfig) {
*out = *in
if in.SendResolved != nil {
in, out := &in.SendResolved, &out.SendResolved
*out = new(bool)
**out = **in
}
if in.RoutingKey != nil {
in, out := &in.RoutingKey, &out.RoutingKey
*out = new(SecretKeySelector)
**out = **in
}
if in.ServiceKey != nil {
in, out := &in.ServiceKey, &out.ServiceKey
*out = new(SecretKeySelector)
**out = **in
}
if in.Details != nil {
in, out := &in.Details, &out.Details
*out = make([]KeyValue, len(*in))
copy(*out, *in)
}
if in.PagerDutyImageConfigs != nil {
in, out := &in.PagerDutyImageConfigs, &out.PagerDutyImageConfigs
*out = make([]PagerDutyImageConfig, len(*in))
copy(*out, *in)
}
if in.PagerDutyLinkConfigs != nil {
in, out := &in.PagerDutyLinkConfigs, &out.PagerDutyLinkConfigs
*out = make([]PagerDutyLinkConfig, len(*in))
copy(*out, *in)
}
if in.HTTPConfig != nil {
in, out := &in.HTTPConfig, &out.HTTPConfig
*out = new(HTTPConfig)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PagerDutyConfig.
func (in *PagerDutyConfig) DeepCopy() *PagerDutyConfig {
if in == nil {
return nil
}
out := new(PagerDutyConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PagerDutyImageConfig) DeepCopyInto(out *PagerDutyImageConfig) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PagerDutyImageConfig.
func (in *PagerDutyImageConfig) DeepCopy() *PagerDutyImageConfig {
if in == nil {
return nil
}
out := new(PagerDutyImageConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PagerDutyLinkConfig) DeepCopyInto(out *PagerDutyLinkConfig) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PagerDutyLinkConfig.
func (in *PagerDutyLinkConfig) DeepCopy() *PagerDutyLinkConfig {
if in == nil {
return nil
}
out := new(PagerDutyLinkConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PushoverConfig) DeepCopyInto(out *PushoverConfig) {
*out = *in
if in.SendResolved != nil {
in, out := &in.SendResolved, &out.SendResolved
*out = new(bool)
**out = **in
}
if in.UserKey != nil {
in, out := &in.UserKey, &out.UserKey
*out = new(SecretKeySelector)
**out = **in
}
if in.Token != nil {
in, out := &in.Token, &out.Token
*out = new(SecretKeySelector)
**out = **in
}
if in.HTTPConfig != nil {
in, out := &in.HTTPConfig, &out.HTTPConfig
*out = new(HTTPConfig)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushoverConfig.
func (in *PushoverConfig) DeepCopy() *PushoverConfig {
if in == nil {
return nil
}
out := new(PushoverConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Receiver) DeepCopyInto(out *Receiver) {
*out = *in
if in.OpsGenieConfigs != nil {
in, out := &in.OpsGenieConfigs, &out.OpsGenieConfigs
*out = make([]OpsGenieConfig, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.PagerDutyConfigs != nil {
in, out := &in.PagerDutyConfigs, &out.PagerDutyConfigs
*out = make([]PagerDutyConfig, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.SlackConfigs != nil {
in, out := &in.SlackConfigs, &out.SlackConfigs
*out = make([]SlackConfig, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.WebhookConfigs != nil {
in, out := &in.WebhookConfigs, &out.WebhookConfigs
*out = make([]WebhookConfig, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.WeChatConfigs != nil {
in, out := &in.WeChatConfigs, &out.WeChatConfigs
*out = make([]WeChatConfig, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.EmailConfigs != nil {
in, out := &in.EmailConfigs, &out.EmailConfigs
*out = make([]EmailConfig, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.VictorOpsConfigs != nil {
in, out := &in.VictorOpsConfigs, &out.VictorOpsConfigs
*out = make([]VictorOpsConfig, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.PushoverConfigs != nil {
in, out := &in.PushoverConfigs, &out.PushoverConfigs
*out = make([]PushoverConfig, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.SNSConfigs != nil {
in, out := &in.SNSConfigs, &out.SNSConfigs
*out = make([]SNSConfig, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.TelegramConfigs != nil {
in, out := &in.TelegramConfigs, &out.TelegramConfigs
*out = make([]TelegramConfig, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Receiver.
func (in *Receiver) DeepCopy() *Receiver {
if in == nil {
return nil
}
out := new(Receiver)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Route) DeepCopyInto(out *Route) {
*out = *in
if in.GroupBy != nil {
in, out := &in.GroupBy, &out.GroupBy
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Matchers != nil {
in, out := &in.Matchers, &out.Matchers
*out = make([]Matcher, len(*in))
copy(*out, *in)
}
if in.Routes != nil {
in, out := &in.Routes, &out.Routes
*out = make([]v1.JSON, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.MuteTimeIntervals != nil {
in, out := &in.MuteTimeIntervals, &out.MuteTimeIntervals
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Route.
func (in *Route) DeepCopy() *Route {
if in == nil {
return nil
}
out := new(Route)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SNSConfig) DeepCopyInto(out *SNSConfig) {
*out = *in
if in.SendResolved != nil {
in, out := &in.SendResolved, &out.SendResolved
*out = new(bool)
**out = **in
}
if in.Sigv4 != nil {
in, out := &in.Sigv4, &out.Sigv4
*out = new(monitoringv1.Sigv4)
(*in).DeepCopyInto(*out)
}
if in.Attributes != nil {
in, out := &in.Attributes, &out.Attributes
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.HTTPConfig != nil {
in, out := &in.HTTPConfig, &out.HTTPConfig
*out = new(HTTPConfig)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SNSConfig.
func (in *SNSConfig) DeepCopy() *SNSConfig {
if in == nil {
return nil
}
out := new(SNSConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SecretKeySelector) DeepCopyInto(out *SecretKeySelector) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretKeySelector.
func (in *SecretKeySelector) DeepCopy() *SecretKeySelector {
if in == nil {
return nil
}
out := new(SecretKeySelector)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SlackAction) DeepCopyInto(out *SlackAction) {
*out = *in
if in.ConfirmField != nil {
in, out := &in.ConfirmField, &out.ConfirmField
*out = new(SlackConfirmationField)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SlackAction.
func (in *SlackAction) DeepCopy() *SlackAction {
if in == nil {
return nil
}
out := new(SlackAction)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SlackConfig) DeepCopyInto(out *SlackConfig) {
*out = *in
if in.SendResolved != nil {
in, out := &in.SendResolved, &out.SendResolved
*out = new(bool)
**out = **in
}
if in.APIURL != nil {
in, out := &in.APIURL, &out.APIURL
*out = new(SecretKeySelector)
**out = **in
}
if in.Fields != nil {
in, out := &in.Fields, &out.Fields
*out = make([]SlackField, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.MrkdwnIn != nil {
in, out := &in.MrkdwnIn, &out.MrkdwnIn
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Actions != nil {
in, out := &in.Actions, &out.Actions
*out = make([]SlackAction, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.HTTPConfig != nil {
in, out := &in.HTTPConfig, &out.HTTPConfig
*out = new(HTTPConfig)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SlackConfig.
func (in *SlackConfig) DeepCopy() *SlackConfig {
if in == nil {
return nil
}
out := new(SlackConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SlackConfirmationField) DeepCopyInto(out *SlackConfirmationField) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SlackConfirmationField.
func (in *SlackConfirmationField) DeepCopy() *SlackConfirmationField {
if in == nil {
return nil
}
out := new(SlackConfirmationField)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SlackField) DeepCopyInto(out *SlackField) {
*out = *in
if in.Short != nil {
in, out := &in.Short, &out.Short
*out = new(bool)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SlackField.
func (in *SlackField) DeepCopy() *SlackField {
if in == nil {
return nil
}
out := new(SlackField)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TelegramConfig) DeepCopyInto(out *TelegramConfig) {
*out = *in
if in.SendResolved != nil {
in, out := &in.SendResolved, &out.SendResolved
*out = new(bool)
**out = **in
}
if in.BotToken != nil {
in, out := &in.BotToken, &out.BotToken
*out = new(SecretKeySelector)
**out = **in
}
if in.DisableNotifications != nil {
in, out := &in.DisableNotifications, &out.DisableNotifications
*out = new(bool)
**out = **in
}
if in.HTTPConfig != nil {
in, out := &in.HTTPConfig, &out.HTTPConfig
*out = new(HTTPConfig)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TelegramConfig.
func (in *TelegramConfig) DeepCopy() *TelegramConfig {
if in == nil {
return nil
}
out := new(TelegramConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TimeInterval) DeepCopyInto(out *TimeInterval) {
*out = *in
if in.TimeIntervals != nil {
in, out := &in.TimeIntervals, &out.TimeIntervals
*out = make([]TimePeriod, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TimeInterval.
func (in *TimeInterval) DeepCopy() *TimeInterval {
if in == nil {
return nil
}
out := new(TimeInterval)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TimePeriod) DeepCopyInto(out *TimePeriod) {
*out = *in
if in.Times != nil {
in, out := &in.Times, &out.Times
*out = make([]TimeRange, len(*in))
copy(*out, *in)
}
if in.Weekdays != nil {
in, out := &in.Weekdays, &out.Weekdays
*out = make([]WeekdayRange, len(*in))
copy(*out, *in)
}
if in.DaysOfMonth != nil {
in, out := &in.DaysOfMonth, &out.DaysOfMonth
*out = make([]DayOfMonthRange, len(*in))
copy(*out, *in)
}
if in.Months != nil {
in, out := &in.Months, &out.Months
*out = make([]MonthRange, len(*in))
copy(*out, *in)
}
if in.Years != nil {
in, out := &in.Years, &out.Years
*out = make([]YearRange, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TimePeriod.
func (in *TimePeriod) DeepCopy() *TimePeriod {
if in == nil {
return nil
}
out := new(TimePeriod)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TimeRange) DeepCopyInto(out *TimeRange) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TimeRange.
func (in *TimeRange) DeepCopy() *TimeRange {
if in == nil {
return nil
}
out := new(TimeRange)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VictorOpsConfig) DeepCopyInto(out *VictorOpsConfig) {
*out = *in
if in.SendResolved != nil {
in, out := &in.SendResolved, &out.SendResolved
*out = new(bool)
**out = **in
}
if in.APIKey != nil {
in, out := &in.APIKey, &out.APIKey
*out = new(SecretKeySelector)
**out = **in
}
if in.CustomFields != nil {
in, out := &in.CustomFields, &out.CustomFields
*out = make([]KeyValue, len(*in))
copy(*out, *in)
}
if in.HTTPConfig != nil {
in, out := &in.HTTPConfig, &out.HTTPConfig
*out = new(HTTPConfig)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VictorOpsConfig.
func (in *VictorOpsConfig) DeepCopy() *VictorOpsConfig {
if in == nil {
return nil
}
out := new(VictorOpsConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WeChatConfig) DeepCopyInto(out *WeChatConfig) {
*out = *in
if in.SendResolved != nil {
in, out := &in.SendResolved, &out.SendResolved
*out = new(bool)
**out = **in
}
if in.APISecret != nil {
in, out := &in.APISecret, &out.APISecret
*out = new(SecretKeySelector)
**out = **in
}
if in.HTTPConfig != nil {
in, out := &in.HTTPConfig, &out.HTTPConfig
*out = new(HTTPConfig)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WeChatConfig.
func (in *WeChatConfig) DeepCopy() *WeChatConfig {
if in == nil {
return nil
}
out := new(WeChatConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WebhookConfig) DeepCopyInto(out *WebhookConfig) {
*out = *in
if in.SendResolved != nil {
in, out := &in.SendResolved, &out.SendResolved
*out = new(bool)
**out = **in
}
if in.URL != nil {
in, out := &in.URL, &out.URL
*out = new(string)
**out = **in
}
if in.URLSecret != nil {
in, out := &in.URLSecret, &out.URLSecret
*out = new(SecretKeySelector)
**out = **in
}
if in.HTTPConfig != nil {
in, out := &in.HTTPConfig, &out.HTTPConfig
*out = new(HTTPConfig)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookConfig.
func (in *WebhookConfig) DeepCopy() *WebhookConfig {
if in == nil {
return nil
}
out := new(WebhookConfig)
in.DeepCopyInto(out)
return out
}

View file

@ -21,6 +21,7 @@ import (
v1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
v1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1"
v1beta1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1beta1"
schema "k8s.io/apimachinery/pkg/runtime/schema"
cache "k8s.io/client-go/tools/cache"
)
@ -71,6 +72,10 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
case v1alpha1.SchemeGroupVersion.WithResource("alertmanagerconfigs"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Monitoring().V1alpha1().AlertmanagerConfigs().Informer()}, nil
// Group=monitoring.coreos.com, Version=v1beta1
case v1beta1.SchemeGroupVersion.WithResource("alertmanagerconfigs"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Monitoring().V1beta1().AlertmanagerConfigs().Informer()}, nil
}
return nil, fmt.Errorf("no informer found for %v", resource)

View file

@ -20,6 +20,7 @@ import (
internalinterfaces "github.com/prometheus-operator/prometheus-operator/pkg/client/informers/externalversions/internalinterfaces"
v1 "github.com/prometheus-operator/prometheus-operator/pkg/client/informers/externalversions/monitoring/v1"
v1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/client/informers/externalversions/monitoring/v1alpha1"
v1beta1 "github.com/prometheus-operator/prometheus-operator/pkg/client/informers/externalversions/monitoring/v1beta1"
)
// Interface provides access to each of this group's versions.
@ -28,6 +29,8 @@ type Interface interface {
V1() v1.Interface
// V1alpha1 provides access to shared informers for resources in V1alpha1.
V1alpha1() v1alpha1.Interface
// V1beta1 provides access to shared informers for resources in V1beta1.
V1beta1() v1beta1.Interface
}
type group struct {
@ -50,3 +53,8 @@ func (g *group) V1() v1.Interface {
func (g *group) V1alpha1() v1alpha1.Interface {
return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions)
}
// V1beta1 returns a new v1beta1.Interface.
func (g *group) V1beta1() v1beta1.Interface {
return v1beta1.New(g.factory, g.namespace, g.tweakListOptions)
}

View file

@ -0,0 +1,88 @@
// Copyright The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by informer-gen. DO NOT EDIT.
package v1beta1
import (
"context"
time "time"
monitoringv1beta1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1beta1"
internalinterfaces "github.com/prometheus-operator/prometheus-operator/pkg/client/informers/externalversions/internalinterfaces"
v1beta1 "github.com/prometheus-operator/prometheus-operator/pkg/client/listers/monitoring/v1beta1"
versioned "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
watch "k8s.io/apimachinery/pkg/watch"
cache "k8s.io/client-go/tools/cache"
)
// AlertmanagerConfigInformer provides access to a shared informer and lister for
// AlertmanagerConfigs.
type AlertmanagerConfigInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1beta1.AlertmanagerConfigLister
}
type alertmanagerConfigInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
// NewAlertmanagerConfigInformer constructs a new informer for AlertmanagerConfig type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewAlertmanagerConfigInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredAlertmanagerConfigInformer(client, namespace, resyncPeriod, indexers, nil)
}
// NewFilteredAlertmanagerConfigInformer constructs a new informer for AlertmanagerConfig type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredAlertmanagerConfigInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.MonitoringV1beta1().AlertmanagerConfigs(namespace).List(context.TODO(), options)
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.MonitoringV1beta1().AlertmanagerConfigs(namespace).Watch(context.TODO(), options)
},
},
&monitoringv1beta1.AlertmanagerConfig{},
resyncPeriod,
indexers,
)
}
func (f *alertmanagerConfigInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredAlertmanagerConfigInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *alertmanagerConfigInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&monitoringv1beta1.AlertmanagerConfig{}, f.defaultInformer)
}
func (f *alertmanagerConfigInformer) Lister() v1beta1.AlertmanagerConfigLister {
return v1beta1.NewAlertmanagerConfigLister(f.Informer().GetIndexer())
}

View file

@ -0,0 +1,43 @@
// Copyright The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by informer-gen. DO NOT EDIT.
package v1beta1
import (
internalinterfaces "github.com/prometheus-operator/prometheus-operator/pkg/client/informers/externalversions/internalinterfaces"
)
// Interface provides access to all the informers in this group version.
type Interface interface {
// AlertmanagerConfigs returns a AlertmanagerConfigInformer.
AlertmanagerConfigs() AlertmanagerConfigInformer
}
type version struct {
factory internalinterfaces.SharedInformerFactory
namespace string
tweakListOptions internalinterfaces.TweakListOptionsFunc
}
// New returns a new Interface.
func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
// AlertmanagerConfigs returns a AlertmanagerConfigInformer.
func (v *version) AlertmanagerConfigs() AlertmanagerConfigInformer {
return &alertmanagerConfigInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}

View file

@ -0,0 +1,97 @@
// Copyright The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by lister-gen. DO NOT EDIT.
package v1beta1
import (
v1beta1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1beta1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
// AlertmanagerConfigLister helps list AlertmanagerConfigs.
// All objects returned here must be treated as read-only.
type AlertmanagerConfigLister interface {
// List lists all AlertmanagerConfigs in the indexer.
// Objects returned here must be treated as read-only.
List(selector labels.Selector) (ret []*v1beta1.AlertmanagerConfig, err error)
// AlertmanagerConfigs returns an object that can list and get AlertmanagerConfigs.
AlertmanagerConfigs(namespace string) AlertmanagerConfigNamespaceLister
AlertmanagerConfigListerExpansion
}
// alertmanagerConfigLister implements the AlertmanagerConfigLister interface.
type alertmanagerConfigLister struct {
indexer cache.Indexer
}
// NewAlertmanagerConfigLister returns a new AlertmanagerConfigLister.
func NewAlertmanagerConfigLister(indexer cache.Indexer) AlertmanagerConfigLister {
return &alertmanagerConfigLister{indexer: indexer}
}
// List lists all AlertmanagerConfigs in the indexer.
func (s *alertmanagerConfigLister) List(selector labels.Selector) (ret []*v1beta1.AlertmanagerConfig, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1beta1.AlertmanagerConfig))
})
return ret, err
}
// AlertmanagerConfigs returns an object that can list and get AlertmanagerConfigs.
func (s *alertmanagerConfigLister) AlertmanagerConfigs(namespace string) AlertmanagerConfigNamespaceLister {
return alertmanagerConfigNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// AlertmanagerConfigNamespaceLister helps list and get AlertmanagerConfigs.
// All objects returned here must be treated as read-only.
type AlertmanagerConfigNamespaceLister interface {
// List lists all AlertmanagerConfigs in the indexer for a given namespace.
// Objects returned here must be treated as read-only.
List(selector labels.Selector) (ret []*v1beta1.AlertmanagerConfig, err error)
// Get retrieves the AlertmanagerConfig from the indexer for a given namespace and name.
// Objects returned here must be treated as read-only.
Get(name string) (*v1beta1.AlertmanagerConfig, error)
AlertmanagerConfigNamespaceListerExpansion
}
// alertmanagerConfigNamespaceLister implements the AlertmanagerConfigNamespaceLister
// interface.
type alertmanagerConfigNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all AlertmanagerConfigs in the indexer for a given namespace.
func (s alertmanagerConfigNamespaceLister) List(selector labels.Selector) (ret []*v1beta1.AlertmanagerConfig, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v1beta1.AlertmanagerConfig))
})
return ret, err
}
// Get retrieves the AlertmanagerConfig from the indexer for a given namespace and name.
func (s alertmanagerConfigNamespaceLister) Get(name string) (*v1beta1.AlertmanagerConfig, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1beta1.Resource("alertmanagerconfig"), name)
}
return obj.(*v1beta1.AlertmanagerConfig), nil
}

View file

@ -0,0 +1,25 @@
// Copyright The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by lister-gen. DO NOT EDIT.
package v1beta1
// AlertmanagerConfigListerExpansion allows custom methods to be added to
// AlertmanagerConfigLister.
type AlertmanagerConfigListerExpansion interface{}
// AlertmanagerConfigNamespaceListerExpansion allows custom methods to be added to
// AlertmanagerConfigNamespaceLister.
type AlertmanagerConfigNamespaceListerExpansion interface{}

View file

@ -22,6 +22,7 @@ import (
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/typed/monitoring/v1"
monitoringv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/typed/monitoring/v1alpha1"
monitoringv1beta1 "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/typed/monitoring/v1beta1"
discovery "k8s.io/client-go/discovery"
rest "k8s.io/client-go/rest"
flowcontrol "k8s.io/client-go/util/flowcontrol"
@ -31,6 +32,7 @@ type Interface interface {
Discovery() discovery.DiscoveryInterface
MonitoringV1() monitoringv1.MonitoringV1Interface
MonitoringV1alpha1() monitoringv1alpha1.MonitoringV1alpha1Interface
MonitoringV1beta1() monitoringv1beta1.MonitoringV1beta1Interface
}
// Clientset contains the clients for groups. Each group has exactly one
@ -39,6 +41,7 @@ type Clientset struct {
*discovery.DiscoveryClient
monitoringV1 *monitoringv1.MonitoringV1Client
monitoringV1alpha1 *monitoringv1alpha1.MonitoringV1alpha1Client
monitoringV1beta1 *monitoringv1beta1.MonitoringV1beta1Client
}
// MonitoringV1 retrieves the MonitoringV1Client
@ -51,6 +54,11 @@ func (c *Clientset) MonitoringV1alpha1() monitoringv1alpha1.MonitoringV1alpha1In
return c.monitoringV1alpha1
}
// MonitoringV1beta1 retrieves the MonitoringV1beta1Client
func (c *Clientset) MonitoringV1beta1() monitoringv1beta1.MonitoringV1beta1Interface {
return c.monitoringV1beta1
}
// Discovery retrieves the DiscoveryClient
func (c *Clientset) Discovery() discovery.DiscoveryInterface {
if c == nil {
@ -99,6 +107,10 @@ func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset,
if err != nil {
return nil, err
}
cs.monitoringV1beta1, err = monitoringv1beta1.NewForConfigAndClient(&configShallowCopy, httpClient)
if err != nil {
return nil, err
}
cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfigAndClient(&configShallowCopy, httpClient)
if err != nil {
@ -122,6 +134,7 @@ func New(c rest.Interface) *Clientset {
var cs Clientset
cs.monitoringV1 = monitoringv1.New(c)
cs.monitoringV1alpha1 = monitoringv1alpha1.New(c)
cs.monitoringV1beta1 = monitoringv1beta1.New(c)
cs.DiscoveryClient = discovery.NewDiscoveryClient(c)
return &cs

View file

@ -22,6 +22,8 @@ import (
fakemonitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/typed/monitoring/v1/fake"
monitoringv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/typed/monitoring/v1alpha1"
fakemonitoringv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/typed/monitoring/v1alpha1/fake"
monitoringv1beta1 "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/typed/monitoring/v1beta1"
fakemonitoringv1beta1 "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/typed/monitoring/v1beta1/fake"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/discovery"
@ -88,3 +90,8 @@ func (c *Clientset) MonitoringV1() monitoringv1.MonitoringV1Interface {
func (c *Clientset) MonitoringV1alpha1() monitoringv1alpha1.MonitoringV1alpha1Interface {
return &fakemonitoringv1alpha1.FakeMonitoringV1alpha1{Fake: &c.Fake}
}
// MonitoringV1beta1 retrieves the MonitoringV1beta1Client
func (c *Clientset) MonitoringV1beta1() monitoringv1beta1.MonitoringV1beta1Interface {
return &fakemonitoringv1beta1.FakeMonitoringV1beta1{Fake: &c.Fake}
}

View file

@ -19,6 +19,7 @@ package fake
import (
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
monitoringv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1"
monitoringv1beta1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
@ -32,6 +33,7 @@ var codecs = serializer.NewCodecFactory(scheme)
var localSchemeBuilder = runtime.SchemeBuilder{
monitoringv1.AddToScheme,
monitoringv1alpha1.AddToScheme,
monitoringv1beta1.AddToScheme,
}
// AddToScheme adds all types of this clientset into the given scheme. This allows composition

View file

@ -19,6 +19,7 @@ package scheme
import (
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
monitoringv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1"
monitoringv1beta1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
@ -32,6 +33,7 @@ var ParameterCodec = runtime.NewParameterCodec(Scheme)
var localSchemeBuilder = runtime.SchemeBuilder{
monitoringv1.AddToScheme,
monitoringv1alpha1.AddToScheme,
monitoringv1beta1.AddToScheme,
}
// AddToScheme adds all types of this clientset into the given scheme. This allows composition

View file

@ -0,0 +1,176 @@
// Copyright The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by client-gen. DO NOT EDIT.
package v1beta1
import (
"context"
"time"
v1beta1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1beta1"
scheme "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/scheme"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
rest "k8s.io/client-go/rest"
)
// AlertmanagerConfigsGetter has a method to return a AlertmanagerConfigInterface.
// A group's client should implement this interface.
type AlertmanagerConfigsGetter interface {
AlertmanagerConfigs(namespace string) AlertmanagerConfigInterface
}
// AlertmanagerConfigInterface has methods to work with AlertmanagerConfig resources.
type AlertmanagerConfigInterface interface {
Create(ctx context.Context, alertmanagerConfig *v1beta1.AlertmanagerConfig, opts v1.CreateOptions) (*v1beta1.AlertmanagerConfig, error)
Update(ctx context.Context, alertmanagerConfig *v1beta1.AlertmanagerConfig, opts v1.UpdateOptions) (*v1beta1.AlertmanagerConfig, error)
Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta1.AlertmanagerConfig, error)
List(ctx context.Context, opts v1.ListOptions) (*v1beta1.AlertmanagerConfigList, error)
Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.AlertmanagerConfig, err error)
AlertmanagerConfigExpansion
}
// alertmanagerConfigs implements AlertmanagerConfigInterface
type alertmanagerConfigs struct {
client rest.Interface
ns string
}
// newAlertmanagerConfigs returns a AlertmanagerConfigs
func newAlertmanagerConfigs(c *MonitoringV1beta1Client, namespace string) *alertmanagerConfigs {
return &alertmanagerConfigs{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the alertmanagerConfig, and returns the corresponding alertmanagerConfig object, and an error if there is any.
func (c *alertmanagerConfigs) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.AlertmanagerConfig, err error) {
result = &v1beta1.AlertmanagerConfig{}
err = c.client.Get().
Namespace(c.ns).
Resource("alertmanagerconfigs").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do(ctx).
Into(result)
return
}
// List takes label and field selectors, and returns the list of AlertmanagerConfigs that match those selectors.
func (c *alertmanagerConfigs) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.AlertmanagerConfigList, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &v1beta1.AlertmanagerConfigList{}
err = c.client.Get().
Namespace(c.ns).
Resource("alertmanagerconfigs").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Do(ctx).
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested alertmanagerConfigs.
func (c *alertmanagerConfigs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
opts.Watch = true
return c.client.Get().
Namespace(c.ns).
Resource("alertmanagerconfigs").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Watch(ctx)
}
// Create takes the representation of a alertmanagerConfig and creates it. Returns the server's representation of the alertmanagerConfig, and an error, if there is any.
func (c *alertmanagerConfigs) Create(ctx context.Context, alertmanagerConfig *v1beta1.AlertmanagerConfig, opts v1.CreateOptions) (result *v1beta1.AlertmanagerConfig, err error) {
result = &v1beta1.AlertmanagerConfig{}
err = c.client.Post().
Namespace(c.ns).
Resource("alertmanagerconfigs").
VersionedParams(&opts, scheme.ParameterCodec).
Body(alertmanagerConfig).
Do(ctx).
Into(result)
return
}
// Update takes the representation of a alertmanagerConfig and updates it. Returns the server's representation of the alertmanagerConfig, and an error, if there is any.
func (c *alertmanagerConfigs) Update(ctx context.Context, alertmanagerConfig *v1beta1.AlertmanagerConfig, opts v1.UpdateOptions) (result *v1beta1.AlertmanagerConfig, err error) {
result = &v1beta1.AlertmanagerConfig{}
err = c.client.Put().
Namespace(c.ns).
Resource("alertmanagerconfigs").
Name(alertmanagerConfig.Name).
VersionedParams(&opts, scheme.ParameterCodec).
Body(alertmanagerConfig).
Do(ctx).
Into(result)
return
}
// Delete takes name of the alertmanagerConfig and deletes it. Returns an error if one occurs.
func (c *alertmanagerConfigs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("alertmanagerconfigs").
Name(name).
Body(&opts).
Do(ctx).
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *alertmanagerConfigs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
var timeout time.Duration
if listOpts.TimeoutSeconds != nil {
timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
}
return c.client.Delete().
Namespace(c.ns).
Resource("alertmanagerconfigs").
VersionedParams(&listOpts, scheme.ParameterCodec).
Timeout(timeout).
Body(&opts).
Do(ctx).
Error()
}
// Patch applies the patch and returns the patched alertmanagerConfig.
func (c *alertmanagerConfigs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.AlertmanagerConfig, err error) {
result = &v1beta1.AlertmanagerConfig{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("alertmanagerconfigs").
Name(name).
SubResource(subresources...).
VersionedParams(&opts, scheme.ParameterCodec).
Body(data).
Do(ctx).
Into(result)
return
}

View file

@ -0,0 +1,18 @@
// Copyright The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by client-gen. DO NOT EDIT.
// This package has the automatically generated typed clients.
package v1beta1

View file

@ -0,0 +1,18 @@
// Copyright The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by client-gen. DO NOT EDIT.
// Package fake has the automatically generated clients.
package fake

View file

@ -0,0 +1,128 @@
// Copyright The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
"context"
v1beta1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
)
// FakeAlertmanagerConfigs implements AlertmanagerConfigInterface
type FakeAlertmanagerConfigs struct {
Fake *FakeMonitoringV1beta1
ns string
}
var alertmanagerconfigsResource = schema.GroupVersionResource{Group: "monitoring.coreos.com", Version: "v1beta1", Resource: "alertmanagerconfigs"}
var alertmanagerconfigsKind = schema.GroupVersionKind{Group: "monitoring.coreos.com", Version: "v1beta1", Kind: "AlertmanagerConfig"}
// Get takes name of the alertmanagerConfig, and returns the corresponding alertmanagerConfig object, and an error if there is any.
func (c *FakeAlertmanagerConfigs) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.AlertmanagerConfig, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(alertmanagerconfigsResource, c.ns, name), &v1beta1.AlertmanagerConfig{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.AlertmanagerConfig), err
}
// List takes label and field selectors, and returns the list of AlertmanagerConfigs that match those selectors.
func (c *FakeAlertmanagerConfigs) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.AlertmanagerConfigList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(alertmanagerconfigsResource, alertmanagerconfigsKind, c.ns, opts), &v1beta1.AlertmanagerConfigList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v1beta1.AlertmanagerConfigList{ListMeta: obj.(*v1beta1.AlertmanagerConfigList).ListMeta}
for _, item := range obj.(*v1beta1.AlertmanagerConfigList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested alertmanagerConfigs.
func (c *FakeAlertmanagerConfigs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(alertmanagerconfigsResource, c.ns, opts))
}
// Create takes the representation of a alertmanagerConfig and creates it. Returns the server's representation of the alertmanagerConfig, and an error, if there is any.
func (c *FakeAlertmanagerConfigs) Create(ctx context.Context, alertmanagerConfig *v1beta1.AlertmanagerConfig, opts v1.CreateOptions) (result *v1beta1.AlertmanagerConfig, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(alertmanagerconfigsResource, c.ns, alertmanagerConfig), &v1beta1.AlertmanagerConfig{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.AlertmanagerConfig), err
}
// Update takes the representation of a alertmanagerConfig and updates it. Returns the server's representation of the alertmanagerConfig, and an error, if there is any.
func (c *FakeAlertmanagerConfigs) Update(ctx context.Context, alertmanagerConfig *v1beta1.AlertmanagerConfig, opts v1.UpdateOptions) (result *v1beta1.AlertmanagerConfig, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(alertmanagerconfigsResource, c.ns, alertmanagerConfig), &v1beta1.AlertmanagerConfig{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.AlertmanagerConfig), err
}
// Delete takes name of the alertmanagerConfig and deletes it. Returns an error if one occurs.
func (c *FakeAlertmanagerConfigs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteActionWithOptions(alertmanagerconfigsResource, c.ns, name, opts), &v1beta1.AlertmanagerConfig{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeAlertmanagerConfigs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(alertmanagerconfigsResource, c.ns, listOpts)
_, err := c.Fake.Invokes(action, &v1beta1.AlertmanagerConfigList{})
return err
}
// Patch applies the patch and returns the patched alertmanagerConfig.
func (c *FakeAlertmanagerConfigs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.AlertmanagerConfig, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(alertmanagerconfigsResource, c.ns, name, pt, data, subresources...), &v1beta1.AlertmanagerConfig{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.AlertmanagerConfig), err
}

View file

@ -0,0 +1,38 @@
// Copyright The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
v1beta1 "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/typed/monitoring/v1beta1"
rest "k8s.io/client-go/rest"
testing "k8s.io/client-go/testing"
)
type FakeMonitoringV1beta1 struct {
*testing.Fake
}
func (c *FakeMonitoringV1beta1) AlertmanagerConfigs(namespace string) v1beta1.AlertmanagerConfigInterface {
return &FakeAlertmanagerConfigs{c, namespace}
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *FakeMonitoringV1beta1) RESTClient() rest.Interface {
var ret *rest.RESTClient
return ret
}

View file

@ -0,0 +1,19 @@
// Copyright The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by client-gen. DO NOT EDIT.
package v1beta1
type AlertmanagerConfigExpansion interface{}

View file

@ -0,0 +1,105 @@
// Copyright The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by client-gen. DO NOT EDIT.
package v1beta1
import (
"net/http"
v1beta1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1beta1"
"github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/scheme"
rest "k8s.io/client-go/rest"
)
type MonitoringV1beta1Interface interface {
RESTClient() rest.Interface
AlertmanagerConfigsGetter
}
// MonitoringV1beta1Client is used to interact with features provided by the monitoring.coreos.com group.
type MonitoringV1beta1Client struct {
restClient rest.Interface
}
func (c *MonitoringV1beta1Client) AlertmanagerConfigs(namespace string) AlertmanagerConfigInterface {
return newAlertmanagerConfigs(c, namespace)
}
// NewForConfig creates a new MonitoringV1beta1Client for the given config.
// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient),
// where httpClient was generated with rest.HTTPClientFor(c).
func NewForConfig(c *rest.Config) (*MonitoringV1beta1Client, error) {
config := *c
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
httpClient, err := rest.HTTPClientFor(&config)
if err != nil {
return nil, err
}
return NewForConfigAndClient(&config, httpClient)
}
// NewForConfigAndClient creates a new MonitoringV1beta1Client for the given config and http client.
// Note the http client provided takes precedence over the configured transport values.
func NewForConfigAndClient(c *rest.Config, h *http.Client) (*MonitoringV1beta1Client, error) {
config := *c
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
client, err := rest.RESTClientForConfigAndClient(&config, h)
if err != nil {
return nil, err
}
return &MonitoringV1beta1Client{client}, nil
}
// NewForConfigOrDie creates a new MonitoringV1beta1Client for the given config and
// panics if there is an error in the config.
func NewForConfigOrDie(c *rest.Config) *MonitoringV1beta1Client {
client, err := NewForConfig(c)
if err != nil {
panic(err)
}
return client
}
// New creates a new MonitoringV1beta1Client for the given RESTClient.
func New(c rest.Interface) *MonitoringV1beta1Client {
return &MonitoringV1beta1Client{c}
}
func setConfigDefaults(config *rest.Config) error {
gv := v1beta1.SchemeGroupVersion
config.GroupVersion = &gv
config.APIPath = "/apis"
config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
if config.UserAgent == "" {
config.UserAgent = rest.DefaultKubernetesUserAgent()
}
return nil
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *MonitoringV1beta1Client) RESTClient() rest.Interface {
if c == nil {
return nil
}
return c.restClient
}

View file

@ -0,0 +1,10 @@
#!/usr/bin/env bash
# exit immediately when a command fails
set -e
# only exit with zero if all commands of the pipeline exit successfully
set -o pipefail
# error on unset variables
set -u
jsonnet -J scripts/generate/vendor scripts/generate/conversion-webhook-patch-for-alermanagerconfig-crd.jsonnet > "example/alertmanager-crd-conversion/patch.json"

View file

@ -0,0 +1,18 @@
// Create JSON patch that configures the conversion webhook for the Alertmanager CRD.
local alertmanagerConfigCrd = (import 'prometheus-operator/alertmanagerconfigs-crd.json');
local conversion = (import 'prometheus-operator/conversion.libsonnet');
local admissionWebhook = (import 'prometheus-operator/admission-webhook.libsonnet');
local config = (import 'config.jsonnet');
local aw = admissionWebhook(config {
image: 'quay.io/prometheus-operator/admission-webhook:v' + config.version,
});
{
apiVersion: alertmanagerConfigCrd.apiVersion,
kind: alertmanagerConfigCrd.kind,
metadata: alertmanagerConfigCrd.metadata,
} + conversion({
name: aw.service.metadata.name,
namespace: aw.service.metadata.namespace,
})

View file

@ -17,6 +17,7 @@ package e2e
import (
"context"
"fmt"
"reflect"
"strconv"
"strings"
"testing"
@ -38,6 +39,7 @@ import (
"github.com/prometheus-operator/prometheus-operator/pkg/alertmanager"
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
monitoringv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1"
monitoringv1beta1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1beta1"
)
func testAMCreateDeleteCluster(t *testing.T) {
@ -712,6 +714,82 @@ inhibit_rules:
}
}
func testAlertmanagerConfigVersions(t *testing.T) {
// Don't run Alertmanager tests in parallel. See
// https://github.com/prometheus/alertmanager/issues/1835 for details.
testCtx := framework.NewTestCtx(t)
defer testCtx.Cleanup(t)
ns := framework.CreateNamespace(context.Background(), t, testCtx)
framework.SetupPrometheusRBAC(context.Background(), t, testCtx, ns)
alertmanager := framework.MakeBasicAlertmanager("amconfig-versions", 1)
alertmanager.Spec.AlertmanagerConfigSelector = &metav1.LabelSelector{}
alertmanager, err := framework.CreateAlertmanagerAndWaitUntilReady(context.Background(), ns, alertmanager)
if err != nil {
t.Fatal(err)
}
amcfgV1alpha1 := &monitoringv1alpha1.AlertmanagerConfig{
ObjectMeta: metav1.ObjectMeta{
Name: "amcfg-v1alpha1",
},
Spec: monitoringv1alpha1.AlertmanagerConfigSpec{
Route: &monitoringv1alpha1.Route{
Receiver: "webhook",
Matchers: []monitoringv1alpha1.Matcher{{
Name: "job",
Value: "webapp.+",
Regex: true,
}},
},
Receivers: []monitoringv1alpha1.Receiver{{
Name: "webhook",
}},
},
}
if _, err := framework.MonClientV1alpha1.AlertmanagerConfigs(alertmanager.Namespace).Create(context.Background(), amcfgV1alpha1, metav1.CreateOptions{}); err != nil {
t.Fatalf("failed to create v1alpha1 AlertmanagerConfig object: %v", err)
}
amcfgV1beta1Converted, err := framework.MonClientV1beta1.AlertmanagerConfigs(alertmanager.Namespace).Get(context.Background(), amcfgV1alpha1.Name, metav1.GetOptions{})
if err != nil {
t.Fatalf("failed to get v1beta1 AlertmanagerConfig object: %v", err)
}
expected := []monitoringv1beta1.Matcher{{Name: "job", Value: "webapp.+", MatchType: monitoringv1beta1.MatchRegexp}}
if !reflect.DeepEqual(amcfgV1beta1Converted.Spec.Route.Matchers, expected) {
t.Fatalf("expected %#v matcher, got %#v", expected, amcfgV1beta1Converted.Spec.Route.Matchers)
}
amcfgV1beta1 := &monitoringv1beta1.AlertmanagerConfig{
ObjectMeta: metav1.ObjectMeta{
Name: "amcfg-v1beta1",
},
Spec: monitoringv1beta1.AlertmanagerConfigSpec{
Route: &monitoringv1beta1.Route{
Receiver: "webhook",
Matchers: []monitoringv1beta1.Matcher{{
Name: "job",
Value: "webapp.+",
MatchType: "=~",
}},
},
Receivers: []monitoringv1beta1.Receiver{{
Name: "webhook",
}},
},
}
if _, err := framework.MonClientV1beta1.AlertmanagerConfigs(alertmanager.Namespace).Create(context.Background(), amcfgV1beta1, metav1.CreateOptions{}); err != nil {
t.Fatalf("failed to create v1beta1 AlertmanagerConfig object: %v", err)
}
if _, err := framework.MonClientV1alpha1.AlertmanagerConfigs(alertmanager.Namespace).Get(context.Background(), amcfgV1beta1.Name, metav1.GetOptions{}); err != nil {
t.Fatalf("failed to get v1alpha1 AlertmanagerConfig object: %v", err)
}
}
func testAlertmanagerConfigCRD(t *testing.T) {
// Don't run Alertmanager tests in parallel. See
// https://github.com/prometheus/alertmanager/issues/1835 for details.

View file

@ -163,6 +163,7 @@ func testAllNSAlertmanager(t *testing.T) {
"AMReloadConfig": testAMReloadConfig,
"AMZeroDowntimeRollingDeployment": testAMZeroDowntimeRollingDeployment,
"AMAlertmanagerConfigCRD": testAlertmanagerConfigCRD,
"AMAlertmanagerConfigVersions": testAlertmanagerConfigVersions,
"AMUserDefinedAMConfigFromSecret": testUserDefinedAlertmanagerConfigFromSecret,
"AMUserDefinedAMConfigFromCustomResource": testUserDefinedAlertmanagerConfigFromCustomResource,
"AMPreserveUserAddedMetadata": testAMPreserveUserAddedMetadata,

View file

@ -104,7 +104,7 @@ func WaitForCRDReady(listFunc func(opts metav1.ListOptions) (runtime.Object, err
func (f *Framework) CreateCRDAndWaitUntilReady(ctx context.Context, crdName string, listFunc func(opts metav1.ListOptions) (runtime.Object, error)) error {
crdName = strings.ToLower(crdName)
group := monitoring.GroupName
assetPath := "../../example/prometheus-operator-crd/" + group + "_" + crdName + ".yaml"
assetPath := "../../example/prometheus-operator-crd-full/" + group + "_" + crdName + ".yaml"
crd, err := f.MakeCRD(assetPath)
if err != nil {

View file

@ -16,6 +16,7 @@ package framework
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
@ -24,12 +25,15 @@ import (
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
@ -41,6 +45,7 @@ import (
monitoringv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1"
v1monitoringclient "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/typed/monitoring/v1"
v1alpha1monitoringclient "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/typed/monitoring/v1alpha1"
v1beta1monitoringclient "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/typed/monitoring/v1beta1"
)
const (
@ -56,6 +61,7 @@ type Framework struct {
KubeClient kubernetes.Interface
MonClientV1 v1monitoringclient.MonitoringV1Interface
MonClientV1alpha1 v1alpha1monitoringclient.MonitoringV1alpha1Interface
MonClientV1beta1 v1beta1monitoringclient.MonitoringV1beta1Interface
APIServerClient apiclient.Interface
HTTPClient *http.Client
MasterHost string
@ -90,17 +96,23 @@ func New(kubeconfig, opImage string) (*Framework, error) {
return nil, errors.Wrap(err, "creating v1 monitoring client failed")
}
mClientV1alpha1, err := v1alpha1monitoringclient.NewForConfig(config)
mClientv1alpha1, err := v1alpha1monitoringclient.NewForConfig(config)
if err != nil {
return nil, errors.Wrap(err, "creating v1alpha1 monitoring client failed")
}
mClientv1beta1, err := v1beta1monitoringclient.NewForConfig(config)
if err != nil {
return nil, errors.Wrap(err, "creating v1beta1 monitoring client failed")
}
f := &Framework{
RestConfig: config,
MasterHost: config.Host,
KubeClient: cli,
MonClientV1: mClientV1,
MonClientV1alpha1: mClientV1alpha1,
MonClientV1alpha1: mClientv1alpha1,
MonClientV1beta1: mClientv1beta1,
APIServerClient: apiCli,
HTTPClient: httpc,
DefaultTimeout: time.Minute,
@ -279,7 +291,14 @@ func (f *Framework) CreatePrometheusOperator(ctx context.Context, ns, opImage st
return f.MonClientV1alpha1.AlertmanagerConfigs(v1.NamespaceAll).List(ctx, opts)
})
if err != nil {
return nil, errors.Wrap(err, "initialize AlertmanagerConfig CRD")
return nil, errors.Wrap(err, "initialize AlertmanagerConfig v1alpha1 CRD")
}
err = WaitForCRDReady(func(opts metav1.ListOptions) (runtime.Object, error) {
return f.MonClientV1beta1.AlertmanagerConfigs(v1.NamespaceAll).List(ctx, opts)
})
if err != nil {
return nil, errors.Wrap(err, "wait for AlertmanagerConfig v1beta1 CRD")
}
deploy, err := MakeDeployment("../../example/rbac/prometheus-operator/prometheus-operator-deployment.yaml")
@ -387,7 +406,7 @@ func (f *Framework) CreatePrometheusOperator(ctx context.Context, ns, opImage st
}
if createResourceAdmissionHooks {
b, err := f.CreateAdmissionWebhookServer(ctx, ns, webhookServerImage)
webhookService, b, err := f.CreateAdmissionWebhookServer(ctx, ns, webhookServerImage)
if err != nil {
return nil, errors.Wrap(err, "failed to create webhook server")
}
@ -409,6 +428,12 @@ func (f *Framework) CreatePrometheusOperator(ctx context.Context, ns, opImage st
return nil, errors.Wrap(err, "failed to create validating webhook for AlertManagerConfigs")
}
finalizers = append(finalizers, finalizer)
finalizer, err = f.configureAlertmanagerConfigConversion(ctx, webhookService, b)
if err != nil {
return nil, errors.Wrap(err, "failed to configure conversion webhook for AlertManagerConfigs")
}
finalizers = append(finalizers, finalizer)
}
return finalizers, nil
@ -448,14 +473,80 @@ func (f *Framework) SetupPrometheusRBACGlobal(ctx context.Context, t *testing.T,
}
}
func (f *Framework) configureAlertmanagerConfigConversion(ctx context.Context, svc *v1.Service, cert []byte) (FinalizerFn, error) {
patch, err := f.MakeCRD("../../example/alertmanager-crd-conversion/patch.json")
if err != nil {
return nil, err
}
crd, err := f.GetCRD(ctx, patch.Name)
if err != nil {
return nil, err
}
originalBytes, err := json.Marshal(crd)
if err != nil {
return nil, err
}
patch.Spec.Conversion.Webhook.ClientConfig.Service.Name = svc.Name
patch.Spec.Conversion.Webhook.ClientConfig.Service.Namespace = svc.Namespace
patch.Spec.Conversion.Webhook.ClientConfig.Service.Port = &svc.Spec.Ports[0].Port
patch.Spec.Conversion.Webhook.ClientConfig.CABundle = cert
crd.Spec.Conversion = patch.Spec.Conversion
patchBytes, err := json.Marshal(crd)
if err != nil {
return nil, err
}
jsonResult, err := strategicpatch.StrategicMergePatch(originalBytes, patchBytes, apiextensionsv1.CustomResourceDefinition{})
if err != nil {
return nil, fmt.Errorf("failed to generate merge patch: %w", err)
}
crd, err = f.APIServerClient.ApiextensionsV1().CustomResourceDefinitions().Patch(
ctx,
crd.Name,
types.StrategicMergePatchType,
jsonResult,
metav1.PatchOptions{},
)
if err != nil {
return nil, fmt.Errorf("failed to patch CustomResourceDefinition object: %w", err)
}
if crd.Spec.Conversion.Strategy != apiextensionsv1.WebhookConverter {
return nil, fmt.Errorf("expected conversion strategy to be %s, got %s", apiextensionsv1.WebhookConverter, crd.Spec.Conversion.Strategy)
}
finalizerFn := func() error {
crd, err := f.GetCRD(ctx, patch.Name)
if err != nil {
return err
}
crd.Spec.Conversion = nil
_, err = f.APIServerClient.ApiextensionsV1().CustomResourceDefinitions().Update(ctx, crd, metav1.UpdateOptions{})
if err != nil {
return fmt.Errorf("unable to reset conversion configuration of AlertmanagerConfig CRD: %w", err)
}
return err
}
return finalizerFn, nil
}
// CreateAdmissionWebhookServer deploys an HTTPS server
// Acts as a validating and mutating webhook server for PrometheusRule and AlertManagerConfig
// Returns the CA, which can be used to access the server over TLS
// Returns the service and the CA which can be used to access the server over TLS
func (f *Framework) CreateAdmissionWebhookServer(
ctx context.Context,
namespace string,
image string,
) ([]byte, error) {
) (*v1.Service, []byte, error) {
certBytes, keyBytes, err := certutil.GenerateSelfSignedCertKey(
fmt.Sprintf("%s.%s.svc", admissionWebhookServiceName, namespace),
@ -463,18 +554,21 @@ func (f *Framework) CreateAdmissionWebhookServer(
nil,
)
if err != nil {
return nil, errors.Wrap(err, "failed to generate certificate and key")
return nil, nil, errors.Wrap(err, "failed to generate certificate and key")
}
if err := f.CreateSecretWithCert(ctx, certBytes, keyBytes, namespace, standaloneAdmissionHookSecretName); err != nil {
return nil, errors.Wrap(err, "failed to create admission webhook secret")
return nil, nil, errors.Wrap(err, "failed to create admission webhook secret")
}
deploy, err := MakeDeployment("../../example/admission-webhook/deployment.yaml")
if err != nil {
return nil, err
return nil, nil, err
}
// Only 1 replica needed for the tests.
deploy.Spec.Replicas = func(i int32) *int32 { return &i }(1)
deploy.Spec.Template.Spec.Containers[0].Args = append(deploy.Spec.Template.Spec.Containers[0].Args, "--log-level=debug")
if image != "" {
@ -482,7 +576,7 @@ func (f *Framework) CreateAdmissionWebhookServer(
deploy.Spec.Template.Spec.Containers[0].Image = image
repoAndTag := strings.Split(image, ":")
if len(repoAndTag) != 2 {
return nil, errors.Errorf(
return nil, nil, errors.Errorf(
"expected image '%v' split by colon to result in two substrings but got '%v'",
image,
repoAndTag,
@ -501,32 +595,31 @@ func (f *Framework) CreateAdmissionWebhookServer(
_, err = f.createServiceAccount(ctx, namespace, "../../example/admission-webhook/service-account.yaml")
if err != nil {
return nil, err
return nil, nil, err
}
err = f.CreateDeployment(ctx, namespace, deploy)
if err != nil {
return nil, err
return nil, nil, err
}
opts := metav1.ListOptions{LabelSelector: fields.SelectorFromSet(fields.Set(deploy.Spec.Template.ObjectMeta.Labels)).String()}
err = f.WaitForPodsReady(ctx, namespace, f.DefaultTimeout, 2, opts)
err = f.WaitForPodsReady(ctx, namespace, f.DefaultTimeout, int(*deploy.Spec.Replicas), opts)
if err != nil {
return nil, errors.Wrap(err, "failed to wait for admission webhook server to become ready")
return nil, nil, errors.Wrap(err, "failed to wait for admission webhook server to become ready")
}
service, err := MakeService("../../example/admission-webhook/service.yaml")
if err != nil {
return certBytes, errors.Wrap(err, "cannot parse service file")
return nil, nil, errors.Wrap(err, "cannot parse service file")
}
service.Namespace = namespace
service.Spec.ClusterIP = ""
service.Spec.Ports = []v1.ServicePort{{Name: "https", Port: 443, TargetPort: intstr.FromInt(8443)}}
if _, err := f.CreateServiceAndWaitUntilReady(ctx, namespace, service); err != nil {
return certBytes, errors.Wrap(err, "failed to create admission webhook server service")
return nil, nil, errors.Wrap(err, "failed to create admission webhook server service")
}
return certBytes, nil
return service, certBytes, nil
}

View file

@ -16,6 +16,7 @@ webhooks:
- monitoring.coreos.com
apiVersions:
- v1alpha1
- v1beta1
operations:
- CREATE
- UPDATE
@ -23,4 +24,4 @@ webhooks:
- alertmanagerconfigs
sideEffects: None
admissionReviewVersions:
- v1
- v1