diff --git a/.github/workflows/ctr.yaml b/.github/workflows/ctr.yaml index ca53ea3..1a5c1c1 100644 --- a/.github/workflows/ctr.yaml +++ b/.github/workflows/ctr.yaml @@ -13,12 +13,19 @@ on: required: true env: REGISTRY: "ghcr.io" - IMAGE_NAME: "sando38/ejabberd" + IMAGE_NAME: "sando38/helm-ejabberd" PATCH_DIR: "image" jobs: build_and_push: - runs-on: ubuntu-latest + name: ${{ matrix.config.arch }} - build container image + runs-on: ${{ matrix.config.runs-on }} + strategy: + matrix: + config: + - { arch: amd64, runs-on: ubuntu-24.04 } + - { arch: arm64, runs-on: ubuntu-24.04-arm } + fail-fast: false steps: - name: Check out repository code uses: actions/checkout@v4 @@ -39,9 +46,6 @@ jobs: working-directory: ./ejabberd-source run: git apply ../image/${{ env.REF }}/patches/*.patch - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -51,29 +55,32 @@ jobs: with: images: | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - ${{ env.TAG }} - - name: Build linux/amd64 image + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build linux/${{ matrix.config.arch }} image uses: docker/build-push-action@v5 + id: build with: build-args: | METHOD=direct VERSION=${{ env.TAG }} VARIANT=hardened - # cache-from: type=gha - # cache-to: type=gha,mode=max context: ./ejabberd-source/. file: ./image/${{ env.REF }}/Dockerfile labels: ${{ steps.meta.outputs.labels }} - platforms: linux/amd64 - load: true - tags: ${{ steps.meta.outputs.tags }} + platforms: linux/${{ matrix.config.arch }} + outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true - name: Smoke Test run: | docker run -d --name ejabberd \ - ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.TAG }} + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }} docker exec ejabberd ejabberdctl started docker exec ejabberd ejabberdctl status docker stop ejabberd @@ -82,26 +89,65 @@ jobs: if: failure() || success() run: docker logs ejabberd - - name: Log in to the Container registry + - name: Export digest | ${{ matrix.config.arch }} + run: | + mkdir -p /tmp/digests + digest="${{ steps.build.outputs.digest }}" + touch "/tmp/digests/${digest#sha256:}" + + - name: Upload digest | ${{ matrix.config.arch }} + uses: actions/upload-artifact@v4 + with: + name: digests-${{ matrix.config.arch }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 5 + + publish: + name: publish manifest + runs-on: ubuntu-24.04 + needs: [build_and_push] + steps: + + - name: Check out repository code + uses: actions/checkout@v4 + + - name: Extract container image tag + run: | + echo "TAG=$(awk 'END{print}' ${{ env.PATCH_DIR }}/tag)" >> $GITHUB_ENV + + - name: Log in to ${{ env.REGISTRY }} uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.EJABBERD_REPO_TOKEN }} + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} - - name: Push linux/amd64 image - uses: docker/build-push-action@v5 - if: github.event_name == 'push' + - name: Download digests + uses: actions/download-artifact@v4 with: - build-args: | - METHOD=direct - VERSION=${{ env.TAG }} - VARIANT=hardened - # cache-from: type=gha - # cache-to: type=gha,mode=max - context: ./ejabberd-source/. - file: ./image/${{ env.REF }}/Dockerfile - labels: ${{ steps.meta.outputs.labels }} - platforms: linux/amd64 - push: true - tags: ${{ steps.meta.outputs.tags }} + path: /tmp/digests + pattern: digests-* + merge-multiple: true + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v4 + with: + images: | + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + ${{ env.TAG }} + + - name: Create manifest list and push + working-directory: /tmp/digests + run: | + docker buildx imagetools create $(jq -r '"-t " + (.tags | join(" -t "))' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *) + + - name: Inspect image + run: | + docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} diff --git a/image/24.07/Dockerfile b/image/24.07/Dockerfile index a58a48c..e409454 100644 --- a/image/24.07/Dockerfile +++ b/image/24.07/Dockerfile @@ -225,13 +225,6 @@ COPY --from=runtime / / COPY --from=build /rootfs / COPY --from=elector /elector/elector /usr/local/bin/elector -HEALTHCHECK \ - --interval=1m \ - --timeout=5s \ - --start-period=5s \ - --retries=10 \ - CMD ejabberdctl status - WORKDIR /$HOME USER $USER VOLUME ["/$HOME"] diff --git a/image/24.12/Dockerfile b/image/24.12/Dockerfile new file mode 100644 index 0000000..6e24902 --- /dev/null +++ b/image/24.12/Dockerfile @@ -0,0 +1,244 @@ +#' Define default build variables +## source ARGs +ARG GO_VSN='1.23' +ARG ERLANG_VSN='27.2' +ARG ELIXIR_VSN='1.17.3' +## general ARGs +ARG UID='9000' +ARG USER='ejabberd' +ARG HOME="opt/$USER" +ARG BUILD_DIR="/$USER" +ARG VERSION='master' + +################################################################################ +#' build elector +FROM cgr.dev/chainguard/wolfi-base AS elector +ARG GO_VSN +RUN apk -U upgrade --available && apk add --no-cache \ + build-base \ + git \ + go-${GO_VSN} + +WORKDIR /elector +RUN git clone https://github.com/sando38/k8s-elector \ + --branch update-packages --depth 1 . + +RUN go get google.golang.org/protobuf@v1.33.0 \ + && go get golang.org/x/net@v0.23.0 \ + && go mod tidy + +ARG TARGETARCH +ARG TARGETOS +RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \ + go build -a -installsuffix cgo -ldflags "${LDFLAGS}" -o elector cmd/elector.go + +################################################################################ +#' Build and base image +FROM cgr.dev/chainguard/wolfi-base AS erlang +ARG ERLANG_VSN +ENV LC_ALL='C.UTF-8' \ + LANG='C.UTF-8' + +RUN apk -U upgrade --available && apk add --no-cache \ + autoconf \ + automake \ + bash \ + build-base \ + ca-certificates-bundle \ + curl \ + expat-dev \ + file \ + freetds freetds-dev \ + freetype-dev \ + gd-dev \ + git \ + libjpeg-dev \ + libpng-dev \ + libwebp-dev \ + linux-pam-dev \ + ncurses-dev \ + pax-utils \ + perl-dev \ + openssl \ + openssl-dev \ + sqlite-dev \ + unixodbc unixodbc-dev \ + wget \ + yaml-dev \ + zlib-dev + +ARG ERLANG_VSN +RUN wget -O - https://github.com/erlang/otp/releases/download/OTP-"$ERLANG_VSN"/otp_src_"$ERLANG_VSN".tar.gz \ + | tar -xzf - + +WORKDIR /otp_src_"$ERLANG_VSN" +## https://github.com/processone/ejabberd/commit/b288d5c76370e44fef3a9caa6fbb888435057a2a +RUN sed -i 's|if(size == 0 && (sql_type == SQL_LONGVARCHAR|if((sql_type == SQL_LONGVARCHAR|g' lib/odbc/c_src/odbcserver.c +RUN ./configure \ + --prefix=/usr \ + --sysconfdir=/etc \ + --mandir=/usr/share/man \ + --infodir=/usr/share/info \ + --host="$CHOST" \ + --build="$CBUILD" \ + --enable-threads \ + --enable-shared-zlib \ + --enable-ssl=dynamic-ssl-lib \ + --enable-jit \ + && make install + +WORKDIR / +ARG ELIXIR_VSN +RUN wget -O - https://github.com/elixir-lang/elixir/archive/v"$ELIXIR_VSN".tar.gz \ + | tar -xzf - + +WORKDIR /elixir-"$ELIXIR_VSN" +RUN make install clean + +RUN mix local.hex --force \ + && mix local.rebar --force + +################################################################################ +#' Build and prepare ejabberd +FROM erlang AS build +ENV LC_ALL='C.UTF-8' \ + LANG='C.UTF-8' + +ARG BUILD_DIR +COPY / $BUILD_DIR/ + +WORKDIR $BUILD_DIR/.ejabberd-modules/sources/ejabberd-contrib + +# include some ejabberd contribution modules which may be nice for clusters +RUN git clone https://github.com/processone/ejabberd-contrib --depth 1 . \ + && install mod_s3_upload/src/*.erl $BUILD_DIR/src \ + && install mod_s3_upload/include/*.hrl $BUILD_DIR/include \ + && install mod_ecaptcha/src/*.erl $BUILD_DIR/src \ + && install ejabberd_auth_http/src/*.erl $BUILD_DIR/src \ + && rm -rf mod_ecaptcha mod_http_redirect mod_s3_upload + +WORKDIR $BUILD_DIR +RUN mv .github/container/ejabberdctl.template . \ + && ./autogen.sh \ + && ./configure --with-rebar=mix --enable-all \ + && make deps \ + && make rel + +WORKDIR /rootfs +ARG VERSION +ARG HOME +RUN mkdir -p $HOME $HOME-$VERSION \ +&& cp -r $BUILD_DIR/_build/prod/rel/ejabberd/* $HOME-$VERSION \ + && mv $HOME-$VERSION/conf $HOME/conf + +RUN cp -p $BUILD_DIR/tools/captcha*.sh $HOME-$VERSION/lib + +RUN find "$HOME-$VERSION/bin" -name 'ejabberd' -delete \ + && find "$HOME-$VERSION/releases" -name 'COOKIE' -delete + +## openssl does not find /etc/ssl/openssl.cnf in workflow, check later +# RUN wget -O "$HOME/conf/cacert.pem" 'https://curl.se/ca/cacert.pem' \ +# && sed -i '/^loglevel:/a \ \ +# \nca_file: /opt/ejabberd/conf/cacert.pem \ +# \ncertfiles: \ +# \n - /opt/ejabberd/conf/server.pem' "$HOME/conf/ejabberd.yml" +RUN mkdir -p usr/local/bin $HOME/conf $HOME/database $HOME/logs $HOME/upload + +ARG BUILD_DIR +RUN if [ ! -d $HOME/.ejabberd-modules ]; \ + then \ + if [ -d $BUILD_DIR/.ejabberd-modules ]; \ + then cp -r $BUILD_DIR/.ejabberd-modules $HOME; \ + else git clone https://github.com/processone/ejabberd-contrib --depth 1 \ + $HOME/.ejabberd-modules/sources/ejabberd-contrib; \ + fi \ + fi + +## openssl does not find /etc/ssl/openssl.cnf in workflow, check later +# RUN export PEM=$HOME/conf/server.pem \ +# && openssl req -x509 \ +# -batch \ +# -nodes \ +# -newkey rsa:4096 \ +# -keyout $PEM \ +# -out $PEM \ +# -days 3650 \ +# -subj "/CN=localhost" + +RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \ + && echo -e \ + "#!/bin/sh \ + \n[ -z \$ERLANG_NODE_ARG ] && export ERLANG_NODE_ARG=ejabberd@localhost \ + \nexport CONFIG_DIR=/$HOME/conf \ + \nexport LOGS_DIR=/$HOME/logs \ + \nexport SPOOL_DIR=/$HOME/database \ + \nexec /$(find $home_root_dir -name ejabberdctl) \"\$@\"" \ + > usr/local/bin/ejabberdctl \ + && chmod +x usr/local/bin/* \ + && scanelf --needed --nobanner --format '%n#p' --recursive "$PWD" \ + | tr ',' '\n' \ + | sort -u \ + | awk 'system("[ -e $PWD" $1 " ]") == 0 { next } { print "so:" $1 }' \ + > /tmp/runDeps + +ARG UID +RUN chown -R $UID:$UID $HOME + +################################################################################ +#' Get AlpineÄs busybox for ejabberdctl script +FROM cgr.dev/chainguard/wolfi-base AS runtime +RUN apk -U upgrade --available --no-cache + +ARG USER +ARG UID +ARG HOME +RUN addgroup $USER -g $UID \ + && adduser -s /sbin/nologin -D -u $UID -h /$HOME -G $USER $USER + +COPY --from=build /tmp/runDeps /tmp/runDeps +RUN apk add --no-cache -t .ejabberd-rundeps \ + $(cat /tmp/runDeps) \ + freetds unixodbc \ + bind-tools \ + busybox \ + ca-certificates-bundle \ + gettext \ + jq \ + netcat-openbsd \ + tini \ + wget + +RUN apk del --repositories-file /dev/null \ + wolfi-base \ + wolfi-keys \ + apk-tools \ + && rm -rf /var/cache/apk /etc/apk /tmp/* \ + && find /lib/apk/db -type f -not -name 'installed' -delete + +################################################################################ +#' Forge release image +FROM scratch AS release +ARG USER +ARG HOME +ENV ERL_DIST_PORT='5210' \ + LC_ALL='C.UTF-8' \ + LANG='C.UTF-8' + +COPY --from=runtime / / +COPY --from=build /rootfs / +COPY --from=elector /elector/elector /usr/local/bin/elector + +HEALTHCHECK \ + --interval=1m \ + --timeout=5s \ + --start-period=5s \ + --retries=10 \ + CMD ejabberdctl status + +WORKDIR /$HOME +USER $USER +VOLUME ["/$HOME"] +EXPOSE 5210 5222 5223 5280 + +ENTRYPOINT ["/sbin/tini","--","ejabberdctl"] +CMD ["foreground"] diff --git a/image/24.12/patches/mix.exs.patch b/image/24.12/patches/mix.exs.patch new file mode 100644 index 0000000..4a95c18 --- /dev/null +++ b/image/24.12/patches/mix.exs.patch @@ -0,0 +1,28 @@ +diff --git a/mix.exs b/mix.exs +index 45f2479b..532eccc1 100644 +--- a/mix.exs ++++ b/mix.exs +@@ -47,6 +47,7 @@ defmodule Ejabberd.MixProject do + :logger, :mix] + ++ cond_apps(), + included_applications: [:mnesia, :os_mon, ++ :cuesport, :fusco, :ecaptcha, + :cache_tab, :eimp, :mqtree, :p1_acme, + :p1_oauth2, :pkix] + ++ cond_included_apps()] +@@ -131,12 +132,15 @@ defmodule Ejabberd.MixProject do + + defp deps do + [{:cache_tab, "~> 1.0"}, ++ {:cuesport, github: "goj/cuesport", manager: :rebar3}, + {:dialyxir, "~> 1.2", only: [:test], runtime: false}, ++ {:ecaptcha, github: "seriyps/ecaptcha"}, + {:eimp, "~> 1.0"}, + {:ex_doc, "~> 0.31", only: [:dev, :edoc], runtime: false}, + {:fast_tls, "~> 1.1.22"}, + {:fast_xml, "~> 1.1.53", override: true}, + {:fast_yaml, "~> 1.0"}, ++ {:fusco, "~> 0.1"}, + {:idna, "~> 6.0"}, + {:mqtree, "~> 1.0"}, + {:p1_acme, "~> 1.0"}, diff --git a/image/24.12/scripts/ejabberdctl b/image/24.12/scripts/ejabberdctl new file mode 100644 index 0000000..dc3f75d --- /dev/null +++ b/image/24.12/scripts/ejabberdctl @@ -0,0 +1,13 @@ +#!/bin/sh + +# Determine pod's cluster name +pod_name="${POD_NAME:-$(hostname -s)}" # e.g. pod-0 +pod_endpoint_name="$(hostname -f)" # e.g. pod-0.servicename.namespace.svc.cluster.local +sts_name="$(echo $pod_name | sed 's|-[0-9]\+||g')" + +[ -z "$ERLANG_NODE_ARG" ] && export ERLANG_NODE_ARG="$sts_name@$pod_endpoint_name" +export CONFIG_DIR="$HOME"/conf +export LOGS_DIR="$HOME"/logs +export SPOOL_DIR="$HOME"/database + +exec $(find /opt -name ejabberdctl) "$@" diff --git a/image/24.12/scripts/run.sh b/image/24.12/scripts/run.sh new file mode 100644 index 0000000..0165344 --- /dev/null +++ b/image/24.12/scripts/run.sh @@ -0,0 +1,256 @@ +#!/bin/sh + +set -e +set -u + +myself=${0##*/} + +info() +{ + echo "$myself: $*" +} + +error() +{ + echo >&2 "$myself: $*" +} + +info '=> Start init script for ejabberd k8s container ...' +info '==> Helm chart source code can be found on github:' +info ' https://github.com/sando38/helm-ejabberd' +info '==> Official ejabberd documentation on https://docs.ejabberd.im ...' +info '==> Source code on https://github.com/processone/ejabberd ...' +info '' +info '=> NOTE:' +info ' If you run this image in a single app environment, e.g. with Docker,' +info ' then set the environment variable:' +info ' ERLANG_NODE_ARG=ejabberd@localhost' +info ' for improved compatibility. More info can be found here:' +info ' https://github.com/processone/docker-ejabberd/tree/master/ecs#change-mnesia-node-name' +info '' +info '=> Thanks to all who made this work possible. Special thanks to Holger' +info ' @weiss for open ears and brainstorming!' +info '' +info "=> Pod started on $(date)." +info '' + +# Common variables +pod_name="${POD_NAME:-$(hostname -s)}" # e.g. pod-0 +pod_endpoint_name="$(hostname -f)" # e.g. pod-0.servicename.namespace.svc.cluster.local +headless_svc="$(hostname -d)" # e.g. servicename.namespace.default.svc.cluster.local +pod_namespace="${POD_NAMESPACE:-default}" +sts_name="$(echo $pod_name | sed 's|-[0-9]\+||g')" +election_name="${ELECTION_NAME:-ejabberd}" +election_url="${ELECTION_URL:-127.0.0.1:4040}" +election_ttl="${ELECTION_TTL:-5s}" +svc_pod_names="$(nslookup -q=srv "$headless_svc" | grep "$headless_svc" | awk '{print $NF}')" +cluster_pod_names="$(echo $svc_pod_names | sed -e "s|$pod_name.$headless_svc||g")" +api_url="${HTTP_API_URL:-127.0.0.1:5281}" +ready_file="$HOME/.ejabberd_ready" + +[ -e "$ready_file" ] && rm "$ready_file" + +info "==> This is ejabberd node $sts_name@$pod_endpoint_name ..." + +## Inspired by https://github.com/Lykos153/ejabberd-cluster-k8s +## TODO: Write an ejabberd module for https://github.com/vapor-ware/k8s-elector +## or check: https://github.com/bitwalker/libcluster +## https://github.com/pedro-gutierrez/cluster +_start_elector() { + info "==> Start elector sidecar service on $election_url ..." + elector -v 2 \ + -election "$election_name" \ + -namespace "$pod_namespace" \ + -http "$election_url" \ + -ttl "$election_ttl" & + export pid_elector=$! + + info "==> Wait for elector sidecar to be available on $election_url ..." + while ! nc -z $(echo $election_url | sed -e 's/:/ /'); do sleep 1; done + info "==> elector sidecar is available on $election_url ..." +} + +# trap for graceful shutdown +_shutdown() { + rm -f $HOME/.ejabberd_terminate_false + if [ "${ELECTOR_ENABLED:-false}" = 'true' ] + then kill -s TERM "$pid_elector" + fi + ## disconnection from cluster happens automatically + info "==> Gracefully shut down ejabberd pod $sts_name@$pod_endpoint_name ..." + ejabberdctl stop + ejabberdctl stopped +} + +# List cluster members +_cluster_member() { + members="$(wget -q -O - --post-data '{}' $api_url/api/list_cluster | egrep -o '[^][]+' | tr ',' '\n')" + printf "$members" +} + +# (Re-)join cluster - DNSSRV derived +_join_cluster_dns() { + [ -e "$HOME/.ejabberd_ready" ] && rm $HOME/.ejabberd_ready + join_pod_name="$(echo $cluster_pod_names | sort -n | awk 'NR==1{print $1}')" + if [ "$pod_name" = "$sts_name-0" ] + then + info "==> $pod_name has ordinal zero ..." + touch $HOME/.ejabberd_ready + elif echo "$(_cluster_member)" | grep -q "$join_pod_name" + then + info "==> $pod_name is clustered w/ pod w/ lowest ordinal $join_pod_name ..." + touch $HOME/.ejabberd_ready + else + info "==> Leaving former cluster ..." + NO_WARNINGS=true ejabberdctl leave_cluster "$sts_name@$pod_name.${headless_svc}" + while ! nc -z $join_pod_name ${ERL_DIST_PORT:-5210}; do sleep 1; done + info "==> Will (re-)join ejabberd pod $sts_name@$join_pod_name ..." + ejabberdctl join_cluster "$sts_name@$join_pod_name" && sleep 5s + if echo "$(_cluster_member)" | grep -q "$join_pod_name" + then touch $HOME/.ejabberd_ready + fi + fi +} + +# (Re-)join cluster - election derived +_join_cluster_elector() { + [ -e "$HOME/.ejabberd_ready" ] && rm $HOME/.ejabberd_ready + if [ "$(echo $(_cluster_member) | wc -l)" -gt "1" ] + then + info "==> Leaving non-leader cluster ..." + NO_WARNINGS=true ejabberdctl leave_cluster "$sts_name@$pod_name.${headless_svc}" + fi + while ! nc -z $leader.${headless_svc} ${ERL_DIST_PORT:-5210}; do sleep 1; done + info "==> Will (re-)join leader "$sts_name@$leader.${headless_svc}" ..." + ejabberdctl join_cluster "$sts_name@$leader.${headless_svc}" && sleep 5s + if echo "$(_cluster_member)" | grep -q "$leader" + then touch $HOME/.ejabberd_ready + fi +} + +# trap SIGTERM +trap '_shutdown' SIGTERM +touch $HOME/.ejabberd_terminate_false + +# check first if sidecar has rendered the configmaps/secrets already +while [ ! -e $HOME/conf/ejabberd.yml ] +do + info "===> $HOME/conf/ejabberd.yml not yet rendered, waiting ..." && sleep 3 +done +sleep 3 + +info '==> Start ejabberd main process ...' +ejabberdctl start +tail -n+1 -F logs/ejabberd.log & + pid_ejabberd_log=$! +tail -n+1 -F logs/error.log & + pid_error_log=$! +tail -n+1 -F logs/crash.log & + pid_crash_log=$! +tail -n+1 -F logs/erlang.log & + pid_erlang_log=$! +ejabberdctl started + +## Start elector, if enabled +[ "${ELECTOR_ENABLED:-false}" = 'true' ] && _start_elector + +while true; do + +pod_status="$(wget -q -O - --post-data '{}' $api_url/api/status || echo 'unhealthy')" +if [ ! "$pod_status" = 'unhealthy' ] && [ -e $HOME/.ejabberd_ready ] +then + if [ "${ELECTOR_ENABLED:-false}" = 'true' ] + then + export leader="$(wget -cq $election_url -O - | jq -r .leader)" + ## leader is always right! + if [ "$leader" = "$pod_name" ] + then + info "==> $pod_name is elected leader and healthy ..." + [ ! -e "$HOME/.ejabberd_ready" ] && touch $HOME/.ejabberd_ready + ## if leader is not part of fellow's cluster list, re-join leader. + elif echo "$(_cluster_member)" | grep -q "$leader" + then + info "==> $pod_name is fellow, healthy and connected to leader $leader ..." + [ ! -e "$HOME/.ejabberd_ready" ] && touch $HOME/.ejabberd_ready + else + info "==> $pod_name is fellow, but not connected to leader $leader ..." + _join_cluster_elector + fi + elif [ ! -z "$cluster_pod_names" ] && [ ! "$cluster_pod_names" = 'NXDOMAIN' ] + then + info "==> Other healthy pods detected ..." + _join_cluster_dns + else + info "==> No other healthy pods detected ..." + [ ! -e "$HOME/.ejabberd_ready" ] && touch $HOME/.ejabberd_ready + fi +elif [ ! "$pod_status" = 'unhealthy' ] && [ ! -e $HOME/.ejabberd_ready ] +then + if [ "${K8S_CLUSTERING:-false}" = 'false' ] + then + [ ! -e "$HOME/.ejabberd_ready" ] && touch $HOME/.ejabberd_ready + elif [ "${ELECTOR_ENABLED:-false}" = 'true' ] + then + export leader="$(wget -cq $election_url -O - | jq -r .leader)" + ## leader is always right! + if [ "$leader" = "$pod_name" ] + then + info "==> $pod_name is elected leader and healthy ..." + [ ! -e "$HOME/.ejabberd_ready" ] && touch $HOME/.ejabberd_ready + ## if leader is not part of fellow's cluster list, re-join leader. + elif echo "$(_cluster_member)" | grep -q "$leader" + then + info "==> $pod_name is fellow, healthy and connected to leader $leader ..." + [ ! -e "$HOME/.ejabberd_ready" ] && touch $HOME/.ejabberd_ready + else + info "==> $pod_name is fellow, but not connected to leader $leader ..." + _join_cluster_elector + fi + fi + if [ ! -e $HOME/.ejabberd_ready ] + then + vhosts="$(ejabberdctl registered_vhosts)" + for vhost in $vhosts + do + info "==> $pod_name is healthy, but not ready, delete mnesia for $vhost ..." + ejabberdctl delete_mnesia "$vhost" + done + if [ "${ELECTOR_ENABLED:-false}" = 'true' ] + then + export leader="$(wget -cq $election_url -O - | jq -r .leader)" + #ejabberdctl set_master "$sts_name@$leader.${headless_svc}" + _join_cluster_elector + else _join_cluster_dns + fi + fi + if [ ! -e $HOME/.ejabberd_ready ] + then + info "==> $pod_name is still not ready, restart ejabberd while removing mnesia folder ..." + ejabberdctl stop + ejabberdctl stopped + rm -rf $HOME/database/* + ejabberdctl start + ejabberdctl started + if [ "${ELECTOR_ENABLED:-false}" = 'true' ] + then + export leader="$(wget -cq $election_url -O - | jq -r .leader)" + #ejabberdctl set_master "$sts_name@$leader.${headless_svc}" + _join_cluster_elector + else _join_cluster_dns + fi + fi +else + info "==> $pod_name unhealthy ..." + [ -e "$HOME/.ejabberd_ready" ] && rm $HOME/.ejabberd_ready + [ ! $(ejabberdctl started) ] && ejabberdctl start && ejabberdctl started || _shutdown +fi + +info "==> check again in 15s ..." +sleep 15s + +if [ ! -e $HOME/.ejabberd_terminate_false ] +then break +fi +done + +wait ${pid_elector-} diff --git a/image/tag b/image/tag index fe8e8da..40a9818 100644 --- a/image/tag +++ b/image/tag @@ -2,4 +2,4 @@ # -k8sX specifies the image release, new releases have a higher ordinal number # changes in this file trigger a rebuild of the container image -24.07-k8s1 +24.12-k8s1