blob: ba48ec21912521fda8f999362622f5f2d4180b29 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 controllers
import (
b64 "encoding/base64"
"fmt"
"github.com/apache/solr-operator/controllers/util"
zkv1beta1 "github.com/pravega/zookeeper-operator/pkg/apis/zookeeper/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"reflect"
"strings"
"testing"
solr "github.com/apache/solr-operator/api/v1beta1"
"github.com/onsi/gomega"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
extv1 "k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
func emptyRequests(requests chan reconcile.Request) {
for len(requests) > 0 {
<-requests
}
}
func expectStatefulSet(t *testing.T, g *gomega.GomegaWithT, requests chan reconcile.Request, expectedRequest reconcile.Request, statefulSetKey types.NamespacedName) *appsv1.StatefulSet {
stateful := &appsv1.StatefulSet{}
g.Eventually(func() error { return testClient.Get(context.TODO(), statefulSetKey, stateful) }, timeout).
Should(gomega.Succeed())
// Verify the statefulSet Specs
testMapContainsOther(t, "StatefulSet pod template selector", stateful.Spec.Template.Labels, stateful.Spec.Selector.MatchLabels)
assert.GreaterOrEqual(t, len(stateful.Spec.Selector.MatchLabels), 1, "StatefulSet pod template selector must have at least 1 label")
// Delete the StatefulSet and expect Reconcile to be called for StatefulSet deletion
g.Expect(testClient.Delete(context.TODO(), stateful)).NotTo(gomega.HaveOccurred())
g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedRequest)))
g.Eventually(func() error { return testClient.Get(context.TODO(), statefulSetKey, stateful) }, timeout).
Should(gomega.Succeed())
// Manually delete StatefulSet since GC isn't enabled in the test control plane
g.Eventually(func() error { return testClient.Delete(context.TODO(), stateful) }, timeout).
Should(gomega.MatchError("statefulsets.apps \"" + statefulSetKey.Name + "\" not found"))
return stateful
}
func expectNoStatefulSet(g *gomega.GomegaWithT, statefulSetKey types.NamespacedName) {
stateful := &appsv1.StatefulSet{}
g.Eventually(func() error { return testClient.Get(context.TODO(), statefulSetKey, stateful) }, timeout).
Should(gomega.MatchError("StatefulSet.apps \"" + statefulSetKey.Name + "\" not found"))
}
func expectService(t *testing.T, g *gomega.GomegaWithT, requests chan reconcile.Request, expectedRequest reconcile.Request, serviceKey types.NamespacedName, selectorLables map[string]string) *corev1.Service {
service := &corev1.Service{}
g.Eventually(func() error { return testClient.Get(context.TODO(), serviceKey, service) }, timeout).
Should(gomega.Succeed())
assert.Equal(t, selectorLables, service.Spec.Selector, "Service is not pointing to the correct Pods.")
// Delete the Service and expect Reconcile to be called for Service deletion
g.Expect(testClient.Delete(context.TODO(), service)).NotTo(gomega.HaveOccurred())
g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedRequest)))
g.Eventually(func() error { return testClient.Get(context.TODO(), serviceKey, service) }, timeout).
Should(gomega.Succeed())
// Manually delete Service since GC isn't enabled in the test control plane
g.Eventually(func() error { return testClient.Delete(context.TODO(), service) }, timeout).
Should(gomega.MatchError("services \"" + serviceKey.Name + "\" not found"))
return service
}
func expectNoService(g *gomega.GomegaWithT, serviceKey types.NamespacedName, message string) {
service := &corev1.Service{}
g.Eventually(func() error { return testClient.Get(context.TODO(), serviceKey, service) }, timeout).
Should(gomega.MatchError("Service \""+serviceKey.Name+"\" not found"), message)
}
func expectIngress(g *gomega.GomegaWithT, requests chan reconcile.Request, expectedRequest reconcile.Request, ingressKey types.NamespacedName) *extv1.Ingress {
ingress := &extv1.Ingress{}
g.Eventually(func() error { return testClient.Get(context.TODO(), ingressKey, ingress) }, timeout).
Should(gomega.Succeed())
// Delete the Ingress and expect Reconcile to be called for Ingress deletion
g.Expect(testClient.Delete(context.TODO(), ingress)).NotTo(gomega.HaveOccurred())
g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedRequest)))
g.Eventually(func() error { return testClient.Get(context.TODO(), ingressKey, ingress) }, timeout).
Should(gomega.Succeed())
// Manually delete Ingress since GC isn't enabled in the test control plane
g.Eventually(func() error { return testClient.Delete(context.TODO(), ingress) }, timeout).
Should(gomega.MatchError("ingresses.extensions \"" + ingressKey.Name + "\" not found"))
return ingress
}
func expectNoIngress(g *gomega.GomegaWithT, ingressKey types.NamespacedName) {
ingress := &extv1.Ingress{}
g.Eventually(func() error { return testClient.Get(context.TODO(), ingressKey, ingress) }, timeout).
Should(gomega.MatchError("Ingress.extensions \"" + ingressKey.Name + "\" not found"))
}
func expectConfigMap(t *testing.T, g *gomega.GomegaWithT, requests chan reconcile.Request, expectedRequest reconcile.Request, configMapKey types.NamespacedName, configMapData map[string]string) *corev1.ConfigMap {
configMap := &corev1.ConfigMap{}
g.Eventually(func() error { return testClient.Get(context.TODO(), configMapKey, configMap) }, timeout).
Should(gomega.Succeed())
// Verify the ConfigMap Specs
for k, d := range configMapData {
assert.Equal(t, configMap.Data[k], d, "ConfigMap does not have the correct data.")
}
// Delete the ConfigMap and expect Reconcile to be called for Deployment deletion
g.Expect(testClient.Delete(context.TODO(), configMap)).NotTo(gomega.HaveOccurred())
g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedRequest)))
g.Eventually(func() error { return testClient.Get(context.TODO(), configMapKey, configMap) }, timeout).
Should(gomega.Succeed())
// Manually delete ConfigMap since GC isn't enabled in the test control plane
g.Eventually(func() error { return testClient.Delete(context.TODO(), configMap) }, timeout).
Should(gomega.MatchError("configmaps \"" + configMapKey.Name + "\" not found"))
return configMap
}
func expectNoConfigMap(g *gomega.GomegaWithT, configMapKey types.NamespacedName) {
configMap := &corev1.ConfigMap{}
g.Eventually(func() error { return testClient.Get(context.TODO(), configMapKey, configMap) }, timeout).
Should(gomega.MatchError("ConfigMap \"" + configMapKey.Name + "\" not found"))
}
func expectDeployment(t *testing.T, g *gomega.GomegaWithT, requests chan reconcile.Request, expectedRequest reconcile.Request, deploymentKey types.NamespacedName, configMapName string) *appsv1.Deployment {
deploy := &appsv1.Deployment{}
g.Eventually(func() error { return testClient.Get(context.TODO(), deploymentKey, deploy) }, timeout).
Should(gomega.Succeed())
// Verify the deployment Specs
testMapContainsOther(t, "Deployment pod template selector", deploy.Spec.Template.Labels, deploy.Spec.Selector.MatchLabels)
assert.GreaterOrEqual(t, len(deploy.Spec.Selector.MatchLabels), 1, "Deployment pod template selector must have at least 1 label")
if configMapName != "" {
if assert.LessOrEqual(t, 1, len(deploy.Spec.Template.Spec.Volumes), "Deployment should have at least 1 volume, the configMap. Less were found.") {
assert.Equal(t, configMapName, deploy.Spec.Template.Spec.Volumes[0].ConfigMap.Name, "Deployment configMap volume has the wrong name.")
}
}
// Delete the Deployment and expect Reconcile to be called for Deployment deletion
g.Expect(testClient.Delete(context.TODO(), deploy)).NotTo(gomega.HaveOccurred())
g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedRequest)))
g.Eventually(func() error { return testClient.Get(context.TODO(), deploymentKey, deploy) }, timeout).
Should(gomega.Succeed())
// Manually delete Deployment since GC isn't enabled in the test control plane
g.Eventually(func() error { return testClient.Delete(context.TODO(), deploy) }, timeout).
Should(gomega.MatchError("deployments.apps \"" + deploymentKey.Name + "\" not found"))
return deploy
}
func verifyUserSuppliedTLSConfig(t *testing.T, tls *solr.SolrTLSOptions, expectedKeystorePasswordSecretName string, expectedKeystorePasswordSecretKey string, expectedTlsSecretName string, needsPkcs12InitContainer bool) {
assert.NotNil(t, tls)
assert.Equal(t, expectedKeystorePasswordSecretName, tls.KeyStorePasswordSecret.Name)
assert.Equal(t, expectedKeystorePasswordSecretKey, tls.KeyStorePasswordSecret.Key)
assert.Equal(t, expectedTlsSecretName, tls.PKCS12Secret.Name)
assert.Equal(t, "keystore.p12", tls.PKCS12Secret.Key)
// is there a separate truststore?
expectedTrustStorePath := ""
if tls.TrustStoreSecret != nil {
expectedTrustStorePath = util.DefaultTrustStorePath + "/" + tls.TrustStoreSecret.Key
}
expectTLSEnvVars(t, util.TLSEnvVars(tls, needsPkcs12InitContainer), expectedKeystorePasswordSecretName, expectedKeystorePasswordSecretKey, needsPkcs12InitContainer, expectedTrustStorePath)
}
func createTLSOptions(tlsSecretName string, keystorePassKey string, restartOnTLSSecretUpdate bool) *solr.SolrTLSOptions {
return &solr.SolrTLSOptions{
KeyStorePasswordSecret: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{Name: tlsSecretName},
Key: keystorePassKey,
},
PKCS12Secret: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{Name: tlsSecretName},
Key: util.Pkcs12KeystoreFile,
},
RestartOnTLSSecretUpdate: restartOnTLSSecretUpdate,
}
}
func createMockTLSSecret(ctx context.Context, apiClient client.Client, secretName string, secretKey string, ns string, keystorePasswordKey string) (corev1.Secret, error) {
secretData := map[string][]byte{}
secretData[secretKey] = []byte(b64.StdEncoding.EncodeToString([]byte("mock keystore")))
secretData[util.TLSCertKey] = []byte(b64.StdEncoding.EncodeToString([]byte("mock tls.crt")))
if keystorePasswordKey != "" {
secretData[keystorePasswordKey] = []byte(b64.StdEncoding.EncodeToString([]byte("mock keystore password")))
}
mockTLSSecret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{Name: secretName, Namespace: ns},
Data: secretData,
Type: corev1.SecretTypeOpaque,
}
err := apiClient.Create(ctx, &mockTLSSecret)
return mockTLSSecret, err
}
func createBasicAuthSecret(name string, key string, ns string) *corev1.Secret {
secretData := map[string][]byte{corev1.BasicAuthUsernameKey: []byte(key), corev1.BasicAuthPasswordKey: []byte("secret password")}
return &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: ns}, Data: secretData, Type: corev1.SecretTypeBasicAuth}
}
// Ensures all the TLS env vars, volume mounts and initContainers are setup for the PodTemplateSpec
func expectTLSConfigOnPodTemplate(t *testing.T, tls *solr.SolrTLSOptions, podTemplate *corev1.PodTemplateSpec, needsPkcs12InitContainer bool) *corev1.Container {
assert.NotNil(t, podTemplate.Spec.Volumes)
var keystoreVol *corev1.Volume = nil
for _, vol := range podTemplate.Spec.Volumes {
if vol.Name == "keystore" {
keystoreVol = &vol
break
}
}
assert.NotNil(t, keystoreVol, fmt.Sprintf("keystore volume not found in pod template; volumes: %v", podTemplate.Spec.Volumes))
assert.NotNil(t, keystoreVol.VolumeSource.Secret, "Didn't find TLS keystore volume in sts config!")
assert.Equal(t, tls.PKCS12Secret.Name, keystoreVol.VolumeSource.Secret.SecretName)
// check the SOLR_SSL_ related env vars on the sts
assert.NotNil(t, podTemplate.Spec.Containers)
assert.True(t, len(podTemplate.Spec.Containers) > 0)
mainContainer := podTemplate.Spec.Containers[0]
assert.NotNil(t, mainContainer, "Didn't find the main solrcloud-node container in the sts!")
assert.NotNil(t, mainContainer.Env, "Didn't find the main solrcloud-node container in the sts!")
// is there a separate truststore?
expectedTrustStorePath := ""
if tls.TrustStoreSecret != nil {
expectedTrustStorePath = util.DefaultTrustStorePath + "/" + tls.TrustStoreSecret.Key
}
expectTLSEnvVars(t, mainContainer.Env, tls.KeyStorePasswordSecret.Name, tls.KeyStorePasswordSecret.Key, needsPkcs12InitContainer, expectedTrustStorePath)
// different trust store?
if tls.TrustStoreSecret != nil {
var truststoreVol *corev1.Volume = nil
for _, vol := range podTemplate.Spec.Volumes {
if vol.Name == "truststore" {
truststoreVol = &vol
break
}
}
assert.NotNil(t, truststoreVol, fmt.Sprintf("truststore volume not found in pod template; volumes: %v", podTemplate.Spec.Volumes))
assert.NotNil(t, truststoreVol.VolumeSource.Secret, "Didn't find TLS truststore volume in sts config!")
assert.Equal(t, tls.TrustStoreSecret.Name, truststoreVol.VolumeSource.Secret.SecretName)
}
// initContainers
if needsPkcs12InitContainer {
var pkcs12Vol *corev1.Volume = nil
for _, vol := range podTemplate.Spec.Volumes {
if vol.Name == "pkcs12" {
pkcs12Vol = &vol
break
}
}
assert.NotNil(t, pkcs12Vol, "Didn't find TLS keystore volume in sts config!")
assert.NotNil(t, pkcs12Vol.EmptyDir, "pkcs12 vol should by an emptyDir")
assert.NotNil(t, podTemplate.Spec.InitContainers)
var expInitContainer *corev1.Container = nil
for _, cnt := range podTemplate.Spec.InitContainers {
if cnt.Name == "gen-pkcs12-keystore" {
expInitContainer = &cnt
break
}
}
expCmd := "openssl pkcs12 -export -in /var/solr/tls/tls.crt -in /var/solr/tls/ca.crt -inkey /var/solr/tls/tls.key -out /var/solr/tls/pkcs12/keystore.p12 -passout pass:${SOLR_SSL_KEY_STORE_PASSWORD}"
assert.NotNil(t, expInitContainer, "Didn't find the gen-pkcs12-keystore InitContainer in the sts!")
assert.Equal(t, expCmd, expInitContainer.Command[2])
}
if tls.ClientAuth == solr.Need {
// verify the probes use a command with SSL opts
tlsProps := "-Djavax.net.ssl.keyStore=$SOLR_SSL_KEY_STORE -Djavax.net.ssl.keyStorePassword=$SOLR_SSL_KEY_STORE_PASSWORD " +
"-Djavax.net.ssl.trustStore=$SOLR_SSL_TRUST_STORE -Djavax.net.ssl.trustStorePassword=$SOLR_SSL_TRUST_STORE_PASSWORD"
assert.NotNil(t, mainContainer.LivenessProbe, "main container should have a liveness probe defined")
assert.NotNil(t, mainContainer.LivenessProbe.Exec, "liveness probe should have an exec when auth is enabled")
assert.True(t, strings.Contains(mainContainer.LivenessProbe.Exec.Command[2], tlsProps), "liveness probe should invoke java with SSL opts")
assert.NotNil(t, mainContainer.ReadinessProbe, "main container should have a readiness probe defined")
assert.NotNil(t, mainContainer.ReadinessProbe.Exec, "readiness probe should have an exec when auth is enabled")
assert.True(t, strings.Contains(mainContainer.ReadinessProbe.Exec.Command[2], tlsProps), "readiness probe should invoke java with SSL opts")
}
return &mainContainer // return as a convenience in case tests want to do more checking on the main container
}
// ensure the TLS related env vars are set for the Solr pod
func expectTLSEnvVars(t *testing.T, envVars []corev1.EnvVar, expectedKeystorePasswordSecretName string, expectedKeystorePasswordSecretKey string, needsPkcs12InitContainer bool, expectedTruststorePath string) {
assert.NotNil(t, envVars)
envVars = filterVarsByName(envVars, func(n string) bool {
return strings.HasPrefix(n, "SOLR_SSL_")
})
assert.True(t, len(envVars) == 9)
expectedKeystorePath := util.DefaultKeyStorePath + "/keystore.p12"
if needsPkcs12InitContainer {
expectedKeystorePath = util.DefaultWritableKeyStorePath + "/keystore.p12"
}
if expectedTruststorePath == "" {
expectedTruststorePath = expectedKeystorePath
}
for _, envVar := range envVars {
if envVar.Name == "SOLR_SSL_ENABLED" {
assert.Equal(t, "true", envVar.Value)
}
if envVar.Name == "SOLR_SSL_KEY_STORE" {
assert.Equal(t, expectedKeystorePath, envVar.Value)
}
if envVar.Name == "SOLR_SSL_TRUST_STORE" {
assert.Equal(t, expectedTruststorePath, envVar.Value)
}
if envVar.Name == "SOLR_SSL_KEY_STORE_PASSWORD" {
assert.NotNil(t, envVar.ValueFrom)
assert.NotNil(t, envVar.ValueFrom.SecretKeyRef)
assert.Equal(t, expectedKeystorePasswordSecretName, envVar.ValueFrom.SecretKeyRef.Name)
assert.Equal(t, expectedKeystorePasswordSecretKey, envVar.ValueFrom.SecretKeyRef.Key)
}
}
}
// filter env vars by name using a supplied match function
func filterVarsByName(envVars []corev1.EnvVar, f func(string) bool) []corev1.EnvVar {
filtered := make([]corev1.EnvVar, 0)
for _, v := range envVars {
if f(v.Name) {
filtered = append(filtered, v)
}
}
return filtered
}
func testPodEnvVariables(t *testing.T, expectedEnvVars map[string]string, foundEnvVars []corev1.EnvVar) {
testGenericPodEnvVariables(t, expectedEnvVars, foundEnvVars, "SOLR_OPTS")
}
func testMetricsPodEnvVariables(t *testing.T, expectedEnvVars map[string]string, foundEnvVars []corev1.EnvVar) {
testGenericPodEnvVariables(t, expectedEnvVars, foundEnvVars, "JAVA_OPTS")
}
func testGenericPodEnvVariables(t *testing.T, expectedEnvVars map[string]string, foundEnvVars []corev1.EnvVar, lastVarName string) {
matchCount := 0
for _, envVar := range foundEnvVars {
if expectedVal, match := expectedEnvVars[envVar.Name]; match {
matchCount += 1
assert.Equal(t, expectedVal, envVar.Value, "Wrong value for env variable '%s' in podSpec", envVar.Name)
}
}
assert.Equal(t, len(expectedEnvVars), matchCount, "Not all expected env variables found in podSpec")
assert.Equal(t, lastVarName, foundEnvVars[len(foundEnvVars)-1].Name, lastVarName+" must be the last envVar set, as it uses other envVars.")
}
func testPodTolerations(t *testing.T, expectedTolerations []corev1.Toleration, foundTolerations []corev1.Toleration) {
assert.True(t, reflect.DeepEqual(expectedTolerations, foundTolerations), "Expected tolerations and found tolerations don't match")
}
func testPodProbe(t *testing.T, expectedProbe *corev1.Probe, foundProbe *corev1.Probe) {
assert.True(t, reflect.DeepEqual(expectedProbe, foundProbe), "Expected probe and found probe don't match")
}
func testMapsEqual(t *testing.T, mapName string, expected map[string]string, found map[string]string) {
assert.Equal(t, expected, found, "Expected and found %s are not the same", mapName)
}
func testMapContainsOther(t *testing.T, mapName string, base map[string]string, other map[string]string) {
for k, v := range other {
foundV, foundExists := base[k]
assert.True(t, foundExists, "Expected key '%s' does not exist in found %s", k, mapName)
if foundExists {
assert.Equal(t, v, foundV, "Wrong value for %s key '%s'", mapName, k)
}
}
}
func testACLEnvVars(t *testing.T, actualEnvVars []corev1.EnvVar) {
/*
This test verifies ACL related env vars are set correctly and in the correct order, but expects a very specific config to be used in your test SolrCloud config:
AllACL: &solr.ZookeeperACL{
SecretRef: "secret-name",
UsernameKey: "user",
PasswordKey: "pass",
},
ReadOnlyACL: &solr.ZookeeperACL{
SecretRef: "read-secret-name",
UsernameKey: "read-only-user",
PasswordKey: "read-only-pass",
},
*/
f := false
zkAclEnvVars := []corev1.EnvVar{
{
Name: "ZK_ALL_ACL_USERNAME",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{Name: "secret-name"},
Key: "user",
Optional: &f,
},
},
},
{
Name: "ZK_ALL_ACL_PASSWORD",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{Name: "secret-name"},
Key: "pass",
Optional: &f,
},
},
},
{
Name: "ZK_READ_ACL_USERNAME",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{Name: "read-secret-name"},
Key: "read-only-user",
Optional: &f,
},
},
},
{
Name: "ZK_READ_ACL_PASSWORD",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{Name: "read-secret-name"},
Key: "read-only-pass",
Optional: &f,
},
},
},
{
Name: "SOLR_ZK_CREDS_AND_ACLS",
Value: "-DzkACLProvider=org.apache.solr.common.cloud.VMParamsAllAndReadonlyDigestZkACLProvider -DzkCredentialsProvider=org.apache.solr.common.cloud.VMParamsSingleSetCredentialsDigestZkCredentialsProvider -DzkDigestUsername=$(ZK_ALL_ACL_USERNAME) -DzkDigestPassword=$(ZK_ALL_ACL_PASSWORD) -DzkDigestReadonlyUsername=$(ZK_READ_ACL_USERNAME) -DzkDigestReadonlyPassword=$(ZK_READ_ACL_PASSWORD)",
ValueFrom: nil,
},
}
assert.Equal(t, zkAclEnvVars, actualEnvVars, "ZK ACL Env Vars are not correct")
}
func cleanupTest(g *gomega.GomegaWithT, namespace string) {
deleteOpts := []client.DeleteAllOfOption{
client.InNamespace(namespace),
}
cleanupObjects := []runtime.Object{
// Solr Operator CRDs, modify this list whenever CRDs are added/deleted
&solr.SolrCloud{}, &solr.SolrBackup{}, &solr.SolrPrometheusExporter{},
// Dependency CRDs
&zkv1beta1.ZookeeperCluster{},
// All dependent Kubernetes types, in order of dependence (deployment then replicaSet then pod)
&corev1.ConfigMap{}, &batchv1.Job{}, &extv1.Ingress{},
&corev1.PersistentVolumeClaim{}, &corev1.PersistentVolume{},
&appsv1.StatefulSet{}, &appsv1.Deployment{}, &appsv1.ReplicaSet{}, &corev1.Pod{}, &corev1.PersistentVolumeClaim{},
&corev1.Secret{},
}
cleanupTestObjects(g, namespace, deleteOpts, cleanupObjects)
// Delete all Services separately (https://github.com/kubernetes/kubernetes/pull/85802#issuecomment-561239845)
opts := []client.ListOption{
client.InNamespace(namespace),
}
services := &corev1.ServiceList{}
g.Eventually(func() error { return testClient.List(context.TODO(), services, opts...) }, timeout).Should(gomega.Succeed())
for _, item := range services.Items {
g.Eventually(func() error { return testClient.Delete(context.TODO(), &item) }, timeout).Should(gomega.Succeed())
}
}
func cleanupTestObjects(g *gomega.GomegaWithT, namespace string, deleteOpts []client.DeleteAllOfOption, objects []runtime.Object) {
// Delete all SolrClouds
for _, obj := range objects {
g.Eventually(func() error { return testClient.DeleteAllOf(context.TODO(), obj, deleteOpts...) }, timeout).Should(gomega.Succeed())
}
}
var (
testKubeDomain = "kube.domain.com"
testDomain = "test.domain.com"
testAdditionalDomains = []string{
"test1.domain.com",
"test2.domain.com",
}
testPodAnnotations = map[string]string{
"testP1": "valueP1",
"testP2": "valueP2",
}
testPodLabels = map[string]string{
"testP3": "valueP3",
"testP4": "valueP4",
}
testSSAnnotations = map[string]string{
"testSS1": "valueSS1",
"testSS2": "valueSS2",
}
testSSLabels = map[string]string{
"testSS3": "valueSS3",
"testSS4": "valueSS4",
}
testIngressLabels = map[string]string{
"testI1": "valueI1",
"testI2": "valueI2",
}
testIngressAnnotations = map[string]string{
"testI3": "valueI3",
"testI4": "valueI4",
}
testCommonServiceLabels = map[string]string{
"testCS1": "valueCS1",
"testCS2": "valueCS2",
}
testCommonServiceAnnotations = map[string]string{
"testCS3": "valueCS3",
"testCS4": "valueCS4",
}
testHeadlessServiceLabels = map[string]string{
"testHS1": "valueHS1",
"testHS2": "valueHS2",
}
testHeadlessServiceAnnotations = map[string]string{
"testHS3": "valueHS3",
"testHS4": "valueHS4",
}
testNodeServiceLabels = map[string]string{
"testNS1": "valueNS1",
"testNS2": "valueNS2",
}
testNodeServiceAnnotations = map[string]string{
"testNS3": "valueNS3",
"testNS4": "valueNS4",
}
testConfigMapLabels = map[string]string{
"testCM1": "valueCM1",
"testCM2": "valueCM2",
}
testConfigMapAnnotations = map[string]string{
"testCM3": "valueCM3",
"testCM4": "valueCM4",
}
testDeploymentAnnotations = map[string]string{
"testD1": "valueD1",
"testD2": "valueD2",
}
testDeploymentLabels = map[string]string{
"testD3": "valueD3",
"testD4": "valueD4",
}
testMetricsServiceLabels = map[string]string{
"testS1": "valueS1",
"testS2": "valueS2",
}
testMetricsServiceAnnotations = map[string]string{
"testS3": "valueS3",
"testS4": "valueS4",
}
testNodeSelectors = map[string]string{
"beta.kubernetes.io/arch": "amd64",
"beta.kubernetes.io/os": "linux",
"solrclouds": "true",
}
testProbeLivenessNonDefaults = &corev1.Probe{
InitialDelaySeconds: 20,
TimeoutSeconds: 1,
SuccessThreshold: 1,
FailureThreshold: 3,
PeriodSeconds: 10,
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
Scheme: corev1.URISchemeHTTP,
Path: "/solr/admin/info/system",
Port: intstr.FromInt(8983),
},
},
}
testProbeReadinessNonDefaults = &corev1.Probe{
InitialDelaySeconds: 15,
TimeoutSeconds: 1,
SuccessThreshold: 1,
FailureThreshold: 3,
PeriodSeconds: 5,
Handler: corev1.Handler{
TCPSocket: &corev1.TCPSocketAction{
Port: intstr.FromInt(8983),
},
},
}
testProbeStartup = &corev1.Probe{
InitialDelaySeconds: 1,
TimeoutSeconds: 1,
SuccessThreshold: 1,
FailureThreshold: 5,
PeriodSeconds: 5,
Handler: corev1.Handler{
Exec: &corev1.ExecAction{
Command: []string{
"ls",
},
},
},
}
testTolerations = []corev1.Toleration{
{
Effect: "NoSchedule",
Key: "node-restriction.kubernetes.io/dedicated",
Value: "solrclouds",
Operator: "Exists",
},
}
testTolerationsPromExporter = []corev1.Toleration{
{
Effect: "NoSchedule",
Operator: "Exists",
},
}
testPriorityClass = "p4"
testImagePullSecretName = "MAIN_SECRET"
testAdditionalImagePullSecrets = []corev1.LocalObjectReference{
{Name: "ADDITIONAL_SECRET_1"},
{Name: "ADDITIONAL_SECRET_2"},
}
testTerminationGracePeriodSeconds = int64(50)
extraVars = []corev1.EnvVar{
{
Name: "VAR_1",
Value: "VAL_1",
},
{
Name: "VAR_2",
ValueFrom: &corev1.EnvVarSource{
FieldRef: &corev1.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.name",
},
},
},
}
one = int64(1)
two = int64(2)
four = int32(4)
five = int32(5)
podSecurityContext = corev1.PodSecurityContext{
RunAsUser: &one,
RunAsGroup: &two,
}
extraVolumes = []solr.AdditionalVolume{
{
Name: "vol1",
Source: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
DefaultContainerMount: &corev1.VolumeMount{
Name: "ignore",
ReadOnly: false,
MountPath: "/test/mount/path",
SubPath: "sub/",
},
},
}
affinity = &corev1.Affinity{
PodAffinity: &corev1.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{
{
TopologyKey: "testKey",
},
},
PreferredDuringSchedulingIgnoredDuringExecution: nil,
},
}
resources = corev1.ResourceRequirements{
Limits: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceCPU: *resource.NewMilliQuantity(5300, resource.DecimalSI),
},
Requests: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceEphemeralStorage: resource.MustParse("5Gi"),
},
}
extraContainers1 = []corev1.Container{
{
Name: "container1",
Image: "image1",
TerminationMessagePolicy: "File",
TerminationMessagePath: "/dev/termination-log",
ImagePullPolicy: "Always",
},
{
Name: "container2",
Image: "image2",
TerminationMessagePolicy: "File",
TerminationMessagePath: "/dev/termination-log",
ImagePullPolicy: "Always",
},
}
extraContainers2 = []corev1.Container{
{
Name: "container3",
Image: "image3",
TerminationMessagePolicy: "File",
TerminationMessagePath: "/dev/termination-log",
ImagePullPolicy: "Always",
},
{
Name: "container4",
Image: "image4",
TerminationMessagePolicy: "File",
TerminationMessagePath: "/dev/termination-log",
ImagePullPolicy: "Always",
},
}
testServiceAccountName = "test-service-account"
)