func GenerateSolrPrometheusExporterDeployment()

in controllers/util/prometheus_exporter_util.go [52:396]


func GenerateSolrPrometheusExporterDeployment(solrPrometheusExporter *solr.SolrPrometheusExporter, solrConnectionInfo SolrConnectionInfo, solrCloudImage *solr.ContainerImage, configXmlMd5 string, tls *TLSCerts, basicAuthMd5 string) *appsv1.Deployment {
	gracePeriodTerm := int64(10)
	singleReplica := int32(1)
	fsGroup := int64(SolrMetricsPort)

	labels := solrPrometheusExporter.SharedLabelsWith(solrPrometheusExporter.GetLabels())
	var annotations map[string]string
	selectorLabels := solrPrometheusExporter.SharedLabels()

	labels["technology"] = solr.SolrPrometheusExporterTechnologyLabel
	selectorLabels["technology"] = solr.SolrPrometheusExporterTechnologyLabel

	podLabels := labels
	var podAnnotations map[string]string
	var imagePullSecrets []corev1.LocalObjectReference

	customDeploymentOptions := solrPrometheusExporter.Spec.CustomKubeOptions.DeploymentOptions
	if nil != customDeploymentOptions {
		labels = MergeLabelsOrAnnotations(labels, customDeploymentOptions.Labels)
		annotations = customDeploymentOptions.Annotations
	}

	customPodOptions := solrPrometheusExporter.Spec.CustomKubeOptions.PodOptions.DeepCopy()
	if nil != customPodOptions {
		podLabels = MergeLabelsOrAnnotations(podLabels, customPodOptions.Labels)
		podAnnotations = customPodOptions.Annotations
		imagePullSecrets = customPodOptions.ImagePullSecrets
	}

	envVars := []corev1.EnvVar{
		{
			Name: "POD_NAME",
			ValueFrom: &corev1.EnvVarSource{
				FieldRef: &corev1.ObjectFieldSelector{
					FieldPath:  "metadata.name",
					APIVersion: "v1",
				},
			},
		},
		{
			Name: "POD_NAMESPACE",
			ValueFrom: &corev1.EnvVarSource{
				FieldRef: &corev1.ObjectFieldSelector{
					FieldPath:  "metadata.namespace",
					APIVersion: "v1",
				},
			},
		},
		{
			Name:  "PORT",
			Value: strconv.Itoa(SolrMetricsPort),
		},
		{
			Name:  "NUM_THREADS",
			Value: strconv.Itoa(int(solrPrometheusExporter.Spec.NumThreads)),
		},
	}
	var allJavaOpts []string

	var solrVolumes []corev1.Volume
	var volumeMounts []corev1.VolumeMount

	if solrPrometheusExporter.Spec.ScrapeInterval > 0 {
		envVars = append(envVars, corev1.EnvVar{Name: "SCRAPE_INTERVAL", Value: strconv.Itoa(int(solrPrometheusExporter.Spec.ScrapeInterval))})
	}

	// Setup the solrConnectionInfo
	if solrConnectionInfo.CloudZkConnnectionInfo != nil {
		envVars = append(envVars, corev1.EnvVar{Name: "ZK_HOST", Value: solrConnectionInfo.CloudZkConnnectionInfo.ZkConnectionString()})

		// Add ACL information, if given, through Env Vars
		if hasACLs, aclEnvs := AddACLsToEnv(solrConnectionInfo.CloudZkConnnectionInfo.AllACL, solrConnectionInfo.CloudZkConnnectionInfo.ReadOnlyACL); hasACLs {
			envVars = append(envVars, aclEnvs...)

			// The $SOLR_ZK_CREDS_AND_ACLS parameter does not get picked up when running the Prometheus Exporter, it must be added to the JAVA_OPTS.
			allJavaOpts = append(allJavaOpts, "$(SOLR_ZK_CREDS_AND_ACLS)")
		}
	} else if solrConnectionInfo.StandaloneAddress != "" {
		envVars = append(envVars, corev1.EnvVar{Name: "SOLR_URL", Value: solrConnectionInfo.StandaloneAddress})
	}

	// Only add the config if it is passed in from the user. Otherwise, use the default.
	if solrPrometheusExporter.Spec.Config != "" ||
		(solrPrometheusExporter.Spec.CustomKubeOptions.ConfigMapOptions != nil && solrPrometheusExporter.Spec.CustomKubeOptions.ConfigMapOptions.ProvidedConfigMap != "") {
		configMapName := solrPrometheusExporter.MetricsConfigMapName()
		if solrPrometheusExporter.Spec.CustomKubeOptions.ConfigMapOptions != nil && solrPrometheusExporter.Spec.CustomKubeOptions.ConfigMapOptions.ProvidedConfigMap != "" {
			configMapName = solrPrometheusExporter.Spec.CustomKubeOptions.ConfigMapOptions.ProvidedConfigMap
		}
		solrVolumes = []corev1.Volume{{
			Name: "solr-prometheus-exporter-xml",
			VolumeSource: corev1.VolumeSource{
				ConfigMap: &corev1.ConfigMapVolumeSource{
					LocalObjectReference: corev1.LocalObjectReference{
						Name: configMapName,
					},
					Items: []corev1.KeyToPath{
						{
							Key:  PrometheusExporterConfigMapKey,
							Path: PrometheusExporterConfigMapKey,
						},
					},
				},
			},
		}}

		volumeMounts = []corev1.VolumeMount{{Name: "solr-prometheus-exporter-xml", MountPath: "/opt/solr-exporter", ReadOnly: true}}

		envVars = append(envVars, corev1.EnvVar{Name: "CONFIG_FILE", Value: "/opt/solr-exporter/" + PrometheusExporterConfigMapKey})
	} else {
		envVars = append(envVars, corev1.EnvVar{Name: "CONFIG_FILE", Value: "/opt/solr/contrib/prometheus-exporter/conf/solr-exporter-config.xml"})
	}

	entrypoint := DefaultPrometheusExporterEntrypoint
	if solrPrometheusExporter.Spec.ExporterEntrypoint != "" {
		entrypoint = solrPrometheusExporter.Spec.ExporterEntrypoint
	}

	// Add Custom EnvironmentVariables to the solr container
	if nil != customPodOptions {
		// Add environment variables to container
		envVars = append(envVars, customPodOptions.EnvVariables...)

		// Add Custom Volumes to pod
		for _, volume := range customPodOptions.Volumes {
			// Only add the container mount if one has been provided.
			if volume.DefaultContainerMount != nil {
				volume.DefaultContainerMount.Name = volume.Name
				volumeMounts = append(volumeMounts, *volume.DefaultContainerMount)
			}

			solrVolumes = append(solrVolumes, corev1.Volume{
				Name:         volume.Name,
				VolumeSource: volume.Source,
			})
		}
	}

	// basic auth enabled?
	if solrPrometheusExporter.Spec.SolrReference.BasicAuthSecret != "" {
		envVars = append(envVars, BasicAuthEnvVars(solrPrometheusExporter.Spec.SolrReference.BasicAuthSecret)...)
		allJavaOpts = append(allJavaOpts, "-Dbasicauth=$(BASIC_AUTH_USER):$(BASIC_AUTH_PASS)")
		allJavaOpts = append(allJavaOpts, "-Dsolr.httpclient.builder.factory=org.apache.solr.client.solrj.impl.PreemptiveBasicAuthClientBuilderFactory")
	}

	// the order of env vars in the array is important for the $(var) syntax to work
	// since JAVA_OPTS refers to $(SOLR_SSL_*) if TLS is enabled, it needs to be last
	if len(allJavaOpts) > 0 {
		envVars = append(envVars, corev1.EnvVar{Name: "JAVA_OPTS", Value: strings.Join(allJavaOpts, " ")})
	}

	containerImage := solrPrometheusExporter.Spec.Image
	if containerImage == nil {
		containerImage = solrCloudImage
	}

	defaultProbeHandler := corev1.ProbeHandler{
		HTTPGet: &corev1.HTTPGetAction{
			Scheme: corev1.URISchemeHTTP,
			// TODO: When 9.0 is the minimum supported version, this can be "/-/healthy"
			Path: "/metrics?name[]=",
			Port: intstr.FromInt(SolrMetricsPort),
		},
	}

	var containerSecurityContext *corev1.SecurityContext
	if customPodOptions != nil {
		containerSecurityContext = customPodOptions.ContainerSecurityContext
	}

	containers := []corev1.Container{
		{
			Name:            SolrPrometheusExporterContainer,
			Image:           containerImage.ToImageName(),
			ImagePullPolicy: containerImage.PullPolicy,
			Ports:           []corev1.ContainerPort{{ContainerPort: SolrMetricsPort, Name: SolrMetricsPortName, Protocol: corev1.ProtocolTCP}},
			VolumeMounts:    volumeMounts,
			Command:         []string{entrypoint},
			Env:             envVars,

			StartupProbe: &corev1.Probe{
				ProbeHandler:        defaultProbeHandler,
				InitialDelaySeconds: 2,
				TimeoutSeconds:      2,
				PeriodSeconds:       2,
				SuccessThreshold:    1,
				FailureThreshold:    10,
			},
			LivenessProbe: &corev1.Probe{
				ProbeHandler:     defaultProbeHandler,
				TimeoutSeconds:   2,
				PeriodSeconds:    10,
				SuccessThreshold: 1,
				FailureThreshold: 3,
			},
			ReadinessProbe: &corev1.Probe{
				ProbeHandler:     defaultProbeHandler,
				TimeoutSeconds:   2,
				PeriodSeconds:    5,
				SuccessThreshold: 1,
				FailureThreshold: 3,
			},
			SecurityContext: containerSecurityContext,
		},
	}

	var initContainers []corev1.Container
	if customPodOptions != nil {
		if len(customPodOptions.SidecarContainers) > 0 {
			containers = append(containers, customPodOptions.SidecarContainers...)
		}

		if len(customPodOptions.InitContainers) > 0 {
			initContainers = customPodOptions.InitContainers
		}
	}

	// track the MD5 of the custom exporter config in the pod spec annotations,
	// so we get a rolling restart when the configMap changes
	if configXmlMd5 != "" {
		if podAnnotations == nil {
			podAnnotations = make(map[string]string, 1)
		}
		podAnnotations[PrometheusExporterConfigXmlMd5Annotation] = configXmlMd5
	}

	// if the basic-auth secret changes, we want to restart the deployment pods
	if basicAuthMd5 != "" {
		if podAnnotations == nil {
			podAnnotations = make(map[string]string, 1)
		}
		podAnnotations[BasicAuthMd5Annotation] = basicAuthMd5
	}

	deployment := &appsv1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Name:        solrPrometheusExporter.MetricsDeploymentName(),
			Namespace:   solrPrometheusExporter.GetNamespace(),
			Labels:      labels,
			Annotations: annotations,
		},
		Spec: appsv1.DeploymentSpec{
			Selector: &metav1.LabelSelector{
				MatchLabels: selectorLabels,
			},
			Replicas: &singleReplica,
			Template: corev1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels:      podLabels,
					Annotations: podAnnotations,
				},
				Spec: corev1.PodSpec{
					TerminationGracePeriodSeconds: &gracePeriodTerm,
					SecurityContext: &corev1.PodSecurityContext{
						FSGroup: &fsGroup,
					},
					Volumes:        solrVolumes,
					InitContainers: initContainers,
					Containers:     containers,
				},
			},
		},
	}

	if containerImage.ImagePullSecret != "" {
		imagePullSecrets = append(
			imagePullSecrets,
			corev1.LocalObjectReference{Name: containerImage.ImagePullSecret},
		)
	}

	deployment.Spec.Template.Spec.ImagePullSecrets = imagePullSecrets

	if nil != customPodOptions {
		metricsContainer := &deployment.Spec.Template.Spec.Containers[0]
		if customPodOptions.ServiceAccountName != "" {
			deployment.Spec.Template.Spec.ServiceAccountName = customPodOptions.ServiceAccountName
		}

		if customPodOptions.Affinity != nil {
			deployment.Spec.Template.Spec.Affinity = customPodOptions.Affinity
		}

		if customPodOptions.Resources.Limits != nil || customPodOptions.Resources.Requests != nil {
			metricsContainer.Resources = customPodOptions.Resources
		}

		if customPodOptions.PodSecurityContext != nil {
			deployment.Spec.Template.Spec.SecurityContext = customPodOptions.PodSecurityContext
		}

		if customPodOptions.Tolerations != nil {
			deployment.Spec.Template.Spec.Tolerations = customPodOptions.Tolerations
		}

		if customPodOptions.NodeSelector != nil {
			deployment.Spec.Template.Spec.NodeSelector = customPodOptions.NodeSelector
		}

		if customPodOptions.PriorityClassName != "" {
			deployment.Spec.Template.Spec.PriorityClassName = customPodOptions.PriorityClassName
		}

		if customPodOptions.TerminationGracePeriodSeconds != nil {
			deployment.Spec.Template.Spec.TerminationGracePeriodSeconds = customPodOptions.TerminationGracePeriodSeconds
		}

		if customPodOptions.ReadinessProbe != nil {
			// Default Prometheus Exporter container does not contain a readinessProbe, so copy the livenessProbe
			baseProbe := metricsContainer.LivenessProbe.DeepCopy()
			metricsContainer.ReadinessProbe = customizeProbe(baseProbe, *customPodOptions.ReadinessProbe)
		}

		if customPodOptions.StartupProbe != nil {
			// Default Prometheus Exporter container does not contain a startupProbe, so copy the livenessProbe
			baseProbe := metricsContainer.LivenessProbe.DeepCopy()
			metricsContainer.StartupProbe = customizeProbe(baseProbe, *customPodOptions.StartupProbe)
		}

		if customPodOptions.LivenessProbe != nil {
			metricsContainer.LivenessProbe = customizeProbe(metricsContainer.LivenessProbe, *customPodOptions.LivenessProbe)
		}

		if customPodOptions.Lifecycle != nil {
			metricsContainer.Lifecycle = customPodOptions.Lifecycle
		}

		if len(customPodOptions.TopologySpreadConstraints) > 0 {
			deployment.Spec.Template.Spec.TopologySpreadConstraints = customPodOptions.TopologySpreadConstraints

			// Set the label selector for constraints to the statefulSet label selector, if none is provided
			for i := range deployment.Spec.Template.Spec.TopologySpreadConstraints {
				if deployment.Spec.Template.Spec.TopologySpreadConstraints[i].LabelSelector == nil {
					deployment.Spec.Template.Spec.TopologySpreadConstraints[i].LabelSelector = deployment.Spec.Selector.DeepCopy()
				}
			}
		}
	}

	// Enrich the deployment definition to allow the exporter to make requests to TLS enabled Solr pods
	if tls != nil && tls.ClientConfig != nil {
		tls.enableTLSOnExporterDeployment(deployment)
	}

	return deployment
}