Skip to content

Commit 803f4a4

Browse files
authored
[FEATURE] Autogenerate SMTP configuration for Grafana (#795)
1 parent 7200b4a commit 803f4a4

File tree

3 files changed

+219
-107
lines changed

3 files changed

+219
-107
lines changed

CHANGELOG.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,16 @@
1212
* [UPGRADE] OpenSearch Data Source Plugin to Grafana upgraded from 2.28.0 to 2.29.1
1313

1414
* **Metrics**
15-
* [CHANGE] The Grafana alerts targeting SAS Viya that previously were provided by default have been moved to the samples directory. Given the variability of SAS Viya environments, these alerts are now optional. They can be copied to USER_DIR/monitoring/alerting and customized to fit the SAS Viya environment prior to deployment. They have also been split into separate files for easier customization. See the [Alerting Samples README](samples/alerts/README.md) for more details.
15+
* [FEATURE] Automatically define the SMTP server configuration to permit Grafana to send e-mails.
16+
If enabled (by setting `AUTOGENERATE_SMTP` to 'true'), this optional feature allows admins to define
17+
email-based contact points for alerts. Users need to provide connection information via the environment
18+
variables: `SMTP_SERVER, SMTP_PORT`, `SMTP_FROM_ADDRESS` and `SMTP_FROM_NAME`. See [Configure Email Settings for Grafana Alerts](https://documentation.sas.com/doc/en/obsrvcdc/v_003/obsrvdply/n0auhd4hutsf7xn169hfvriysz4e.htm#p1fql9ekyxckamn1jtlujeauhy72)
19+
for more information.
20+
* [CHANGE] The Grafana alerts targeting SAS Viya that previously were provided by default have been moved
21+
to the samples directory. Given the variability of SAS Viya environments, these alerts are now optional.
22+
They can be copied to USER_DIR/monitoring/alerting and customized to fit the SAS Viya environment prior
23+
to deployment. They have also been split into separate files for easier customization. See the [Alerting Samples README](samples/alerts/README.md) for more details.
24+
1625

1726
## Version 1.2.41 (19AUG2025)
1827
* **Metrics**

bin/autogenerate-include.sh

Lines changed: 163 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -6,118 +6,177 @@
66

77

88
function checkYqVersion {
9-
# confirm yq installed and correct version
10-
local goodver yq_version
11-
goodver="yq \(.+mikefarah.+\) version (v)?(4\.(3[2-9]|[4-9][0-9])\..+)"
12-
yq_version=$(yq --version)
13-
if [ "$?" == "1" ]; then
14-
log_error "Required component [yq] not available."
15-
return 1
16-
elif [[ ! $yq_version =~ $goodver ]]; then
17-
log_error "Incorrect version [$yq_version] found; version 4.32.2+ required."
18-
return 1
19-
else
20-
log_debug "A valid version [$yq_version] of yq detected"
21-
return 0
22-
fi
9+
# confirm yq installed and correct version
10+
local goodver yq_version
11+
goodver="yq \(.+mikefarah.+\) version (v)?(4\.(3[2-9]|[4-9][0-9])\..+)"
12+
yq_version=$(yq --version)
13+
if [ "$?" == "1" ]; then
14+
log_error "Required component [yq] not available."
15+
return 1
16+
elif [[ ! $yq_version =~ $goodver ]]; then
17+
log_error "Incorrect version [$yq_version] found; version 4.32.2+ required."
18+
return 1
19+
else
20+
log_debug "A valid version [$yq_version] of yq detected"
21+
return 0
22+
fi
2323
}
2424

2525
export -f checkYqVersion
2626

2727
function create_ingress_certs {
28-
local certFile keyFile namespace secretName
29-
30-
namespace="$1"
31-
secretName="$2"
32-
certFile="${3:-$INGRESS_CERT}"
33-
keyFile="${4:-$INGRESS_KEY}"
34-
35-
if [ -f "$certFile" ] && [ -f "$keyFile" ]; then
36-
kubectl delete secret "$secretName" --namespace "$namespace" --ignore-not-found
37-
kubectl create secret tls "$secretName" --namespace "$namespace" --key="$keyFile" --cert="$certFile"
38-
kubectl -n $namespace label secret $secretName managed-by="v4m-es-script"
39-
elif [ ! -z "$certFile$keyFile" ]; then
40-
log_warn "Missing Ingress certificate file; specified Ingress cert [$certFile] and/or key [$keyFile] file is missing."
41-
log_warn "Create the missing Kubernetes secrets after deployment; use command: kubectl -create secret tls $secretName --namespace $namespace --key=cert_key_file --cert=cert_file"
42-
fi
28+
local certFile keyFile namespace secretName
29+
30+
namespace="$1"
31+
secretName="$2"
32+
certFile="${3:-$INGRESS_CERT}"
33+
keyFile="${4:-$INGRESS_KEY}"
34+
35+
if [ -f "$certFile" ] && [ -f "$keyFile" ]; then
36+
kubectl delete secret "$secretName" --namespace "$namespace" --ignore-not-found
37+
kubectl create secret tls "$secretName" --namespace "$namespace" --key="$keyFile" --cert="$certFile"
38+
kubectl -n $namespace label secret $secretName managed-by="v4m-es-script"
39+
elif [ ! -z "$certFile$keyFile" ]; then
40+
log_warn "Missing Ingress certificate file; specified Ingress cert [$certFile] and/or key [$keyFile] file is missing."
41+
log_warn "Create the missing Kubernetes secrets after deployment; use command: kubectl -create secret tls $secretName --namespace $namespace --key=cert_key_file --cert=cert_file"
42+
fi
4343
}
4444

4545
export -f create_ingress_certs
4646

4747
AUTOGENERATE_INGRESS="${AUTOGENERATE_INGRESS:-false}"
4848
AUTOGENERATE_STORAGECLASS="${AUTOGENERATE_STORAGECLASS:-false}"
49+
AUTOGENERATE_SMTP="${AUTOGENERATE_SMTP:-false}"
4950

50-
if [ "$AUTOGENERATE_INGRESS" != "true" ] && [ "$AUTOGENERATE_STORAGECLASS" != "true" ]; then
51-
log_debug "No autogeneration of YAML enabled"
52-
export AUTOGENERATE_SOURCED="NotNeeded"
51+
if [ "$AUTOGENERATE_INGRESS" != "true" ] && [ "$AUTOGENERATE_STORAGECLASS" != "true" ] && [ "$AUTOGENERATE_SMTP" != "true" ]; then
52+
log_debug "No autogeneration of YAML enabled"
53+
export AUTOGENERATE_SOURCED="NotNeeded"
5354
fi
5455

5556
if [ -z "$AUTOGENERATE_SOURCED" ]; then
5657

57-
if ! checkYqVersion; then
58-
exit 1
59-
fi
60-
61-
if [ "$AUTOGENERATE_INGRESS" == "true" ]; then
62-
63-
# Confirm NOT on OpenShift
64-
if [ "$OPENSHIFT_CLUSTER" == "true" ]; then
65-
log_error "Setting AUTOGENERATE_INGRESS to 'true' is not valid on OpenShift clusters."
66-
log_error "Web applications will be made accessible via OpenShift routes instead (if enabled)."
67-
68-
export AUTOGENERATE_INGRESS="false"
69-
exit 1
70-
fi
71-
72-
73-
#validate required inputs
74-
BASE_DOMAIN="${BASE_DOMAIN}"
75-
if [ -z "$BASE_DOMAIN" ]; then
76-
log_error "Required parameter [BASE_DOMAIN] not provided"
77-
exit 1
78-
fi
79-
80-
ROUTING="${ROUTING:-host}"
81-
82-
if [ "$ROUTING" == "path" ]; then
83-
export MON_TLS_PATH_INGRESS="true"
84-
log_debug "Path ingress requested, setting MON_TLS_PATH_INGRESS to 'true'"
85-
elif [ "$ROUTING" != "host" ] && [ "$ROUTING" != "path" ]; then
86-
log_error "Invalid ROUTING value, valid values are 'host' or 'path'"
87-
exit 1
88-
fi
89-
90-
INGRESS_CERT="${INGRESS_CERT}"
91-
INGRESS_KEY="${INGRESS_KEY}"
92-
if [ "$INGRESS_CERT/$INGRESS_KEY" != "/" ]; then
93-
if [ ! -f "$INGRESS_CERT" ] || [ ! -f "$INGRESS_KEY" ]; then
94-
# Only WARN b/c missing cert doesn't prevent deployment and it can be created afterwards
95-
log_warn "Missing Ingress certificate file; specified Ingress cert [$INGRESS_CERT] and/or key [$INGRESS_KEY] file is missing."
96-
log_warn "You can create the missing Kubernetes secrets after deployment. See Enable TLS for Ingress topic in Help Center documentation."
97-
#unset variable values to prevent further attempted use
98-
unset INGRESS_CERT
99-
unset INGRESS_KEY
100-
else
101-
log_debug "Ingress cert [$INGRESS_CERT] and key [$INGRESS_KEY] files exist."
102-
fi
103-
fi
104-
105-
log_info "Autogeneration of Ingress definitions has been enabled"
106-
107-
fi
108-
109-
if [ "$AUTOGENERATE_STORAGECLASS" == "true" ]; then
110-
111-
log_info "Autogeneration of StorageClass specfication has been enabled"
112-
113-
fi
114-
115-
export AUTOGENERATE_SOURCED="true"
116-
117-
elif [ "$AUTOGENERATE_SOURCED" == "NotNeeded" ]; then
118-
log_debug "autogenerate-include.sh not needed"
119-
else
120-
log_debug "autogenerate-include.sh was already sourced [$AUTOGENERATE_SOURCED]"
58+
if ! checkYqVersion; then
59+
exit 1
60+
fi
61+
62+
if [ "$AUTOGENERATE_INGRESS" == "true" ]; then
63+
64+
# Confirm NOT on OpenShift
65+
if [ "$OPENSHIFT_CLUSTER" == "true" ]; then
66+
log_error "Setting AUTOGENERATE_INGRESS to 'true' is not valid on OpenShift clusters."
67+
log_error "Web applications will be made accessible via OpenShift routes instead (if enabled)."
68+
69+
export AUTOGENERATE_INGRESS="false"
70+
exit 1
71+
fi
72+
73+
#validate required inputs
74+
BASE_DOMAIN="${BASE_DOMAIN}"
75+
if [ -z "$BASE_DOMAIN" ]; then
76+
log_error "Required parameter [BASE_DOMAIN] not provided"
77+
exit 1
78+
fi
79+
80+
ROUTING="${ROUTING:-host}"
81+
82+
if [ "$ROUTING" == "path" ]; then
83+
export MON_TLS_PATH_INGRESS="true"
84+
log_debug "Path ingress requested, setting MON_TLS_PATH_INGRESS to 'true'"
85+
elif [ "$ROUTING" != "host" ] && [ "$ROUTING" != "path" ]; then
86+
log_error "Invalid ROUTING value, valid values are 'host' or 'path'"
87+
exit 1
88+
fi
89+
90+
INGRESS_CERT="${INGRESS_CERT}"
91+
INGRESS_KEY="${INGRESS_KEY}"
92+
if [ "$INGRESS_CERT/$INGRESS_KEY" != "/" ]; then
93+
if [ ! -f "$INGRESS_CERT" ] || [ ! -f "$INGRESS_KEY" ]; then
94+
# Only WARN b/c missing cert doesn't prevent deployment and it can be created afterwards
95+
log_warn "Missing Ingress certificate file; specified Ingress cert [$INGRESS_CERT] and/or key [$INGRESS_KEY] file is missing."
96+
log_warn "You can create the missing Kubernetes secrets after deployment. See Enable TLS for Ingress topic in Help Center documentation."
97+
#unset variable values to prevent further attempted use
98+
unset INGRESS_CERT
99+
unset INGRESS_KEY
100+
else
101+
log_debug "Ingress cert [$INGRESS_CERT] and key [$INGRESS_KEY] files exist."
102+
fi
103+
fi
104+
105+
log_info "Autogeneration of Ingress definitions has been enabled"
106+
107+
fi
108+
109+
if [ "$AUTOGENERATE_STORAGECLASS" == "true" ]; then
110+
log_info "Autogeneration of StorageClass specfication has been enabled"
111+
fi
112+
113+
if [ "$AUTOGENERATE_SMTP" == "true" ]; then
114+
115+
#required
116+
# shellcheck disable=SC2269
117+
SMTP_HOST="${SMTP_HOST}"
118+
# shellcheck disable=SC2269
119+
SMTP_PORT="${SMTP_PORT}"
120+
# shellcheck disable=SC2269
121+
SMTP_FROM_ADDRESS="${SMTP_FROM_ADDRESS}"
122+
# shellcheck disable=SC2269
123+
SMTP_FROM_NAME="${SMTP_FROM_NAME}"
124+
125+
#optional
126+
# shellcheck disable=SC2269
127+
SMTP_USER="${SMTP_USER}"
128+
# shellcheck disable=SC2269
129+
SMTP_PASSWORD="${SMTP_PASSWORD}"
130+
SMTP_USER_SECRET="${SMTP_USER_SECRET:-grafana-smtp-user}"
131+
SMTP_SKIP_VERIFY="${SMTP_SKIP_VERIFY:-false}"
132+
SMTP_TLS_CERT_FILE="${SMTP_TLS_CERT_FILE:-/cert/tls.crt}"
133+
SMTP_TLS_KEY_FILE="${SMTP_TLS_KEY_FILE:-/cert/tls.key}"
134+
135+
log_info "Autogeneration of SMTP Configuration has been enabled"
136+
137+
if [ -z "$SMTP_HOST" ]; then
138+
log_error "Required parameter [SMTP_HOST] not provided"
139+
exit 1
140+
fi
141+
142+
if [ -z "$SMTP_PORT" ]; then
143+
log_error "Required parameter [SMTP_PORT] not provided"
144+
exit 1
145+
fi
146+
147+
if [ -z "$SMTP_FROM_ADDRESS" ]; then
148+
log_error "Required parameter [SMTP_FROM_ADDRESS] not provided"
149+
exit 1
150+
fi
151+
152+
if [ -z "$SMTP_FROM_NAME" ]; then
153+
log_error "Required parameter [SMTP_FROM_NAME] not provided"
154+
exit 1
155+
fi
156+
157+
# Handle SMTP user credentials
158+
if [ -n "$(kubectl get secret -n "$MON_NS" "$SMTP_USER_SECRET" --ignore-not-found -o name 2> /dev/null)" ]; then
159+
log_debug "Secret [$SMTP_USER_SECRET] exists; will use it for SMTP user credentials"
160+
elif [ -z "$SMTP_USER" ] && [ -z "$SMTP_PASSWORD" ]; then
161+
log_debug "Neither SMTP_USER nor SMTP_PASSWORD are set; skipping creation of secret [$SMTP_USER_SECRET]"
162+
elif [ -z "$SMTP_USER" ] || [ -z "$SMTP_PASSWORD" ]; then
163+
log_error "Complete SMTP Credentials NOT provided; MUST provide BOTH [SMTP_USER] and [SMTP_PASSWORD]"
164+
log_info "SMTP_USER is set to [$SMTP_USER] and SMTP_PASSWORD is set to [$SMTP_PASSWORD]"
165+
exit 1
166+
else
167+
log_debug "Secret [$MON_NS/$SMTP_USER_SECRET] will need to be created later."
168+
# shellcheck disable=SC2034
169+
smtpCreateUserSecret="true"
170+
fi
171+
172+
fi
173+
174+
export AUTOGENERATE_SOURCED="true"
175+
176+
elif [ "$AUTOGENERATE_SOURCED" == "NotNeeded" ]; then
177+
log_debug "autogenerate-include.sh not needed"
178+
else
179+
log_debug "autogenerate-include.sh was already sourced [$AUTOGENERATE_SOURCED]"
121180
fi
122181

123182

@@ -131,19 +190,17 @@ function checkStorageClass {
131190
storageClass="${2:-$STORAGECLASS}"
132191

133192
if [ -z "$storageClass" ]; then
134-
log_error "Required parameter not provided. Either [$storageClassEnvVar] or [STORAGECLASS] MUST be provided."
135-
exit 1
193+
log_error "Required parameter not provided. Either [$storageClassEnvVar] or [STORAGECLASS] MUST be provided."
194+
exit 1
136195
else
137-
if $(kubectl get storageClass "$storageClass" -o name &>/dev/null); then
138-
log_debug "The specified StorageClass [$storageClass] exists"
139-
else
140-
log_error "The specified StorageClass [$storageClass] does NOT exist"
141-
exit 1
142-
fi
196+
if $(kubectl get storageClass "$storageClass" -o name &>/dev/null); then
197+
log_debug "The specified StorageClass [$storageClass] exists"
198+
else
199+
log_error "The specified StorageClass [$storageClass] does NOT exist"
200+
exit 1
201+
fi
143202
fi
144203

145204
}
146205

147-
148-
149206
export -f checkStorageClass

monitoring/bin/deploy_monitoring_cluster.sh

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,52 @@ if [ -z "$(kubectl get ns "$MON_NS" -o name 2> /dev/null)" ]; then
7676
disable_sa_token_automount "$MON_NS" default
7777
fi
7878

79+
AUTOGENERATE_SMTP="${AUTOGENERATE_SMTP:-false}"
80+
81+
if [ "$AUTOGENERATE_SMTP" == "true" ]; then
82+
83+
if [ ! -f "$autogeneratedYAMLFile" ]; then
84+
log_debug "Creating file [$autogeneratedYAMLFile]"
85+
touch "$autogeneratedYAMLFile"
86+
else
87+
log_debug "File [$autogeneratedYAMLFile] already exists"
88+
fi
89+
90+
smtpHost="${SMTP_HOST}"
91+
smtpPort="${SMTP_PORT}"
92+
smtpFromAddress="${SMTP_FROM_ADDRESS}"
93+
smtpFromName="${SMTP_FROM_NAME}"
94+
smtpUserSecret="${SMTP_USER_SECRET}"
95+
smtpSkipVerify="${SMTP_SKIP_VERIFY:-false}"
96+
smtpTLSCertFile="${SMTP_TLS_CERT_FILE:-/cert/tls.crt}"
97+
smtpTLSKeyFile="${SMTP_TLS_KEY_FILE:-/cert/tls.key}"
98+
99+
# Create secret to hold SMTP user credentials
100+
if [ "$smtpCreateUserSecret" == "true" ]; then
101+
log_debug "Creating secret [$MON_NS/$SMTP_USER_SECRET] from supplied user [$SMTP_USER] and password."
102+
kubectl create secret generic "$SMTP_USER_SECRET" -n "$MON_NS" --from-literal=user="$SMTP_USER" --from-literal=password="$SMTP_PASSWORD"
103+
fi
104+
105+
yq -i '.grafana."grafana.ini".smtp.enabled=true' "$autogeneratedYAMLFile"
106+
107+
value="$smtpHost:$smtpPort" yq -i '.grafana."grafana.ini".smtp.host=env(value)' "$autogeneratedYAMLFile"
108+
109+
value="$smtpFromAddress" yq -i '.grafana."grafana.ini".smtp.from_address=env(value)' "$autogeneratedYAMLFile"
110+
value="$smtpFromName" yq -i '.grafana."grafana.ini".smtp.from_name=env(value)' "$autogeneratedYAMLFile"
111+
112+
if [ -n "$smtpUserSecret" ]; then
113+
value="$smtpUserSecret" yq -i '.grafana.smtp.existingSecret=env(value)' "$autogeneratedYAMLFile"
114+
fi
115+
116+
value="$smtpSkipVerify" yq -i '.grafana."grafana.ini".smtp.skip_verify=env(value)' "$autogeneratedYAMLFile"
117+
118+
value="$smtpTLSCertFile" yq -i '.grafana."grafana.ini".smtp.cert_file=env(value)' "$autogeneratedYAMLFile"
119+
value="$smtpTLSKeyFile" yq -i '.grafana."grafana.ini".smtp.key_file=env(value)' "$autogeneratedYAMLFile"
120+
121+
else
122+
log_debug "Auto-generation of SMTP not enabled; skipping Grafana SMTP server configuration"
123+
fi
124+
79125
#Generate yaml file with all container-related keys
80126
generateImageKeysFile "$PROMOP_FULL_IMAGE" "monitoring/prom-operator_container_image.template"
81127
if [ -z "$PROM_OPERATOR_CRD_VERSION" ]; then

0 commit comments

Comments
 (0)