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