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