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

Proposed by Jay Kuri
Status: Merged
Merged at revision: acff79e6ece1be0da13262bed4302aae6d4fe5cb
Proposed branch: ~jk0ne/charm-k8s-discourse/+git/discourse-k8s-image-builder:master
Merge into: charm-k8s-discourse:master
Diff against target: 341 lines (+281/-0)
10 files modified
Dockerfile (+54/-0)
build_scripts/build_app (+26/-0)
build_scripts/cleanup_post_build (+12/-0)
build_scripts/get_app_dependencies (+27/-0)
build_scripts/setup_base_layer (+21/-0)
scripts/app_launch (+19/-0)
scripts/assets/discourse.conf.tmpl (+21/-0)
scripts/pod_exit (+15/-0)
scripts/pod_setup (+47/-0)
scripts/pod_start (+39/-0)
Reviewer Review Type Date Requested Status
Tom Haddon Approve
Jay Kuri (community) Needs Resubmitting
Canonical IS Reviewers Pending
Review via email: mp+387079@code.launchpad.net

Commit message

This adds various log handling tweaks along with other cleanup. This also adds the migration sync workaround during pod startup.

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 :

Some comments inline. Overall looks good, mostly just comment formatting, and a few questions about variables.

review: Needs Fixing
Revision history for this message
Jay Kuri (jk0ne) :
Revision history for this message
Tom Haddon (mthaddon) wrote :

I'm not quite sure if you're ready for review on this or still working on it - I think you're ready for review (it's best to add a comment and select "Resubmit" to let reviewers know you're done with changes). Two very small comments inline, but otherwise this is mergeable.

Revision history for this message
Jay Kuri (jk0ne) wrote :

Made the CONTAINER_APP_NAME change requested in previous comments.

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

LGTM

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

Change successfully merged at revision acff79e6ece1be0da13262bed4302aae6d4fe5cb

Preview Diff

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

Subscribers

People subscribed via source and target branches