1
0
Fork 0
mirror of https://github.com/prometheus-operator/prometheus-operator.git synced 2025-04-15 16:56:24 +00:00
prometheus-operator/pkg/prometheus/statefulset_test.go
paulfantom 35b2954459
pkg/prometheus: remove liveness probe
Removing liveness probe to prevent killing prometheus pod during WAL
replay.

This should be reverted around kubernetes 1.21 release. At that point
startupProbe should be added.
2020-09-15 12:05:18 +02:00

1218 lines
36 KiB
Go

// Copyright 2016 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 prometheus
import (
"fmt"
"reflect"
"strings"
"testing"
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
"github.com/stretchr/testify/require"
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/kylelemons/godebug/pretty"
)
var (
defaultTestConfig = &Config{
ConfigReloaderImage: "jimmidyson/configmap-reload:latest",
ConfigReloaderCPU: "100m",
ConfigReloaderMemory: "25Mi",
PrometheusConfigReloaderImage: "quay.io/prometheus-operator/prometheus-config-reloader:latest",
PrometheusDefaultBaseImage: "quay.io/prometheus/prometheus",
ThanosDefaultBaseImage: "quay.io/thanos/thanos",
}
)
func TestStatefulSetLabelingAndAnnotations(t *testing.T) {
labels := map[string]string{
"testlabel": "testlabelvalue",
}
annotations := map[string]string{
"testannotation": "testannotationvalue",
"kubectl.kubernetes.io/last-applied-configuration": "something",
"kubectl.kubernetes.io/something": "something",
}
// kubectl annotations must not be on the statefulset so kubectl does
// not manage the generated object
expectedAnnotations := map[string]string{
"prometheus-operator-input-hash": "",
"testannotation": "testannotationvalue",
}
sset, err := makeStatefulSet(monitoringv1.Prometheus{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
Annotations: annotations,
},
}, defaultTestConfig, nil, "")
require.NoError(t, err)
if !reflect.DeepEqual(labels, sset.Labels) {
t.Log(pretty.Compare(labels, sset.Labels))
t.Fatal("Labels are not properly being propagated to the StatefulSet")
}
if !reflect.DeepEqual(expectedAnnotations, sset.Annotations) {
t.Log(pretty.Compare(expectedAnnotations, sset.Annotations))
t.Fatal("Annotations are not properly being propagated to the StatefulSet")
}
}
func TestPodLabelsAnnotations(t *testing.T) {
annotations := map[string]string{
"testannotation": "testvalue",
}
labels := map[string]string{
"testlabel": "testvalue",
}
sset, err := makeStatefulSet(monitoringv1.Prometheus{
ObjectMeta: metav1.ObjectMeta{},
Spec: monitoringv1.PrometheusSpec{
PodMetadata: &monitoringv1.EmbeddedObjectMetadata{
Annotations: annotations,
Labels: labels,
},
},
}, defaultTestConfig, nil, "")
require.NoError(t, err)
if _, ok := sset.Spec.Template.ObjectMeta.Labels["testlabel"]; !ok {
t.Fatal("Pod labes are not properly propagated")
}
if !reflect.DeepEqual(annotations, sset.Spec.Template.ObjectMeta.Annotations) {
t.Fatal("Pod annotaitons are not properly propagated")
}
}
func TestPodLabelsShouldNotBeSelectorLabels(t *testing.T) {
labels := map[string]string{
"testlabel": "testvalue",
}
sset, err := makeStatefulSet(monitoringv1.Prometheus{
ObjectMeta: metav1.ObjectMeta{},
Spec: monitoringv1.PrometheusSpec{
PodMetadata: &monitoringv1.EmbeddedObjectMetadata{
Labels: labels,
},
},
}, defaultTestConfig, nil, "")
require.NoError(t, err)
if sset.Spec.Selector.MatchLabels["testlabel"] == "testvalue" {
t.Fatal("Pod Selector are not properly propagated")
}
}
func TestStatefulSetPVC(t *testing.T) {
labels := map[string]string{
"testlabel": "testlabelvalue",
}
annotations := map[string]string{
"testannotation": "testannotationvalue",
}
storageClass := "storageclass"
pvc := monitoringv1.EmbeddedPersistentVolumeClaim{
EmbeddedObjectMetadata: monitoringv1.EmbeddedObjectMetadata{
Annotations: annotations,
},
Spec: v1.PersistentVolumeClaimSpec{
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
StorageClassName: &storageClass,
},
}
sset, err := makeStatefulSet(monitoringv1.Prometheus{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
Annotations: annotations,
},
Spec: monitoringv1.PrometheusSpec{
Storage: &monitoringv1.StorageSpec{
VolumeClaimTemplate: pvc,
},
},
}, defaultTestConfig, nil, "")
require.NoError(t, err)
ssetPvc := sset.Spec.VolumeClaimTemplates[0]
if !reflect.DeepEqual(*pvc.Spec.StorageClassName, *ssetPvc.Spec.StorageClassName) {
t.Fatal("Error adding PVC Spec to StatefulSetSpec")
}
}
func TestStatefulSetEmptyDir(t *testing.T) {
labels := map[string]string{
"testlabel": "testlabelvalue",
}
annotations := map[string]string{
"testannotation": "testannotationvalue",
}
emptyDir := v1.EmptyDirVolumeSource{
Medium: v1.StorageMediumMemory,
}
sset, err := makeStatefulSet(monitoringv1.Prometheus{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
Annotations: annotations,
},
Spec: monitoringv1.PrometheusSpec{
Storage: &monitoringv1.StorageSpec{
EmptyDir: &emptyDir,
},
},
}, defaultTestConfig, nil, "")
require.NoError(t, err)
ssetVolumes := sset.Spec.Template.Spec.Volumes
if ssetVolumes[len(ssetVolumes)-1].VolumeSource.EmptyDir != nil && !reflect.DeepEqual(emptyDir.Medium, ssetVolumes[len(ssetVolumes)-1].VolumeSource.EmptyDir.Medium) {
t.Fatal("Error adding EmptyDir Spec to StatefulSetSpec")
}
}
func TestStatefulSetVolumeInitial(t *testing.T) {
expected := &appsv1.StatefulSet{
Spec: appsv1.StatefulSetSpec{
Template: v1.PodTemplateSpec{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
VolumeMounts: []v1.VolumeMount{
{
Name: "config-out",
ReadOnly: true,
MountPath: "/etc/prometheus/config_out",
SubPath: "",
},
{
Name: "tls-assets",
ReadOnly: true,
MountPath: "/etc/prometheus/certs",
SubPath: "",
MountPropagation: nil,
SubPathExpr: "",
},
{
Name: "prometheus-volume-init-test-db",
ReadOnly: false,
MountPath: "/prometheus",
SubPath: "",
},
{
Name: "rules-configmap-one",
ReadOnly: false,
MountPath: "/etc/prometheus/rules/rules-configmap-one",
SubPath: "",
},
{
Name: "secret-test-secret1",
ReadOnly: true,
MountPath: "/etc/prometheus/secrets/test-secret1",
SubPath: "",
},
},
},
},
Volumes: []v1.Volume{
{
Name: "config",
VolumeSource: v1.VolumeSource{
Secret: &v1.SecretVolumeSource{
SecretName: configSecretName("volume-init-test"),
},
},
},
{
Name: "tls-assets",
VolumeSource: v1.VolumeSource{
Secret: &v1.SecretVolumeSource{
SecretName: tlsAssetsSecretName("volume-init-test"),
},
},
},
{
Name: "config-out",
VolumeSource: v1.VolumeSource{
EmptyDir: &v1.EmptyDirVolumeSource{},
},
},
{
Name: "rules-configmap-one",
VolumeSource: v1.VolumeSource{
ConfigMap: &v1.ConfigMapVolumeSource{
LocalObjectReference: v1.LocalObjectReference{
Name: "rules-configmap-one",
},
},
},
},
{
Name: "secret-test-secret1",
VolumeSource: v1.VolumeSource{
Secret: &v1.SecretVolumeSource{
SecretName: "test-secret1",
},
},
},
{
Name: "prometheus-volume-init-test-db",
VolumeSource: v1.VolumeSource{
EmptyDir: &v1.EmptyDirVolumeSource{},
},
},
},
},
},
},
}
sset, err := makeStatefulSet(monitoringv1.Prometheus{
ObjectMeta: metav1.ObjectMeta{
Name: "volume-init-test",
},
Spec: monitoringv1.PrometheusSpec{
Secrets: []string{
"test-secret1",
},
},
}, defaultTestConfig, []string{"rules-configmap-one"}, "")
require.NoError(t, err)
if !reflect.DeepEqual(expected.Spec.Template.Spec.Volumes, sset.Spec.Template.Spec.Volumes) {
fmt.Println(pretty.Compare(expected.Spec.Template.Spec.Volumes, sset.Spec.Template.Spec.Volumes))
t.Fatal("expected volumes to match")
}
if !reflect.DeepEqual(expected.Spec.Template.Spec.Containers[0].VolumeMounts, sset.Spec.Template.Spec.Containers[0].VolumeMounts) {
fmt.Println(pretty.Compare(expected.Spec.Template.Spec.Containers[0].VolumeMounts, sset.Spec.Template.Spec.Containers[0].VolumeMounts))
t.Fatal("expected volume mounts to match")
}
}
func TestMemoryRequestNotAdjustedWhenLimitLarger2Gi(t *testing.T) {
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
Version: "v1.8.2",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceMemory: resource.MustParse("3Gi"),
},
},
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
resourceRequest := sset.Spec.Template.Spec.Containers[0].Resources.Requests[v1.ResourceMemory]
requestString := resourceRequest.String()
resourceLimit := sset.Spec.Template.Spec.Containers[0].Resources.Limits[v1.ResourceMemory]
limitString := resourceLimit.String()
if requestString != "2Gi" {
t.Fatalf("Resource request is expected to be 1Gi, instead found %s", requestString)
}
if limitString != "3Gi" {
t.Fatalf("Resource limit is expected to be 1Gi, instead found %s", limitString)
}
}
func TestAdditionalConfigMap(t *testing.T) {
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
ConfigMaps: []string{"test-cm1"},
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
cmVolumeFound := false
for _, v := range sset.Spec.Template.Spec.Volumes {
if v.Name == "configmap-test-cm1" {
cmVolumeFound = true
}
}
if !cmVolumeFound {
t.Fatal("ConfigMap volume not found")
}
cmMounted := false
for _, v := range sset.Spec.Template.Spec.Containers[0].VolumeMounts {
if v.Name == "configmap-test-cm1" && v.MountPath == "/etc/prometheus/configmaps/test-cm1" {
cmMounted = true
}
}
if !cmMounted {
t.Fatal("ConfigMap volume not mounted")
}
}
func TestMemoryRequestAdjustedWhenOnlyLimitGiven(t *testing.T) {
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
Version: "v1.8.2",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceMemory: resource.MustParse("1Gi"),
},
},
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
resourceRequest := sset.Spec.Template.Spec.Containers[0].Resources.Requests[v1.ResourceMemory]
requestString := resourceRequest.String()
resourceLimit := sset.Spec.Template.Spec.Containers[0].Resources.Limits[v1.ResourceMemory]
limitString := resourceLimit.String()
if requestString != "1Gi" {
t.Fatalf("Resource request is expected to be 1Gi, instead found %s", requestString)
}
if limitString != "1Gi" {
t.Fatalf("Resource limit is expected to be 1Gi, instead found %s", limitString)
}
}
func TestListenLocal(t *testing.T) {
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
ListenLocal: true,
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
found := false
for _, flag := range sset.Spec.Template.Spec.Containers[0].Args {
if flag == "--web.listen-address=127.0.0.1:9090" {
found = true
}
}
if !found {
t.Fatal("Prometheus not listening on loopback when it should.")
}
actualReadinessProbe := sset.Spec.Template.Spec.Containers[0].ReadinessProbe
expectedReadinessProbe := &v1.Probe{
Handler: v1.Handler{
Exec: &v1.ExecAction{
Command: []string{
`sh`,
`-c`,
`if [ -x "$(command -v curl)" ]; then curl http://localhost:9090/-/ready; elif [ -x "$(command -v wget)" ]; then wget -q -O /dev/null http://localhost:9090/-/ready; else exit 1; fi`,
},
},
},
TimeoutSeconds: 3,
PeriodSeconds: 5,
FailureThreshold: 120,
}
if !reflect.DeepEqual(actualReadinessProbe, expectedReadinessProbe) {
t.Fatalf("Readiness probe doesn't match expected. \n\nExpected: %+v\n\nGot: %+v", expectedReadinessProbe, actualReadinessProbe)
}
if len(sset.Spec.Template.Spec.Containers[0].Ports) != 0 {
t.Fatal("Prometheus container should have 0 ports defined")
}
}
func TestTagAndShaAndVersion(t *testing.T) {
{
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
Tag: "my-unrelated-tag",
Version: "v2.3.2",
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
image := sset.Spec.Template.Spec.Containers[0].Image
expected := "quay.io/prometheus/prometheus:my-unrelated-tag"
if image != expected {
t.Fatalf("Unexpected container image.\n\nExpected: %s\n\nGot: %s", expected, image)
}
}
{
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
SHA: "7384a79f4b4991bf8269e7452390249b7c70bcdd10509c8c1c6c6e30e32fb324",
Tag: "my-unrelated-tag",
Version: "v2.3.2",
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
image := sset.Spec.Template.Spec.Containers[0].Image
expected := "quay.io/prometheus/prometheus@sha256:7384a79f4b4991bf8269e7452390249b7c70bcdd10509c8c1c6c6e30e32fb324"
if image != expected {
t.Fatalf("Unexpected container image.\n\nExpected: %s\n\nGot: %s", expected, image)
}
}
{
image := "my-reg/prometheus"
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
SHA: "7384a79f4b4991bf8269e7452390249b7c70bcdd10509c8c1c6c6e30e32fb324",
Tag: "my-unrelated-tag",
Version: "v2.3.2",
Image: &image,
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
resultImage := sset.Spec.Template.Spec.Containers[0].Image
expected := "my-reg/prometheus@sha256:7384a79f4b4991bf8269e7452390249b7c70bcdd10509c8c1c6c6e30e32fb324"
if resultImage != expected {
t.Fatalf("Unexpected container image.\n\nExpected: %s\n\nGot: %s", expected, resultImage)
}
}
{
image := "my-reg/prometheus:latest"
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
SHA: "7384a79f4b4991bf8269e7452390249b7c70bcdd10509c8c1c6c6e30e32fb324",
Tag: "my-unrelated-tag",
Version: "v2.3.2",
Image: &image,
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
resultImage := sset.Spec.Template.Spec.Containers[0].Image
expected := "my-reg/prometheus:latest"
if resultImage != expected {
t.Fatalf("Explicit image should have precedence. Unexpected container image.\n\nExpected: %s\n\nGot: %s", expected, resultImage)
}
}
{
image := "my-reg/prometheus"
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
Version: "v2.3.2",
Image: &image,
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
resultImage := sset.Spec.Template.Spec.Containers[0].Image
expected := "my-reg/prometheus:v2.3.2"
if resultImage != expected {
t.Fatalf("Unexpected container image.\n\nExpected: %s\n\nGot: %s", expected, resultImage)
}
}
{
image := "my-reg/prometheus"
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
SHA: "7384a79f4b4991bf8269e7452390249b7c70bcdd10509c8c1c6c6e30e32fb324",
Version: "v2.3.2",
Image: &image,
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
resultImage := sset.Spec.Template.Spec.Containers[0].Image
expected := "my-reg/prometheus@sha256:7384a79f4b4991bf8269e7452390249b7c70bcdd10509c8c1c6c6e30e32fb324"
if resultImage != expected {
t.Fatalf("Unexpected container image.\n\nExpected: %s\n\nGot: %s", expected, resultImage)
}
}
{
image := "my-reg/prometheus"
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
Image: &image,
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
resultImage := sset.Spec.Template.Spec.Containers[0].Image
expected := "my-reg/prometheus"
if resultImage != expected {
t.Fatalf("Unexpected container image.\n\nExpected: %s\n\nGot: %s", expected, resultImage)
}
}
{
image := "my-reg/prometheus"
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
SHA: "7384a79f4b4991bf8269e7452390249b7c70bcdd10509c8c1c6c6e30e32fb324",
Image: &image,
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
resultImage := sset.Spec.Template.Spec.Containers[0].Image
expected := "my-reg/prometheus@sha256:7384a79f4b4991bf8269e7452390249b7c70bcdd10509c8c1c6c6e30e32fb324"
if resultImage != expected {
t.Fatalf("Unexpected container image.\n\nExpected: %s\n\nGot: %s", expected, resultImage)
}
}
{
image := "my-reg/prometheus"
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
Tag: "my-unrelated-tag",
Image: &image,
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
resultImage := sset.Spec.Template.Spec.Containers[0].Image
expected := "docker.io/my-reg/prometheus:my-unrelated-tag"
if resultImage != expected {
t.Fatalf("Unexpected container image.\n\nExpected: %s\n\nGot: %s", expected, resultImage)
}
}
{
image := "my-reg/prometheus"
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
SHA: "7384a79f4b4991bf8269e7452390249b7c70bcdd10509c8c1c6c6e30e32fb324",
Tag: "my-unrealted-tag",
Image: &image,
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
resultImage := sset.Spec.Template.Spec.Containers[0].Image
expected := "my-reg/prometheus@sha256:7384a79f4b4991bf8269e7452390249b7c70bcdd10509c8c1c6c6e30e32fb324"
if resultImage != expected {
t.Fatalf("Unexpected container image.\n\nExpected: %s\n\nGot: %s", expected, resultImage)
}
}
}
func TestThanosTagAndShaAndVersion(t *testing.T) {
{
thanosTag := "my-unrelated-tag"
thanosVersion := "v0.1.0"
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
Thanos: &monitoringv1.ThanosSpec{
Version: &thanosVersion,
Tag: &thanosTag,
},
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
image := sset.Spec.Template.Spec.Containers[2].Image
expected := "quay.io/thanos/thanos:my-unrelated-tag"
if image != expected {
t.Fatalf("Unexpected container image.\n\nExpected: %s\n\nGot: %s", expected, image)
}
}
{
thanosSHA := "7384a79f4b4991bf8269e7452390249b7c70bcdd10509c8c1c6c6e30e32fb324"
thanosTag := "my-unrelated-tag"
thanosVersion := "v0.1.0-rc.2"
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
Thanos: &monitoringv1.ThanosSpec{
SHA: &thanosSHA,
Version: &thanosVersion,
Tag: &thanosTag,
},
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
image := sset.Spec.Template.Spec.Containers[2].Image
expected := "quay.io/thanos/thanos@sha256:7384a79f4b4991bf8269e7452390249b7c70bcdd10509c8c1c6c6e30e32fb324"
if image != expected {
t.Fatalf("Unexpected container image.\n\nExpected: %s\n\nGot: %s", expected, image)
}
}
{
thanosSHA := "7384a79f4b4991bf8269e7452390249b7c70bcdd10509c8c1c6c6e30e32fb324"
thanosTag := "my-unrelated-tag"
thanosVersion := "v0.1.0-rc.2"
thanosImage := "my-registry/thanos:latest"
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
Thanos: &monitoringv1.ThanosSpec{
SHA: &thanosSHA,
Version: &thanosVersion,
Tag: &thanosTag,
Image: &thanosImage,
},
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
image := sset.Spec.Template.Spec.Containers[2].Image
expected := "my-registry/thanos:latest"
if image != expected {
t.Fatalf("Explicit Thanos image should have precedence. Unexpected container image.\n\nExpected: %s\n\nGot: %s", expected, image)
}
}
}
func TestThanosResourcesNotSet(t *testing.T) {
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
Thanos: &monitoringv1.ThanosSpec{},
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
res := sset.Spec.Template.Spec.Containers[2].Resources
if res.Limits != nil || res.Requests != nil {
t.Fatalf("Unexpected resources defined. \n\nExpected: nil\n\nGot: %v, %v", res.Limits, res.Requests)
}
}
func TestThanosResourcesSet(t *testing.T) {
expected := v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("125m"),
v1.ResourceMemory: resource.MustParse("75Mi"),
},
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("100m"),
v1.ResourceMemory: resource.MustParse("50Mi"),
},
}
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
Thanos: &monitoringv1.ThanosSpec{
Resources: expected,
},
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
actual := sset.Spec.Template.Spec.Containers[2].Resources
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("Unexpected resources defined. \n\nExpected: %v\n\nGot: %v", expected, actual)
}
}
func TestThanosNoObjectStorage(t *testing.T) {
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
Thanos: &monitoringv1.ThanosSpec{},
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
if sset.Spec.Template.Spec.Containers[0].Name != "prometheus" {
t.Fatalf("expected 1st containers to be prometheus, got %s", sset.Spec.Template.Spec.Containers[0].Name)
}
if sset.Spec.Template.Spec.Containers[2].Name != "thanos-sidecar" {
t.Fatalf("expected 3rd container to be thanos-sidecar, got %s", sset.Spec.Template.Spec.Containers[2].Name)
}
for _, arg := range sset.Spec.Template.Spec.Containers[0].Args {
if strings.HasPrefix(arg, "--storage.tsdb.max-block-duration=2h") {
t.Fatal("Prometheus compaction should be disabled")
}
}
for _, arg := range sset.Spec.Template.Spec.Containers[2].Args {
if strings.HasPrefix(arg, "--tsdb.path=") {
t.Fatal("--tsdb.path argument should not be given to the Thanos sidecar")
}
}
for _, vol := range sset.Spec.Template.Spec.Containers[2].VolumeMounts {
if vol.MountPath == storageDir {
t.Fatal("Prometheus data volume should not be mounted in the Thanos sidecar")
}
}
}
func TestThanosObjectStorage(t *testing.T) {
testKey := "thanos-config-secret-test"
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
Thanos: &monitoringv1.ThanosSpec{
ObjectStorageConfig: &v1.SecretKeySelector{
Key: testKey,
},
},
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
if sset.Spec.Template.Spec.Containers[0].Name != "prometheus" {
t.Fatalf("expected 1st containers to be prometheus, got %s", sset.Spec.Template.Spec.Containers[0].Name)
}
if sset.Spec.Template.Spec.Containers[2].Name != "thanos-sidecar" {
t.Fatalf("expected 3rd containers to be thanos-sidecar, got %s", sset.Spec.Template.Spec.Containers[2].Name)
}
var containsEnvVar bool
for _, env := range sset.Spec.Template.Spec.Containers[2].Env {
if env.Name == "OBJSTORE_CONFIG" {
if env.ValueFrom.SecretKeyRef.Key == testKey {
containsEnvVar = true
break
}
}
}
if !containsEnvVar {
t.Fatalf("Thanos sidecar is missing expected OBJSTORE_CONFIG env var with correct value")
}
{
var containsArg bool
const expectedArg = "--objstore.config=$(OBJSTORE_CONFIG)"
for _, arg := range sset.Spec.Template.Spec.Containers[2].Args {
if arg == expectedArg {
containsArg = true
break
}
}
if !containsArg {
t.Fatalf("Thanos sidecar is missing expected argument: %s", expectedArg)
}
}
{
var containsArg bool
const expectedArg = "--storage.tsdb.max-block-duration=2h"
for _, arg := range sset.Spec.Template.Spec.Containers[0].Args {
if arg == expectedArg {
containsArg = true
break
}
}
if !containsArg {
t.Fatalf("Prometheus is missing expected argument: %s", expectedArg)
}
}
{
var found bool
for _, arg := range sset.Spec.Template.Spec.Containers[2].Args {
if strings.HasPrefix(arg, "--tsdb.path=") {
found = true
break
}
}
if !found {
t.Fatalf("--tsdb.path argument should be given to the Thanos sidecar, got %q", strings.Join(sset.Spec.Template.Spec.Containers[3].Args, " "))
}
}
{
var found bool
for _, vol := range sset.Spec.Template.Spec.Containers[2].VolumeMounts {
if vol.MountPath == storageDir {
found = true
break
}
}
if !found {
t.Fatal("Prometheus data volume should be mounted in the Thanos sidecar")
}
}
}
func TestThanosTracing(t *testing.T) {
testKey := "thanos-config-secret-test"
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
Thanos: &monitoringv1.ThanosSpec{
TracingConfig: &v1.SecretKeySelector{
Key: testKey,
},
},
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
if sset.Spec.Template.Spec.Containers[0].Name != "prometheus" {
t.Fatalf("expected 1st containers to be prometheus, got %s", sset.Spec.Template.Spec.Containers[0].Name)
}
if sset.Spec.Template.Spec.Containers[2].Name != "thanos-sidecar" {
t.Fatalf("expected 3rd containers to be thanos-sidecar, got %s", sset.Spec.Template.Spec.Containers[2].Name)
}
var containsEnvVar bool
for _, env := range sset.Spec.Template.Spec.Containers[2].Env {
if env.Name == "TRACING_CONFIG" {
if env.ValueFrom.SecretKeyRef.Key == testKey {
containsEnvVar = true
break
}
}
}
if !containsEnvVar {
t.Fatalf("Thanos sidecar is missing expected TRACING_CONFIG env var with correct value")
}
{
var containsArg bool
const expectedArg = "--tracing.config=$(TRACING_CONFIG)"
for _, arg := range sset.Spec.Template.Spec.Containers[2].Args {
if arg == expectedArg {
containsArg = true
break
}
}
if !containsArg {
t.Fatalf("Thanos sidecar is missing expected argument: %s", expectedArg)
}
}
}
func TestRetentionSize(t *testing.T) {
tests := []struct {
version string
specRetentionSize string
expectedRetentionArg string
shouldContain bool
}{
{"v1.8.2", "2M", "--storage.tsdb.retention.size=2M", false},
{"v1.8.2", "1Gi", "--storage.tsdb.retention.size=1Gi", false},
{"v2.5.0", "2M", "--storage.tsdb.retention.size=2M", false},
{"v2.5.0", "1Gi", "--storage.tsdb.retention.size=1Gi", false},
{"v2.7.0", "2M", "--storage.tsdb.retention.size=2M", true},
{"v2.7.0", "1Gi", "--storage.tsdb.retention.size=1Gi", true},
}
for _, test := range tests {
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
Version: test.version,
RetentionSize: test.specRetentionSize,
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatal(err)
}
promArgs := sset.Spec.Template.Spec.Containers[0].Args
found := false
for _, flag := range promArgs {
if flag == test.expectedRetentionArg {
found = true
break
}
}
if found != test.shouldContain {
if test.shouldContain {
t.Fatalf("expected Prometheus args to contain %v, but got %v", test.expectedRetentionArg, promArgs)
} else {
t.Fatalf("expected Prometheus args to NOT contain %v, but got %v", test.expectedRetentionArg, promArgs)
}
}
}
}
func TestRetention(t *testing.T) {
tests := []struct {
version string
specRetention string
expectedRetentionArg string
}{
{"v1.8.2", "", "-storage.local.retention=24h"},
{"v1.8.2", "1d", "-storage.local.retention=1d"},
{"v2.5.0", "", "--storage.tsdb.retention=24h"},
{"v2.5.0", "1d", "--storage.tsdb.retention=1d"},
{"v2.7.0", "", "--storage.tsdb.retention.time=24h"},
{"v2.7.0", "1d", "--storage.tsdb.retention.time=1d"},
}
for _, test := range tests {
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
Version: test.version,
Retention: test.specRetention,
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatal(err)
}
promArgs := sset.Spec.Template.Spec.Containers[0].Args
found := false
for _, flag := range promArgs {
if flag == test.expectedRetentionArg {
found = true
break
}
}
if !found {
t.Fatalf("expected Prometheus args to contain %v, but got %v", test.expectedRetentionArg, promArgs)
}
}
}
func TestSidecarsNoCPULimits(t *testing.T) {
testConfig := &Config{
ConfigReloaderImage: "jimmidyson/configmap-reload:latest",
ConfigReloaderCPU: "0",
ConfigReloaderMemory: "50Mi",
PrometheusConfigReloaderImage: "quay.io/prometheus-operator/prometheus-config-reloader:latest",
PrometheusDefaultBaseImage: "quay.io/prometheus/prometheus",
ThanosDefaultBaseImage: "quay.io/thanos/thanos:v0.7.0",
}
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{},
}, testConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
expectedResources := v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceMemory: resource.MustParse("50Mi"),
},
Requests: v1.ResourceList{
v1.ResourceMemory: resource.MustParse("50Mi"),
},
}
for _, c := range sset.Spec.Template.Spec.Containers {
if (c.Name == "prometheus-config-reloader" || c.Name == "rules-configmap-reloader") && !reflect.DeepEqual(c.Resources, expectedResources) {
t.Fatal("Unexpected resource requests/limits set, when none should be set.")
}
}
}
func TestSidecarsNoMemoryLimits(t *testing.T) {
testConfig := &Config{
ConfigReloaderImage: "jimmidyson/configmap-reload:latest",
ConfigReloaderCPU: "100m",
ConfigReloaderMemory: "0",
PrometheusConfigReloaderImage: "quay.io/prometheus-operator/prometheus-config-reloader:latest",
PrometheusDefaultBaseImage: "quay.io/prometheus/prometheus",
ThanosDefaultBaseImage: "quay.io/thanos/thanos:v0.7.0",
}
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{},
}, testConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
expectedResources := v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("100m"),
},
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("100m"),
},
}
for _, c := range sset.Spec.Template.Spec.Containers {
if (c.Name == "prometheus-config-reloader" || c.Name == "rules-configmap-reloader") && !reflect.DeepEqual(c.Resources, expectedResources) {
t.Fatal("Unexpected resource requests/limits set, when none should be set.")
}
}
}
func TestAdditionalContainers(t *testing.T) {
// The base to compare everything against
baseSet, err := makeStatefulSet(monitoringv1.Prometheus{}, defaultTestConfig, nil, "")
require.NoError(t, err)
// Add an extra container
addSset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
Containers: []v1.Container{
{
Name: "extra-container",
},
},
},
}, defaultTestConfig, nil, "")
require.NoError(t, err)
if len(baseSet.Spec.Template.Spec.Containers)+1 != len(addSset.Spec.Template.Spec.Containers) {
t.Fatalf("container count mismatch")
}
// Adding a new container with the same name results in a merge and just one container
const existingContainerName = "prometheus"
const containerImage = "madeUpContainerImage"
modSset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
Containers: []v1.Container{
{
Name: existingContainerName,
Image: containerImage,
},
},
},
}, defaultTestConfig, nil, "")
require.NoError(t, err)
if len(baseSet.Spec.Template.Spec.Containers) != len(modSset.Spec.Template.Spec.Containers) {
t.Fatalf("container count mismatch. container %s was added instead of merged", existingContainerName)
}
// Check that adding a container with an existing name results in a single patched container.
for _, c := range modSset.Spec.Template.Spec.Containers {
if c.Name == existingContainerName && c.Image != containerImage {
t.Fatalf("expected container %s to have the image %s but got %s", existingContainerName, containerImage, c.Image)
}
}
}
func TestWALCompression(t *testing.T) {
var (
tr = true
fa = false
)
tests := []struct {
version string
enabled *bool
expectedArg string
shouldContain bool
}{
// Nil should not have either flag.
{"v1.8.2", nil, "-no-storage.tsdb.wal-compression", false},
{"v1.8.2", nil, "-storage.tsdb.wal-compression", false},
{"v1.8.2", &fa, "-no-storage.tsdb.wal-compression", false},
{"v1.8.2", &tr, "-storage.tsdb.wal-compression", false},
{"v2.10.0", nil, "--no-storage.tsdb.wal-compression", false},
{"v2.10.0", nil, "--storage.tsdb.wal-compression", false},
{"v2.10.0", &fa, "--no-storage.tsdb.wal-compression", false},
{"v2.10.0", &tr, "--storage.tsdb.wal-compression", false},
{"v2.11.0", nil, "--no-storage.tsdb.wal-compression", false},
{"v2.11.0", nil, "--storage.tsdb.wal-compression", false},
{"v2.11.0", &fa, "--no-storage.tsdb.wal-compression", true},
{"v2.11.0", &tr, "--storage.tsdb.wal-compression", true},
}
for _, test := range tests {
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
Version: test.version,
WALCompression: test.enabled,
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatal(err)
}
promArgs := sset.Spec.Template.Spec.Containers[0].Args
found := false
for _, flag := range promArgs {
if flag == test.expectedArg {
found = true
break
}
}
if found != test.shouldContain {
if test.shouldContain {
t.Fatalf("expected Prometheus args to contain %v, but got %v", test.expectedArg, promArgs)
} else {
t.Fatalf("expected Prometheus args to NOT contain %v, but got %v", test.expectedArg, promArgs)
}
}
}
}
func TestThanosListenLocal(t *testing.T) {
sset, err := makeStatefulSet(monitoringv1.Prometheus{
Spec: monitoringv1.PrometheusSpec{
Thanos: &monitoringv1.ThanosSpec{
ListenLocal: true,
},
},
}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
foundGrpcFlag := false
foundHTTPFlag := false
for _, flag := range sset.Spec.Template.Spec.Containers[2].Args {
if flag == "--grpc-address=127.0.0.1:10901" {
foundGrpcFlag = true
}
if flag == "--http-address=127.0.0.1:10902" {
foundHTTPFlag = true
}
}
if !foundGrpcFlag || !foundHTTPFlag {
t.Fatal("Thanos not listening on loopback when it should.")
}
}
func TestTerminationPolicy(t *testing.T) {
sset, err := makeStatefulSet(monitoringv1.Prometheus{Spec: monitoringv1.PrometheusSpec{}}, defaultTestConfig, nil, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
for _, c := range sset.Spec.Template.Spec.Containers {
if c.TerminationMessagePolicy != v1.TerminationMessageFallbackToLogsOnError {
t.Fatalf("Unexpected TermintationMessagePolicy. Expected %v got %v", v1.TerminationMessageFallbackToLogsOnError, c.TerminationMessagePolicy)
}
}
}