gitlab/gitlab.yml: Handle "external URL"
[slapos.git] / software / gitlab / instance-gitlab.cfg.in
1 # GitLab instance
2 # NOTE instance/software layout is inspired by gitlab omnibus
3 # NOTE all services are interconnected via unix sockets - because of easier
4 #      security and performance reasons (unix has 2x less latency and more
5 #      throughput compared to tcp over loopback).
6 [buildout]
7 extends = {{ gitlab_parameters_cfg }}
8 parts =
9     directory
10
11 #   gitlab-<prog>
12 # ? mailroom
13 {% set gitlab_progv = 'rails rake unicorn sidekiq' .split() %}
14 {% for prog in gitlab_progv %}
15     gitlab-{{ prog }}
16 {% endfor %}
17
18     gitlab-work
19     gitlab-shell-work
20
21     service-postgresql
22     service-redis
23
24     service-cron
25
26 # std stuff for slapos instance
27 eggs-directory = {{ eggs_directory }}
28 develop-eggs-directory = {{ develop_eggs_directory }}
29 offline = true
30
31
32 ##################################
33 #   GitLab instance parameters   #
34 ##################################
35
36 [instance-parameter]
37 # std stuff to fetch slapos instance parameters
38 recipe  = slapos.cookbook:slapconfiguration
39 computer= ${slap-connection:computer-id}
40 partition=${slap-connection:partition-id}
41 url     = ${slap-connection:server-url}
42 key     = ${slap-connection:key-file}
43 cert    = ${slap-connection:cert-file}
44
45 # autogenerated gitlab instance parameters
46 <= gitlab-parameters
47
48
49
50 #############################
51 #   GitLab instance setup   #
52 #############################
53
54 # 1. directories
55 [directory]
56 recipe  = slapos.cookbook:mkdirectory
57 home    = ${buildout:directory}
58 bin     = ${:home}/bin
59 etc     = ${:home}/etc
60 var     = ${:home}/var
61 log     = ${:var}/log
62 run     = ${:var}/run
63 srv     = ${:home}/srv
64 # slapos startup/service/promise scripts live here:
65 startup = ${:etc}/run
66 service = ${:etc}/service
67 promise = ${:etc}/promise
68
69 # gitlab: etc/ log/ ...
70 [gitlab-dir]
71 recipe  = slapos.cookbook:mkdirectory
72 etc     = ${directory:etc}/gitlab
73 log     = ${directory:log}/gitlab
74
75 var     = ${directory:var}/gitlab
76 tmp     = ${:var}/tmp
77 uploads = ${:var}/uploads
78 assets  = ${:var}/assets
79 backup  = ${directory:var}/backup
80
81 [gitlab-repo-dir]
82 recipe  = slapos.cookbook:mkdirectory
83 repositories    = ${directory:var}/repositories
84 # gitlab wants it to be drwxrws---
85 # FIXME setting such mode with :mkdirectory is not possible, because mkdir(2)
86 # does & 0777 and also there is umask. So we workaround:
87 [gitlab-repo-xdir]
88 recipe  = plone.recipe.command
89 stop-on-error = yes
90 repositories = ${gitlab-repo-dir:repositories}
91 command = chmod 02770 ${:repositories}
92
93 [gitlab]
94 etc     = ${gitlab-dir:etc}
95 log     = ${gitlab-dir:log}
96 var     = ${gitlab-dir:var}
97 tmp     = ${gitlab-dir:tmp}
98 uploads = ${gitlab-dir:uploads}
99 assets  = ${gitlab-dir:assets}
100 backup  = ${gitlab-dir:backup}
101 repositories = ${gitlab-repo-xdir:repositories}
102
103
104 # gitlab-shell: etc/ log/ gitlab_shell_secret ...
105 [gitlab-shell-dir]
106 recipe  = slapos.cookbook:mkdirectory
107 etc     = ${directory:etc}/gitlab-shell
108 log     = ${directory:log}/gitlab-shell
109
110 [gitlab-shell]
111 etc     = ${gitlab-shell-dir:etc}
112 log     = ${gitlab-shell-dir:log}
113 secret  = ${secrets:secrets}/gitlab_shell_secret
114
115
116 # place to keep all secrets
117 [secrets]
118 recipe  = slapos.cookbook:mkdirectory
119 secrets = ${directory:var}/secrets
120 mode    = 0700
121
122
123
124
125 # 2. configuration files
126 [etc-template]
127 recipe  = slapos.recipe.template:jinja2
128 extensions = jinja2.ext.do
129 mode    = 0640
130 import-list =
131     rawfile macrolib.cfg.in     {{ macrolib_cfg_in }}
132 context =
133     raw     autogenerated       # This file was autogenerated. (DO NOT EDIT - changes will be lost)
134     section instance_parameter  instance-parameter
135     import  urlparse            urlparse
136     ${:context-extra}
137 context-extra =
138
139 [gitlab-etc-template]
140 <= etc-template
141 rendered= ${gitlab:etc}/${:_buildout_section_name_}
142
143
144 [config.ru]
145 <= gitlab-etc-template
146 template = {{ config_ru_in }}
147
148 [database.yml]
149 <= gitlab-etc-template
150 template= {{ database_yml_in }}
151 context-extra =
152     section pgsql                   service-postgresql
153
154 [gitlab-shell-config.yml]
155 <= etc-template
156 template= {{ gitlab_shell_config_yml_in }}
157 rendered= ${gitlab-shell:etc}/config.yml
158
159 [gitlab.yml]
160 <= gitlab-etc-template
161 template= {{ gitlab_yml_in }}
162
163 [rack_attack.rb]
164 <= gitlab-etc-template
165 template = {{ rack_attack_rb_in }}
166
167 [resque.yml]
168 <= gitlab-etc-template
169 template= {{ resque_yml_in }}
170 context-extra =
171     section redis                   service-redis
172
173 [smtp_settings.rb]
174 <= gitlab-etc-template
175 template= {{ smtp_settings_rb_in }}
176 # contains smtp password
177 mode    = 0600
178
179 [unicorn.rb]
180 <= gitlab-etc-template
181 template = {{ unicorn_rb_in }}
182
183
184
185 # 3. bin/
186 #   gitlab-<prog>
187 [gitlab-bin]
188 recipe  = slapos.cookbook:wrapper
189 wrapper-path = ${directory:bin}/${:_buildout_section_name_}
190 environment  =
191     BUNDLE_GEMFILE = {{ gitlab_repository_location }}/Gemfile
192     RAILS_ENV = production
193
194 # NOTE sys.argv[1:] implicitly appended
195 # (by slapos.recipe.librecipe.execute.generic_exec() at runtime)
196 command-line =
197     {{ bundler_4gitlab }} exec sh -c
198     'cd ${gitlab-work:location} && ${:prog} "$@"' ${:prog}
199
200 {% for prog in gitlab_progv %}
201 [gitlab-{{ prog }}]
202 <= gitlab-bin
203 prog    = {{ prog }}
204 {% endfor %}
205
206
207 # 4. gitlab- & gitlab-shell- work directories
208 #
209 # Gitlab/Rails operation is tightened that config/ lives inside code, which goes
210 # against having ability to create several instances configured differently
211 # from 1 SR.
212 #
213 # One possibility to overcome this could be to make another Gitlab root
214 # symbolically linked to original SR _and_ several configuration files
215 # symbolically linked to instance place. Unfortunately this does not work -
216 # Ruby determines realpath on module import and Gitlab and Rails lookup config
217 # files relative to imported modules.
218 #
219 # we clone cloned gitlab and add proper links to vendor/bundle and instance
220 # config files.
221 # XXX there is no need for full clone - we only need worktree checkout (a-la `git
222 # worktree add`, but without creating files in original clone)
223 #
224 # This way Gitlab/Rails still think they work in 1 code / 1 instance way,
225 # and we can reuse SR.
226 # XXX better do such tricks with bind mounting, but that requires user namespaces
227
228 [work-base]
229 recipe  = plone.recipe.command
230 stop-on-error = yes
231 location = ${directory:home}/${:_buildout_section_name_}
232 command =
233 # make sure we start from well-defined empty state
234 # (needed e.g. if previous install failed in the middle)
235     rm -rf ${:location}  &&
236 # init work repository and add `software` remote pointing to main repo in SR software/...
237     {{ git }} init ${:location}  &&
238     cd ${:location}  &&
239     {{ git }} remote add software ${:software}  &&
240     ${:update-command}
241
242 update-command =
243     cd ${:location}  &&
244     {{ git }} fetch software  &&
245     {{ git }} reset --hard `cd ${:software} && {{ git }} rev-parse HEAD`  &&
246     ${:tune-command}
247
248
249 # NOTE there is no need to link/create .gitlab_shell_secret - we will set path to it
250 # in gitlab & gitlab-shell configs, and gitlab creates it on its first start
251 [gitlab-work]
252 <= work-base
253 software = {{ gitlab_repository_location }}
254 tune-command =
255 # secret* config.ru tmp/ log/
256     rm -f .secret  &&
257     rm -f config.ru  &&
258     rm -rf log tmp  &&
259     ln -sf ${secrets:secrets}/gitlab_rails_secret .secret  &&
260     ln -sf ${config.ru:rendered} config.ru  &&
261     ln -sf ${gitlab:log} log  &&
262     ln -sf ${gitlab:tmp} tmp  &&
263 # config/
264     cd config  &&
265     ln -sf ${unicorn.rb:rendered} unicorn.rb  &&
266     ln -sf ${gitlab.yml:rendered} gitlab.yml  &&
267     ln -sf ${database.yml:rendered} database.yml  &&
268     ln -sf ${resque.yml:rendered} resque.yml  &&
269     ln -sf ${secrets:secrets}/gitlab_secrets.yml secrets.yml  &&
270 # config/initializers/
271     cd initializers  &&
272     ln -sf ${rack_attack.rb:rendered} rack_attack.rb  &&
273     ln -sf ${smtp_settings.rb:rendered} smtp_settings.rb  &&
274 # public/
275     cd ../../public  &&
276     rm -rf uploads assets  &&
277     ln -sf ${gitlab:uploads} uploads  &&
278     ln -sf ${gitlab:assets} assets  &&
279     true
280
281
282 # ----//---- for gitlab-shell
283 [gitlab-shell-work]
284 <= work-base
285 software = {{ gitlab_shell_repository_location }}
286
287 tune-command =
288     ln -sf ${gitlab-shell-config.yml:rendered}   config.yml  &&
289     true
290
291
292
293 # 5. services
294
295 # [promise-<something>] to generate promise wrapper <something>
296 [promise-wrapper]
297 recipe  = slapos.cookbook:wrapper
298 wrapper-path = !py! '${directory:promise}/' + '${:_buildout_section_name_}'[8:]
299
300
301
302
303 #####################
304 #   Postgresql db   #
305 #####################
306
307 # XXX gitlab-omnibus also tunes:
308 # - shared_buffers
309 # - work_mem
310 # - checkpoint_*
311 # - effective_check_size
312 # - lc_* en_US.UTF-8 -> C  (?)
313 [service-postgresql]
314 recipe  = slapos.cookbook:postgres
315 bin     = {{ postgresql_location }}/bin
316 services= ${directory:service}
317
318 dbname  = gitlabhq_production
319 # NOTE db name must match to what was used in KVM on lab.nexedi.com (restore script grants access to this user)
320 superuser = gitlab-psql
321 # no password - pgsql will listen only on unix sockets (see below) thus access
322 # is protected with filesystem-level permissions.
323 # ( besides, if we use slapos.cookbook:generate.password and do `password = ...`
324 #   the password is stored in plain text in .installed and thus becomes insecure )
325 password=
326
327 pgdata-directory = ${directory:srv}/postgresql
328
329 # empty addresses - listen only on unix socket
330 ipv4    = !py!set([])
331 ipv6    = !py!set([])
332 ipv6-random =
333 port    =
334
335 depend  =
336     ${promise-postgresql:recipe}
337
338 [promise-postgresql]
339 <= promise-wrapper
340 command-line =
341     {{ postgresql_location }}/bin/psql
342         -h ${service-postgresql:pgdata-directory}
343         -U ${service-postgresql:superuser}
344         -d ${service-postgresql:dbname}
345         -c '\q'
346
347 # postgresql logs to stdout/stderr - logs are handled by slapos not us
348 # [logrotate-entry-postgresql]
349
350
351 #############
352 #   Redis   #
353 #############
354 [redis]
355 recipe  = slapos.cookbook:mkdirectory
356 srv     = ${directory:srv}/redis
357 log     = ${directory:log}/redis
358
359
360 [service-redis]
361 recipe  = slapos.cookbook:redis.server
362 wrapper = ${directory:service}/redis
363 promise_wrapper = ${directory:promise}/redis
364
365 server_dir  = ${redis:srv}
366 config_file = ${directory:etc}/redis.conf
367 log_file    = ${redis:log}/redis.log
368 pid_file    = ${directory:run}/redis.pid
369 use_passwd  = false
370 unixsocket  = ${:server_dir}/redis.socket
371 # port = 0 means "don't listen on TCP at all" - listen only on unix socket
372 ipv6    = ::1
373 port    = 0
374
375 server_bin  = {{ redis_binprefix }}/redis-server
376 depend  =
377     ${logrotate-entry-redis:recipe}
378
379
380 # NOTE slapos.cookbook:redis.server setups promise automatically
381
382 [logrotate-entry-redis]
383 <= logrotate-entry
384 log     = ${redis:log}/*.log
385
386
387
388 #############
389 #   cron    #
390 #############
391 [cron-dir]
392 recipe  = slapos.cookbook:mkdirectory
393 cron.d  = ${directory:etc}/cron.d
394 crontabs= ${directory:srv}/cron/crontabs
395 cronstamps = ${directory:var}/cron/cronstamps
396 log     = ${directory:log}/cron
397
398 [service-cron]
399 recipe  = slapos.cookbook:cron
400 binary  = ${directory:service}/crond
401 cron-entries    = ${cron-dir:cron.d}
402 crontabs        = ${cron-dir:crontabs}
403 cronstamps      = ${cron-dir:cronstamps}
404 catcher         = ${cron-simplelogger:wrapper}
405
406 dcrond-binary   = {{ dcron_bin }}
407
408 depends =
409     ${logrotate-entry-cron:recipe}
410
411 # "mailer" that cron uses to emit messages to logfile
412 [cron-simplelogger]
413 recipe  = slapos.cookbook:simplelogger
414 wrapper = ${directory:bin}/${:_buildout_section_name_}
415 log     = ${cron-dir:log}/cron.log
416
417
418 # base entry for clients who registers to cron
419 [cron-entry]
420 recipe  = slapos.cookbook:cron.d
421 # name  = <section-name>.strip_prefix('cron-entry-')
422 # XXX len() is not available in !py! - 11 hardcoded
423 name    = !py!'${:_buildout_section_name_}' [11:]
424 # NOTE _not_ ${service-cron:cron-entries}  - though the value is the same we do
425 # not want service-cron to be instantiated just if a cron-entry is registered.
426 cron-entries = ${cron-dir:cron.d}
427
428 # cron logs are also rotated
429 [logrotate-entry-cron]
430 <= logrotate-entry
431 log     = ${cron-dir:log}/*.log
432
433
434 #######################################
435 #   logrotate base for all services   #
436 #######################################
437 [logrotate-dir]
438 recipe  = slapos.cookbook:mkdirectory
439 srv     = ${directory:srv}/logrotate
440 entries = ${directory:etc}/logrotate.d
441
442 [logrotate]
443 recipe  = slapos.cookbook:logrotate
444 wrapper = ${directory:bin}/${:_buildout_section_name_}
445 conf    = ${directory:etc}/logrotate.conf
446 logrotate-entries   = ${logrotate-dir:entries}
447 state-file  = ${logrotate-dir:srv}/logrotate.status
448
449 logrotate-binary    = {{ logrotate_bin }}
450 gzip-binary     = {{ gzip_bin }}
451 gunzip-binary   = {{ gunzip_bin }}
452
453 depend  = ${cron-entry-logrotate:recipe}
454
455
456 # base entry for clients who registers to logrotate
457 [logrotate-entry]
458 recipe  = slapos.cookbook:logrotate.d
459 logrotate-entries   = ${logrotate:logrotate-entries}
460 # name  = <section-name>.strip_prefix('logrotate-entry-')
461 # XXX len is not available in !py! - 16 hardcoded
462 name    = !py!'${:_buildout_section_name_}'[16:]
463 # NOTE frequency is hardcoded to `daily` in slapos.cookbook:logrotate.d
464 # NOTE backup is also used to add custom logrotate options (hack)
465 backup  = ...
466 # TODO settle whether we need/want olddir or not
467     noolddir
468 # override create emitted by slapos.cookbook:logrotate.d
469     nocreate
470 # do not move log file and this way we do not need to signal its program to
471 # reopen the log. There are a lot of bugs when on such reopen / restart /
472 # graceful-restart something bad happens. Even if copytruncate is a bit racy
473 # and can loose some data, it is better to keep the system the stable way.
474     copytruncate
475
476
477 # hook logrotate into cron
478 [cron-entry-logrotate]
479 <= cron-entry
480 time    = daily
481 command = ${logrotate:wrapper}