blob: 343c71edbcc47f1643adb2cee4ba1d782cf07fad [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 util
import (
solr "github.com/apache/solr-operator/api/v1beta1"
"github.com/go-logr/logr"
zk "github.com/pravega/zookeeper-operator/pkg/apis/zookeeper/v1beta1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
"strings"
)
var log = logf.Log.WithName("controller")
// GenerateZookeeperCluster returns a new ZookeeperCluster pointer generated for the SolrCloud instance
// object: SolrCloud instance
// zkSpec: the spec of the ZookeeperCluster to generate
func GenerateZookeeperCluster(solrCloud *solr.SolrCloud, zkSpec *solr.ZookeeperSpec) *zk.ZookeeperCluster {
labels := solrCloud.SharedLabelsWith(solrCloud.GetLabels())
labels["technology"] = solr.ZookeeperTechnologyLabel
zkCluster := &zk.ZookeeperCluster{
ObjectMeta: metav1.ObjectMeta{
Name: solrCloud.ProvidedZookeeperName(),
Namespace: solrCloud.GetNamespace(),
Labels: labels,
},
Spec: zk.ZookeeperClusterSpec{
Image: zk.ContainerImage{
Repository: zkSpec.Image.Repository,
Tag: zkSpec.Image.Tag,
PullPolicy: zkSpec.Image.PullPolicy,
},
Labels: labels,
Replicas: *zkSpec.Replicas,
Ports: []corev1.ContainerPort{
{
Name: "client",
ContainerPort: 2181,
},
{
Name: "quorum",
ContainerPort: 2888,
},
{
Name: "leader-election",
ContainerPort: 3888,
},
},
},
}
// Add storage information for the ZK Cluster
if zkSpec.Persistence != nil {
// If persistence is provided, then chose it.
zkCluster.Spec.StorageType = "persistence"
} else if zkSpec.Ephemeral != nil {
// If ephemeral is provided, then chose it.
zkCluster.Spec.StorageType = "ephemeral"
} else {
// If neither option is provided, default to the option used for solr (which defaults to ephemeral)
if solrCloud.Spec.StorageOptions.PersistentStorage != nil {
zkCluster.Spec.StorageType = "persistence"
} else {
zkCluster.Spec.StorageType = "ephemeral"
}
}
// Set the persistence/ephemeral options if necessary
if zkSpec.Persistence != nil && zkCluster.Spec.StorageType == "persistence" {
zkCluster.Spec.Persistence = zkSpec.Persistence
} else if zkSpec.Ephemeral != nil && zkCluster.Spec.StorageType == "ephemeral" {
zkCluster.Spec.Ephemeral = zkSpec.Ephemeral
}
// Append Pod Policies if provided by user
if zkSpec.ZookeeperPod.Affinity != nil {
zkCluster.Spec.Pod.Affinity = zkSpec.ZookeeperPod.Affinity
}
if zkSpec.ZookeeperPod.Resources.Limits != nil || zkSpec.ZookeeperPod.Resources.Requests != nil {
zkCluster.Spec.Pod.Resources = zkSpec.ZookeeperPod.Resources
}
if zkSpec.ZookeeperPod.Tolerations != nil {
zkCluster.Spec.Pod.Tolerations = zkSpec.ZookeeperPod.Tolerations
}
if zkSpec.ZookeeperPod.NodeSelector != nil {
zkCluster.Spec.Pod.NodeSelector = zkSpec.ZookeeperPod.NodeSelector
}
if zkSpec.ZookeeperPod.Env != nil {
zkCluster.Spec.Pod.Env = zkSpec.ZookeeperPod.Env
}
if solrCloud.Spec.SolrAddressability.KubeDomain != "" {
zkCluster.Spec.KubernetesClusterDomain = solrCloud.Spec.SolrAddressability.KubeDomain
}
if zkSpec.ZookeeperPod.ServiceAccountName != "" {
zkCluster.Spec.Pod.ServiceAccountName = zkSpec.ZookeeperPod.ServiceAccountName
}
return zkCluster
}
// CopyZookeeperClusterFields copies the owned fields from one ZookeeperCluster to another
// Returns true if the fields copied from don't match to.
func CopyZookeeperClusterFields(from, to *zk.ZookeeperCluster, logger logr.Logger) bool {
logger = logger.WithValues("kind", "zookeeperCluster")
requireUpdate := CopyLabelsAndAnnotations(&from.ObjectMeta, &to.ObjectMeta, logger)
if !DeepEqualWithNils(to.Spec.Replicas, from.Spec.Replicas) {
logger.Info("Update required because field changed", "field", "Spec.Replicas", "from", to.Spec.Replicas, "to", from.Spec.Replicas)
requireUpdate = true
}
to.Spec.Replicas = from.Spec.Replicas
if !DeepEqualWithNils(to.Spec.Image.Repository, from.Spec.Image.Repository) {
logger.Info("Update required because field changed", "field", "Spec.Image.Repository", "from", to.Spec.Image.Repository, "to", from.Spec.Image.Repository)
requireUpdate = true
}
to.Spec.Image.Repository = from.Spec.Image.Repository
if from.Spec.Image.Tag != "" && !DeepEqualWithNils(to.Spec.Image.Tag, from.Spec.Image.Tag) {
logger.Info("Update required because field changed", "field", "Spec.Image.Tag", "from", to.Spec.Image.Tag, "to", from.Spec.Image.Tag)
requireUpdate = true
}
to.Spec.Image.Tag = from.Spec.Image.Tag
if !DeepEqualWithNils(to.Spec.Image.PullPolicy, from.Spec.Image.PullPolicy) {
logger.Info("Update required because field changed", "field", "Spec.Image.PullPolicy", "from", to.Spec.Image.PullPolicy, "to", from.Spec.Image.PullPolicy)
requireUpdate = true
}
to.Spec.Image.PullPolicy = from.Spec.Image.PullPolicy
if from.Spec.Persistence != nil {
if to.Spec.Persistence == nil {
logger.Info("Update required because field changed", "field", "Spec.Persistence", "from", to.Spec.Persistence, "to", from.Spec.Persistence)
requireUpdate = true
to.Spec.Persistence = from.Spec.Persistence
} else {
if !DeepEqualWithNils(to.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Requests, from.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Requests) {
logger.Info("Update required because field changed", "field", "Spec.Persistence.PersistentVolumeClaimSpec.Resources.Requests", "from", to.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Requests, "to", from.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Requests)
requireUpdate = true
to.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Requests = from.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Requests
}
if !DeepEqualWithNils(to.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Limits, from.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Limits) {
logger.Info("Update required because field changed", "field", "Spec.Persistence.PersistentVolumeClaimSpec.Resources.Limits", "from", to.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Limits, "to", from.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Limits)
requireUpdate = true
to.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Limits = from.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Limits
}
if !DeepEqualWithNils(to.Spec.Persistence.PersistentVolumeClaimSpec.AccessModes, from.Spec.Persistence.PersistentVolumeClaimSpec.AccessModes) {
logger.Info("Update required because field changed", "field", "Spec.Persistence.PersistentVolumeClaimSpec.AccessModes", "from", to.Spec.Persistence.PersistentVolumeClaimSpec.AccessModes, "to", from.Spec.Persistence.PersistentVolumeClaimSpec.AccessModes)
requireUpdate = true
to.Spec.Persistence.PersistentVolumeClaimSpec.AccessModes = from.Spec.Persistence.PersistentVolumeClaimSpec.AccessModes
}
if !DeepEqualWithNils(to.Spec.Persistence.PersistentVolumeClaimSpec.StorageClassName, from.Spec.Persistence.PersistentVolumeClaimSpec.StorageClassName) {
logger.Info("Update required because field changed", "field", "Spec.Persistence.PersistentVolumeClaimSpec.StorageClassName", "from", to.Spec.Persistence.PersistentVolumeClaimSpec.StorageClassName, "to", from.Spec.Persistence.PersistentVolumeClaimSpec.StorageClassName)
requireUpdate = true
to.Spec.Persistence.PersistentVolumeClaimSpec.StorageClassName = from.Spec.Persistence.PersistentVolumeClaimSpec.StorageClassName
}
if !DeepEqualWithNils(to.Spec.Persistence.VolumeReclaimPolicy, from.Spec.Persistence.VolumeReclaimPolicy) {
logger.Info("Update required because field changed", "field", "Spec.Persistence.VolumeReclaimPolicy", "from", to.Spec.Persistence.VolumeReclaimPolicy, "to", from.Spec.Persistence.VolumeReclaimPolicy)
requireUpdate = true
to.Spec.Persistence.VolumeReclaimPolicy = from.Spec.Persistence.VolumeReclaimPolicy
}
}
}
/* Uncomment when the following PR is merged in: https://github.com/pravega/zookeeper-operator/pull/64
Otherwise the ZK Operator will create persistence when none is given, and this will infinitely loop.
else if to.Spec.Persistence != nil {
requireUpdate = true
to.Spec.Persistence = nil
}*/
if !DeepEqualWithNils(to.Spec.Pod.Resources, from.Spec.Pod.Resources) {
logger.Info("Update required because field changed", "field", "Spec.Pod.Resources", "from", to.Spec.Pod.Resources, "to", from.Spec.Pod.Resources)
requireUpdate = true
to.Spec.Pod.Resources = from.Spec.Pod.Resources
}
if !DeepEqualWithNils(to.Spec.Pod.Env, from.Spec.Pod.Env) {
logger.Info("Update required because field changed", "field", "Spec.Pod.Env", "from", to.Spec.Pod.Env, "to", from.Spec.Pod.Env)
requireUpdate = true
to.Spec.Pod.Env = from.Spec.Pod.Env
}
if !DeepEqualWithNils(to.Spec.Pod.Tolerations, from.Spec.Pod.Tolerations) {
logger.Info("Update required because field changed", "field", "Spec.Pod.Tolerations", "from", to.Spec.Pod.Tolerations, "to", from.Spec.Pod.Tolerations)
requireUpdate = true
to.Spec.Pod.Tolerations = from.Spec.Pod.Tolerations
}
if !DeepEqualWithNils(to.Spec.Pod.NodeSelector, from.Spec.Pod.NodeSelector) {
logger.Info("Update required because field changed", "field", "Spec.Pod.NodeSelector", "from", to.Spec.Pod.NodeSelector, "to", from.Spec.Pod.NodeSelector)
requireUpdate = true
to.Spec.Pod.NodeSelector = from.Spec.Pod.NodeSelector
}
// The Zookeeper operator defaults the pod affinity, so we only want to require an update if the requested affinity is not null
// But always change it so that the change will be picked up if another change is done.
if !DeepEqualWithNils(to.Spec.Pod.Affinity, from.Spec.Pod.Affinity) && from.Spec.Pod.Affinity != nil {
logger.Info("Update required because field changed", "field", "Spec.Pod.Affinity", "from", to.Spec.Pod.Affinity, "to", from.Spec.Pod.Affinity)
requireUpdate = true
}
to.Spec.Pod.Affinity = from.Spec.Pod.Affinity
// The Zookeeper Operator defaults the serviceAccountName to "default", therefore only update if either of the following
// - The new serviceAccountName is not empty
// - The old serviceAccountName is not "default", so we know we want to switch to the default value.
if !DeepEqualWithNils(to.Spec.Pod.ServiceAccountName, from.Spec.Pod.ServiceAccountName) && (from.Spec.Pod.ServiceAccountName != "" || to.Spec.Pod.ServiceAccountName != "default") {
logger.Info("Update required because field changed", "field", "Spec.Pod.ServiceAccountName", "from", to.Spec.Pod.ServiceAccountName, "to", from.Spec.Pod.ServiceAccountName)
requireUpdate = true
to.Spec.Pod.ServiceAccountName = from.Spec.Pod.ServiceAccountName
}
if !DeepEqualWithNils(to.Spec.KubernetesClusterDomain, from.Spec.KubernetesClusterDomain) && from.Spec.KubernetesClusterDomain != "" {
logger.Info("Update required because field changed", "field", "Spec.KubernetesClusterDomain", "from", to.Spec.KubernetesClusterDomain, "to", from.Spec.KubernetesClusterDomain)
requireUpdate = true
}
to.Spec.KubernetesClusterDomain = from.Spec.KubernetesClusterDomain
return requireUpdate
}
// AddACLsToEnv creates the neccessary environment variables for using ZK ACLs, and returns whether ACLs were provided.
// info: Zookeeper Connection Information
func AddACLsToEnv(allACL *solr.ZookeeperACL, readOnlyACL *solr.ZookeeperACL) (hasACLs bool, envVars []corev1.EnvVar) {
if allACL == nil && readOnlyACL == nil {
return false, envVars
}
f := false
var zkDigests []string
if allACL != nil {
envVars = append(envVars,
corev1.EnvVar{
Name: "ZK_ALL_ACL_USERNAME",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: allACL.SecretRef,
},
Key: allACL.UsernameKey,
Optional: &f,
},
},
},
corev1.EnvVar{
Name: "ZK_ALL_ACL_PASSWORD",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: allACL.SecretRef,
},
Key: allACL.PasswordKey,
Optional: &f,
},
},
})
zkDigests = append(zkDigests, "-DzkDigestUsername=$(ZK_ALL_ACL_USERNAME)", "-DzkDigestPassword=$(ZK_ALL_ACL_PASSWORD)")
}
if readOnlyACL != nil {
envVars = append(envVars,
corev1.EnvVar{
Name: "ZK_READ_ACL_USERNAME",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: readOnlyACL.SecretRef,
},
Key: readOnlyACL.UsernameKey,
Optional: &f,
},
},
},
corev1.EnvVar{
Name: "ZK_READ_ACL_PASSWORD",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: readOnlyACL.SecretRef,
},
Key: readOnlyACL.PasswordKey,
Optional: &f,
},
},
})
zkDigests = append(zkDigests, "-DzkDigestReadonlyUsername=$(ZK_READ_ACL_USERNAME)", "-DzkDigestReadonlyPassword=$(ZK_READ_ACL_PASSWORD)")
}
envVars = append(envVars,
corev1.EnvVar{
Name: "SOLR_ZK_CREDS_AND_ACLS",
Value: "-DzkACLProvider=org.apache.solr.common.cloud.VMParamsAllAndReadonlyDigestZkACLProvider -DzkCredentialsProvider=org.apache.solr.common.cloud.VMParamsSingleSetCredentialsDigestZkCredentialsProvider " + strings.Join(zkDigests, " "),
})
return true, envVars
}