Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions .github/workflows/kb_beta_release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: kb_beta_release

# Publishes a single, explicitly-pinned killbill/killbill:<version> docker image for beta / release-candidate versions
# (e.g. 0.25.1), without touching KAUI and `latest` tag (hence named "beta" release: it only ever publishes an
# explicit, pinned version tag and never promotes/updates `latest`).
#
# Unlike release.yml, this workflow does not reuse the already-published killbill/base:latest or
# killbill/killbill:latest images as parents, and it does not publish any intermediate killbill/base:beta-* image to
# Docker Hub either. Instead, it uses a single dedicated, self-contained multi-stage Dockerfile
# (docker/templates/killbill/beta/Dockerfile.template, stages: base -> shell -> final) so that:
# - branch-local changes (e.g. a Tomcat/JDK bump on a branch such as java2x) are actually baked into the resulting
# image, instead of silently falling back to whatever is currently published under :latest on Docker Hub, and
# - no intermediate image is ever published publicly - only the final killbill/killbill:<version> tag is pushed.
#
# Ansible/ansible-galaxy note: the base stage still installs Tomcat/Java/KPM via this repo's own ansible roles
# (same as release.yml), to keep user/group/permissions/ACL setup identical to a normal release image rather than
# hand-rolling it differently here.
#
# IMPORTANT - killbill-cloud git ref used for the ansible roles: the base stage fetches the ansible roles fresh from
# GitHub via `ansible-galaxy install git+...,<ref>` - it does NOT use this workflow run's own checkout for that. Rather
# than a separate input, this workflow uses ${{ github.ref_name }}, i.e. whatever branch you pick in the
# "Use workflow from" selector above.
#
# Concretely: if you pick "java2x" there, make sure your ansible/Dockerfile changes on java2x are already committed
# AND pushed to origin/java2x before running this. Uncommitted or unpushed local changes will NOT be picked up, since
# the clone happens fresh from GitHub.
on:
workflow_dispatch:
inputs:
killbill_version:
description: 'Kill Bill version to release (must already be published to Maven Central, e.g. 0.25.1)'
required: true
nexus_repository:
description: 'Nexus repository for KPM'
required: true
default: 'maven2'

jobs:
docker:
name: Publish killbill/killbill beta/RC image
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Login to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

# Bake the pinned Kill Bill version into the Dockerfile (same __VERSION__ substitution dockerTemplate.sh does for
# docker/templates/killbill/tagged/Dockerfile.template), then build+push the final stage only.
# The `base` and `shell` (See "stage" in the template) intermediate stages are never pushed. They exist purely
# inside this one buildx invocation's build graph.
- name: Build and push killbill beta/RC image
run: |
cd docker/templates/killbill
sed -e "s/__VERSION__/${{ github.event.inputs.killbill_version }}/" beta/Dockerfile.template > beta/Dockerfile
sed -e "s/__VERSION__/${{ github.event.inputs.killbill_version }}/" tagged/kpm.yml.template > tagged/kpm.yml
docker buildx build --push --platform=linux/arm64,linux/amd64 --no-cache \
--target final \
-t killbill/killbill:${{ github.event.inputs.killbill_version }} \
-f beta/Dockerfile \
--build-arg KILLBILL_CLOUD_REF=${{ github.ref_name }} \
--build-arg NEXUS_REPOSITORY=${{ github.event.inputs.nexus_repository }} \
.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ docker/templates/kaui/tagged/Dockerfile
docker/templates/killbill/latest/Dockerfile
docker/templates/killbill/tagged/Dockerfile
docker/templates/killbill/tagged/kpm.yml
docker/templates/killbill/beta/Dockerfile
4 changes: 2 additions & 2 deletions ansible/java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
- name: Install Java
hosts: all
vars:
java_package: openjdk-11-jdk
default_java_home: /usr/lib/jvm/java-11-openjdk-amd64
java_package: openjdk-21-jdk
default_java_home: /usr/lib/jvm/java-21-openjdk-amd64
java_home: /usr/lib/jvm/default-java
tasks:
- name: Install Java
Expand Down
4 changes: 2 additions & 2 deletions docker/templates/base/latest/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ RUN apt-get update && \
libapr1 \
mysql-client \
net-tools \
openjdk-11-jdk-headless \
openjdk-21-jdk-headless \
python3-lxml \
sudo \
unzip \
Expand All @@ -32,7 +32,7 @@ RUN apt-get update && \
rm -rf /var/lib/apt/lists/*

# Configure default JAVA_HOME path
RUN ln -s java-11-openjdk-$(dpkg --print-architecture) /usr/lib/jvm/default-java
RUN ln -s java-21-openjdk-$(dpkg --print-architecture) /usr/lib/jvm/default-java
ENV JAVA_HOME=/usr/lib/jvm/default-java
ENV JSSE_HOME=$JAVA_HOME/jre/

Expand Down
191 changes: 191 additions & 0 deletions docker/templates/killbill/beta/Dockerfile.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
# Dedicated, self-contained multi-stage Dockerfile for beta/RC killbill releases.
#
# Why this exists?
# - Beta/RC versions may need base-image-level changes (e.g. a newer Tomcat/JDK) that are not yet part of the publicly
# published killbill/base:latest or killbill/killbill:latest images. Building FROM those would silently ignore any
# such branch-local change.
# - Avoid to use separate, intermediate `killbill/base:beta-*` tag to Docker Hub just to have another "base" image.
# - Using docker buildx's multi-stage build (FROM ... AS <stage>) to keeps all of that intermediate state private.
# And then do single `docker buildx build` invocation. This way, nothing but the final image is ever pushed.
#
# This intentionally duplicates the *Dockerfile instructions* normally split across:
# docker/templates/base/latest/Dockerfile (stage: base)
# docker/templates/killbill/latest/Dockerfile (stage: shell)
# docker/templates/killbill/tagged/Dockerfile.template (stage: final)

# ---------------------------------------------------------------------------
# Stage: base (mirrors docker/templates/base/latest/Dockerfile)
# ---------------------------------------------------------------------------
FROM ubuntu:24.04 AS builder-base
LABEL maintainer="killbilling-users@googlegroups.com"

USER root

ENV LC_ALL=C.UTF-8
ENV LANG=C.UTF-8
ENV PYTHONIOENCODING=utf8

ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get install --no-install-recommends -y \
ansible \
apt-utils \
curl \
gettext-base \
git \
less \
libapr1 \
mysql-client \
net-tools \
openjdk-21-jdk-headless \
python3-lxml \
sudo \
unzip \
vim && \
rm -rf /var/lib/apt/lists/*

# Configure default JAVA_HOME path
RUN ln -s java-21-openjdk-$(dpkg --print-architecture) /usr/lib/jvm/default-java
ENV JAVA_HOME=/usr/lib/jvm/default-java
ENV JSSE_HOME=$JAVA_HOME/jre/

ENV TOMCAT_OWNER=tomcat
ENV TOMCAT_GROUP=tomcat
ENV TOMCAT_HOME=/var/lib/tomcat
RUN adduser $TOMCAT_OWNER \
--home $TOMCAT_HOME \
--disabled-password \
--gecos '' && \
usermod -aG sudo $TOMCAT_GROUP && \
echo "$TOMCAT_OWNER:$TOMCAT_OWNER" | chpasswd && \
echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers

USER $TOMCAT_OWNER
WORKDIR $TOMCAT_HOME

# Install ansible roles dependencies.
# KILLBILL_CLOUD_REF is a git ref (branch/tag/SHA) of the killbill-cloud repository, used by
# ansible-galaxy below to fetch the ansible roles that install Tomcat/Java/KPM. This Dockerfile
# has no opinion on which ref that should be - the caller (see kb_beta_release.yml) decides that.
ARG KILLBILL_CLOUD_REF
RUN ansible-galaxy collection install community.general && \
ansible-galaxy install git+https://git@github.com/killbill/killbill-cloud.git,$KILLBILL_CLOUD_REF
ENV KILLBILL_CLOUD_ANSIBLE_ROLES=$TOMCAT_HOME/.ansible/roles/killbill-cloud/ansible
ENV ENV_HOST_IP=localhost
ENV ANSIBLE_OPTS="-i localhost, \
-e ansible_connection=local \
-e ansible_python_interpreter=/usr/bin/python3 \
-e java_home=$JAVA_HOME \
-vv"

# Install KPM
ENV NEXUS_URL=https://repo1.maven.org
ARG NEXUS_REPOSITORY
ENV NEXUS_REPOSITORY=${NEXUS_REPOSITORY:-maven2}
ENV KPM_INSTALL_DIR=/opt
RUN ansible-playbook $ANSIBLE_OPTS \
-e kpm_install_dir=$KPM_INSTALL_DIR \
-e kpm_path=$KPM_INSTALL_DIR/kpm-latest \
-e kpm_owner=$TOMCAT_OWNER \
-e kpm_group=$TOMCAT_GROUP \
-e nexus_url=$NEXUS_URL \
-e nexus_repository=$NEXUS_REPOSITORY \
$KILLBILL_CLOUD_ANSIBLE_ROLES/kpm.yml
ENV PATH="/opt/kpm-latest:${PATH}"

# Install Tomcat (without native libraries)
ENV CATALINA_BASE=$TOMCAT_HOME
ENV CATALINA_HOME=/usr/share/tomcat
ENV CATALINA_PID=$CATALINA_BASE/tomcat.pid
ENV CATALINA_TMPDIR=/var/tmp
ENV TOMCAT_INSTALL_DIR=/opt
ENV TOMCAT_NATIVE_LIBDIR=$CATALINA_HOME/native-jni-lib
ENV LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$TOMCAT_NATIVE_LIBDIR
RUN ansible-playbook $ANSIBLE_OPTS \
-e tomcat_install_dir=$TOMCAT_INSTALL_DIR \
-e tomcat_native_libdir=$TOMCAT_NATIVE_LIBDIR \
-e tomcat_owner=$TOMCAT_OWNER \
-e tomcat_group=$TOMCAT_GROUP \
-e tomcat_home=$TOMCAT_HOME \
-e catalina_home=$CATALINA_HOME \
-e catalina_base=$CATALINA_BASE \
--skip-tags native \
$KILLBILL_CLOUD_ANSIBLE_ROLES/tomcat.yml

# Build Tomcat native libraries
FROM builder-base AS builder-tomcat
USER $TOMCAT_OWNER
RUN ansible-playbook $ANSIBLE_OPTS \
-e tomcat_install_dir=$TOMCAT_INSTALL_DIR \
-e tomcat_native_libdir=$TOMCAT_NATIVE_LIBDIR \
-e tomcat_owner=$TOMCAT_OWNER \
-e tomcat_group=$TOMCAT_GROUP \
-e tomcat_home=$TOMCAT_HOME \
-e catalina_home=$CATALINA_HOME \
-e catalina_base=$CATALINA_BASE \
-t native \
$KILLBILL_CLOUD_ANSIBLE_ROLES/tomcat.yml

FROM builder-base AS base
COPY --from=builder-tomcat /usr/share/tomcat/native-jni-lib /usr/share/tomcat/native-jni-lib

# ---------------------------------------------------------------------------
# Stage: shell (mirrors docker/templates/killbill/latest/Dockerfile)
# ---------------------------------------------------------------------------
FROM base AS shell
LABEL maintainer="killbilling-users@googlegroups.com"

ENV KILLBILL_INSTALL_DIR=/var/lib/killbill

RUN sudo mkdir -p $KILLBILL_INSTALL_DIR $KILLBILL_INSTALL_DIR/bundles $KILLBILL_INSTALL_DIR/config
RUN sudo chown -R $TOMCAT_OWNER:$TOMCAT_GROUP $KILLBILL_INSTALL_DIR

ENV KB_org_killbill_osgi_bundle_install_dir=$KILLBILL_INSTALL_DIR/bundles
ENV KB_org_killbill_billing_osgi_bundles_jruby_conf_dir=$KILLBILL_INSTALL_DIR/config
ENV KB_org_killbill_server_baseUrl=http://$ENV_HOST_IP:8080
ENV KB_org_killbill_billing_plugin_kpm_kpmPath=$KPM_INSTALL_DIR/kpm-latest/kpm
ENV KB_org_killbill_billing_plugin_kpm_bundlesPath=$KILLBILL_INSTALL_DIR/bundles
ENV KB_org_killbill_security_shiroResourcePath=$KILLBILL_INSTALL_DIR/config/shiro.ini
ENV KB_org_killbill_notificationq_main_queue_mode=POLLING
ENV KB_org_killbill_notificationq_analytics_queue_mode=POLLING
ENV KB_ADMIN_PASSWORD=password

COPY ./latest/setenv2.sh $CATALINA_BASE/bin
COPY ./latest/shiro.ini.template $KILLBILL_INSTALL_DIR/config

ENV KPM_INSTALL_CMD="ansible-playbook $ANSIBLE_OPTS \
-e kpm_install_dir=$KPM_INSTALL_DIR \
-e kpm_path=$KPM_INSTALL_DIR/kpm-latest \
-e kpm_yml=$KILLBILL_INSTALL_DIR/kpm.yml \
-e killbill_kpm_yml=$KILLBILL_INSTALL_DIR/kpm.yml \
-e kb_config_dir=$KILLBILL_INSTALL_DIR \
-e kb_plugins_dir=$KILLBILL_INSTALL_DIR/bundles \
-e tomcat_owner=$TOMCAT_OWNER \
-e tomcat_group=$TOMCAT_GROUP \
-e catalina_base=$CATALINA_BASE \
$KILLBILL_CLOUD_ANSIBLE_ROLES/killbill.yml"

# Install Logstash dependencies
ENV LOGSTASH_ENABLED=true
RUN ansible-playbook $ANSIBLE_OPTS $KILLBILL_CLOUD_ANSIBLE_ROLES/killbill_json_logging.yml --tags download

# ---------------------------------------------------------------------------
# Stage: final (mirrors docker/templates/killbill/tagged/Dockerfile.template)
# ---------------------------------------------------------------------------
FROM shell AS final
LABEL maintainer="killbilling-users@googlegroups.com"

ENV KILLBILL_VERSION=__VERSION__

# Pinned kpm.yml (version baked in, not resolved at runtime like the "latest" shell image). Reuses
# templates/killbill/tagged/kpm.yml as-is (build context is templates/killbill/, and __VERSION__ substitution is done
# by the workflow before build, same as docker/dockerTemplate.sh does for the tagged/ template).
COPY ./tagged/kpm.yml $KILLBILL_INSTALL_DIR

# Install Kill Bill + the default OSGi bundles (kpm, logger, metrics) at build time
RUN $KPM_INSTALL_CMD $KILLBILL_CLOUD_ANSIBLE_ROLES/killbill_json_logging.yml

COPY ./tagged/killbill.sh $KILLBILL_INSTALL_DIR

EXPOSE 8080
CMD ["/var/lib/killbill/killbill.sh"]
Loading