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