diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9396a6b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +/*/**/Dockerfile linguist-generated +/*/**/docker-entrypoint.sh linguist-generated +/Dockerfile.template linguist-language=Dockerfile diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c4c0ead --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,48 @@ +name: GitHub CI + +on: + pull_request: + push: + schedule: + - cron: 0 0 * * 0 + +defaults: + run: + shell: 'bash -Eeuo pipefail -x {0}' + +jobs: + + generate-jobs: + name: Generate Jobs + runs-on: ubuntu-latest + outputs: + strategy: ${{ steps.generate-jobs.outputs.strategy }} + steps: + - uses: actions/checkout@v4 + - uses: docker-library/bashbrew@HEAD + - id: generate-jobs + name: Generate Jobs + run: | + strategy="$("$BASHBREW_SCRIPTS/github-actions/generate.sh")" + echo "strategy=$strategy" >> "$GITHUB_OUTPUT" + jq . <<<"$strategy" # sanity check / debugging aid + + test: + needs: generate-jobs + strategy: ${{ fromJson(needs.generate-jobs.outputs.strategy) }} + name: ${{ matrix.name }} + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - name: Prepare Environment + run: ${{ matrix.runs.prepare }} + - name: Pull Dependencies + run: ${{ matrix.runs.pull }} + - name: Build ${{ matrix.name }} + run: ${{ matrix.runs.build }} + - name: History ${{ matrix.name }} + run: ${{ matrix.runs.history }} + - name: Test ${{ matrix.name }} + run: ${{ matrix.runs.test }} + - name: '"docker images"' + run: ${{ matrix.runs.images }} diff --git a/.github/workflows/verify-templating.yml b/.github/workflows/verify-templating.yml new file mode 100644 index 0000000..e822ba6 --- /dev/null +++ b/.github/workflows/verify-templating.yml @@ -0,0 +1,19 @@ +name: Verify Templating + +on: + pull_request: + push: + workflow_dispatch: + +defaults: + run: + shell: 'bash -Eeuo pipefail -x {0}' + +jobs: + apply-templates: + name: Check For Uncomitted Changes + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: ./apply-templates.sh + - run: git diff --exit-code diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d548f66 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.jq-template.awk diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b17576a..0000000 --- a/.travis.yml +++ /dev/null @@ -1,24 +0,0 @@ -language: bash -services: docker - -env: - - VERSION=3.0 - - VERSION=2.2 - - VERSION=2.1 - -install: - - git clone https://github.com/docker-library/official-images.git ~/official-images - -before_script: - - env | sort - - cd "$VERSION" - - image="cassandra:$VERSION" - -script: - - docker build -t "$image" . - - ~/official-images/test/run.sh "$image" - -after_script: - - docker images - -# vim:set et ts=2 sw=2: diff --git a/2.1/Dockerfile b/2.1/Dockerfile deleted file mode 100644 index 5b37e2b..0000000 --- a/2.1/Dockerfile +++ /dev/null @@ -1,26 +0,0 @@ -FROM debian:jessie-backports - -RUN apt-key adv --keyserver ha.pool.sks-keyservers.net --recv-keys 514A2AD631A57A16DD0047EC749D6EEC0353B12C - -RUN echo 'deb http://www.apache.org/dist/cassandra/debian 21x main' >> /etc/apt/sources.list.d/cassandra.list - -ENV CASSANDRA_VERSION 2.1.11 - -RUN apt-get update \ - && apt-get install -y cassandra="$CASSANDRA_VERSION" \ - && rm -rf /var/lib/apt/lists/* - -ENV CASSANDRA_CONFIG /etc/cassandra - -COPY docker-entrypoint.sh /docker-entrypoint.sh -ENTRYPOINT ["/docker-entrypoint.sh"] - -VOLUME /var/lib/cassandra - -# 7000: intra-node communication -# 7001: TLS intra-node communication -# 7199: JMX -# 9042: CQL -# 9160: thrift service -EXPOSE 7000 7001 7199 9042 9160 -CMD ["cassandra", "-f"] diff --git a/2.1/docker-entrypoint.sh b/2.1/docker-entrypoint.sh deleted file mode 100755 index 31fd2a0..0000000 --- a/2.1/docker-entrypoint.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash -set -e - -# first arg is `-f` or `--some-option` -if [ "${1:0:1}" = '-' ]; then - set -- cassandra -f "$@" -fi - -if [ "$1" = 'cassandra' ]; then - : ${CASSANDRA_RPC_ADDRESS='0.0.0.0'} - - : ${CASSANDRA_LISTEN_ADDRESS='auto'} - if [ "$CASSANDRA_LISTEN_ADDRESS" = 'auto' ]; then - CASSANDRA_LISTEN_ADDRESS="$(hostname --ip-address)" - fi - - : ${CASSANDRA_BROADCAST_ADDRESS="$CASSANDRA_LISTEN_ADDRESS"} - - if [ "$CASSANDRA_BROADCAST_ADDRESS" = 'auto' ]; then - CASSANDRA_BROADCAST_ADDRESS="$(hostname --ip-address)" - fi - : ${CASSANDRA_BROADCAST_RPC_ADDRESS:=$CASSANDRA_BROADCAST_ADDRESS} - - if [ -n "${CASSANDRA_NAME:+1}" ]; then - : ${CASSANDRA_SEEDS:="cassandra"} - fi - : ${CASSANDRA_SEEDS:="$CASSANDRA_BROADCAST_ADDRESS"} - - sed -ri 's/(- seeds:) "127.0.0.1"/\1 "'"$CASSANDRA_SEEDS"'"/' "$CASSANDRA_CONFIG/cassandra.yaml" - - for yaml in \ - broadcast_address \ - broadcast_rpc_address \ - cluster_name \ - endpoint_snitch \ - listen_address \ - num_tokens \ - rpc_address \ - start_rpc \ - ; do - var="CASSANDRA_${yaml^^}" - val="${!var}" - if [ "$val" ]; then - sed -ri 's/^(# )?('"$yaml"':).*/\2 '"$val"'/' "$CASSANDRA_CONFIG/cassandra.yaml" - fi - done - - for rackdc in dc rack; do - var="CASSANDRA_${rackdc^^}" - val="${!var}" - if [ "$val" ]; then - sed -ri 's/^('"$rackdc"'=).*/\1 '"$val"'/' "$CASSANDRA_CONFIG/cassandra-rackdc.properties" - fi - done -fi - -exec "$@" diff --git a/2.2/Dockerfile b/2.2/Dockerfile deleted file mode 100644 index 965cc83..0000000 --- a/2.2/Dockerfile +++ /dev/null @@ -1,26 +0,0 @@ -FROM debian:jessie-backports - -RUN apt-key adv --keyserver ha.pool.sks-keyservers.net --recv-keys 514A2AD631A57A16DD0047EC749D6EEC0353B12C - -RUN echo 'deb http://www.apache.org/dist/cassandra/debian 22x main' >> /etc/apt/sources.list.d/cassandra.list - -ENV CASSANDRA_VERSION 2.2.3 - -RUN apt-get update \ - && apt-get install -y cassandra="$CASSANDRA_VERSION" \ - && rm -rf /var/lib/apt/lists/* - -ENV CASSANDRA_CONFIG /etc/cassandra - -COPY docker-entrypoint.sh /docker-entrypoint.sh -ENTRYPOINT ["/docker-entrypoint.sh"] - -VOLUME /var/lib/cassandra - -# 7000: intra-node communication -# 7001: TLS intra-node communication -# 7199: JMX -# 9042: CQL -# 9160: thrift service -EXPOSE 7000 7001 7199 9042 9160 -CMD ["cassandra", "-f"] diff --git a/2.2/docker-entrypoint.sh b/2.2/docker-entrypoint.sh deleted file mode 100755 index 31fd2a0..0000000 --- a/2.2/docker-entrypoint.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash -set -e - -# first arg is `-f` or `--some-option` -if [ "${1:0:1}" = '-' ]; then - set -- cassandra -f "$@" -fi - -if [ "$1" = 'cassandra' ]; then - : ${CASSANDRA_RPC_ADDRESS='0.0.0.0'} - - : ${CASSANDRA_LISTEN_ADDRESS='auto'} - if [ "$CASSANDRA_LISTEN_ADDRESS" = 'auto' ]; then - CASSANDRA_LISTEN_ADDRESS="$(hostname --ip-address)" - fi - - : ${CASSANDRA_BROADCAST_ADDRESS="$CASSANDRA_LISTEN_ADDRESS"} - - if [ "$CASSANDRA_BROADCAST_ADDRESS" = 'auto' ]; then - CASSANDRA_BROADCAST_ADDRESS="$(hostname --ip-address)" - fi - : ${CASSANDRA_BROADCAST_RPC_ADDRESS:=$CASSANDRA_BROADCAST_ADDRESS} - - if [ -n "${CASSANDRA_NAME:+1}" ]; then - : ${CASSANDRA_SEEDS:="cassandra"} - fi - : ${CASSANDRA_SEEDS:="$CASSANDRA_BROADCAST_ADDRESS"} - - sed -ri 's/(- seeds:) "127.0.0.1"/\1 "'"$CASSANDRA_SEEDS"'"/' "$CASSANDRA_CONFIG/cassandra.yaml" - - for yaml in \ - broadcast_address \ - broadcast_rpc_address \ - cluster_name \ - endpoint_snitch \ - listen_address \ - num_tokens \ - rpc_address \ - start_rpc \ - ; do - var="CASSANDRA_${yaml^^}" - val="${!var}" - if [ "$val" ]; then - sed -ri 's/^(# )?('"$yaml"':).*/\2 '"$val"'/' "$CASSANDRA_CONFIG/cassandra.yaml" - fi - done - - for rackdc in dc rack; do - var="CASSANDRA_${rackdc^^}" - val="${!var}" - if [ "$val" ]; then - sed -ri 's/^('"$rackdc"'=).*/\1 '"$val"'/' "$CASSANDRA_CONFIG/cassandra-rackdc.properties" - fi - done -fi - -exec "$@" diff --git a/3.0/Dockerfile b/3.0/Dockerfile index 74f3ca9..c96aa37 100644 --- a/3.0/Dockerfile +++ b/3.0/Dockerfile @@ -1,22 +1,173 @@ -FROM debian:jessie-backports +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "apply-templates.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# -RUN apt-key adv --keyserver ha.pool.sks-keyservers.net --recv-keys 514A2AD631A57A16DD0047EC749D6EEC0353B12C +FROM eclipse-temurin:8-jre-jammy -RUN echo 'deb http://www.apache.org/dist/cassandra/debian 30x main' >> /etc/apt/sources.list.d/cassandra.list +# explicitly set user/group IDs +RUN set -eux; \ + groupadd -r cassandra --gid=999; \ + useradd -r -g cassandra --uid=999 cassandra -ENV CASSANDRA_VERSION 3.0.0 +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ +# solves warning: "jemalloc shared library could not be preloaded to speed up memory allocations" + libjemalloc2 \ +# "free" is used by cassandra-env.sh + procps \ +# "cqlsh" needs a python interpreter + python2 \ +# "ip" is not required by Cassandra itself, but is commonly used in scripting Cassandra's configuration (since it is so fixated on explicit IP addresses) + iproute2 \ +# Cassandra will automatically use numactl if available +# https://github.com/apache/cassandra/blob/18bcda2d4c2eba7370a0b21f33eed37cb730bbb3/bin/cassandra#L90-L100 +# https://github.com/apache/cassandra/commit/604c0e87dc67fa65f6904ef9a98a029c9f2f865a + numactl \ + ; \ + rm -rf /var/lib/apt/lists/*; \ +# https://issues.apache.org/jira/browse/CASSANDRA-15767 ("bin/cassandra" only looks for "libjemalloc.so" or "libjemalloc.so.1" which doesn't match our "libjemalloc.so.2") + libjemalloc="$(readlink -e /usr/lib/*/libjemalloc.so.2)"; \ + ln -sT "$libjemalloc" /usr/local/lib/libjemalloc.so; \ + ldconfig -RUN apt-get update \ - && apt-get install -y cassandra="$CASSANDRA_VERSION" \ - && rm -rf /var/lib/apt/lists/* +# grab gosu for easy step-down from root +# https://github.com/tianon/gosu/releases +ENV GOSU_VERSION 1.17 +RUN set -eux; \ + savedAptMark="$(apt-mark showmanual)"; \ + apt-get update; \ + apt-get install -y --no-install-recommends ca-certificates gnupg wget; \ + rm -rf /var/lib/apt/lists/*; \ + dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ + wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \ + wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \ + export GNUPGHOME="$(mktemp -d)"; \ + gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ + gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \ + gpgconf --kill all; \ + rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \ + apt-mark auto '.*' > /dev/null; \ + [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + chmod +x /usr/local/bin/gosu; \ + gosu --version; \ + gosu nobody true -ENV CASSANDRA_CONFIG /etc/cassandra +ENV CASSANDRA_HOME /opt/cassandra +ENV CASSANDRA_CONF /etc/cassandra +ENV PATH $CASSANDRA_HOME/bin:$PATH -COPY docker-entrypoint.sh /docker-entrypoint.sh -ENTRYPOINT ["/docker-entrypoint.sh"] +# https://cwiki.apache.org/confluence/display/CASSANDRA2/DebianPackaging#DebianPackaging-AddingRepositoryKeys +# $ docker run --rm buildpack-deps:bullseye-curl bash -c 'wget -qO- https://downloads.apache.org/cassandra/KEYS | gpg --batch --import &> /dev/null && gpg --batch --list-keys --with-fingerprint --with-colons' | awk -F: '$1 == "pub" && $2 == "-" { pub = 1 } pub && $1 == "fpr" { fpr = $10 } $1 == "sub" { pub = 0 } pub && fpr && $1 == "uid" && $2 == "-" { print "#", $10; print "\t" fpr " \\"; pub = 0 }' +ENV GPG_KEYS \ +# Eric Evans + CEC86BB4A0BA9D0F90397CAEF8358FA2F2833C93 \ +# Eric Evans + C4965EE9E3015D192CCCF2B6F758CE318D77295D \ +# Sylvain Lebresne (pcmanus) + 5AED1BF378E9A19DADE1BCB34BD736A82B5C1B00 \ +# T Jake Luciani + 514A2AD631A57A16DD0047EC749D6EEC0353B12C \ +# Michael Shuler + A26E528B271F19B9E5D8E19EA278B781FE4B2BDA \ +# Michael Semb Wever + A4C465FEA0C552561A392A61E91335D77E3E87CB \ +# Alex Petrov + 9E66CEC6106D578D0B1EB9BFF1000962B7F6840C \ +# Jordan West + C4009872C59B49561310D966D0062876AF30F054 \ +# Brandon Williams + B7842CDAF36E6A3214FAE35D5E85B9AE0B84C041 \ +# Ekaterina Buryanova Dimitrova (CODE SIGNING KEY) + 3E9C876907A560ACA00964F363E9BAD215BBF5F0 \ +# Sam Tunnicliffe (CODE SIGNING KEY) + F8B7FD00E05C932991A2CD6150EE103D162C5A55 \ +# Stefan Miklosovic + 7464AAD9068241C50BA6A26232F35CB2F546D93E \ +# Berenguer Blasi (Code Signing Key) + CEC5C50B9C629EF0F5AB2706650B72EB14CCD622 + +ENV CASSANDRA_VERSION 3.0.32 +ENV CASSANDRA_SHA512 26043c329bc4c179bc50c2d7bd59e85292bfc8ed2af6502b636fe2fc583d32678649a69a2b0bdc6ff1f2798f7c4fd7fd84a7714d25c2e6172ab5cd1ae2d5d05c + +RUN set -eux; \ + savedAptMark="$(apt-mark showmanual)"; \ + apt-get update; \ + apt-get install -y --no-install-recommends ca-certificates gnupg wget; \ + rm -rf /var/lib/apt/lists/*; \ + \ + ddist() { \ + local f="$1"; shift; \ + local distFile="$1"; shift; \ + local success=; \ + local distUrl=; \ + for distUrl in \ +# https://github.com/docker-library/tomcat/pull/308 + https://dlcdn.apache.org/ \ +# if the version is outdated, we have to pull from the archive + https://archive.apache.org/dist/ \ + ; do \ + if wget --progress=dot:giga -O "$f" "$distUrl$distFile" && [ -s "$f" ]; then \ + success=1; \ + break; \ + fi; \ + done; \ + [ -n "$success" ]; \ + }; \ + \ + ddist 'cassandra-bin.tgz' "cassandra/$CASSANDRA_VERSION/apache-cassandra-$CASSANDRA_VERSION-bin.tar.gz"; \ + echo "$CASSANDRA_SHA512 *cassandra-bin.tgz" | sha512sum --check --strict -; \ + \ + ddist 'cassandra-bin.tgz.asc' "cassandra/$CASSANDRA_VERSION/apache-cassandra-$CASSANDRA_VERSION-bin.tar.gz.asc"; \ + export GNUPGHOME="$(mktemp -d)"; \ + for key in $GPG_KEYS; do \ + gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key"; \ + done; \ + gpg --batch --verify cassandra-bin.tgz.asc cassandra-bin.tgz; \ + rm -rf "$GNUPGHOME"; \ + \ + apt-mark auto '.*' > /dev/null; \ + [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + \ + mkdir -p "$CASSANDRA_HOME"; \ + tar --extract --file cassandra-bin.tgz --directory "$CASSANDRA_HOME" --strip-components 1; \ + rm cassandra-bin.tgz*; \ + \ + [ ! -e "$CASSANDRA_CONF" ]; \ + mv "$CASSANDRA_HOME/conf" "$CASSANDRA_CONF"; \ + ln -sT "$CASSANDRA_CONF" "$CASSANDRA_HOME/conf"; \ + \ + dpkgArch="$(dpkg --print-architecture)"; \ + case "$dpkgArch" in \ + ppc64el) \ +# https://issues.apache.org/jira/browse/CASSANDRA-13345 +# "The stack size specified is too small, Specify at least 328k" + grep -- '-Xss256k' "$CASSANDRA_CONF/cassandra-env.sh"; \ + sed -ri 's/-Xss256k/-Xss512k/g' "$CASSANDRA_CONF/cassandra-env.sh"; \ + grep -- '-Xss512k' "$CASSANDRA_CONF/cassandra-env.sh"; \ + ;; \ + esac; \ + \ + mkdir -p "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra; \ + chown -R cassandra:cassandra "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra; \ + chmod 1777 "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra; \ + chmod -R a+rwX "$CASSANDRA_CONF"; \ + ln -sT /var/lib/cassandra "$CASSANDRA_HOME/data"; \ + ln -sT /var/log/cassandra "$CASSANDRA_HOME/logs"; \ + \ +# smoke test + cassandra -v VOLUME /var/lib/cassandra +COPY docker-entrypoint.sh /usr/local/bin/ +RUN ln -s usr/local/bin/docker-entrypoint.sh /docker-entrypoint.sh # backwards compat +ENTRYPOINT ["docker-entrypoint.sh"] + # 7000: intra-node communication # 7001: TLS intra-node communication # 7199: JMX diff --git a/3.0/docker-entrypoint.sh b/3.0/docker-entrypoint.sh index 31fd2a0..f39af13 100755 --- a/3.0/docker-entrypoint.sh +++ b/3.0/docker-entrypoint.sh @@ -1,23 +1,57 @@ -#!/bin/bash +#!/usr/bin/env bash set -e # first arg is `-f` or `--some-option` -if [ "${1:0:1}" = '-' ]; then +# or there are no args +if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then set -- cassandra -f "$@" fi +# allow the container to be started with `--user` +if [ "$1" = 'cassandra' -a "$(id -u)" = '0' ]; then + find "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra \ + \! -user cassandra -exec chown cassandra '{}' + + exec gosu cassandra "$BASH_SOURCE" "$@" +fi + +_ip_address() { + # scrape the first non-localhost IP address of the container + # in Swarm Mode, we often get two IPs -- the container IP, and the (shared) VIP, and the container IP should always be first + ip address | awk ' + $1 != "inet" { next } # only lines with ip addresses + $NF == "lo" { next } # skip loopback devices + $2 ~ /^127[.]/ { next } # skip loopback addresses + $2 ~ /^169[.]254[.]/ { next } # skip link-local addresses + { + gsub(/\/.+$/, "", $2) + print $2 + exit + } + ' +} + +# "sed -i", but without "mv" (which doesn't work on a bind-mounted file, for example) +_sed-in-place() { + local filename="$1"; shift + local tempFile + tempFile="$(mktemp)" + sed "$@" "$filename" > "$tempFile" + cat "$tempFile" > "$filename" + rm "$tempFile" +} + if [ "$1" = 'cassandra' ]; then : ${CASSANDRA_RPC_ADDRESS='0.0.0.0'} : ${CASSANDRA_LISTEN_ADDRESS='auto'} if [ "$CASSANDRA_LISTEN_ADDRESS" = 'auto' ]; then - CASSANDRA_LISTEN_ADDRESS="$(hostname --ip-address)" + CASSANDRA_LISTEN_ADDRESS="$(_ip_address)" fi : ${CASSANDRA_BROADCAST_ADDRESS="$CASSANDRA_LISTEN_ADDRESS"} if [ "$CASSANDRA_BROADCAST_ADDRESS" = 'auto' ]; then - CASSANDRA_BROADCAST_ADDRESS="$(hostname --ip-address)" + CASSANDRA_BROADCAST_ADDRESS="$(_ip_address)" fi : ${CASSANDRA_BROADCAST_RPC_ADDRESS:=$CASSANDRA_BROADCAST_ADDRESS} @@ -25,8 +59,9 @@ if [ "$1" = 'cassandra' ]; then : ${CASSANDRA_SEEDS:="cassandra"} fi : ${CASSANDRA_SEEDS:="$CASSANDRA_BROADCAST_ADDRESS"} - - sed -ri 's/(- seeds:) "127.0.0.1"/\1 "'"$CASSANDRA_SEEDS"'"/' "$CASSANDRA_CONFIG/cassandra.yaml" + + _sed-in-place "$CASSANDRA_CONF/cassandra.yaml" \ + -r 's/(- seeds:).*/\1 "'"$CASSANDRA_SEEDS"'"/' for yaml in \ broadcast_address \ @@ -41,7 +76,8 @@ if [ "$1" = 'cassandra' ]; then var="CASSANDRA_${yaml^^}" val="${!var}" if [ "$val" ]; then - sed -ri 's/^(# )?('"$yaml"':).*/\2 '"$val"'/' "$CASSANDRA_CONFIG/cassandra.yaml" + _sed-in-place "$CASSANDRA_CONF/cassandra.yaml" \ + -r 's/^(# )?('"$yaml"':).*/\2 '"$val"'/' fi done @@ -49,7 +85,8 @@ if [ "$1" = 'cassandra' ]; then var="CASSANDRA_${rackdc^^}" val="${!var}" if [ "$val" ]; then - sed -ri 's/^('"$rackdc"'=).*/\1 '"$val"'/' "$CASSANDRA_CONFIG/cassandra-rackdc.properties" + _sed-in-place "$CASSANDRA_CONF/cassandra-rackdc.properties" \ + -r 's/^('"$rackdc"'=).*/\1 '"$val"'/' fi done fi diff --git a/3.11/Dockerfile b/3.11/Dockerfile new file mode 100644 index 0000000..15f8105 --- /dev/null +++ b/3.11/Dockerfile @@ -0,0 +1,177 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "apply-templates.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# + +FROM eclipse-temurin:8-jre-jammy + +# explicitly set user/group IDs +RUN set -eux; \ + groupadd -r cassandra --gid=999; \ + useradd -r -g cassandra --uid=999 cassandra + +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ +# solves warning: "jemalloc shared library could not be preloaded to speed up memory allocations" + libjemalloc2 \ +# "free" is used by cassandra-env.sh + procps \ +# "cqlsh" needs a python interpreter + python2 \ +# "ip" is not required by Cassandra itself, but is commonly used in scripting Cassandra's configuration (since it is so fixated on explicit IP addresses) + iproute2 \ +# Cassandra will automatically use numactl if available +# https://github.com/apache/cassandra/blob/18bcda2d4c2eba7370a0b21f33eed37cb730bbb3/bin/cassandra#L90-L100 +# https://github.com/apache/cassandra/commit/604c0e87dc67fa65f6904ef9a98a029c9f2f865a + numactl \ + ; \ + rm -rf /var/lib/apt/lists/*; \ +# https://issues.apache.org/jira/browse/CASSANDRA-15767 ("bin/cassandra" only looks for "libjemalloc.so" or "libjemalloc.so.1" which doesn't match our "libjemalloc.so.2") + libjemalloc="$(readlink -e /usr/lib/*/libjemalloc.so.2)"; \ + ln -sT "$libjemalloc" /usr/local/lib/libjemalloc.so; \ + ldconfig + +# grab gosu for easy step-down from root +# https://github.com/tianon/gosu/releases +ENV GOSU_VERSION 1.17 +RUN set -eux; \ + savedAptMark="$(apt-mark showmanual)"; \ + apt-get update; \ + apt-get install -y --no-install-recommends ca-certificates gnupg wget; \ + rm -rf /var/lib/apt/lists/*; \ + dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ + wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \ + wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \ + export GNUPGHOME="$(mktemp -d)"; \ + gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ + gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \ + gpgconf --kill all; \ + rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \ + apt-mark auto '.*' > /dev/null; \ + [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + chmod +x /usr/local/bin/gosu; \ + gosu --version; \ + gosu nobody true + +ENV CASSANDRA_HOME /opt/cassandra +ENV CASSANDRA_CONF /etc/cassandra +ENV PATH $CASSANDRA_HOME/bin:$PATH + +# https://cwiki.apache.org/confluence/display/CASSANDRA2/DebianPackaging#DebianPackaging-AddingRepositoryKeys +# $ docker run --rm buildpack-deps:bullseye-curl bash -c 'wget -qO- https://downloads.apache.org/cassandra/KEYS | gpg --batch --import &> /dev/null && gpg --batch --list-keys --with-fingerprint --with-colons' | awk -F: '$1 == "pub" && $2 == "-" { pub = 1 } pub && $1 == "fpr" { fpr = $10 } $1 == "sub" { pub = 0 } pub && fpr && $1 == "uid" && $2 == "-" { print "#", $10; print "\t" fpr " \\"; pub = 0 }' +ENV GPG_KEYS \ +# Eric Evans + CEC86BB4A0BA9D0F90397CAEF8358FA2F2833C93 \ +# Eric Evans + C4965EE9E3015D192CCCF2B6F758CE318D77295D \ +# Sylvain Lebresne (pcmanus) + 5AED1BF378E9A19DADE1BCB34BD736A82B5C1B00 \ +# T Jake Luciani + 514A2AD631A57A16DD0047EC749D6EEC0353B12C \ +# Michael Shuler + A26E528B271F19B9E5D8E19EA278B781FE4B2BDA \ +# Michael Semb Wever + A4C465FEA0C552561A392A61E91335D77E3E87CB \ +# Alex Petrov + 9E66CEC6106D578D0B1EB9BFF1000962B7F6840C \ +# Jordan West + C4009872C59B49561310D966D0062876AF30F054 \ +# Brandon Williams + B7842CDAF36E6A3214FAE35D5E85B9AE0B84C041 \ +# Ekaterina Buryanova Dimitrova (CODE SIGNING KEY) + 3E9C876907A560ACA00964F363E9BAD215BBF5F0 \ +# Sam Tunnicliffe (CODE SIGNING KEY) + F8B7FD00E05C932991A2CD6150EE103D162C5A55 \ +# Stefan Miklosovic + 7464AAD9068241C50BA6A26232F35CB2F546D93E \ +# Berenguer Blasi (Code Signing Key) + CEC5C50B9C629EF0F5AB2706650B72EB14CCD622 + +ENV CASSANDRA_VERSION 3.11.19 +ENV CASSANDRA_SHA512 42d7732c2b81c65a960101d1146603d430de341adcdf8d0ffc649753a340cf64dad696050f2ec01faff5f15e726f4f2a459f0b3ac281569b957f7726f51d43e0 + +RUN set -eux; \ + savedAptMark="$(apt-mark showmanual)"; \ + apt-get update; \ + apt-get install -y --no-install-recommends ca-certificates gnupg wget; \ + rm -rf /var/lib/apt/lists/*; \ + \ + ddist() { \ + local f="$1"; shift; \ + local distFile="$1"; shift; \ + local success=; \ + local distUrl=; \ + for distUrl in \ +# https://github.com/docker-library/tomcat/pull/308 + https://dlcdn.apache.org/ \ +# if the version is outdated, we have to pull from the archive + https://archive.apache.org/dist/ \ + ; do \ + if wget --progress=dot:giga -O "$f" "$distUrl$distFile" && [ -s "$f" ]; then \ + success=1; \ + break; \ + fi; \ + done; \ + [ -n "$success" ]; \ + }; \ + \ + ddist 'cassandra-bin.tgz' "cassandra/$CASSANDRA_VERSION/apache-cassandra-$CASSANDRA_VERSION-bin.tar.gz"; \ + echo "$CASSANDRA_SHA512 *cassandra-bin.tgz" | sha512sum --check --strict -; \ + \ + ddist 'cassandra-bin.tgz.asc' "cassandra/$CASSANDRA_VERSION/apache-cassandra-$CASSANDRA_VERSION-bin.tar.gz.asc"; \ + export GNUPGHOME="$(mktemp -d)"; \ + for key in $GPG_KEYS; do \ + gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key"; \ + done; \ + gpg --batch --verify cassandra-bin.tgz.asc cassandra-bin.tgz; \ + rm -rf "$GNUPGHOME"; \ + \ + apt-mark auto '.*' > /dev/null; \ + [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + \ + mkdir -p "$CASSANDRA_HOME"; \ + tar --extract --file cassandra-bin.tgz --directory "$CASSANDRA_HOME" --strip-components 1; \ + rm cassandra-bin.tgz*; \ + \ + [ ! -e "$CASSANDRA_CONF" ]; \ + mv "$CASSANDRA_HOME/conf" "$CASSANDRA_CONF"; \ + ln -sT "$CASSANDRA_CONF" "$CASSANDRA_HOME/conf"; \ + \ + dpkgArch="$(dpkg --print-architecture)"; \ + case "$dpkgArch" in \ + ppc64el) \ +# https://issues.apache.org/jira/browse/CASSANDRA-13345 +# "The stack size specified is too small, Specify at least 328k" + grep -- '^-Xss256k$' "$CASSANDRA_CONF/jvm.options"; \ + sed -ri 's/^-Xss256k$/-Xss512k/' "$CASSANDRA_CONF/jvm.options"; \ + grep -- '^-Xss512k$' "$CASSANDRA_CONF/jvm.options"; \ + ;; \ + esac; \ + \ + mkdir -p "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra; \ + chown -R cassandra:cassandra "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra; \ + chmod 1777 "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra; \ + chmod -R a+rwX "$CASSANDRA_CONF"; \ + ln -sT /var/lib/cassandra "$CASSANDRA_HOME/data"; \ + ln -sT /var/log/cassandra "$CASSANDRA_HOME/logs"; \ + \ +# smoke test + cassandra -v + +VOLUME /var/lib/cassandra + +COPY docker-entrypoint.sh /usr/local/bin/ +RUN ln -s usr/local/bin/docker-entrypoint.sh /docker-entrypoint.sh # backwards compat +ENTRYPOINT ["docker-entrypoint.sh"] + +# 7000: intra-node communication +# 7001: TLS intra-node communication +# 7199: JMX +# 9042: CQL +# 9160: thrift service +EXPOSE 7000 7001 7199 9042 9160 +CMD ["cassandra", "-f"] diff --git a/3.11/docker-entrypoint.sh b/3.11/docker-entrypoint.sh new file mode 100755 index 0000000..f39af13 --- /dev/null +++ b/3.11/docker-entrypoint.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash +set -e + +# first arg is `-f` or `--some-option` +# or there are no args +if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then + set -- cassandra -f "$@" +fi + +# allow the container to be started with `--user` +if [ "$1" = 'cassandra' -a "$(id -u)" = '0' ]; then + find "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra \ + \! -user cassandra -exec chown cassandra '{}' + + exec gosu cassandra "$BASH_SOURCE" "$@" +fi + +_ip_address() { + # scrape the first non-localhost IP address of the container + # in Swarm Mode, we often get two IPs -- the container IP, and the (shared) VIP, and the container IP should always be first + ip address | awk ' + $1 != "inet" { next } # only lines with ip addresses + $NF == "lo" { next } # skip loopback devices + $2 ~ /^127[.]/ { next } # skip loopback addresses + $2 ~ /^169[.]254[.]/ { next } # skip link-local addresses + { + gsub(/\/.+$/, "", $2) + print $2 + exit + } + ' +} + +# "sed -i", but without "mv" (which doesn't work on a bind-mounted file, for example) +_sed-in-place() { + local filename="$1"; shift + local tempFile + tempFile="$(mktemp)" + sed "$@" "$filename" > "$tempFile" + cat "$tempFile" > "$filename" + rm "$tempFile" +} + +if [ "$1" = 'cassandra' ]; then + : ${CASSANDRA_RPC_ADDRESS='0.0.0.0'} + + : ${CASSANDRA_LISTEN_ADDRESS='auto'} + if [ "$CASSANDRA_LISTEN_ADDRESS" = 'auto' ]; then + CASSANDRA_LISTEN_ADDRESS="$(_ip_address)" + fi + + : ${CASSANDRA_BROADCAST_ADDRESS="$CASSANDRA_LISTEN_ADDRESS"} + + if [ "$CASSANDRA_BROADCAST_ADDRESS" = 'auto' ]; then + CASSANDRA_BROADCAST_ADDRESS="$(_ip_address)" + fi + : ${CASSANDRA_BROADCAST_RPC_ADDRESS:=$CASSANDRA_BROADCAST_ADDRESS} + + if [ -n "${CASSANDRA_NAME:+1}" ]; then + : ${CASSANDRA_SEEDS:="cassandra"} + fi + : ${CASSANDRA_SEEDS:="$CASSANDRA_BROADCAST_ADDRESS"} + + _sed-in-place "$CASSANDRA_CONF/cassandra.yaml" \ + -r 's/(- seeds:).*/\1 "'"$CASSANDRA_SEEDS"'"/' + + for yaml in \ + broadcast_address \ + broadcast_rpc_address \ + cluster_name \ + endpoint_snitch \ + listen_address \ + num_tokens \ + rpc_address \ + start_rpc \ + ; do + var="CASSANDRA_${yaml^^}" + val="${!var}" + if [ "$val" ]; then + _sed-in-place "$CASSANDRA_CONF/cassandra.yaml" \ + -r 's/^(# )?('"$yaml"':).*/\2 '"$val"'/' + fi + done + + for rackdc in dc rack; do + var="CASSANDRA_${rackdc^^}" + val="${!var}" + if [ "$val" ]; then + _sed-in-place "$CASSANDRA_CONF/cassandra-rackdc.properties" \ + -r 's/^('"$rackdc"'=).*/\1 '"$val"'/' + fi + done +fi + +exec "$@" diff --git a/4.0/Dockerfile b/4.0/Dockerfile new file mode 100644 index 0000000..4e13900 --- /dev/null +++ b/4.0/Dockerfile @@ -0,0 +1,176 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "apply-templates.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# + +FROM eclipse-temurin:11-jre-jammy + +# explicitly set user/group IDs +RUN set -eux; \ + groupadd -r cassandra --gid=999; \ + useradd -r -g cassandra --uid=999 cassandra + +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ +# solves warning: "jemalloc shared library could not be preloaded to speed up memory allocations" + libjemalloc2 \ +# "free" is used by cassandra-env.sh + procps \ +# "cqlsh" needs a python interpreter + python3 \ +# "ip" is not required by Cassandra itself, but is commonly used in scripting Cassandra's configuration (since it is so fixated on explicit IP addresses) + iproute2 \ +# Cassandra will automatically use numactl if available +# https://github.com/apache/cassandra/blob/18bcda2d4c2eba7370a0b21f33eed37cb730bbb3/bin/cassandra#L90-L100 +# https://github.com/apache/cassandra/commit/604c0e87dc67fa65f6904ef9a98a029c9f2f865a + numactl \ + ; \ + rm -rf /var/lib/apt/lists/*; \ +# https://issues.apache.org/jira/browse/CASSANDRA-15767 ("bin/cassandra" only looks for "libjemalloc.so" or "libjemalloc.so.1" which doesn't match our "libjemalloc.so.2") + libjemalloc="$(readlink -e /usr/lib/*/libjemalloc.so.2)"; \ + ln -sT "$libjemalloc" /usr/local/lib/libjemalloc.so; \ + ldconfig + +# grab gosu for easy step-down from root +# https://github.com/tianon/gosu/releases +ENV GOSU_VERSION 1.17 +RUN set -eux; \ + savedAptMark="$(apt-mark showmanual)"; \ + apt-get update; \ + apt-get install -y --no-install-recommends ca-certificates gnupg wget; \ + rm -rf /var/lib/apt/lists/*; \ + dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ + wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \ + wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \ + export GNUPGHOME="$(mktemp -d)"; \ + gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ + gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \ + gpgconf --kill all; \ + rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \ + apt-mark auto '.*' > /dev/null; \ + [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + chmod +x /usr/local/bin/gosu; \ + gosu --version; \ + gosu nobody true + +ENV CASSANDRA_HOME /opt/cassandra +ENV CASSANDRA_CONF /etc/cassandra +ENV PATH $CASSANDRA_HOME/bin:$PATH + +# https://cwiki.apache.org/confluence/display/CASSANDRA2/DebianPackaging#DebianPackaging-AddingRepositoryKeys +# $ docker run --rm buildpack-deps:bullseye-curl bash -c 'wget -qO- https://downloads.apache.org/cassandra/KEYS | gpg --batch --import &> /dev/null && gpg --batch --list-keys --with-fingerprint --with-colons' | awk -F: '$1 == "pub" && $2 == "-" { pub = 1 } pub && $1 == "fpr" { fpr = $10 } $1 == "sub" { pub = 0 } pub && fpr && $1 == "uid" && $2 == "-" { print "#", $10; print "\t" fpr " \\"; pub = 0 }' +ENV GPG_KEYS \ +# Eric Evans + CEC86BB4A0BA9D0F90397CAEF8358FA2F2833C93 \ +# Eric Evans + C4965EE9E3015D192CCCF2B6F758CE318D77295D \ +# Sylvain Lebresne (pcmanus) + 5AED1BF378E9A19DADE1BCB34BD736A82B5C1B00 \ +# T Jake Luciani + 514A2AD631A57A16DD0047EC749D6EEC0353B12C \ +# Michael Shuler + A26E528B271F19B9E5D8E19EA278B781FE4B2BDA \ +# Michael Semb Wever + A4C465FEA0C552561A392A61E91335D77E3E87CB \ +# Alex Petrov + 9E66CEC6106D578D0B1EB9BFF1000962B7F6840C \ +# Jordan West + C4009872C59B49561310D966D0062876AF30F054 \ +# Brandon Williams + B7842CDAF36E6A3214FAE35D5E85B9AE0B84C041 \ +# Ekaterina Buryanova Dimitrova (CODE SIGNING KEY) + 3E9C876907A560ACA00964F363E9BAD215BBF5F0 \ +# Sam Tunnicliffe (CODE SIGNING KEY) + F8B7FD00E05C932991A2CD6150EE103D162C5A55 \ +# Stefan Miklosovic + 7464AAD9068241C50BA6A26232F35CB2F546D93E \ +# Berenguer Blasi (Code Signing Key) + CEC5C50B9C629EF0F5AB2706650B72EB14CCD622 + +ENV CASSANDRA_VERSION 4.0.18 +ENV CASSANDRA_SHA512 e9e4bb3149dce09b5f8dbe55d32892b147cdd13be7863851b0a1cb95113636e4433563e668759b49a8293c1cd766b331d27958f93331e9b3966951fc1c4681e6 + +RUN set -eux; \ + savedAptMark="$(apt-mark showmanual)"; \ + apt-get update; \ + apt-get install -y --no-install-recommends ca-certificates gnupg wget; \ + rm -rf /var/lib/apt/lists/*; \ + \ + ddist() { \ + local f="$1"; shift; \ + local distFile="$1"; shift; \ + local success=; \ + local distUrl=; \ + for distUrl in \ +# https://github.com/docker-library/tomcat/pull/308 + https://dlcdn.apache.org/ \ +# if the version is outdated, we have to pull from the archive + https://archive.apache.org/dist/ \ + ; do \ + if wget --progress=dot:giga -O "$f" "$distUrl$distFile" && [ -s "$f" ]; then \ + success=1; \ + break; \ + fi; \ + done; \ + [ -n "$success" ]; \ + }; \ + \ + ddist 'cassandra-bin.tgz' "cassandra/$CASSANDRA_VERSION/apache-cassandra-$CASSANDRA_VERSION-bin.tar.gz"; \ + echo "$CASSANDRA_SHA512 *cassandra-bin.tgz" | sha512sum --check --strict -; \ + \ + ddist 'cassandra-bin.tgz.asc' "cassandra/$CASSANDRA_VERSION/apache-cassandra-$CASSANDRA_VERSION-bin.tar.gz.asc"; \ + export GNUPGHOME="$(mktemp -d)"; \ + for key in $GPG_KEYS; do \ + gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key"; \ + done; \ + gpg --batch --verify cassandra-bin.tgz.asc cassandra-bin.tgz; \ + rm -rf "$GNUPGHOME"; \ + \ + apt-mark auto '.*' > /dev/null; \ + [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + \ + mkdir -p "$CASSANDRA_HOME"; \ + tar --extract --file cassandra-bin.tgz --directory "$CASSANDRA_HOME" --strip-components 1; \ + rm cassandra-bin.tgz*; \ + \ + [ ! -e "$CASSANDRA_CONF" ]; \ + mv "$CASSANDRA_HOME/conf" "$CASSANDRA_CONF"; \ + ln -sT "$CASSANDRA_CONF" "$CASSANDRA_HOME/conf"; \ + \ + dpkgArch="$(dpkg --print-architecture)"; \ + case "$dpkgArch" in \ + ppc64el) \ +# https://issues.apache.org/jira/browse/CASSANDRA-13345 +# "The stack size specified is too small, Specify at least 328k" + grep -- '^-Xss256k$' "$CASSANDRA_CONF/jvm-server.options"; \ + sed -ri 's/^-Xss256k$/-Xss512k/' "$CASSANDRA_CONF/jvm-server.options"; \ + grep -- '^-Xss512k$' "$CASSANDRA_CONF/jvm-server.options"; \ + ;; \ + esac; \ + \ + mkdir -p "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra; \ + chown -R cassandra:cassandra "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra; \ + chmod 1777 "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra; \ + chmod -R a+rwX "$CASSANDRA_CONF"; \ + ln -sT /var/lib/cassandra "$CASSANDRA_HOME/data"; \ + ln -sT /var/log/cassandra "$CASSANDRA_HOME/logs"; \ + \ +# smoke test + cassandra -v + +VOLUME /var/lib/cassandra + +COPY docker-entrypoint.sh /usr/local/bin/ +ENTRYPOINT ["docker-entrypoint.sh"] + +# 7000: intra-node communication +# 7001: TLS intra-node communication +# 7199: JMX +# 9042: CQL +# 9160: thrift service +EXPOSE 7000 7001 7199 9042 9160 +CMD ["cassandra", "-f"] diff --git a/4.0/docker-entrypoint.sh b/4.0/docker-entrypoint.sh new file mode 100755 index 0000000..f39af13 --- /dev/null +++ b/4.0/docker-entrypoint.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash +set -e + +# first arg is `-f` or `--some-option` +# or there are no args +if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then + set -- cassandra -f "$@" +fi + +# allow the container to be started with `--user` +if [ "$1" = 'cassandra' -a "$(id -u)" = '0' ]; then + find "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra \ + \! -user cassandra -exec chown cassandra '{}' + + exec gosu cassandra "$BASH_SOURCE" "$@" +fi + +_ip_address() { + # scrape the first non-localhost IP address of the container + # in Swarm Mode, we often get two IPs -- the container IP, and the (shared) VIP, and the container IP should always be first + ip address | awk ' + $1 != "inet" { next } # only lines with ip addresses + $NF == "lo" { next } # skip loopback devices + $2 ~ /^127[.]/ { next } # skip loopback addresses + $2 ~ /^169[.]254[.]/ { next } # skip link-local addresses + { + gsub(/\/.+$/, "", $2) + print $2 + exit + } + ' +} + +# "sed -i", but without "mv" (which doesn't work on a bind-mounted file, for example) +_sed-in-place() { + local filename="$1"; shift + local tempFile + tempFile="$(mktemp)" + sed "$@" "$filename" > "$tempFile" + cat "$tempFile" > "$filename" + rm "$tempFile" +} + +if [ "$1" = 'cassandra' ]; then + : ${CASSANDRA_RPC_ADDRESS='0.0.0.0'} + + : ${CASSANDRA_LISTEN_ADDRESS='auto'} + if [ "$CASSANDRA_LISTEN_ADDRESS" = 'auto' ]; then + CASSANDRA_LISTEN_ADDRESS="$(_ip_address)" + fi + + : ${CASSANDRA_BROADCAST_ADDRESS="$CASSANDRA_LISTEN_ADDRESS"} + + if [ "$CASSANDRA_BROADCAST_ADDRESS" = 'auto' ]; then + CASSANDRA_BROADCAST_ADDRESS="$(_ip_address)" + fi + : ${CASSANDRA_BROADCAST_RPC_ADDRESS:=$CASSANDRA_BROADCAST_ADDRESS} + + if [ -n "${CASSANDRA_NAME:+1}" ]; then + : ${CASSANDRA_SEEDS:="cassandra"} + fi + : ${CASSANDRA_SEEDS:="$CASSANDRA_BROADCAST_ADDRESS"} + + _sed-in-place "$CASSANDRA_CONF/cassandra.yaml" \ + -r 's/(- seeds:).*/\1 "'"$CASSANDRA_SEEDS"'"/' + + for yaml in \ + broadcast_address \ + broadcast_rpc_address \ + cluster_name \ + endpoint_snitch \ + listen_address \ + num_tokens \ + rpc_address \ + start_rpc \ + ; do + var="CASSANDRA_${yaml^^}" + val="${!var}" + if [ "$val" ]; then + _sed-in-place "$CASSANDRA_CONF/cassandra.yaml" \ + -r 's/^(# )?('"$yaml"':).*/\2 '"$val"'/' + fi + done + + for rackdc in dc rack; do + var="CASSANDRA_${rackdc^^}" + val="${!var}" + if [ "$val" ]; then + _sed-in-place "$CASSANDRA_CONF/cassandra-rackdc.properties" \ + -r 's/^('"$rackdc"'=).*/\1 '"$val"'/' + fi + done +fi + +exec "$@" diff --git a/4.1/Dockerfile b/4.1/Dockerfile new file mode 100644 index 0000000..287d5db --- /dev/null +++ b/4.1/Dockerfile @@ -0,0 +1,176 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "apply-templates.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# + +FROM eclipse-temurin:11-jre-jammy + +# explicitly set user/group IDs +RUN set -eux; \ + groupadd -r cassandra --gid=999; \ + useradd -r -g cassandra --uid=999 cassandra + +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ +# solves warning: "jemalloc shared library could not be preloaded to speed up memory allocations" + libjemalloc2 \ +# "free" is used by cassandra-env.sh + procps \ +# "cqlsh" needs a python interpreter + python3 \ +# "ip" is not required by Cassandra itself, but is commonly used in scripting Cassandra's configuration (since it is so fixated on explicit IP addresses) + iproute2 \ +# Cassandra will automatically use numactl if available +# https://github.com/apache/cassandra/blob/18bcda2d4c2eba7370a0b21f33eed37cb730bbb3/bin/cassandra#L90-L100 +# https://github.com/apache/cassandra/commit/604c0e87dc67fa65f6904ef9a98a029c9f2f865a + numactl \ + ; \ + rm -rf /var/lib/apt/lists/*; \ +# https://issues.apache.org/jira/browse/CASSANDRA-15767 ("bin/cassandra" only looks for "libjemalloc.so" or "libjemalloc.so.1" which doesn't match our "libjemalloc.so.2") + libjemalloc="$(readlink -e /usr/lib/*/libjemalloc.so.2)"; \ + ln -sT "$libjemalloc" /usr/local/lib/libjemalloc.so; \ + ldconfig + +# grab gosu for easy step-down from root +# https://github.com/tianon/gosu/releases +ENV GOSU_VERSION 1.17 +RUN set -eux; \ + savedAptMark="$(apt-mark showmanual)"; \ + apt-get update; \ + apt-get install -y --no-install-recommends ca-certificates gnupg wget; \ + rm -rf /var/lib/apt/lists/*; \ + dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ + wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \ + wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \ + export GNUPGHOME="$(mktemp -d)"; \ + gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ + gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \ + gpgconf --kill all; \ + rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \ + apt-mark auto '.*' > /dev/null; \ + [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + chmod +x /usr/local/bin/gosu; \ + gosu --version; \ + gosu nobody true + +ENV CASSANDRA_HOME /opt/cassandra +ENV CASSANDRA_CONF /etc/cassandra +ENV PATH $CASSANDRA_HOME/bin:$PATH + +# https://cwiki.apache.org/confluence/display/CASSANDRA2/DebianPackaging#DebianPackaging-AddingRepositoryKeys +# $ docker run --rm buildpack-deps:bullseye-curl bash -c 'wget -qO- https://downloads.apache.org/cassandra/KEYS | gpg --batch --import &> /dev/null && gpg --batch --list-keys --with-fingerprint --with-colons' | awk -F: '$1 == "pub" && $2 == "-" { pub = 1 } pub && $1 == "fpr" { fpr = $10 } $1 == "sub" { pub = 0 } pub && fpr && $1 == "uid" && $2 == "-" { print "#", $10; print "\t" fpr " \\"; pub = 0 }' +ENV GPG_KEYS \ +# Eric Evans + CEC86BB4A0BA9D0F90397CAEF8358FA2F2833C93 \ +# Eric Evans + C4965EE9E3015D192CCCF2B6F758CE318D77295D \ +# Sylvain Lebresne (pcmanus) + 5AED1BF378E9A19DADE1BCB34BD736A82B5C1B00 \ +# T Jake Luciani + 514A2AD631A57A16DD0047EC749D6EEC0353B12C \ +# Michael Shuler + A26E528B271F19B9E5D8E19EA278B781FE4B2BDA \ +# Michael Semb Wever + A4C465FEA0C552561A392A61E91335D77E3E87CB \ +# Alex Petrov + 9E66CEC6106D578D0B1EB9BFF1000962B7F6840C \ +# Jordan West + C4009872C59B49561310D966D0062876AF30F054 \ +# Brandon Williams + B7842CDAF36E6A3214FAE35D5E85B9AE0B84C041 \ +# Ekaterina Buryanova Dimitrova (CODE SIGNING KEY) + 3E9C876907A560ACA00964F363E9BAD215BBF5F0 \ +# Sam Tunnicliffe (CODE SIGNING KEY) + F8B7FD00E05C932991A2CD6150EE103D162C5A55 \ +# Stefan Miklosovic + 7464AAD9068241C50BA6A26232F35CB2F546D93E \ +# Berenguer Blasi (Code Signing Key) + CEC5C50B9C629EF0F5AB2706650B72EB14CCD622 + +ENV CASSANDRA_VERSION 4.1.9 +ENV CASSANDRA_SHA512 ece9397a8d85a2dd7974932efca0a9b7a004850c34ecb0ec0fd4d30606a56dd7257a36b8bfb8b974ba154ab79887539eabaee8692cc8db606837848042104be7 + +RUN set -eux; \ + savedAptMark="$(apt-mark showmanual)"; \ + apt-get update; \ + apt-get install -y --no-install-recommends ca-certificates gnupg wget; \ + rm -rf /var/lib/apt/lists/*; \ + \ + ddist() { \ + local f="$1"; shift; \ + local distFile="$1"; shift; \ + local success=; \ + local distUrl=; \ + for distUrl in \ +# https://github.com/docker-library/tomcat/pull/308 + https://dlcdn.apache.org/ \ +# if the version is outdated, we have to pull from the archive + https://archive.apache.org/dist/ \ + ; do \ + if wget --progress=dot:giga -O "$f" "$distUrl$distFile" && [ -s "$f" ]; then \ + success=1; \ + break; \ + fi; \ + done; \ + [ -n "$success" ]; \ + }; \ + \ + ddist 'cassandra-bin.tgz' "cassandra/$CASSANDRA_VERSION/apache-cassandra-$CASSANDRA_VERSION-bin.tar.gz"; \ + echo "$CASSANDRA_SHA512 *cassandra-bin.tgz" | sha512sum --check --strict -; \ + \ + ddist 'cassandra-bin.tgz.asc' "cassandra/$CASSANDRA_VERSION/apache-cassandra-$CASSANDRA_VERSION-bin.tar.gz.asc"; \ + export GNUPGHOME="$(mktemp -d)"; \ + for key in $GPG_KEYS; do \ + gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key"; \ + done; \ + gpg --batch --verify cassandra-bin.tgz.asc cassandra-bin.tgz; \ + rm -rf "$GNUPGHOME"; \ + \ + apt-mark auto '.*' > /dev/null; \ + [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + \ + mkdir -p "$CASSANDRA_HOME"; \ + tar --extract --file cassandra-bin.tgz --directory "$CASSANDRA_HOME" --strip-components 1; \ + rm cassandra-bin.tgz*; \ + \ + [ ! -e "$CASSANDRA_CONF" ]; \ + mv "$CASSANDRA_HOME/conf" "$CASSANDRA_CONF"; \ + ln -sT "$CASSANDRA_CONF" "$CASSANDRA_HOME/conf"; \ + \ + dpkgArch="$(dpkg --print-architecture)"; \ + case "$dpkgArch" in \ + ppc64el) \ +# https://issues.apache.org/jira/browse/CASSANDRA-13345 +# "The stack size specified is too small, Specify at least 328k" + grep -- '^-Xss256k$' "$CASSANDRA_CONF/jvm-server.options"; \ + sed -ri 's/^-Xss256k$/-Xss512k/' "$CASSANDRA_CONF/jvm-server.options"; \ + grep -- '^-Xss512k$' "$CASSANDRA_CONF/jvm-server.options"; \ + ;; \ + esac; \ + \ + mkdir -p "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra; \ + chown -R cassandra:cassandra "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra; \ + chmod 1777 "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra; \ + chmod -R a+rwX "$CASSANDRA_CONF"; \ + ln -sT /var/lib/cassandra "$CASSANDRA_HOME/data"; \ + ln -sT /var/log/cassandra "$CASSANDRA_HOME/logs"; \ + \ +# smoke test + cassandra -v + +VOLUME /var/lib/cassandra + +COPY docker-entrypoint.sh /usr/local/bin/ +ENTRYPOINT ["docker-entrypoint.sh"] + +# 7000: intra-node communication +# 7001: TLS intra-node communication +# 7199: JMX +# 9042: CQL +# 9160: thrift service +EXPOSE 7000 7001 7199 9042 9160 +CMD ["cassandra", "-f"] diff --git a/4.1/docker-entrypoint.sh b/4.1/docker-entrypoint.sh new file mode 100755 index 0000000..f39af13 --- /dev/null +++ b/4.1/docker-entrypoint.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash +set -e + +# first arg is `-f` or `--some-option` +# or there are no args +if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then + set -- cassandra -f "$@" +fi + +# allow the container to be started with `--user` +if [ "$1" = 'cassandra' -a "$(id -u)" = '0' ]; then + find "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra \ + \! -user cassandra -exec chown cassandra '{}' + + exec gosu cassandra "$BASH_SOURCE" "$@" +fi + +_ip_address() { + # scrape the first non-localhost IP address of the container + # in Swarm Mode, we often get two IPs -- the container IP, and the (shared) VIP, and the container IP should always be first + ip address | awk ' + $1 != "inet" { next } # only lines with ip addresses + $NF == "lo" { next } # skip loopback devices + $2 ~ /^127[.]/ { next } # skip loopback addresses + $2 ~ /^169[.]254[.]/ { next } # skip link-local addresses + { + gsub(/\/.+$/, "", $2) + print $2 + exit + } + ' +} + +# "sed -i", but without "mv" (which doesn't work on a bind-mounted file, for example) +_sed-in-place() { + local filename="$1"; shift + local tempFile + tempFile="$(mktemp)" + sed "$@" "$filename" > "$tempFile" + cat "$tempFile" > "$filename" + rm "$tempFile" +} + +if [ "$1" = 'cassandra' ]; then + : ${CASSANDRA_RPC_ADDRESS='0.0.0.0'} + + : ${CASSANDRA_LISTEN_ADDRESS='auto'} + if [ "$CASSANDRA_LISTEN_ADDRESS" = 'auto' ]; then + CASSANDRA_LISTEN_ADDRESS="$(_ip_address)" + fi + + : ${CASSANDRA_BROADCAST_ADDRESS="$CASSANDRA_LISTEN_ADDRESS"} + + if [ "$CASSANDRA_BROADCAST_ADDRESS" = 'auto' ]; then + CASSANDRA_BROADCAST_ADDRESS="$(_ip_address)" + fi + : ${CASSANDRA_BROADCAST_RPC_ADDRESS:=$CASSANDRA_BROADCAST_ADDRESS} + + if [ -n "${CASSANDRA_NAME:+1}" ]; then + : ${CASSANDRA_SEEDS:="cassandra"} + fi + : ${CASSANDRA_SEEDS:="$CASSANDRA_BROADCAST_ADDRESS"} + + _sed-in-place "$CASSANDRA_CONF/cassandra.yaml" \ + -r 's/(- seeds:).*/\1 "'"$CASSANDRA_SEEDS"'"/' + + for yaml in \ + broadcast_address \ + broadcast_rpc_address \ + cluster_name \ + endpoint_snitch \ + listen_address \ + num_tokens \ + rpc_address \ + start_rpc \ + ; do + var="CASSANDRA_${yaml^^}" + val="${!var}" + if [ "$val" ]; then + _sed-in-place "$CASSANDRA_CONF/cassandra.yaml" \ + -r 's/^(# )?('"$yaml"':).*/\2 '"$val"'/' + fi + done + + for rackdc in dc rack; do + var="CASSANDRA_${rackdc^^}" + val="${!var}" + if [ "$val" ]; then + _sed-in-place "$CASSANDRA_CONF/cassandra-rackdc.properties" \ + -r 's/^('"$rackdc"'=).*/\1 '"$val"'/' + fi + done +fi + +exec "$@" diff --git a/5.0/Dockerfile b/5.0/Dockerfile new file mode 100644 index 0000000..ec95807 --- /dev/null +++ b/5.0/Dockerfile @@ -0,0 +1,176 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "apply-templates.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# + +FROM eclipse-temurin:17-jre-jammy + +# explicitly set user/group IDs +RUN set -eux; \ + groupadd -r cassandra --gid=999; \ + useradd -r -g cassandra --uid=999 cassandra + +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ +# solves warning: "jemalloc shared library could not be preloaded to speed up memory allocations" + libjemalloc2 \ +# "free" is used by cassandra-env.sh + procps \ +# "cqlsh" needs a python interpreter + python3 \ +# "ip" is not required by Cassandra itself, but is commonly used in scripting Cassandra's configuration (since it is so fixated on explicit IP addresses) + iproute2 \ +# Cassandra will automatically use numactl if available +# https://github.com/apache/cassandra/blob/18bcda2d4c2eba7370a0b21f33eed37cb730bbb3/bin/cassandra#L90-L100 +# https://github.com/apache/cassandra/commit/604c0e87dc67fa65f6904ef9a98a029c9f2f865a + numactl \ + ; \ + rm -rf /var/lib/apt/lists/*; \ +# https://issues.apache.org/jira/browse/CASSANDRA-15767 ("bin/cassandra" only looks for "libjemalloc.so" or "libjemalloc.so.1" which doesn't match our "libjemalloc.so.2") + libjemalloc="$(readlink -e /usr/lib/*/libjemalloc.so.2)"; \ + ln -sT "$libjemalloc" /usr/local/lib/libjemalloc.so; \ + ldconfig + +# grab gosu for easy step-down from root +# https://github.com/tianon/gosu/releases +ENV GOSU_VERSION 1.17 +RUN set -eux; \ + savedAptMark="$(apt-mark showmanual)"; \ + apt-get update; \ + apt-get install -y --no-install-recommends ca-certificates gnupg wget; \ + rm -rf /var/lib/apt/lists/*; \ + dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ + wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \ + wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \ + export GNUPGHOME="$(mktemp -d)"; \ + gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ + gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \ + gpgconf --kill all; \ + rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \ + apt-mark auto '.*' > /dev/null; \ + [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + chmod +x /usr/local/bin/gosu; \ + gosu --version; \ + gosu nobody true + +ENV CASSANDRA_HOME /opt/cassandra +ENV CASSANDRA_CONF /etc/cassandra +ENV PATH $CASSANDRA_HOME/bin:$PATH + +# https://cwiki.apache.org/confluence/display/CASSANDRA2/DebianPackaging#DebianPackaging-AddingRepositoryKeys +# $ docker run --rm buildpack-deps:bullseye-curl bash -c 'wget -qO- https://downloads.apache.org/cassandra/KEYS | gpg --batch --import &> /dev/null && gpg --batch --list-keys --with-fingerprint --with-colons' | awk -F: '$1 == "pub" && $2 == "-" { pub = 1 } pub && $1 == "fpr" { fpr = $10 } $1 == "sub" { pub = 0 } pub && fpr && $1 == "uid" && $2 == "-" { print "#", $10; print "\t" fpr " \\"; pub = 0 }' +ENV GPG_KEYS \ +# Eric Evans + CEC86BB4A0BA9D0F90397CAEF8358FA2F2833C93 \ +# Eric Evans + C4965EE9E3015D192CCCF2B6F758CE318D77295D \ +# Sylvain Lebresne (pcmanus) + 5AED1BF378E9A19DADE1BCB34BD736A82B5C1B00 \ +# T Jake Luciani + 514A2AD631A57A16DD0047EC749D6EEC0353B12C \ +# Michael Shuler + A26E528B271F19B9E5D8E19EA278B781FE4B2BDA \ +# Michael Semb Wever + A4C465FEA0C552561A392A61E91335D77E3E87CB \ +# Alex Petrov + 9E66CEC6106D578D0B1EB9BFF1000962B7F6840C \ +# Jordan West + C4009872C59B49561310D966D0062876AF30F054 \ +# Brandon Williams + B7842CDAF36E6A3214FAE35D5E85B9AE0B84C041 \ +# Ekaterina Buryanova Dimitrova (CODE SIGNING KEY) + 3E9C876907A560ACA00964F363E9BAD215BBF5F0 \ +# Sam Tunnicliffe (CODE SIGNING KEY) + F8B7FD00E05C932991A2CD6150EE103D162C5A55 \ +# Stefan Miklosovic + 7464AAD9068241C50BA6A26232F35CB2F546D93E \ +# Berenguer Blasi (Code Signing Key) + CEC5C50B9C629EF0F5AB2706650B72EB14CCD622 + +ENV CASSANDRA_VERSION 5.0.4 +ENV CASSANDRA_SHA512 16de8bb78af94dabfd916825f492de7040a7311791bc1d630f14707d88f7132e51ae38dad7ebbaf13d2d154af0bdeefe318d680798b8e247ae4117cbc0e2f6df + +RUN set -eux; \ + savedAptMark="$(apt-mark showmanual)"; \ + apt-get update; \ + apt-get install -y --no-install-recommends ca-certificates gnupg wget; \ + rm -rf /var/lib/apt/lists/*; \ + \ + ddist() { \ + local f="$1"; shift; \ + local distFile="$1"; shift; \ + local success=; \ + local distUrl=; \ + for distUrl in \ +# https://github.com/docker-library/tomcat/pull/308 + https://dlcdn.apache.org/ \ +# if the version is outdated, we have to pull from the archive + https://archive.apache.org/dist/ \ + ; do \ + if wget --progress=dot:giga -O "$f" "$distUrl$distFile" && [ -s "$f" ]; then \ + success=1; \ + break; \ + fi; \ + done; \ + [ -n "$success" ]; \ + }; \ + \ + ddist 'cassandra-bin.tgz' "cassandra/$CASSANDRA_VERSION/apache-cassandra-$CASSANDRA_VERSION-bin.tar.gz"; \ + echo "$CASSANDRA_SHA512 *cassandra-bin.tgz" | sha512sum --check --strict -; \ + \ + ddist 'cassandra-bin.tgz.asc' "cassandra/$CASSANDRA_VERSION/apache-cassandra-$CASSANDRA_VERSION-bin.tar.gz.asc"; \ + export GNUPGHOME="$(mktemp -d)"; \ + for key in $GPG_KEYS; do \ + gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key"; \ + done; \ + gpg --batch --verify cassandra-bin.tgz.asc cassandra-bin.tgz; \ + rm -rf "$GNUPGHOME"; \ + \ + apt-mark auto '.*' > /dev/null; \ + [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + \ + mkdir -p "$CASSANDRA_HOME"; \ + tar --extract --file cassandra-bin.tgz --directory "$CASSANDRA_HOME" --strip-components 1; \ + rm cassandra-bin.tgz*; \ + \ + [ ! -e "$CASSANDRA_CONF" ]; \ + mv "$CASSANDRA_HOME/conf" "$CASSANDRA_CONF"; \ + ln -sT "$CASSANDRA_CONF" "$CASSANDRA_HOME/conf"; \ + \ + dpkgArch="$(dpkg --print-architecture)"; \ + case "$dpkgArch" in \ + ppc64el) \ +# https://issues.apache.org/jira/browse/CASSANDRA-13345 +# "The stack size specified is too small, Specify at least 328k" + grep -- '^-Xss256k$' "$CASSANDRA_CONF/jvm-server.options"; \ + sed -ri 's/^-Xss256k$/-Xss512k/' "$CASSANDRA_CONF/jvm-server.options"; \ + grep -- '^-Xss512k$' "$CASSANDRA_CONF/jvm-server.options"; \ + ;; \ + esac; \ + \ + mkdir -p "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra; \ + chown -R cassandra:cassandra "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra; \ + chmod 1777 "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra; \ + chmod -R a+rwX "$CASSANDRA_CONF"; \ + ln -sT /var/lib/cassandra "$CASSANDRA_HOME/data"; \ + ln -sT /var/log/cassandra "$CASSANDRA_HOME/logs"; \ + \ +# smoke test + cassandra -v + +VOLUME /var/lib/cassandra + +COPY docker-entrypoint.sh /usr/local/bin/ +ENTRYPOINT ["docker-entrypoint.sh"] + +# 7000: intra-node communication +# 7001: TLS intra-node communication +# 7199: JMX +# 9042: CQL +# 9160: thrift service +EXPOSE 7000 7001 7199 9042 9160 +CMD ["cassandra", "-f"] diff --git a/5.0/docker-entrypoint.sh b/5.0/docker-entrypoint.sh new file mode 100755 index 0000000..f39af13 --- /dev/null +++ b/5.0/docker-entrypoint.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash +set -e + +# first arg is `-f` or `--some-option` +# or there are no args +if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then + set -- cassandra -f "$@" +fi + +# allow the container to be started with `--user` +if [ "$1" = 'cassandra' -a "$(id -u)" = '0' ]; then + find "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra \ + \! -user cassandra -exec chown cassandra '{}' + + exec gosu cassandra "$BASH_SOURCE" "$@" +fi + +_ip_address() { + # scrape the first non-localhost IP address of the container + # in Swarm Mode, we often get two IPs -- the container IP, and the (shared) VIP, and the container IP should always be first + ip address | awk ' + $1 != "inet" { next } # only lines with ip addresses + $NF == "lo" { next } # skip loopback devices + $2 ~ /^127[.]/ { next } # skip loopback addresses + $2 ~ /^169[.]254[.]/ { next } # skip link-local addresses + { + gsub(/\/.+$/, "", $2) + print $2 + exit + } + ' +} + +# "sed -i", but without "mv" (which doesn't work on a bind-mounted file, for example) +_sed-in-place() { + local filename="$1"; shift + local tempFile + tempFile="$(mktemp)" + sed "$@" "$filename" > "$tempFile" + cat "$tempFile" > "$filename" + rm "$tempFile" +} + +if [ "$1" = 'cassandra' ]; then + : ${CASSANDRA_RPC_ADDRESS='0.0.0.0'} + + : ${CASSANDRA_LISTEN_ADDRESS='auto'} + if [ "$CASSANDRA_LISTEN_ADDRESS" = 'auto' ]; then + CASSANDRA_LISTEN_ADDRESS="$(_ip_address)" + fi + + : ${CASSANDRA_BROADCAST_ADDRESS="$CASSANDRA_LISTEN_ADDRESS"} + + if [ "$CASSANDRA_BROADCAST_ADDRESS" = 'auto' ]; then + CASSANDRA_BROADCAST_ADDRESS="$(_ip_address)" + fi + : ${CASSANDRA_BROADCAST_RPC_ADDRESS:=$CASSANDRA_BROADCAST_ADDRESS} + + if [ -n "${CASSANDRA_NAME:+1}" ]; then + : ${CASSANDRA_SEEDS:="cassandra"} + fi + : ${CASSANDRA_SEEDS:="$CASSANDRA_BROADCAST_ADDRESS"} + + _sed-in-place "$CASSANDRA_CONF/cassandra.yaml" \ + -r 's/(- seeds:).*/\1 "'"$CASSANDRA_SEEDS"'"/' + + for yaml in \ + broadcast_address \ + broadcast_rpc_address \ + cluster_name \ + endpoint_snitch \ + listen_address \ + num_tokens \ + rpc_address \ + start_rpc \ + ; do + var="CASSANDRA_${yaml^^}" + val="${!var}" + if [ "$val" ]; then + _sed-in-place "$CASSANDRA_CONF/cassandra.yaml" \ + -r 's/^(# )?('"$yaml"':).*/\2 '"$val"'/' + fi + done + + for rackdc in dc rack; do + var="CASSANDRA_${rackdc^^}" + val="${!var}" + if [ "$val" ]; then + _sed-in-place "$CASSANDRA_CONF/cassandra-rackdc.properties" \ + -r 's/^('"$rackdc"'=).*/\1 '"$val"'/' + fi + done +fi + +exec "$@" diff --git a/Dockerfile.template b/Dockerfile.template index f9f4686..710118b 100644 --- a/Dockerfile.template +++ b/Dockerfile.template @@ -1,22 +1,193 @@ -FROM debian:jessie-backports +{{ + def major: env.version | split(".")[0] | tonumber +-}} +FROM {{ .FROM.version }} -RUN apt-key adv --keyserver ha.pool.sks-keyservers.net --recv-keys 514A2AD631A57A16DD0047EC749D6EEC0353B12C +# explicitly set user/group IDs +RUN set -eux; \ + groupadd -r cassandra --gid=999; \ + useradd -r -g cassandra --uid=999 cassandra -RUN echo 'deb http://www.apache.org/dist/cassandra/debian %%CASSANDRA_DIST%%x main' >> /etc/apt/sources.list.d/cassandra.list +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ +# solves warning: "jemalloc shared library could not be preloaded to speed up memory allocations" + libjemalloc2 \ +# "free" is used by cassandra-env.sh + procps \ +# "cqlsh" needs a python interpreter +{{ + # python3 is only supported in 4.0+ + # https://issues.apache.org/jira/browse/CASSANDRA-10190 + # https://github.com/apache/cassandra/blob/cassandra-3.11.16/bin/cqlsh#L18-L26 + # https://github.com/apache/cassandra/blob/cassandra-3.0.29/bin/cqlsh#L18-L26 + # (only looking for/accepting Python 2.7 specifically) + if major < 4 then ( +-}} + python2 \ +{{ ) else ( -}} + python3 \ +{{ ) end -}} +# "ip" is not required by Cassandra itself, but is commonly used in scripting Cassandra's configuration (since it is so fixated on explicit IP addresses) + iproute2 \ +# Cassandra will automatically use numactl if available +# https://github.com/apache/cassandra/blob/18bcda2d4c2eba7370a0b21f33eed37cb730bbb3/bin/cassandra#L90-L100 +# https://github.com/apache/cassandra/commit/604c0e87dc67fa65f6904ef9a98a029c9f2f865a + numactl \ + ; \ + rm -rf /var/lib/apt/lists/*; \ +# https://issues.apache.org/jira/browse/CASSANDRA-15767 ("bin/cassandra" only looks for "libjemalloc.so" or "libjemalloc.so.1" which doesn't match our "libjemalloc.so.2") + libjemalloc="$(readlink -e /usr/lib/*/libjemalloc.so.2)"; \ + ln -sT "$libjemalloc" /usr/local/lib/libjemalloc.so; \ + ldconfig -ENV CASSANDRA_VERSION %%CASSANDRA_VERSION%% +# grab gosu for easy step-down from root +# https://github.com/tianon/gosu/releases +ENV GOSU_VERSION 1.17 +RUN set -eux; \ + savedAptMark="$(apt-mark showmanual)"; \ + apt-get update; \ + apt-get install -y --no-install-recommends ca-certificates gnupg wget; \ + rm -rf /var/lib/apt/lists/*; \ + dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ + wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \ + wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \ + export GNUPGHOME="$(mktemp -d)"; \ + gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ + gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \ + gpgconf --kill all; \ + rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \ + apt-mark auto '.*' > /dev/null; \ + [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + chmod +x /usr/local/bin/gosu; \ + gosu --version; \ + gosu nobody true -RUN apt-get update \ - && apt-get install -y cassandra="$CASSANDRA_VERSION" \ - && rm -rf /var/lib/apt/lists/* +ENV CASSANDRA_HOME /opt/cassandra +ENV CASSANDRA_CONF /etc/cassandra +ENV PATH $CASSANDRA_HOME/bin:$PATH -ENV CASSANDRA_CONFIG /etc/cassandra +# https://cwiki.apache.org/confluence/display/CASSANDRA2/DebianPackaging#DebianPackaging-AddingRepositoryKeys +# $ docker run --rm buildpack-deps:bullseye-curl bash -c 'wget -qO- https://downloads.apache.org/cassandra/KEYS | gpg --batch --import &> /dev/null && gpg --batch --list-keys --with-fingerprint --with-colons' | awk -F: '$1 == "pub" && $2 == "-" { pub = 1 } pub && $1 == "fpr" { fpr = $10 } $1 == "sub" { pub = 0 } pub && fpr && $1 == "uid" && $2 == "-" { print "#", $10; print "\t" fpr " \\"; pub = 0 }' +ENV GPG_KEYS \ +# Eric Evans + CEC86BB4A0BA9D0F90397CAEF8358FA2F2833C93 \ +# Eric Evans + C4965EE9E3015D192CCCF2B6F758CE318D77295D \ +# Sylvain Lebresne (pcmanus) + 5AED1BF378E9A19DADE1BCB34BD736A82B5C1B00 \ +# T Jake Luciani + 514A2AD631A57A16DD0047EC749D6EEC0353B12C \ +# Michael Shuler + A26E528B271F19B9E5D8E19EA278B781FE4B2BDA \ +# Michael Semb Wever + A4C465FEA0C552561A392A61E91335D77E3E87CB \ +# Alex Petrov + 9E66CEC6106D578D0B1EB9BFF1000962B7F6840C \ +# Jordan West + C4009872C59B49561310D966D0062876AF30F054 \ +# Brandon Williams + B7842CDAF36E6A3214FAE35D5E85B9AE0B84C041 \ +# Ekaterina Buryanova Dimitrova (CODE SIGNING KEY) + 3E9C876907A560ACA00964F363E9BAD215BBF5F0 \ +# Sam Tunnicliffe (CODE SIGNING KEY) + F8B7FD00E05C932991A2CD6150EE103D162C5A55 \ +# Stefan Miklosovic + 7464AAD9068241C50BA6A26232F35CB2F546D93E \ +# Berenguer Blasi (Code Signing Key) + CEC5C50B9C629EF0F5AB2706650B72EB14CCD622 -COPY docker-entrypoint.sh /docker-entrypoint.sh -ENTRYPOINT ["/docker-entrypoint.sh"] +ENV CASSANDRA_VERSION {{ .version }} +ENV CASSANDRA_SHA512 {{ .sha512 }} + +RUN set -eux; \ + savedAptMark="$(apt-mark showmanual)"; \ + apt-get update; \ + apt-get install -y --no-install-recommends ca-certificates gnupg wget; \ + rm -rf /var/lib/apt/lists/*; \ + \ + ddist() { \ + local f="$1"; shift; \ + local distFile="$1"; shift; \ + local success=; \ + local distUrl=; \ + for distUrl in \ +# https://github.com/docker-library/tomcat/pull/308 + https://dlcdn.apache.org/ \ +# if the version is outdated, we have to pull from the archive + https://archive.apache.org/dist/ \ + ; do \ + if wget --progress=dot:giga -O "$f" "$distUrl$distFile" && [ -s "$f" ]; then \ + success=1; \ + break; \ + fi; \ + done; \ + [ -n "$success" ]; \ + }; \ + \ + ddist 'cassandra-bin.tgz' "cassandra/$CASSANDRA_VERSION/apache-cassandra-$CASSANDRA_VERSION-bin.tar.gz"; \ + echo "$CASSANDRA_SHA512 *cassandra-bin.tgz" | sha512sum --check --strict -; \ + \ + ddist 'cassandra-bin.tgz.asc' "cassandra/$CASSANDRA_VERSION/apache-cassandra-$CASSANDRA_VERSION-bin.tar.gz.asc"; \ + export GNUPGHOME="$(mktemp -d)"; \ + for key in $GPG_KEYS; do \ + gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key"; \ + done; \ + gpg --batch --verify cassandra-bin.tgz.asc cassandra-bin.tgz; \ + rm -rf "$GNUPGHOME"; \ + \ + apt-mark auto '.*' > /dev/null; \ + [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + \ + mkdir -p "$CASSANDRA_HOME"; \ + tar --extract --file cassandra-bin.tgz --directory "$CASSANDRA_HOME" --strip-components 1; \ + rm cassandra-bin.tgz*; \ + \ + [ ! -e "$CASSANDRA_CONF" ]; \ + mv "$CASSANDRA_HOME/conf" "$CASSANDRA_CONF"; \ + ln -sT "$CASSANDRA_CONF" "$CASSANDRA_HOME/conf"; \ + \ + dpkgArch="$(dpkg --print-architecture)"; \ + case "$dpkgArch" in \ + ppc64el) \ +# https://issues.apache.org/jira/browse/CASSANDRA-13345 +# "The stack size specified is too small, Specify at least 328k" +{{ if env.version == "3.0" then ( -}} + grep -- '-Xss256k' "$CASSANDRA_CONF/cassandra-env.sh"; \ + sed -ri 's/-Xss256k/-Xss512k/g' "$CASSANDRA_CONF/cassandra-env.sh"; \ + grep -- '-Xss512k' "$CASSANDRA_CONF/cassandra-env.sh"; \ +{{ ) elif major == 3 then ( -}} + grep -- '^-Xss256k$' "$CASSANDRA_CONF/jvm.options"; \ + sed -ri 's/^-Xss256k$/-Xss512k/' "$CASSANDRA_CONF/jvm.options"; \ + grep -- '^-Xss512k$' "$CASSANDRA_CONF/jvm.options"; \ +{{ ) else ( -}} + grep -- '^-Xss256k$' "$CASSANDRA_CONF/jvm-server.options"; \ + sed -ri 's/^-Xss256k$/-Xss512k/' "$CASSANDRA_CONF/jvm-server.options"; \ + grep -- '^-Xss512k$' "$CASSANDRA_CONF/jvm-server.options"; \ +{{ ) end -}} + ;; \ + esac; \ + \ + mkdir -p "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra; \ + chown -R cassandra:cassandra "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra; \ + chmod 1777 "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra; \ + chmod -R a+rwX "$CASSANDRA_CONF"; \ + ln -sT /var/lib/cassandra "$CASSANDRA_HOME/data"; \ + ln -sT /var/log/cassandra "$CASSANDRA_HOME/logs"; \ + \ +# smoke test + cassandra -v VOLUME /var/lib/cassandra +COPY docker-entrypoint.sh /usr/local/bin/ +{{ if env.version | split(".") | .[0] | tonumber < 4 then ( -}} +RUN ln -s usr/local/bin/docker-entrypoint.sh /docker-entrypoint.sh # backwards compat +{{ ) else "" end -}} +ENTRYPOINT ["docker-entrypoint.sh"] + # 7000: intra-node communication # 7001: TLS intra-node communication # 7199: JMX diff --git a/LICENSE b/LICENSE index 508036e..157ea15 100644 --- a/LICENSE +++ b/LICENSE @@ -176,7 +176,7 @@ END OF TERMS AND CONDITIONS - Copyright 2013-2015 Docker, Inc. + Copyright 2013 Docker, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index aca14ce..697c62d 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,15 @@ -# About this Repo +# https://github.com/docker-library/cassandra -This is the Git repo of the Docker [official image](https://docs.docker.com/docker-hub/official_repos/) for [cassandra](https://registry.hub.docker.com/_/cassandra/). See [the Docker Hub page](https://registry.hub.docker.com/_/cassandra/) for the full readme on how to use this Docker image and for information regarding contributing and issues. +## Maintained by: [the Docker Community](https://github.com/docker-library/cassandra) -The full readme is generated over in [docker-library/docs](https://github.com/docker-library/docs), specificially in [docker-library/docs/cassandra](https://github.com/docker-library/docs/tree/master/cassandra). +This is the Git repo of the [Docker "Official Image"](https://github.com/docker-library/official-images#what-are-official-images) for [`cassandra`](https://hub.docker.com/_/cassandra/) (not to be confused with any official `cassandra` image provided by `cassandra` upstream). See [the Docker Hub page](https://hub.docker.com/_/cassandra/) for the full readme on how to use this Docker image and for information regarding contributing and issues. -See a change merged here that doesn't show up on the Docker Hub yet? Check [the "library/cassandra" manifest file in the docker-library/official-images repo](https://github.com/docker-library/official-images/blob/master/library/cassandra), especially [PRs with the "library/cassandra" label on that repo](https://github.com/docker-library/official-images/labels/library%2Fcassandra). For more information about the official images process, see the [docker-library/official-images readme](https://github.com/docker-library/official-images/blob/master/README.md). +The [full image description on Docker Hub](https://hub.docker.com/_/cassandra/) is generated/maintained over in [the docker-library/docs repository](https://github.com/docker-library/docs), specifically in [the `cassandra` directory](https://github.com/docker-library/docs/tree/master/cassandra). -[![Travis CI](https://img.shields.io/travis/docker-library/cassandra/master.svg)](https://travis-ci.org/docker-library/cassandra/branches) +## See a change merged here that doesn't show up on Docker Hub yet? + +For more information about the full official images change lifecycle, see [the "An image's source changed in Git, now what?" FAQ entry](https://github.com/docker-library/faq#an-images-source-changed-in-git-now-what). + +For outstanding `cassandra` image PRs, check [PRs with the "library/cassandra" label on the official-images repository](https://github.com/docker-library/official-images/labels/library%2Fcassandra). For the current "source of truth" for [`cassandra`](https://hub.docker.com/_/cassandra/), see [the `library/cassandra` file in the official-images repository](https://github.com/docker-library/official-images/blob/master/library/cassandra). diff --git a/apply-templates.sh b/apply-templates.sh new file mode 100755 index 0000000..a8eb135 --- /dev/null +++ b/apply-templates.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +[ -f versions.json ] # run "versions.sh" first + +jqt='.jq-template.awk' +if [ -n "${BASHBREW_SCRIPTS:-}" ]; then + jqt="$BASHBREW_SCRIPTS/jq-template.awk" +elif [ "$BASH_SOURCE" -nt "$jqt" ]; then + # https://github.com/docker-library/bashbrew/blob/master/scripts/jq-template.awk + wget -qO "$jqt" 'https://github.com/docker-library/bashbrew/raw/9f6a35772ac863a0241f147c820354e4008edf38/scripts/jq-template.awk' +fi + +if [ "$#" -eq 0 ]; then + versions="$(jq -r 'keys | map(@sh) | join(" ")' versions.json)" + eval "set -- $versions" +fi + +generated_warning() { + cat <<-EOH + # + # NOTE: THIS DOCKERFILE IS GENERATED VIA "apply-templates.sh" + # + # PLEASE DO NOT EDIT IT DIRECTLY. + # + + EOH +} + +for version; do + export version + + echo "processing $version ..." + + { + generated_warning + gawk -f "$jqt" Dockerfile.template + } > "$version/Dockerfile" + + cp -a docker-entrypoint.sh "$version/" +done diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 31fd2a0..f39af13 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -1,23 +1,57 @@ -#!/bin/bash +#!/usr/bin/env bash set -e # first arg is `-f` or `--some-option` -if [ "${1:0:1}" = '-' ]; then +# or there are no args +if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then set -- cassandra -f "$@" fi +# allow the container to be started with `--user` +if [ "$1" = 'cassandra' -a "$(id -u)" = '0' ]; then + find "$CASSANDRA_CONF" /var/lib/cassandra /var/log/cassandra \ + \! -user cassandra -exec chown cassandra '{}' + + exec gosu cassandra "$BASH_SOURCE" "$@" +fi + +_ip_address() { + # scrape the first non-localhost IP address of the container + # in Swarm Mode, we often get two IPs -- the container IP, and the (shared) VIP, and the container IP should always be first + ip address | awk ' + $1 != "inet" { next } # only lines with ip addresses + $NF == "lo" { next } # skip loopback devices + $2 ~ /^127[.]/ { next } # skip loopback addresses + $2 ~ /^169[.]254[.]/ { next } # skip link-local addresses + { + gsub(/\/.+$/, "", $2) + print $2 + exit + } + ' +} + +# "sed -i", but without "mv" (which doesn't work on a bind-mounted file, for example) +_sed-in-place() { + local filename="$1"; shift + local tempFile + tempFile="$(mktemp)" + sed "$@" "$filename" > "$tempFile" + cat "$tempFile" > "$filename" + rm "$tempFile" +} + if [ "$1" = 'cassandra' ]; then : ${CASSANDRA_RPC_ADDRESS='0.0.0.0'} : ${CASSANDRA_LISTEN_ADDRESS='auto'} if [ "$CASSANDRA_LISTEN_ADDRESS" = 'auto' ]; then - CASSANDRA_LISTEN_ADDRESS="$(hostname --ip-address)" + CASSANDRA_LISTEN_ADDRESS="$(_ip_address)" fi : ${CASSANDRA_BROADCAST_ADDRESS="$CASSANDRA_LISTEN_ADDRESS"} if [ "$CASSANDRA_BROADCAST_ADDRESS" = 'auto' ]; then - CASSANDRA_BROADCAST_ADDRESS="$(hostname --ip-address)" + CASSANDRA_BROADCAST_ADDRESS="$(_ip_address)" fi : ${CASSANDRA_BROADCAST_RPC_ADDRESS:=$CASSANDRA_BROADCAST_ADDRESS} @@ -25,8 +59,9 @@ if [ "$1" = 'cassandra' ]; then : ${CASSANDRA_SEEDS:="cassandra"} fi : ${CASSANDRA_SEEDS:="$CASSANDRA_BROADCAST_ADDRESS"} - - sed -ri 's/(- seeds:) "127.0.0.1"/\1 "'"$CASSANDRA_SEEDS"'"/' "$CASSANDRA_CONFIG/cassandra.yaml" + + _sed-in-place "$CASSANDRA_CONF/cassandra.yaml" \ + -r 's/(- seeds:).*/\1 "'"$CASSANDRA_SEEDS"'"/' for yaml in \ broadcast_address \ @@ -41,7 +76,8 @@ if [ "$1" = 'cassandra' ]; then var="CASSANDRA_${yaml^^}" val="${!var}" if [ "$val" ]; then - sed -ri 's/^(# )?('"$yaml"':).*/\2 '"$val"'/' "$CASSANDRA_CONFIG/cassandra.yaml" + _sed-in-place "$CASSANDRA_CONF/cassandra.yaml" \ + -r 's/^(# )?('"$yaml"':).*/\2 '"$val"'/' fi done @@ -49,7 +85,8 @@ if [ "$1" = 'cassandra' ]; then var="CASSANDRA_${rackdc^^}" val="${!var}" if [ "$val" ]; then - sed -ri 's/^('"$rackdc"'=).*/\1 '"$val"'/' "$CASSANDRA_CONFIG/cassandra-rackdc.properties" + _sed-in-place "$CASSANDRA_CONF/cassandra-rackdc.properties" \ + -r 's/^('"$rackdc"'=).*/\1 '"$val"'/' fi done fi diff --git a/generate-stackbrew-library.sh b/generate-stackbrew-library.sh index 4f710f7..e84d666 100755 --- a/generate-stackbrew-library.sh +++ b/generate-stackbrew-library.sh @@ -1,27 +1,104 @@ -#!/bin/bash -set -e +#!/usr/bin/env bash +set -eu -declare -A aliases -aliases=( - [2.2]='2' - [3.0]='3 latest' +declare -A aliases=( + [3.11]='3' + [4.1]='4' + [5.0]='5 latest' ) +self="$(basename "$BASH_SOURCE")" cd "$(dirname "$(readlink -f "$BASH_SOURCE")")" -versions=( */ ) -versions=( "${versions[@]%/}" ) -url='git://github.com/docker-library/cassandra' +if [ "$#" -eq 0 ]; then + versions="$(jq -r 'keys | map(@sh) | join(" ")' versions.json)" + eval "set -- $versions" +fi -echo '# maintainer: InfoSiftr (@infosiftr)' +# sort version numbers with highest first +IFS=$'\n'; set -- $(sort -rV <<<"$*"); unset IFS + +# get the most recent commit which modified any of "$@" +fileCommit() { + git log -1 --format='format:%H' HEAD -- "$@" +} + +# get the most recent commit which modified "$1/Dockerfile" or any file COPY'd from "$1/Dockerfile" +dirCommit() { + local dir="$1"; shift + ( + cd "$dir" + fileCommit \ + Dockerfile \ + $(git show HEAD:./Dockerfile | awk ' + toupper($1) == "COPY" { + for (i = 2; i < NF; i++) { + print $i + } + } + ') + ) +} + +getArches() { + local repo="$1"; shift + local officialImagesBase="${BASHBREW_LIBRARY:-https://github.com/docker-library/official-images/raw/HEAD/library}/" + + local parentRepoToArchesStr + parentRepoToArchesStr="$( + find -name 'Dockerfile' -exec awk -v officialImagesBase="$officialImagesBase" ' + toupper($1) == "FROM" && $2 !~ /^('"$repo"'|scratch|.*\/.*)(:|$)/ { + printf "%s%s\n", officialImagesBase, $2 + } + ' '{}' + \ + | sort -u \ + | xargs -r bashbrew cat --format '["{{ .RepoName }}:{{ .TagName }}"]="{{ join " " .TagEntry.Architectures }}"' + )" + eval "declare -g -A parentRepoToArches=( $parentRepoToArchesStr )" +} +getArches 'cassandra' + +cat <<-EOH +# this file is generated via https://github.com/docker-library/cassandra/blob/$(fileCommit "$self")/$self + +Maintainers: Tianon Gravi (@tianon), + Joseph Ferguson (@yosifkit) +GitRepo: https://github.com/docker-library/cassandra.git +EOH + +# prints "$2$1$3$1...$N" +join() { + local sep="$1"; shift + local out; printf -v out "${sep//%/%%}%s" "$@" + echo "${out#$sep}" +} + +for version; do + export version + + fullVersion="$(jq -r '.[env.version].version' versions.json)" + + versionAliases=( $fullVersion ) + if [ "$version" != "$fullVersion" ]; then + versionAliases+=( $version ) + fi + versionAliases+=( ${aliases[$version]:-} ) + + suite="$(jq -r '.[env.version].FROM.base' versions.json)" + suiteAliases=( "${versionAliases[@]/%/-$suite}" ) + suiteAliases=( "${suiteAliases[@]//latest-/}" ) + versionAliases+=( "${suiteAliases[@]}" ) + + parent="$(awk 'toupper($1) == "FROM" { print $2 }' "$version/Dockerfile")" + arches="${parentRepoToArches[$parent]}" + + commit="$(dirCommit "$version")" -for version in "${versions[@]}"; do - commit="$(cd "$version" && git log -1 --format='format:%H' -- Dockerfile $(awk 'toupper($1) == "COPY" { for (i = 2; i < NF; i++) { print $i } }' Dockerfile))" - fullVersion="$(grep -m1 'ENV CASSANDRA_VERSION ' "$version/Dockerfile" | cut -d' ' -f3 | cut -d- -f1)" - versionAliases=( $fullVersion $version ${aliases[$version]} ) - echo - for va in "${versionAliases[@]}"; do - echo "$va: ${url}@${commit} $version" - done + cat <<-EOE + Tags: $(join ', ' "${versionAliases[@]}") + Architectures: $(join ', ' $arches) + GitCommit: $commit + Directory: $version + EOE done diff --git a/update.sh b/update.sh index f2b2368..bac2d75 100755 --- a/update.sh +++ b/update.sh @@ -1,29 +1,7 @@ -#!/bin/bash -set -eo pipefail +#!/usr/bin/env bash +set -Eeuo pipefail cd "$(dirname "$(readlink -f "$BASH_SOURCE")")" -versions=( "$@" ) -if [ ${#versions[@]} -eq 0 ]; then - versions=( */ ) -fi -versions=( "${versions[@]%/}" ) - - -travisEnv= -for version in "${versions[@]}"; do - dist="${version//./}" - packagesUrl="http://www.apache.org/dist/cassandra/debian/dists/${dist}x/main/binary-amd64/Packages.gz" - fullVersion="$(curl -fsSL "$packagesUrl" | gunzip | grep -m1 -A10 "^Package: cassandra\$" | grep -m1 '^Version: ' | cut -d' ' -f2)" - - ( - set -x - cp docker-entrypoint.sh "$version/" - sed 's/%%CASSANDRA_DIST%%/'$dist'/g; s/%%CASSANDRA_VERSION%%/'$fullVersion'/g' Dockerfile.template > "$version/Dockerfile" - ) - - travisEnv='\n - VERSION='"$version$travisEnv" -done - -travis="$(awk -v 'RS=\n\n' '$1 == "env:" { $0 = "env:'"$travisEnv"'" } { printf "%s%s", $0, RS }' .travis.yml)" -echo "$travis" > .travis.yml +./versions.sh "$@" +./apply-templates.sh "$@" diff --git a/versions.json b/versions.json new file mode 100644 index 0000000..8e5e11e --- /dev/null +++ b/versions.json @@ -0,0 +1,57 @@ +{ + "3.0": { + "version": "3.0.32", + "sha512": "26043c329bc4c179bc50c2d7bd59e85292bfc8ed2af6502b636fe2fc583d32678649a69a2b0bdc6ff1f2798f7c4fd7fd84a7714d25c2e6172ab5cd1ae2d5d05c", + "java": { + "version": "8" + }, + "FROM": { + "version": "eclipse-temurin:8-jre-jammy", + "base": "jammy" + } + }, + "3.11": { + "version": "3.11.19", + "sha512": "42d7732c2b81c65a960101d1146603d430de341adcdf8d0ffc649753a340cf64dad696050f2ec01faff5f15e726f4f2a459f0b3ac281569b957f7726f51d43e0", + "java": { + "version": "8" + }, + "FROM": { + "version": "eclipse-temurin:8-jre-jammy", + "base": "jammy" + } + }, + "4.0": { + "version": "4.0.18", + "sha512": "e9e4bb3149dce09b5f8dbe55d32892b147cdd13be7863851b0a1cb95113636e4433563e668759b49a8293c1cd766b331d27958f93331e9b3966951fc1c4681e6", + "java": { + "version": "11" + }, + "FROM": { + "version": "eclipse-temurin:11-jre-jammy", + "base": "jammy" + } + }, + "4.1": { + "version": "4.1.9", + "sha512": "ece9397a8d85a2dd7974932efca0a9b7a004850c34ecb0ec0fd4d30606a56dd7257a36b8bfb8b974ba154ab79887539eabaee8692cc8db606837848042104be7", + "java": { + "version": "11" + }, + "FROM": { + "version": "eclipse-temurin:11-jre-jammy", + "base": "jammy" + } + }, + "5.0": { + "version": "5.0.4", + "sha512": "16de8bb78af94dabfd916825f492de7040a7311791bc1d630f14707d88f7132e51ae38dad7ebbaf13d2d154af0bdeefe318d680798b8e247ae4117cbc0e2f6df", + "java": { + "version": "17" + }, + "FROM": { + "version": "eclipse-temurin:17-jre-jammy", + "base": "jammy" + } + } +} diff --git a/versions.sh b/versions.sh new file mode 100755 index 0000000..6532c13 --- /dev/null +++ b/versions.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +# https://cassandra.apache.org/doc/5.0/cassandra/installing/installing.html#prerequisites +# https://cassandra.apache.org/doc/4.1/cassandra/getting_started/installing.html#prerequisites +# https://cassandra.apache.org/doc/3.11/cassandra/getting_started/installing.html#prerequisites +defaultJavaVersion='17' +declare -A javaVersions=( + [3.0]='8' + [3.11]='8' + [4.0]='11' + [4.1]='11' +) +declare -A suiteOverrides=( + # see notes about python2 vs python3 in Dockerfile.template (noble does not have python2) + [3.0]='jammy' + [3.11]='jammy' + # https://issues.apache.org/jira/browse/CASSANDRA-19206: "cqlsh breaks with Python 3.12" ("ModuleNotFoundError: No module named 'six.moves'") + [4.0]='jammy' + [4.1]='jammy' + # "Warning: unsupported version of Python, required 3.6-3.11 but found 3.12" + # https://github.com/apache/cassandra/commit/8fd44ca8fc9e0b0e94932bcd855e2833bf6ca3cb#diff-8d8ae48aaf489a8a0e726d3e4a6230a26dcc76e7c739e8e3968e3f65c995d148 + # https://issues.apache.org/jira/browse/CASSANDRA-19245?focusedCommentId=17803539#comment-17803539 + # https://github.com/apache/cassandra/blob/cassandra-5.0-rc1/bin/cqlsh#L65 + [5.0]='jammy' +) + +cd "$(dirname "$(readlink -f "$BASH_SOURCE")")" + +versions=( "$@" ) +if [ ${#versions[@]} -eq 0 ]; then + versions=( */ ) + json='{}' +else + json="$(< versions.json)" +fi +versions=( "${versions[@]%/}" ) + +for version in "${versions[@]}"; do + export version + + possibleVersions=( $( + git ls-remote --tags 'https://gitbox.apache.org/repos/asf/cassandra.git' "refs/tags/cassandra-$version*" \ + | cut -d/ -f3- \ + | cut -d^ -f1 \ + | cut -d- -f2- \ + | sort -urV + ) ) + + fullVersion= + sha512= + for possibleVersion in "${possibleVersions[@]}"; do + if sha512="$(wget -qO- "https://downloads.apache.org/cassandra/$possibleVersion/apache-cassandra-$possibleVersion-bin.tar.gz.sha512" | grep -oE '[a-f0-9]{128}')" && [ -n "$sha512" ]; then + fullVersion="$possibleVersion" + break + fi + done + if [ -z "$fullVersion" ]; then + echo >&2 "error: failed to find full version for $version" + exit 1 + fi + export fullVersion sha512 + + export javaVersion="${javaVersions[$version]:-$defaultJavaVersion}" + suiteOverride="${suiteOverrides[$version]:-}" + + # for the given Java version, find the "default" Eclipse Temurin tag with stable specificity ("X-jre-SUITE") + from="$( + bashbrew --arch amd64 list --arch-filter "https://github.com/docker-library/official-images/raw/HEAD/library/eclipse-temurin:$javaVersion-jre${suiteOverride:+-$suiteOverride}" \ + | grep -F ":$javaVersion-jre-" \ + | tail -1 + )" + export from + + echo "$version: $fullVersion (FROM $from)" + + json="$(jq <<<"$json" -c ' + .[env.version] = { + version: env.fullVersion, + sha512: env.sha512, + java: { + version: env.javaVersion, + }, + FROM: { + # this structure is a little bit awkward, but gives us nice commit messages like "Update 5.0 to FROM eclipse-temurin:17-jre-jammy" + version: env.from, + base: (env.from | split(":\(env.javaVersion)-jre-")[1]), + }, + } + ')" +done + +jq <<<"$json" . > versions.json