]> git.openstreetmap.org Git - chef.git/blob - cookbooks/nominatim/recipes/default.rb
452f3a640ede062ff3b2617ca6be9bc7a4464c0b
[chef.git] / cookbooks / nominatim / recipes / default.rb
1 #
2 # Cookbook:: nominatim
3 # Recipe:: base
4 #
5 # Copyright:: 2015, OpenStreetMap Foundation
6 #
7 # Licensed under the Apache License, Version 2.0 (the "License");
8 # you may not use this file except in compliance with the License.
9 # You may obtain a copy of the License at
10 #
11 #     https://www.apache.org/licenses/LICENSE-2.0
12 #
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
18 #
19
20 include_recipe "accounts"
21 include_recipe "prometheus"
22
23 if platform?("debian")
24   include_recipe "postgresql"
25   include_recipe "python"
26   include_recipe "nginx"
27   include_recipe "git"
28   include_recipe "fail2ban"
29
30   basedir = data_bag_item("accounts", "nominatim")["home"]
31   project_directory = "#{basedir}/planet-project"
32   bin_directory = "#{basedir}/bin"
33   cfg_directory = "#{basedir}/etc"
34   ui_directory = "#{basedir}/ui"
35   qa_bin_directory = "#{basedir}/Nominatim-Data-Analyser"
36   qa_data_directory = "#{basedir}/qa-data"
37
38   directory basedir do
39     owner "nominatim"
40     group "nominatim"
41     mode "755"
42     recursive true
43   end
44
45   [basedir, bin_directory, cfg_directory, project_directory, ui_directory].each do |path|
46     directory path do
47       owner "nominatim"
48       group "nominatim"
49       mode "755"
50     end
51   end
52
53   if node[:nominatim][:flatnode_file]
54     directory File.dirname(node[:nominatim][:flatnode_file]) do
55       recursive true
56     end
57   end
58
59   directory "#{bin_directory}/maintenance" do
60     owner "nominatim"
61     group "nominatim"
62     mode "775"
63   end
64
65   ## Log directory setup
66
67   directory node[:nominatim][:logdir] do
68     owner "nominatim"
69     group "nominatim"
70     mode "755"
71     recursive true
72   end
73
74   file "#{node[:nominatim][:logdir]}/query.log" do
75     action :create_if_missing
76     owner "www-data"
77     group "adm"
78     mode "664"
79   end
80
81   ### Postgresql
82
83   postgresql_version = node[:nominatim][:dbcluster].split("/").first
84   postgis_version = node[:nominatim][:postgis]
85
86   package "postgresql-#{postgresql_version}-postgis-#{postgis_version}"
87
88   node[:nominatim][:dbadmins].each do |user|
89     postgresql_user user do
90       cluster node[:nominatim][:dbcluster]
91       superuser true
92     end
93   end
94
95   postgresql_user "nominatim" do
96     cluster node[:nominatim][:dbcluster]
97     superuser true
98   end
99
100   postgresql_user "www-data" do
101     cluster node[:nominatim][:dbcluster]
102   end
103
104   ### Nominatim
105
106   python_directory = "#{basedir}/venv"
107
108   package %w[
109     build-essential
110     libicu-dev
111     python3-dev
112     pkg-config
113     osm2pgsql
114   ]
115
116   python_virtualenv python_directory do
117     interpreter "/usr/bin/python3"
118   end
119
120   # These are updated during the database update.
121   python_package "nominatim-db" do
122     python_virtualenv python_directory
123     extra_index_url node[:nominatim][:pip_index]
124   end
125
126   python_package "nominatim-api" do
127     python_virtualenv python_directory
128     extra_index_url node[:nominatim][:pip_index]
129   end
130
131   remote_directory "#{project_directory}/static-website" do
132     source "website"
133     owner "nominatim"
134     group "nominatim"
135     mode "755"
136     files_owner "nominatim"
137     files_group "nominatim"
138     files_mode "644"
139     purge false
140   end
141
142   template "#{project_directory}/.env" do
143     source "nominatim.env.erb"
144     owner "nominatim"
145     group "nominatim"
146     mode "664"
147     variables :base_url => node[:nominatim][:state] == "off" ? node[:fqdn] : "nominatim.openstreetmap.org",
148               :dbname => node[:nominatim][:dbname],
149               :flatnode_file => node[:nominatim][:flatnode_file],
150               :log_file => "#{node[:nominatim][:logdir]}/query.log",
151               :pool_size => node[:nominatim][:api_pool_size],
152               :query_timeout => node[:nominatim][:api_query_timeout],
153               :request_timeout => node[:nominatim][:api_request_timeout]
154   end
155
156   remote_file "#{project_directory}/secondary_importance.sql.gz" do
157     action :create_if_missing
158     source "https://nominatim.org/data/wikimedia-secondary-importance.sql.gz"
159     owner "nominatim"
160     group "nominatim"
161     mode "644"
162   end
163
164   remote_file "#{project_directory}/wikimedia-importance.csv.gz" do
165     action :create_if_missing
166     source "https://nominatim.org/data/wikimedia-importance.csv.gz"
167     owner "nominatim"
168     group "nominatim"
169     mode "644"
170   end
171
172   %w[gb_postcodes.csv.gz us_postcodes.csv.gz].each do |fname|
173     remote_file "#{project_directory}/#{fname}" do
174       action :create
175       source "https://nominatim.org/data/#{fname}"
176       owner "nominatim"
177       group "nominatim"
178       mode "644"
179     end
180   end
181
182   # Webserver + frontend
183
184   %w[user_agent referrer email generic].each do |name|
185     file "#{cfg_directory}/nginx_blocked_#{name}.conf" do
186       action :create_if_missing
187       owner "nominatim"
188       group "adm"
189       mode "664"
190     end
191   end
192
193   systemd_service "nominatim" do
194     description "Nominatim running as a gunicorn application"
195     user "www-data"
196     group "www-data"
197     working_directory project_directory
198     standard_output "append:#{node[:nominatim][:logdir]}/gunicorn.log"
199     standard_error "inherit"
200     exec_start "#{python_directory}/bin/gunicorn --max-requests 200000 -b unix:/run/gunicorn-nominatim.openstreetmap.org.sock -w #{node[:nominatim][:api_workers]} -k uvicorn.workers.UvicornWorker 'nominatim_api.server.falcon.server:run_wsgi()'"
201     exec_reload "/bin/kill -s HUP $MAINPID"
202     kill_mode "mixed"
203     timeout_stop_sec 5
204     private_tmp true
205     requires "nominatim.socket"
206     after "network.target"
207   end
208
209   systemd_socket "nominatim" do
210     description "Gunicorn socket for Nominatim"
211     listen_stream "/run/gunicorn-nominatim.openstreetmap.org.sock"
212     socket_user "www-data"
213   end
214
215   ssl_certificate node[:fqdn] do
216     domains [node[:fqdn],
217              "nominatim.openstreetmap.org",
218              "nominatim.osm.org",
219              "nominatim.openstreetmap.com",
220              "nominatim.openstreetmap.net",
221              "nominatim.openstreetmaps.org",
222              "nominatim.openmaps.org",
223              "nominatim.qgis.org"]
224     notifies :reload, "service[nginx]"
225   end
226
227   nginx_site "default" do
228     action [:delete]
229   end
230
231   frontends = search(:node, "recipes:web\\:\\:frontend").sort_by(&:name)
232
233   nginx_site "nominatim" do
234     template "nginx.erb"
235     directory project_directory
236     variables :pools => node[:nominatim][:fpm_pools],
237               :frontends => frontends,
238               :confdir => "#{basedir}/etc",
239               :ui_directory => ui_directory
240   end
241
242   template "/etc/logrotate.d/nginx" do
243     source "logrotate.nginx.erb"
244     owner "root"
245     group "root"
246     mode "644"
247   end
248
249   ### Import, update and maintenance scripts
250
251   %w[nominatim-update
252      nominatim-update-data
253      nominatim-update-refresh-db
254      nominatim-daily-maintenance].each do |fname|
255     template "#{bin_directory}/#{fname}" do
256       source "#{fname}.erb"
257       owner "nominatim"
258       group "nominatim"
259       mode "554"
260       variables :bindir => bin_directory,
261                 :projectdir => project_directory,
262                 :venvprefix => "#{python_directory}/bin/",
263                 :qadatadir => qa_data_directory
264     end
265   end
266
267   systemd_service "nominatim-update" do
268     description "Update the Nominatim database"
269     exec_start "#{bin_directory}/nominatim-update"
270     restart "on-success"
271     standard_output "journal"
272     standard_error "inherit"
273     working_directory project_directory
274   end
275
276   systemd_service "nominatim-update-maintenance-trigger" do
277     description "Trigger daily maintenance tasks for Nominatim DB"
278     exec_start "ln -sf #{bin_directory}/nominatim-daily-maintenance #{bin_directory}/maintenance/"
279     user "nominatim"
280   end
281
282   systemd_timer "nominatim-update-maintenance-trigger" do
283     action node[:nominatim][:state] != "off" ? :create : :delete
284     description "Schedule daily maintenance tasks for Nominatim DB"
285     on_calendar "*-*-* 02:03:00 UTC"
286   end
287
288   service "nominatim-update-maintenance-trigger" do
289     action node[:nominatim][:state] != "off" ? :enable : :disable
290   end
291
292   ## Nominatim UI
293
294   git ui_directory do
295     action :sync
296     repository node[:nominatim][:ui_repository]
297     revision node[:nominatim][:ui_revision]
298     user "nominatim"
299     group "nominatim"
300   end
301
302   template "#{ui_directory}/dist/theme/config.theme.js" do
303     source "ui-config.js.erb"
304     owner "nominatim"
305     group "nominatim"
306     mode "664"
307   end
308
309   ## Nominatim QA
310
311   if node[:nominatim][:enable_qa_tiles]
312     python_package "nominatim-data-analyser" do
313       python_virtualenv python_directory
314       extra_index_url node[:nominatim][:pip_index]
315     end
316
317     directory qa_data_directory do
318       owner "nominatim"
319       group "nominatim"
320       mode "755"
321       recursive true
322     end
323
324     template "#{project_directory}/qa-config.yaml" do
325       source "qa_config.erb"
326       owner "nominatim"
327       group "nominatim"
328       mode "755"
329       variables :outputdir => "#{qa_data_directory}/new"
330     end
331
332     ssl_certificate "qa-tile.nominatim.openstreetmap.org" do
333       domains ["qa-tile.nominatim.openstreetmap.org"]
334       notifies :reload, "service[nginx]"
335     end
336
337     nginx_site "qa-tiles.nominatim" do
338       template "nginx-qa-tiles.erb"
339       directory qa_data_directory
340       variables :qa_data_directory => qa_data_directory
341     end
342   end
343
344   ## Logging and monitoring
345
346   template "/etc/logrotate.d/nominatim" do
347     source "logrotate.nominatim.erb"
348     owner "root"
349     group "root"
350     mode "644"
351   end
352
353   prometheus_exporter "nominatim" do
354     port 8082
355     user "www-data"
356     restrict_address_families "AF_UNIX"
357     options [
358       "--nominatim.query-log=#{node[:nominatim][:logdir]}/query.log",
359       "--nominatim.database-name=#{node[:nominatim][:dbname]}"
360     ]
361   end
362
363 ################################# END OF DEBIAN #############################
364
365 else
366
367   if node[:nominatim][:api_flavour] == "php"
368     include_recipe "php::fpm"
369   end
370
371   basedir = data_bag_item("accounts", "nominatim")["home"]
372   email_errors = data_bag_item("accounts", "lonvia")["email"]
373
374   directory basedir do
375     owner "nominatim"
376     group "nominatim"
377     mode "755"
378     recursive true
379   end
380
381   ## Log directory setup
382
383   directory node[:nominatim][:logdir] do
384     owner "nominatim"
385     group "nominatim"
386     mode "755"
387     recursive true
388   end
389
390   file "#{node[:nominatim][:logdir]}/query.log" do
391     action :create_if_missing
392     owner "www-data"
393     group "adm"
394     mode "664"
395   end
396
397   file "#{node[:nominatim][:logdir]}/update.log" do
398     action :create_if_missing
399     owner "nominatim"
400     group "adm"
401     mode "664"
402   end
403
404   ## Postgresql
405
406   include_recipe "postgresql"
407
408   postgresql_version = node[:nominatim][:dbcluster].split("/").first
409   postgis_version = node[:nominatim][:postgis]
410
411   package "postgresql-#{postgresql_version}-postgis-#{postgis_version}"
412
413   node[:nominatim][:dbadmins].each do |user|
414     postgresql_user user do
415       cluster node[:nominatim][:dbcluster]
416       superuser true
417       only_if { node[:nominatim][:state] != "slave" }
418     end
419   end
420
421   postgresql_user "nominatim" do
422     cluster node[:nominatim][:dbcluster]
423     superuser true
424     only_if { node[:nominatim][:state] != "slave" }
425   end
426
427   postgresql_user "www-data" do
428     cluster node[:nominatim][:dbcluster]
429     only_if { node[:nominatim][:state] != "slave" }
430   end
431
432   directory "#{basedir}/tablespaces" do
433     owner "postgres"
434     group "postgres"
435     mode "700"
436   end
437
438   # NOTE: tablespaces must be exactly in the same location on each
439   #       Nominatim instance when replication is in use. Therefore
440   #       use symlinks to canonical directory locations.
441   node[:nominatim][:tablespaces].each do |name, location|
442     directory location do
443       owner "postgres"
444       group "postgres"
445       mode "700"
446       recursive true
447     end
448
449     link "#{basedir}/tablespaces/#{name}" do
450       to location
451     end
452
453     postgresql_tablespace name do
454       cluster node[:nominatim][:dbcluster]
455       location "#{basedir}/tablespaces/#{name}"
456     end
457   end
458
459   ## Nominatim backend
460
461   include_recipe "git"
462   include_recipe "python"
463
464   python_directory = "#{basedir}/venv"
465
466   package %w[
467     build-essential
468     cmake
469     g++
470     libboost-dev
471     libboost-system-dev
472     libboost-filesystem-dev
473     libexpat1-dev
474     zlib1g-dev
475     libbz2-dev
476     libpq-dev
477     libproj-dev
478     liblua5.3-dev
479     libluajit-5.1-dev
480     libicu-dev
481     nlohmann-json3-dev
482     lua5.3
483     python3-pyosmium
484     python3-psycopg2
485     python3-dotenv
486     python3-psutil
487     python3-jinja2
488     python3-icu
489     python3-datrie
490     python3-yaml
491     python3-sqlalchemy-ext
492     python3-geoalchemy2
493     python3-asyncpg
494     python3-dev
495     pkg-config
496     ruby
497     ruby-file-tail
498     ruby-pg
499     ruby-webrick
500   ]
501
502   if node[:nominatim][:api_flavour] == "php"
503     package %w[
504       php-pgsql
505       php-intl
506     ]
507   elsif node[:nominatim][:api_flavour] == "python"
508
509     python_virtualenv python_directory do
510       interpreter "/usr/bin/python3"
511     end
512
513     python_package "SQLAlchemy" do
514       python_virtualenv python_directory
515       version "2.0.32"
516     end
517
518     python_package "PyICU" do
519       python_virtualenv python_directory
520       version "2.13.1"
521     end
522
523     python_package "psycopg[binary]" do
524       python_virtualenv python_directory
525       version "3.2.1"
526     end
527
528     python_package "psycopg2-binary" do
529       python_virtualenv python_directory
530       version "2.9.9"
531     end
532
533     python_package "python-dotenv" do
534       python_virtualenv python_directory
535       version "1.0.1"
536     end
537
538     python_package "pygments" do
539       python_virtualenv python_directory
540       version "2.18.0"
541     end
542
543     python_package "PyYAML" do
544       python_virtualenv python_directory
545       version "6.0.2"
546     end
547
548     python_package "falcon" do
549       python_virtualenv python_directory
550       version "3.1.3"
551     end
552
553     python_package "uvicorn" do
554       python_virtualenv python_directory
555       version "0.30.5"
556     end
557
558     python_package "gunicorn" do
559       python_virtualenv python_directory
560       version "22.0.0"
561     end
562
563     python_package "jinja2" do
564       python_virtualenv python_directory
565       version "3.1.4"
566     end
567
568     python_package "datrie" do
569       python_virtualenv python_directory
570       version "0.8.2"
571     end
572
573     python_package "psutil" do
574       python_virtualenv python_directory
575       version "6.0.0"
576     end
577
578     python_package "osmium" do
579       python_virtualenv python_directory
580       version "3.7.0"
581     end
582   end
583
584   source_directory = "#{basedir}/src/nominatim"
585   build_directory = "#{basedir}/src/build"
586   project_directory = "#{basedir}/planet-project"
587   bin_directory = "#{basedir}/bin"
588   cfg_directory = "#{basedir}/etc"
589   ui_directory = "#{basedir}/ui"
590   qa_bin_directory = "#{basedir}/src/Nominatim-Data-Analyser"
591   qa_data_directory = "#{basedir}/qa-data"
592
593   [basedir, "#{basedir}/src", cfg_directory, bin_directory, build_directory, project_directory].each do |path|
594     directory path do
595       owner "nominatim"
596       group "nominatim"
597       mode "755"
598       recursive true
599     end
600   end
601
602   directory "#{bin_directory}/maintenance" do
603     owner "nominatim"
604     group "nominatim"
605     mode "775"
606   end
607
608   if node[:nominatim][:flatnode_file]
609     directory File.dirname(node[:nominatim][:flatnode_file]) do
610       recursive true
611     end
612   end
613
614   remote_directory "#{project_directory}/static-website" do
615     source "website"
616     owner "nominatim"
617     group "nominatim"
618     mode "755"
619     files_owner "nominatim"
620     files_group "nominatim"
621     files_mode "644"
622     purge false
623   end
624
625   # Normally syncing via chef is a bad idea because syncing might involve
626   # an update of database functions which should not be done while an update
627   # is ongoing. Therefore we sync in between update cycles. There is an
628   # exception for slaves: they get DB function updates from the master, so
629   # only the source code needs to be updated, which chef may do.
630   git source_directory do
631     action node[:nominatim][:state] == "slave" ? :sync : :checkout
632     repository node[:nominatim][:repository]
633     revision node[:nominatim][:revision]
634     enable_submodules true
635     user "nominatim"
636     group "nominatim"
637     not_if { node[:nominatim][:state] != "slave" && File.exist?("#{source_directory}/README.md") }
638     notifies :run, "execute[compile_nominatim]"
639   end
640
641   remote_file "#{source_directory}/data/country_osm_grid.sql.gz" do
642     action :create_if_missing
643     source "https://nominatim.org/data/country_grid.sql.gz"
644     owner "nominatim"
645     group "nominatim"
646     mode "644"
647   end
648
649   execute "compile_nominatim" do
650     action :nothing
651     user "nominatim"
652     cwd build_directory
653     command "cmake #{source_directory} && make"
654     notifies :run, "execute[install_nominatim]"
655   end
656
657   execute "install_nominatim" do
658     action :nothing
659     cwd build_directory
660     command "make install"
661   end
662
663   # Project directory
664
665   template "#{project_directory}/.env" do
666     source "nominatim.env.erb"
667     owner "nominatim"
668     group "nominatim"
669     mode "664"
670     variables :base_url => node[:nominatim][:state] == "off" ? node[:fqdn] : "nominatim.openstreetmap.org",
671               :dbname => node[:nominatim][:dbname],
672               :flatnode_file => node[:nominatim][:flatnode_file],
673               :log_file => "#{node[:nominatim][:logdir]}/query.log",
674               :tokenizer => node[:nominatim][:config][:tokenizer],
675               :forward_dependencies => node[:nominatim][:config][:forward_dependencies],
676               :pool_size => node[:nominatim][:api_pool_size],
677               :query_timeout => node[:nominatim][:api_query_timeout],
678               :request_timeout => node[:nominatim][:api_request_timeout]
679   end
680
681   remote_file "#{project_directory}/secondary_importance.sql.gz" do
682     action :create_if_missing
683     source "https://nominatim.org/data/wikimedia-secondary-importance.sql.gz"
684     owner "nominatim"
685     group "nominatim"
686     mode "644"
687   end
688
689   remote_file "#{project_directory}/wikimedia-importance.sql.gz" do
690     action :create_if_missing
691     source "https://nominatim.org/data/wikimedia-importance.sql.gz"
692     owner "nominatim"
693     group "nominatim"
694     mode "644"
695   end
696
697   %w[gb_postcodes.csv.gz us_postcodes.csv.gz].each do |fname|
698     remote_file "#{project_directory}/#{fname}" do
699       action :create
700       source "https://nominatim.org/data/#{fname}"
701       owner "nominatim"
702       group "nominatim"
703       mode "644"
704     end
705   end
706
707   # Webserver + frontend
708
709   %w[user_agent referrer email generic].each do |name|
710     file "#{cfg_directory}/nginx_blocked_#{name}.conf" do
711       action :create_if_missing
712       owner "nominatim"
713       group "adm"
714       mode "664"
715     end
716   end
717
718   if node[:nominatim][:api_flavour] == "php"
719     node[:nominatim][:fpm_pools].each do |name, data|
720       php_fpm name do
721         port data[:port]
722         pm data[:pm]
723         pm_max_children data[:max_children]
724         pm_start_servers 20
725         pm_min_spare_servers 10
726         pm_max_spare_servers 20
727         pm_max_requests 10000
728         prometheus_port data[:prometheus_port]
729       end
730     end
731   elsif node[:nominatim][:api_flavour] == "python"
732     systemd_service "nominatim" do
733       description "Nominatim running as a gunicorn application"
734       user "www-data"
735       group "www-data"
736       working_directory project_directory
737       standard_output "append:#{node[:nominatim][:logdir]}/gunicorn.log"
738       standard_error "inherit"
739       exec_start "#{python_directory}/bin/gunicorn --max-requests 200000 -b unix:/run/gunicorn-nominatim.openstreetmap.org.sock -w #{node[:nominatim][:api_workers]} -k uvicorn.workers.UvicornWorker nominatim_api.server.falcon.server:run_wsgi"
740       exec_reload "/bin/kill -s HUP $MAINPID"
741       environment :PYTHONPATH => "/usr/local/lib/nominatim/lib-python/"
742       kill_mode "mixed"
743       timeout_stop_sec 5
744       private_tmp true
745       requires "nominatim.socket"
746       after "network.target"
747     end
748
749     systemd_socket "nominatim" do
750       description "Gunicorn socket for Nominatim"
751       listen_stream "/run/gunicorn-nominatim.openstreetmap.org.sock"
752       socket_user "www-data"
753     end
754   end
755
756   ssl_certificate node[:fqdn] do
757     domains [node[:fqdn],
758              "nominatim.openstreetmap.org",
759              "nominatim.osm.org",
760              "nominatim.openstreetmap.com",
761              "nominatim.openstreetmap.net",
762              "nominatim.openstreetmaps.org",
763              "nominatim.openmaps.org",
764              "nominatim.qgis.org"]
765     notifies :reload, "service[nginx]"
766   end
767
768   include_recipe "nginx"
769
770   nginx_site "default" do
771     action [:delete]
772   end
773
774   frontends = search(:node, "recipes:web\\:\\:frontend").sort_by(&:name)
775
776   nginx_site "nominatim" do
777     template "nginx.erb"
778     directory project_directory
779     variables :pools => node[:nominatim][:fpm_pools],
780               :frontends => frontends,
781               :confdir => "#{basedir}/etc",
782               :ui_directory => ui_directory
783   end
784
785   template "/etc/logrotate.d/nginx" do
786     source "logrotate.nginx.erb"
787     owner "root"
788     group "root"
789     mode "644"
790   end
791
792   # Updates
793
794   %w[nominatim-update
795      nominatim-update-source
796      nominatim-update-refresh-db
797      nominatim-update-data
798      nominatim-daily-maintenance].each do |fname|
799     template "#{bin_directory}/#{fname}" do
800       source "#{fname}.erb"
801       owner "nominatim"
802       group "nominatim"
803       mode "554"
804       variables :bindir => bin_directory,
805                 :srcdir => source_directory,
806                 :builddir => build_directory,
807                 :projectdir => project_directory,
808                 :qabindir => qa_bin_directory,
809                 :qadatadir => qa_data_directory
810     end
811   end
812
813   systemd_service "nominatim-update" do
814     description "Update the Nominatim database"
815     exec_start "#{bin_directory}/nominatim-update"
816     restart "on-success"
817     standard_output "append:#{node[:nominatim][:logdir]}/update.log"
818     standard_error "inherit"
819     working_directory project_directory
820   end
821
822   systemd_service "nominatim-update-maintenance-trigger" do
823     description "Trigger daily maintenance tasks for Nominatim DB"
824     exec_start "ln -sf #{bin_directory}/nominatim-daily-maintenance #{bin_directory}/maintenance/"
825     user "nominatim"
826   end
827
828   systemd_timer "nominatim-update-maintenance-trigger" do
829     action node[:nominatim][:state] != "off" ? :create : :delete
830     description "Schedule daily maintenance tasks for Nominatim DB"
831     on_calendar "*-*-* 02:03:00 UTC"
832   end
833
834   service "nominatim-update-maintenance-trigger" do
835     action node[:nominatim][:state] != "off" ? :enable : :disable
836   end
837
838   # Nominatim UI
839
840   git ui_directory do
841     action :sync
842     repository node[:nominatim][:ui_repository]
843     revision node[:nominatim][:ui_revision]
844     user "nominatim"
845     group "nominatim"
846   end
847
848   template "#{ui_directory}/dist/theme/config.theme.js" do
849     source "ui-config.js.erb"
850     owner "nominatim"
851     group "nominatim"
852     mode "664"
853   end
854
855   # Nominatim QA
856
857   if node[:nominatim][:enable_qa_tiles]
858     package "python3-geojson"
859
860     git qa_bin_directory do
861       repository node[:nominatim][:qa_repository]
862       revision node[:nominatim][:qa_revision]
863       enable_submodules true
864       user "nominatim"
865       group "nominatim"
866       notifies :run, "execute[compile_qa]"
867     end
868
869     execute "compile_qa" do
870       action :nothing
871       user "nominatim"
872       cwd "#{qa_bin_directory}/clustering-vt"
873       command "make"
874     end
875
876     directory qa_data_directory do
877       owner "nominatim"
878       group "nominatim"
879       mode "755"
880       recursive true
881     end
882
883     template "#{qa_bin_directory}/analyser/config/config.yaml" do
884       source "qa_config.erb"
885       owner "nominatim"
886       group "nominatim"
887       mode "755"
888       variables :outputdir => "#{qa_data_directory}/new"
889     end
890
891     ssl_certificate "qa-tile.nominatim.openstreetmap.org" do
892       domains ["qa-tile.nominatim.openstreetmap.org"]
893       notifies :reload, "service[nginx]"
894     end
895
896     nginx_site "qa-tiles.nominatim" do
897       template "nginx-qa-tiles.erb"
898       directory build_directory
899       variables :qa_data_directory => qa_data_directory
900     end
901
902   end
903
904   # Replication
905
906   cron_d "nominatim-clean-db" do
907     action node[:nominatim][:state] == "master" ? :create : :delete
908     minute "5"
909     hour "*/4"
910     user "postgres"
911     command "#{bin_directory}/clean-db-nominatim"
912     mailto email_errors
913   end
914
915   if node[:nominatim][:state] == "master"
916     postgresql_user "replication" do
917       cluster node[:nominatim][:dbcluster]
918       password data_bag_item("nominatim", "passwords")["replication"]
919       replication true
920     end
921
922     directory node[:rsyncd][:modules][:archive][:path] do
923       owner "postgres"
924       group "postgres"
925       mode "700"
926     end
927
928     template "#{bin_directory}/clean-db-nominatim" do
929       source "clean-db-nominatim.erb"
930       owner "nominatim"
931       group "nominatim"
932       mode "755"
933       variables :archive_dir => node[:rsyncd][:modules][:archive][:path],
934                 :update_stop_file => "#{basedir}/status/updates_disabled",
935                 :streaming_clients => search(:node, "nominatim_state:slave").map { |slave| slave[:fqdn] }.join(" ")
936     end
937   end
938
939   # Maintenance
940
941   cron_d "nominatim-backup" do
942     action (node[:nominatim][:enable_backup] && node[:nominatim][:state] != "off") ? :create : :delete
943     minute "0"
944     hour "3"
945     day "1"
946     user "nominatim"
947     command "#{bin_directory}/backup-nominatim"
948     mailto email_errors
949   end
950
951   cron_d "nominatim-vacuum-db" do
952     action node[:nominatim][:state] != "off" ? :create : :delete
953     minute "20"
954     hour "0"
955     user "postgres"
956     command "#{bin_directory}/vacuum-db-nominatim"
957     mailto email_errors
958   end
959
960   %w[backup-nominatim vacuum-db-nominatim].each do |fname|
961     template "#{bin_directory}/#{fname}" do
962       source "#{fname}.erb"
963       owner "nominatim"
964       group "nominatim"
965       mode "755"
966       variables :db => node[:nominatim][:dbname]
967     end
968   end
969
970   # Logging
971
972   template "/etc/logrotate.d/nominatim" do
973     source "logrotate.nominatim.erb"
974     owner "root"
975     group "root"
976     mode "644"
977   end
978
979   # Monitoring
980   prometheus_exporter "nominatim" do
981     port 8082
982     user "www-data"
983     restrict_address_families "AF_UNIX"
984     options [
985       "--nominatim.query-log=#{node[:nominatim][:logdir]}/query.log",
986       "--nominatim.database-name=#{node[:nominatim][:dbname]}"
987     ]
988   end
989
990   include_recipe "fail2ban"
991 end # platform?('debian')
992
993 frontend_addresses = frontends.collect { |f| f.ipaddresses(:role => :external) }
994
995 fail2ban_jail "nominatim_limit_req" do
996   filter "nginx-limit-req"
997   logpath "#{node[:nominatim][:logdir]}/nominatim.openstreetmap.org-error.log"
998   ports [80, 443]
999   maxretry 20
1000   ignoreips frontend_addresses.flatten.sort
1001 end