Merge ~jk0ne/charm-k8s-discourse/+git/charm-k8s-discourse:master into charm-k8s-discourse:master

Proposed by Jay Kuri
Status: Merged
Approved by: Tom Haddon
Approved revision: 203ab0843bacb7d66337f56d0ae146e3306a2ec5
Merged at revision: d6c805ea40aaa5ec19fe6b755fd03174e68f0f79
Proposed branch: ~jk0ne/charm-k8s-discourse/+git/charm-k8s-discourse:master
Merge into: charm-k8s-discourse:master
Diff against target: 366 lines (+293/-1)
11 files modified
Makefile (+14/-1)
image/Dockerfile (+54/-0)
image/build_scripts/build_app (+26/-0)
image/build_scripts/cleanup_post_build (+12/-0)
image/build_scripts/get_app_dependencies (+27/-0)
image/build_scripts/setup_base_layer (+21/-0)
image/scripts/app_launch (+16/-0)
image/scripts/assets/discourse.conf.tmpl (+22/-0)
image/scripts/pod_exit (+15/-0)
image/scripts/pod_setup (+47/-0)
image/scripts/pod_start (+39/-0)
Reviewer Review Type Date Requested Status
Tom Haddon Approve
Canonical IS Reviewers Pending
Review via email: mp+390853@code.launchpad.net

Commit message

Incorporate image building to charm codebase.

To post a comment you must log in.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

Revision history for this message
Tom Haddon (mthaddon) wrote :

LGTM, thx

review: Approve
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Change successfully merged at revision d6c805ea40aaa5ec19fe6b755fd03174e68f0f79

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/Makefile b/Makefile
2index f81e542..00ca7aa 100644
3--- a/Makefile
4+++ b/Makefile
5@@ -1,3 +1,7 @@
6+DISCOURSE_VERSION ?= v2.5.1
7+IMAGE_VERSION ?= $(DISCOURSE_VERSION)
8+IMAGE_NAME ?=discourse
9+
10 blacken:
11 @echo "Normalising python layout with black."
12 @tox -e black
13@@ -20,4 +24,13 @@ clean:
14 discourse.charm: src/*.py requirements.txt
15 charmcraft build
16
17-.PHONY: blacken lint test unittest clean
18+build-image:
19+ @echo "Building the image."
20+ @docker build \
21+ --no-cache=true \
22+ --build-arg CONTAINER_APP_VERSION='$(DISCOURSE_VERSION)' \
23+ -t $(IMAGE_NAME):$(IMAGE_VERSION) \
24+ image/
25+
26+
27+.PHONY: blacken lint test unittest clean build-image
28diff --git a/image/Dockerfile b/image/Dockerfile
29new file mode 100644
30index 0000000..7baf5cb
31--- /dev/null
32+++ b/image/Dockerfile
33@@ -0,0 +1,54 @@
34+FROM ubuntu:focal
35+
36+# ARGS become environment variables, but can be overridden using the
37+# --build-arg var=foo option to docker build. This allows you to have a
38+# default build image, but customize certain options such as app version or
39+# userids, etc.
40+ARG CONTAINER_APP_NAME
41+ARG CONTAINER_APP_VERSION
42+ARG CONTAINER_APP_USERNAME
43+ARG CONTAINER_APP_UID
44+ARG CONTAINER_APP_GROUP
45+ARG CONTAINER_APP_GID
46+
47+# Copy any args we got into the environment.
48+ENV CONTAINER_APP_NAME ${CONTAINER_APP_NAME:-discourse}
49+ENV CONTAINER_APP_VERSION ${CONTAINER_APP_VERSION:-v2.4.3}
50+ENV CONTAINER_APP_USERNAME ${CONTAINER_APP_USERNAME:-discourse}
51+ENV CONTAINER_APP_UID ${CONTAINER_APP_UID:-200}
52+ENV CONTAINER_APP_GROUP ${CONTAINER_APP_GROUP:-discourse}
53+ENV CONTAINER_APP_GID ${CONTAINER_APP_GID:-200}
54+
55+# CONTAINER_APP_ROOT is where files related to this application go. This
56+# environment variable is available in the build scripts. This should usually be
57+# a subdirectory of /srv.
58+ENV CONTAINER_APP_ROOT=/srv/discourse
59+
60+# Application specific environment variables go here.
61+ENV GEM_HOME ${CONTAINER_APP_ROOT}/.gem
62+
63+# We don't want packages prompting us during install.
64+ENV DEBIAN_FRONTEND=noninteractive
65+
66+# Copy our build and runtime scripts into the image.
67+COPY --chown=${CONTAINER_APP_UID}:${CONTAINER_APP_GID} build_scripts /srv/build_scripts
68+
69+# The setup_base_layer script performs steps that are very unlikely to change
70+# for this image.
71+RUN /srv/build_scripts/setup_base_layer
72+
73+# The get_app_dependencies script gets any application-specific packages needed
74+# for this build.
75+RUN /srv/build_scripts/get_app_dependencies
76+
77+# Run build process.
78+RUN /srv/build_scripts/build_app
79+
80+# Run post-build cleanup.
81+RUN /srv/build_scripts/cleanup_post_build
82+
83+# Copy run-time scripts into the container.
84+COPY --chown=${CONTAINER_APP_UID}:${CONTAINER_APP_GID} scripts /srv/scripts
85+
86+# Set our entrypoint.
87+ENTRYPOINT /srv/scripts/pod_start
88diff --git a/image/build_scripts/build_app b/image/build_scripts/build_app
89new file mode 100755
90index 0000000..01128e1
91--- /dev/null
92+++ b/image/build_scripts/build_app
93@@ -0,0 +1,26 @@
94+#!/bin/bash
95+#
96+# build_app
97+#
98+# This builds the application. This should include retrieving the app if it is
99+# built from source, as well as any build, install and post-install setup.
100+
101+echo "Building ${CONTAINER_APP_NAME} ${CONTAINER_APP_VERSION} in ${CONTAINER_APP_ROOT}..."
102+
103+mkdir -p ${GEM_HOME}
104+chown ${CONTAINER_APP_USERNAME}:${CONTAINER_APP_GROUP} ${GEM_HOME}
105+
106+cd ${CONTAINER_APP_ROOT}
107+git clone --depth 1 --branch ${CONTAINER_APP_VERSION} https://github.com/discourse/discourse.git ${CONTAINER_APP_VERSION}
108+chown -R ${CONTAINER_APP_USERNAME}:${CONTAINER_APP_GROUP} ${CONTAINER_APP_VERSION}
109+ln -s ${CONTAINER_APP_ROOT}/${CONTAINER_APP_VERSION} app
110+cd ${CONTAINER_APP_ROOT}/app
111+
112+# This must be done as the discourse user in order to avoid permission
113+# problems later.
114+su -s /bin/bash -c 'bin/bundle install' ${CONTAINER_APP_USERNAME}
115+
116+# If intermediate files are generated by the build process or other files are
117+# generated that are not needed at runtime, remove them here to save image size.
118+# If we don't do this they will become part of the image even if removed later.
119+find ${CONTAINER_APP_ROOT} -name tmp -type d -exec rm -rf {} +
120diff --git a/image/build_scripts/cleanup_post_build b/image/build_scripts/cleanup_post_build
121new file mode 100755
122index 0000000..2c2fe6d
123--- /dev/null
124+++ b/image/build_scripts/cleanup_post_build
125@@ -0,0 +1,12 @@
126+#!/bin/bash
127+#
128+# cleanup_post_build
129+#
130+# This does any post-build steps that need to be run and removes anything that
131+# should not exist in the image. Note that removals here do not save space, they
132+# simply remove the file references from the running image.
133+
134+apt-get autoremove
135+apt-get clean
136+
137+rm -rf /srv/build_scripts
138diff --git a/image/build_scripts/get_app_dependencies b/image/build_scripts/get_app_dependencies
139new file mode 100755
140index 0000000..65b267a
141--- /dev/null
142+++ b/image/build_scripts/get_app_dependencies
143@@ -0,0 +1,27 @@
144+#!/bin/bash
145+#
146+# get_app_dependencies
147+#
148+# Installs application-specific dependencies for this image.
149+
150+apt-get install -y brotli \
151+ gifsicle \
152+ git \
153+ imagemagick \
154+ jhead \
155+ jpegoptim \
156+ libjpeg-turbo-progs \
157+ libpq-dev \
158+ libxml2-dev \
159+ libxslt1-dev \
160+ libz-dev \
161+ node-uglify \
162+ optipng \
163+ pngquant \
164+ postgresql-client \
165+ postgresql-client-common \
166+ redis-tools \
167+ ruby2.7 \
168+ ruby2.7-dev \
169+ zlib1g-dev
170+
171diff --git a/image/build_scripts/setup_base_layer b/image/build_scripts/setup_base_layer
172new file mode 100755
173index 0000000..9d3584e
174--- /dev/null
175+++ b/image/build_scripts/setup_base_layer
176@@ -0,0 +1,21 @@
177+#!/bin/bash
178+#
179+# setup_base_layer
180+#
181+# This sets up the base image with common tools and packages that are needed for
182+# most images.
183+#
184+# Update our packages.
185+apt-get update
186+
187+# Set our timezone to UTC.
188+ln -s /usr/share/zoneinfo/UTC /etc/localtime
189+
190+# Get our base packages.
191+apt-get install -y gettext-base tzdata ubuntu-dev-tools git
192+
193+# Create our application group.
194+/usr/sbin/addgroup --gid ${CONTAINER_APP_GID} ${CONTAINER_APP_GROUP}
195+
196+# Create our application user - this also creates our app root.
197+/usr/sbin/adduser --uid ${CONTAINER_APP_UID} --home ${CONTAINER_APP_ROOT} --gid ${CONTAINER_APP_GID} --system ${CONTAINER_APP_USERNAME}
198diff --git a/image/scripts/app_launch b/image/scripts/app_launch
199new file mode 100755
200index 0000000..0beae97
201--- /dev/null
202+++ b/image/scripts/app_launch
203@@ -0,0 +1,16 @@
204+#!/bin/bash
205+
206+cd ${CONTAINER_APP_ROOT}/app
207+
208+export RAILS_ENV=${DISCOURSE_RAILS_ENVIRONMENT:-production}
209+export UNICORN_BIND_ALL=0.0.0.0
210+export UNICORN_SIDEKIQS=1
211+
212+# Putting the tail's in the background gets the various logs outputting to
213+# stdout. The sed allows us to distinguish where a given log line comes form.
214+touch ${CONTAINER_APP_ROOT}/app/log/production.log
215+touch ${CONTAINER_APP_ROOT}/app/log/unicorn-stderr.log
216+tail -f ${CONTAINER_APP_ROOT}/app/log/production.log |sed 's/^/discourse: /' &
217+tail -f ${CONTAINER_APP_ROOT}/app/log/unicorn-stderr.log |sed 's/^/unicorn-error: /' 1>&2 &
218+
219+su -s /bin/bash -c "bin/bundle exec unicorn -c ${CONTAINER_APP_ROOT}/app/config/unicorn.conf.rb" ${CONTAINER_APP_USERNAME} 2>&1
220diff --git a/image/scripts/assets/discourse.conf.tmpl b/image/scripts/assets/discourse.conf.tmpl
221new file mode 100644
222index 0000000..09958ce
223--- /dev/null
224+++ b/image/scripts/assets/discourse.conf.tmpl
225@@ -0,0 +1,22 @@
226+hostname = $DISCOURSE_HOSTNAME
227+developer_emails = $DISCOURSE_DEVELOPER_EMAILS
228+serve_static_assets = true
229+db_host = $DISCOURSE_POSTGRES_HOST
230+db_username = $DISCOURSE_POSTGRES_USERNAME
231+db_password = $DISCOURSE_POSTGRES_PASSWORD
232+db_name = $DISCOURSE_POSTGRES_DB_NAME
233+smtp_domain = $DISCOURSE_SMTP_DOMAIN
234+smtp_address = $DISCOURSE_SMTP_ADDRESS
235+smtp_port = $DISCOURSE_SMTP_PORT
236+smtp_openssl_verify_mode = $DISCOURSE_SMTP_OPENSSL_VERIFY_MODE
237+smtp_user_name = $DISCOURSE_SMTP_USER_NAME
238+smtp_password = $DISCOURSE_SMTP_PASSWORD
239+smtp_authentication = $DISCOURSE_SMTP_AUTHENTICATION
240+enable_cors = $DISCOURSE_ENABLE_CORS
241+cors_origin = $DISCOURSE_CORS_ORIGIN
242+saml_target_url = $DISCOURSE_SAML_TARGET_URL
243+saml_cert_fingerprint = $DISCOURSE_CERT_FINGERPRINT
244+saml_full_screen_login = $DISCOURSE_FULL_SCREEN_LOGIN
245+refresh_maxmind_db_during_precompile_days = 0
246+redis_host = $DISCOURSE_REDIS_HOST
247+redis_port = $DISCOURSE_REDIS_PORT
248diff --git a/image/scripts/pod_exit b/image/scripts/pod_exit
249new file mode 100755
250index 0000000..cff0efc
251--- /dev/null
252+++ b/image/scripts/pod_exit
253@@ -0,0 +1,15 @@
254+#!/bin/bash
255+
256+# pod_exit
257+#
258+# Run when a pod is terminating, assuming it's terminating normally. This gives
259+# us a chance to do any api-calls, notifications or last-minute cleanup prior to
260+# pod exit.
261+
262+SETUP_EXITCODE=$1
263+APP_EXITCODE=$2
264+
265+echo "Setup exited with exit code ${SETUP_EXITCODE}"
266+echo "App exited with exit code ${APP_EXITCODE}"
267+
268+exit ${APP_EXITCODE}
269diff --git a/image/scripts/pod_setup b/image/scripts/pod_setup
270new file mode 100755
271index 0000000..9e4e08a
272--- /dev/null
273+++ b/image/scripts/pod_setup
274@@ -0,0 +1,47 @@
275+#!/bin/bash
276+
277+cd ${CONTAINER_APP_ROOT}/app
278+
279+DISCOURSE_CONF=${CONTAINER_APP_ROOT}/app/config/discourse.conf
280+
281+export RAILS_ENV=${DISCOURSE_RAILS_ENVIRONMENT:-production}
282+
283+# Generate our discourse.conf: Load config file and remove any lines we don't
284+# have config values for.
285+/usr/bin/envsubst < /srv/scripts/assets/discourse.conf.tmpl | grep -vE '= *$' > $DISCOURSE_CONF
286+
287+### MIGRATION LOCK WORKAROUND STARTS HERE
288+#
289+# Try to get an exclusive lock via redis prior to migration so that we don't
290+# have multiple units running migration at once. This is a workaround because we
291+# don't have an official Juju way to do this coordination at this point. When we
292+# do, this will go away.
293+MIGRATE_LOCK=$(redis-cli -h $DISCOURSE_REDIS_HOST --raw setnx discourse_migrate_lock $HOSTNAME)
294+if [ ${MIGRATE_LOCK} -eq 1 ]; then
295+ # If we are here, we got the migrate lock, so we can do the migrate.
296+ echo "Starting migration at " $(date)
297+ su -s /bin/bash -c "bin/bundle exec rake db:migrate RAILS_ENV=$RAILS_ENV" ${CONTAINER_APP_USERNAME}
298+ redis-cli -h $DISCOURSE_REDIS_HOST --raw del discourse_migrate_lock >/dev/null
299+else
300+ RETRIES=5
301+ while [ "${RETRIES}" -gt 0 ]; do
302+ MIGRATE_HOST=$(redis-cli -h $DISCOURSE_REDIS_HOST --raw get discourse_migrate_lock)
303+ if [ -z "${MIGRATE_HOST}" ]; then
304+ break;
305+ else
306+ echo "Migrate lock found, Migrate running on ${MIGRATE_HOST}, waiting 90s."
307+ sleep 90
308+ fi
309+ RETRIES=$(($RETRIES - 1))
310+ done
311+
312+ if [ $RETRIES -le 0 ]; then
313+ echo "Retried too many times. Can't proceed while migrate is unfinished. Exiting."
314+ exit 1
315+ fi
316+fi
317+### MIGRATION LOCK WORKAROUND ENDS HERE
318+
319+# It is safe to build assets in the background to improve startup time.
320+export SKIP_NODE_UGLIFY="true"
321+su -s /bin/bash -c "bin/bundle exec rake assets:precompile RAILS_ENV=$RAILS_ENV" ${CONTAINER_APP_USERNAME} 2>&1 |sed 's/^/asset-build: /'
322diff --git a/image/scripts/pod_start b/image/scripts/pod_start
323new file mode 100755
324index 0000000..fd3d95f
325--- /dev/null
326+++ b/image/scripts/pod_start
327@@ -0,0 +1,39 @@
328+#!/bin/bash
329+
330+# pod_start
331+#
332+# This script is the primary entrypoint for the pod. It should start up the
333+# primary service and output log data to stdout. It should also exit when the
334+# application exits in order for the pod to terminate properly.
335+
336+# Steps:
337+# 1) Run any pre-launch setup.
338+# 2) Launch application - should not exit until application exits.
339+# 3) Do any pod-exit cleanup.
340+
341+SETUP_EXIT="notrun"
342+APP_EXIT="notrun"
343+
344+# Run pod_setup script first.
345+echo "Pod setup starting..."
346+/srv/scripts/pod_setup
347+SETUP_EXIT=$?
348+
349+if [ $SETUP_EXIT -eq 0 ]; then
350+ echo "${CONTAINER_APP_NAME} setup completed successfully."
351+else
352+ echo "${CONTAINER_APP_NAME} setup failed with exit code: " $SETUP_EXIT
353+fi
354+
355+
356+# If we exited pod_setup ok, we launch the app.
357+if [ $SETUP_EXIT -eq 0 ]; then
358+ echo "Launching ${CONTAINER_APP_NAME}"
359+ /srv/scripts/app_launch
360+ APP_EXIT=$?
361+ echo "${CONTAINER_APP_NAME} exited with code ${APP_EXIT}"
362+fi
363+
364+# Run pod_exit with either the pod_setup exit code, or the app_launch exit code
365+# as the first argument.
366+/srv/scripts/pod_exit $SETUP_EXIT $APP_EXIT

Subscribers

People subscribed via source and target branches