pkg/controller/elasticsearch/initcontainer/prepare_fs_script.go (129 lines of code) (raw):
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License 2.0;
// you may not use this file except in compliance with the Elastic License 2.0.
package initcontainer
import (
"bytes"
"fmt"
"text/template"
"github.com/elastic/cloud-on-k8s/v3/pkg/controller/common/volume"
)
// TemplateParams are the parameters manipulated in the scriptTemplate
type TemplateParams struct {
// SharedVolumes are directories to persist in shared volumes
PluginVolumes volume.SharedVolumeArray
// LinkedFiles are files to link individually
LinkedFiles LinkedFilesArray
// ChownToElasticsearch are paths that need to be chowned to the Elasticsearch user/group.
ChownToElasticsearch []string
// ExpectedAnnotations are the annotations expected on the Pod. Init script waits until these annotations are set by
// the operator.
ExpectedAnnotations *string
// InitContainerTransportCertificatesSecretVolumeMountPath is the path to the volume in the init container that
// contains the transport certificates.
InitContainerTransportCertificatesSecretVolumeMountPath string
// TransportCertificatesSecretVolumeMountPath is the path to the volume in the es container that contains the
// transport certificates.
TransportCertificatesSecretVolumeMountPath string
}
// RenderScriptTemplate renders scriptTemplate using the given TemplateParams
func RenderScriptTemplate(params TemplateParams) (string, error) {
tplBuffer := bytes.Buffer{}
if err := scriptTemplate.Execute(&tplBuffer, params); err != nil {
return "", err
}
return tplBuffer.String(), nil
}
const (
PrepareFsScriptConfigKey = "prepare-fs.sh"
UnsupportedDistroExitCode = 42
)
// scriptTemplate is the main script to be run
// in the prepare-fs init container before ES starts
var scriptTemplate = template.Must(template.New("").Parse(
`#!/usr/bin/env bash
set -eu
{{ if .ExpectedAnnotations }}
function annotations_exist() {
expected_annotations=("$@")
for expected_annotation in "${expected_annotations[@]}"; do
annotation_exists=$(grep -c "^${expected_annotation}=" /mnt/elastic-internal/downward-api/annotations)
if [ "${annotation_exists}" -eq 0 ]; then
return 1
fi
done
return 0
}
{{ end }}
# the operator only works with the default ES distribution
license=/usr/share/elasticsearch/LICENSE.txt
if [[ ! -f $license || $(grep -Exc "ELASTIC LICENSE AGREEMENT|Elastic License 2.0" $license) -ne 1 ]]; then
>&2 echo "unsupported_distribution"
exit ` + fmt.Sprintf("%d", UnsupportedDistroExitCode) + `
fi
# compute time in seconds since the given start time
function duration() {
local start=$1
end=$(date +%s)
echo $((end-start))
}
######################
# START #
######################
script_start=$(date +%s)
echo "Starting init script"
######################
# Files persistence #
######################
# Persist the content of bin/, config/ and plugins/ to a volume,
# so installed plugins files can to be used by the ES container
mv_start=$(date +%s)
{{range .PluginVolumes.Array}}
if [[ -z "$(ls -A {{.ContainerMountPath}})" ]]; then
echo "Empty dir {{.ContainerMountPath}}"
else
echo "Copying {{.ContainerMountPath}}/* to {{.InitContainerMountPath}}/"
# Use "yes" and "-f" as we want the init container to be idempotent and not to fail when executed more than once.
yes | cp -avf {{.ContainerMountPath}}/* {{.InitContainerMountPath}}/
fi
{{end}}
echo "Files copy duration: $(duration $mv_start) sec."
######################
# Config linking #
######################
# Link individual files from their mount location into the config dir
# to a volume, to be used by the ES container
ln_start=$(date +%s)
{{range .LinkedFiles.Array}}
echo "Linking {{.Source}} to {{.Target}}"
ln -sf {{.Source}} {{.Target}}
{{end}}
echo "File linking duration: $(duration $ln_start) sec."
######################
# Volumes chown #
######################
# chown the data and logs volume to the elasticsearch user
# only done when running as root, other cases should be handled
# with a proper security context
chown_start=$(date +%s)
if [[ $EUID -eq 0 ]]; then
{{range .ChownToElasticsearch}}
echo "chowning {{.}} to elasticsearch:elasticsearch"
chown -v elasticsearch:elasticsearch {{.}}
{{end}}
fi
echo "chown duration: $(duration $chown_start) sec."
######################
# Wait for certs #
######################
INIT_CONTAINER_LOCAL_KEY_PATH={{ .InitContainerTransportCertificatesSecretVolumeMountPath }}/${POD_NAME}.tls.key
DISABLED_CERT_MARKER={{ .InitContainerTransportCertificatesSecretVolumeMountPath }}/transport.certs.disabled
# wait for the transport certificates to show up
echo "waiting for the transport certificates (${INIT_CONTAINER_LOCAL_KEY_PATH} or ${DISABLED_CERT_MARKER})"
wait_start=$(date +%s)
while [ ! -f ${INIT_CONTAINER_LOCAL_KEY_PATH} ] && [ ! -f ${DISABLED_CERT_MARKER} ]
do
sleep 0.2
done
echo "wait duration: $(duration wait_start) sec."
if [ -f ${DISABLED_CERT_MARKER} ]; then
echo "Skipped transport certificate check because of .spec.transport.tls.selfSignedCerts.disabled"
fi
{{ if .ExpectedAnnotations }}
echo "Waiting for the following annotations to be set on Pod: {{ .ExpectedAnnotations }}"
ln_start=$(date +%s)
declare -a expected_annotations
expected_annotations=({{ .ExpectedAnnotations }})
while ! annotations_exist "${expected_annotations[@]}"; do sleep 2; done
echo "Waiting for annotations duration: $(duration $ln_start) sec."
{{ end }}
######################
# End #
######################
echo "Init script successful"
echo "Script duration: $(duration $script_start) sec."
`))