diff --git a/bin/helm-operations.sh b/bin/helm-operations.sh new file mode 100755 index 000000000..41522ceb6 --- /dev/null +++ b/bin/helm-operations.sh @@ -0,0 +1,209 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2087 +set -Eeo pipefail + +# Read values from environment variables with defaults +BASE_DIR="/wire-server-deploy" +TARGET_SYSTEM="example.dev" +CERT_MASTER_EMAIL="certmaster@${TARGET_SYSTEM}" + +# this IP should match the DNS A record value for TARGET_SYSTEM +# assuming it to be the public address used by clients to reach public Address +HOST_IP="" +if [ -z "$HOST_IP" ]; then +HOST_IP=$(wget -qO- https://api.ipify.org) +fi + +# picking a node for calling traffic (3rd kube worker node) +CALLING_NODE=$(kubectl get nodes --no-headers | tail -n 1 | awk '{print $1}') +if [[ -z "$CALLING_NODE" ]]; then + echo "Error: could not determine the last kube worker node via kubectl" + exit 1 +fi + +# Creates values.yaml from prod-values.example.yaml and secrets.yaml from prod-secrets.example.yaml +# Works on all chart directories in $BASE_DIR/values/ +process_values() { + + ENV=$1 + TYPE=$2 + charts=(fake-aws smtp rabbitmq databases-ephemeral reaper wire-server webapp account-pages team-settings smallstep-accomp ingress-nginx-controller nginx-ingress-services coturn sftd cert-manager) + + if [[ "$ENV" != "prod" ]] || [[ -z "$TYPE" ]] ; then + echo "Error: This function only supports prod deployments with TYPE as values or secrets. ENV must be 'prod', got: '$ENV' and '$TYPE'" + exit 1 + fi + timestp=$(date +"%Y%m%d_%H%M%S") + + for chart in "${charts[@]}"; do + chart_dir="$BASE_DIR/values/$chart" + if [[ -d "$chart_dir" ]]; then + if [[ -f "$chart_dir/${ENV}-${TYPE}.example.yaml" ]]; then + if [[ ! -f "$chart_dir/${TYPE}.yaml" ]]; then + cp "$chart_dir/${ENV}-${TYPE}.example.yaml" "$chart_dir/${TYPE}.yaml" + echo "Used template ${ENV}-${TYPE}.example.yaml to create $chart_dir/${TYPE}.yaml" + else + echo "$chart_dir/${TYPE}.yaml already exists, archiving it and creating a new one." + mv "$chart_dir/${TYPE}.yaml" "$chart_dir/${TYPE}.yaml.bak.$timestp" + cp "$chart_dir/${ENV}-${TYPE}.example.yaml" "$chart_dir/${TYPE}.yaml" + fi + fi + fi + done +} + +# selectively setting values of following charts which requires additional values +# wire-server, webapp, team-settings, account-pages, nginx-ingress-services, sftd and coturn +configure_values() { + + TEMP_DIR=$(mktemp -d) + trap 'rm -rf $TEMP_DIR' EXIT + + # to find IP address of calling NODE + CALLING_NODE_IP=$(kubectl get node "$CALLING_NODE" -o jsonpath='{.status.addresses[?(@.type=="InternalIP")].address}') + + # Fixing the hosts with TARGET_SYSTEM and setting the turn server + sed -e "s/example.com/$TARGET_SYSTEM/g" \ + "$BASE_DIR/values/wire-server/values.yaml" > "$TEMP_DIR/wire-server-values.yaml" + + # fixing the turnStatic values + yq eval -i ".brig.turnStatic.v2 = [\"turn:$HOST_IP:3478\", \"turn:$HOST_IP:3478?transport=tcp\"]" "$TEMP_DIR/wire-server-values.yaml" + + # Fixing the hosts in webapp team-settings and account-pages charts + for chart in webapp team-settings account-pages; do + sed "s/example.com/$TARGET_SYSTEM/g" "$BASE_DIR/values/$chart/values.yaml" > "$TEMP_DIR/$chart-values.yaml" + done + + # Setting certManager and DNS records + sed -e 's/useCertManager: false/useCertManager: true/g' \ + -e "/certmasterEmail:$/s/certmasterEmail:/certmasterEmail: $CERT_MASTER_EMAIL/" \ + -e "s/example.com/$TARGET_SYSTEM/" \ + "$BASE_DIR/values/nginx-ingress-services/values.yaml" > "$TEMP_DIR/nginx-ingress-services-values.yaml" + + # Fixing SFTD hosts and setting the cert-manager to http01 + sed -e "s/webapp.example.com/webapp.$TARGET_SYSTEM/" \ + -e "s/sftd.example.com/sftd.$TARGET_SYSTEM/" \ + -e 's/name: letsencrypt-prod/name: letsencrypt-http01/' \ + "$BASE_DIR/values/sftd/values.yaml" > "$TEMP_DIR/sftd-values.yaml" + + # Setting coturn node IP values + yq eval -i ".coturnTurnListenIP = \"$CALLING_NODE_IP\"" "$BASE_DIR/values/coturn/values.yaml" + yq eval -i ".coturnTurnRelayIP = \"$CALLING_NODE_IP\"" "$BASE_DIR/values/coturn/values.yaml" + yq eval -i ".coturnTurnExternalIP = \"$HOST_IP\"" "$BASE_DIR/values/coturn/values.yaml" + + # Compare and copy files if different + for file in wire-server-values.yaml webapp-values.yaml team-settings-values.yaml account-pages-values.yaml \ + nginx-ingress-services-values.yaml sftd-values.yaml; do + if ! cmp -s "$TEMP_DIR/$file" "$BASE_DIR/values/${file%-values.yaml}/values.yaml"; then + cp "$TEMP_DIR/$file" "$BASE_DIR/values/${file%-values.yaml}/values.yaml" + echo "Updating $BASE_DIR/values/${file%-values.yaml}/values.yaml" + fi + done + +} + +deploy_charts() { + + local charts=("$@") + echo "Following charts will be deployed: ${charts[*]}" + + for chart in "${charts[@]}"; do + chart_dir="$BASE_DIR/charts/$chart" + values_file="$BASE_DIR/values/$chart/values.yaml" + secrets_file="$BASE_DIR/values/$chart/secrets.yaml" + + if [[ ! -d "$chart_dir" ]]; then + echo "Error: Chart directory $chart_dir does not exist. Exiting fix the charts" + exit 1 + fi + + if [[ ! -f "$values_file" ]]; then + echo "Warning: Values file $values_file does not exist. Deploying without values." + values_file="" + fi + + if [[ ! -f "$secrets_file" ]]; then + secrets_file="" + fi + + helm_command="helm upgrade --install --wait --timeout=15m0s $chart $chart_dir" + + if [[ -n "$values_file" ]]; then + helm_command+=" --values $values_file" + fi + + if [[ -n "$secrets_file" ]]; then + helm_command+=" --values $secrets_file" + fi + + # handle wire-server to inject PostgreSQL password from databases-ephemeral + if [[ "$chart" == "wire-server" ]]; then + + echo "Retrieving PostgreSQL password from databases-ephemeral for wire-server deployment..." + if kubectl get secret wire-postgresql-secret &>/dev/null; then + # Usage: sync-k8s-secret-to-wire-secrets.sh + "$BASE_DIR/bin/sync-k8s-secret-to-wire-secrets.sh" \ + wire-postgresql-secret password \ + "$BASE_DIR/values/wire-server/secrets.yaml" \ + .brig.secrets.pgPassword .galley.secrets.pgPassword + else + echo "⚠️ Warning: PostgreSQL secret 'wire-postgresql-secret' not found, skipping secret sync" + echo " Make sure databases-ephemeral chart is deployed before wire-server" + fi + fi + + echo "Deploying $chart as $helm_command" + eval "$helm_command" + done + + # display running pods post deploying all helm charts in default namespace + kubectl get pods --sort-by=.metadata.creationTimestamp +} + +deploy_cert_manager() { + + kubectl get namespace cert-manager-ns || kubectl create namespace cert-manager-ns + helm upgrade --install -n cert-manager-ns cert-manager "$BASE_DIR/charts/cert-manager" --values "$BASE_DIR/values/cert-manager/values.yaml" + + # display running pods + kubectl get pods --sort-by=.metadata.creationTimestamp -n cert-manager-ns +} + +deploy_calling_services() { + + echo "Deploying sftd and coturn" + # select the node to deploy sftd + kubectl annotate node "$CALLING_NODE" wire.com/external-ip="$HOST_IP" --overwrite + helm upgrade --install sftd "$BASE_DIR/charts/sftd" --set "nodeSelector.kubernetes\\.io/hostname=$CALLING_NODE" --values "$BASE_DIR/values/sftd/values.yaml" + + kubectl annotate node "$CALLING_NODE" wire.com/external-ip="$HOST_IP" --overwrite + helm upgrade --install coturn "$BASE_DIR/charts/coturn" --set "nodeSelector.kubernetes\\.io/hostname=$CALLING_NODE" --values "$BASE_DIR/values/coturn/values.yaml" --values "$BASE_DIR/values/coturn/secrets.yaml" +} + +main() { +# Create prod-values.example.yaml to values.yaml and take backup +process_values "prod" "values" +# Create prod-secrets.example.yaml to secrets.yaml and take backup +process_values "prod" "secrets" + +# configure chart specific variables for each chart in values.yaml file +configure_values + +# deploying with external datastores, useful for prod setup +deploy_charts cassandra-external elasticsearch-external minio-external postgresql-external fake-aws smtp rabbitmq databases-ephemeral reaper wire-server webapp account-pages team-settings smallstep-accomp ingress-nginx-controller + +# deploying cert manager to issue certs, by default letsencrypt-http01 issuer is configured +deploy_cert_manager + +# nginx-ingress-services chart needs cert-manager to be deployed +deploy_charts nginx-ingress-services + +# deploying sft and coturn services +# not implemented yet +deploy_calling_services + +# print status of certs +kubectl get certificate +} + +main diff --git a/bin/offline-deploy.sh b/bin/offline-deploy.sh index cd0da1157..787486726 100755 --- a/bin/offline-deploy.sh +++ b/bin/offline-deploy.sh @@ -39,11 +39,10 @@ echo "Syncing PostgreSQL password from Kubernetes secret..." sudo docker run --network=host -v $PWD:/wire-server-deploy $WSD_CONTAINER ./bin/sync-k8s-secret-to-wire-secrets.sh \ wire-postgresql-external-secret \ password \ - values/wire-server/secrets.yaml \ + values/wire-server/prod-secrets.example.yaml \ .brig.secrets.pgPassword \ .galley.secrets.pgPassword \ .spar.secrets.pgPassword \ .gundeck.secrets.pgPassword - -sudo docker run --network=host -v $PWD:/wire-server-deploy $WSD_CONTAINER ./bin/offline-helm.sh +sudo docker run --network=host -v $PWD:/wire-server-deploy $WSD_CONTAINER ./bin/helm-operations.sh diff --git a/bin/offline-secrets.sh b/bin/offline-secrets.sh index 158c06ba5..c483830bd 100755 --- a/bin/offline-secrets.sh +++ b/bin/offline-secrets.sh @@ -34,9 +34,9 @@ mls_ecdsa_p256_key="$(generate_mls_key -algorithm ec -pkeyopt ec_paramgen_curve: mls_ecdsa_p384_key="$(generate_mls_key -algorithm ec -pkeyopt ec_paramgen_curve:P-384)" mls_ecdsa_p521_key="$(generate_mls_key -algorithm ec -pkeyopt ec_paramgen_curve:P-521)" -if [[ ! -f $VALUES_DIR/wire-server/secrets.yaml ]]; then - echo "Writing $VALUES_DIR/wire-server/secrets.yaml" - cat < $VALUES_DIR/wire-server/secrets.yaml + +echo "Writing $VALUES_DIR/wire-server/prod-secrets.example.yaml" +cat < $VALUES_DIR/wire-server/prod-secrets.example.yaml brig: secrets: pgPassword: verysecurepassword @@ -115,7 +115,13 @@ background-worker: password: guest EOF -fi +echo "Writing $VALUES_DIR/coturn/prod-secrets.example.yaml" +cat < $VALUES_DIR/coturn/prod-secrets.example.yaml +secrets: + zrestSecrets: + - "$zrest" +EOF + if [[ ! -f $ANSIBLE_DIR/inventory/offline/group_vars/all/secrets.yaml ]]; then echo "Writing $ANSIBLE_DIR/inventory/offline/group_vars/all/secrets.yaml" @@ -127,7 +133,7 @@ minio_cargohold_secret_key: "$minio_cargohold_secret_key" EOT fi -PROM_AUTH_FILE="$VALUES_DIR/kube-prometheus-stack/secrets.yaml" +PROM_AUTH_FILE="$VALUES_DIR/kube-prometheus-stack/prod-secrets.example.yaml" if [[ ! -f $PROM_AUTH_FILE ]]; then echo "Writing $PROM_AUTH_FILE" cat < $PROM_AUTH_FILE diff --git a/changelog.d/3-deploy-builds/wpb-22439-helm-operations b/changelog.d/3-deploy-builds/wpb-22439-helm-operations new file mode 100644 index 000000000..fa00d38cc --- /dev/null +++ b/changelog.d/3-deploy-builds/wpb-22439-helm-operations @@ -0,0 +1,3 @@ +Added: bin/helm-operations.sh to replace offline-helm to be more closer to our production instrcutions +Changed: bin/offline-secrets.sh to support helm-operations.sh script and add support for coturn secret +Changed: reduce replica count for sftd and coturn to support wiab-staging diff --git a/values/coturn/prod-values.example.yaml b/values/coturn/prod-values.example.yaml index adde645db..d3e303fe7 100644 --- a/values/coturn/prod-values.example.yaml +++ b/values/coturn/prod-values.example.yaml @@ -1,5 +1,5 @@ # using upstream values for coturn helm -replicaCount: 3 +replicaCount: 1 # image: # tag: some-tag # (only override if you want a newer/different version than what is in the chart) config: @@ -10,7 +10,7 @@ config: coturnTurnExternalIP: "__COTURN_EXT_IP__" coturnTurnListenIP: "__COTURN_HOST_IP__" coturnTurnRelayIP: "__COTURN_HOST_IP__" -coturnFederationListeningIP: "__COTURN_HOST_IP__" +#coturnFederationListeningIP: "__COTURN_HOST_IP__" # Uncomment to enable federation # federate: # enabled: true diff --git a/values/sftd/prod-values.example.yaml b/values/sftd/prod-values.example.yaml index 1455a180c..e10f2d60a 100644 --- a/values/sftd/prod-values.example.yaml +++ b/values/sftd/prod-values.example.yaml @@ -1,4 +1,4 @@ -replicaCount: 3 +replicaCount: 1 # image: # tag: some-tag # (only override if you want a newer/different version than what is in the chart) allowOrigin: https://webapp.example.com