From: Tom Hughes Date: Mon, 17 Jun 2013 22:08:37 +0000 (+0100) Subject: Add tile cookbook X-Git-Url: https://git.openstreetmap.org/chef.git/commitdiff_plain/8e9e103e9f748fe7a104c4fba00cc72a847e2d3a?hp=752d776b04ca8835b943efd4aabc2e60f2e1c3cd Add tile cookbook --- diff --git a/cookbooks/tile/README.rdoc b/cookbooks/tile/README.rdoc new file mode 100644 index 000000000..3de2ec7a3 --- /dev/null +++ b/cookbooks/tile/README.rdoc @@ -0,0 +1,8 @@ += DESCRIPTION: + += REQUIREMENTS: + += ATTRIBUTES: + += USAGE: + diff --git a/cookbooks/tile/attributes/default.rb b/cookbooks/tile/attributes/default.rb new file mode 100644 index 000000000..79d55d10a --- /dev/null +++ b/cookbooks/tile/attributes/default.rb @@ -0,0 +1,3 @@ +default[:tile][:data] = {} +default[:tile][:styles] = {} +default[:tile][:tile_directory] = "/srv/tile.openstreetmap.org/tiles" diff --git a/cookbooks/tile/files/default/html/abuse.png b/cookbooks/tile/files/default/html/abuse.png new file mode 100644 index 000000000..f9f7910d3 Binary files /dev/null and b/cookbooks/tile/files/default/html/abuse.png differ diff --git a/cookbooks/tile/files/default/html/abuse2.png b/cookbooks/tile/files/default/html/abuse2.png new file mode 100644 index 000000000..ba40a46a0 Binary files /dev/null and b/cookbooks/tile/files/default/html/abuse2.png differ diff --git a/cookbooks/tile/files/default/html/abuse3.png b/cookbooks/tile/files/default/html/abuse3.png new file mode 100644 index 000000000..b297ae727 Binary files /dev/null and b/cookbooks/tile/files/default/html/abuse3.png differ diff --git a/cookbooks/tile/files/default/html/clientaccesspolicy.xml b/cookbooks/tile/files/default/html/clientaccesspolicy.xml new file mode 100644 index 000000000..d60ec1d3f --- /dev/null +++ b/cookbooks/tile/files/default/html/clientaccesspolicy.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/cookbooks/tile/files/default/html/favicon.ico b/cookbooks/tile/files/default/html/favicon.ico new file mode 100644 index 000000000..27b042b5c Binary files /dev/null and b/cookbooks/tile/files/default/html/favicon.ico differ diff --git a/cookbooks/tile/files/default/html/update-url-tile.png b/cookbooks/tile/files/default/html/update-url-tile.png new file mode 100644 index 000000000..3456d30ce Binary files /dev/null and b/cookbooks/tile/files/default/html/update-url-tile.png differ diff --git a/cookbooks/tile/files/default/ruby/expire.rb b/cookbooks/tile/files/default/ruby/expire.rb new file mode 100755 index 000000000..1c2993b71 --- /dev/null +++ b/cookbooks/tile/files/default/ruby/expire.rb @@ -0,0 +1,163 @@ +#!/usr/bin/ruby + +require 'rubygems' +require 'proj4' +require 'xml/libxml' +require 'set' +require 'pg' +require 'time' + +module Expire + # projection object to go from latlon -> spherical mercator + PROJ = Proj4::Projection.new(["+proj=merc", "+a=6378137", "+b=6378137", + "+lat_ts=0.0", "+lon_0=0.0", "+x_0=0.0", + "+y_0=0", "+k=1.0", "+units=m", + "+nadgrids=@null", "+no_defs +over"]) + + # width/height of the spherical mercator projection + SIZE=40075016.6855784 + # the size of the meta tile blocks + METATILE = 8 + # the directory root for meta tiles + HASH_ROOT = "/tiles/default/" + # lowest zoom that we want to expire + # MIN_ZOOM=12 + MIN_ZOOM=13 + # highest zoom that we want to expire + MAX_ZOOM=18 + # database parameters + DBNAME="gis" + DBHOST="" + #DBPORT=5432 + DBPORT=5432 + DBTABLE="planet_osm_nodes" + + # turns a spherical mercator coord into a tile coord + def Expire.tile_from_merc(point, zoom) + # renormalise into unit space [0,1] + point.x = 0.5 + point.x / SIZE + point.y = 0.5 - point.y / SIZE + # transform into tile space + point.x = point.x * 2 ** zoom + point.y = point.y * 2 ** zoom + # chop of the fractional parts + [point.x.to_int, point.y.to_int, zoom] + end + + # turns a latlon -> tile x,y given a zoom level + def Expire.tile_from_latlon(latlon, zoom) + # first convert to spherical mercator + point = PROJ.forward(latlon) + tile_from_merc(point, zoom) + end + + # this must match the definition of xyz_to_meta in mod_tile + def Expire.xyz_to_meta(root, x, y, z) + # mask off the final few bits + x &= ~(METATILE - 1) + y &= ~(METATILE - 1) + # generate the path + hash_path = (0..4).collect { |i| + (((x >> 4*i) & 0xf) << 4) | ((y >> 4*i) & 0xf) + }.reverse.join('/') + root + '/' + z.to_s + '/' + hash_path + ".meta" + end + + # time to reset to, some very stupidly early time, before OSM started + EXPIRY_TIME = Time.parse("2000-01-01 00:00:00") + + # expire the meta tile by setting the modified time back + def Expire.expire_meta(meta) + puts "Expiring #{meta}" + File.utime(EXPIRY_TIME, EXPIRY_TIME, meta) + end + + def Expire.expire(change_file) + do_expire(change_file) do |set| + new_set = Set.new + meta_set = Set.new + + # turn all the tiles into expires, putting them in the set + # so that we don't expire things multiple times + set.each do |xy| + # this has to match the routine in mod_tile + meta = xyz_to_meta(HASH_ROOT, xy[0], xy[1], xy[2]) + + meta_set.add(meta) if File.exist? meta + + # add the parent into the set for the next round + new_set.add([xy[0] / 2, xy[1] / 2, xy[2] - 1]) + end + + # expire all meta tiles + meta_set.each do |meta| + expire_meta(meta) + end + + # return the new set, consisting of all the parents + new_set + end + end + + def Expire.do_expire(change_file, &block) + # read in the osm change file + doc = XML::Document.file(change_file) + + # hash map to contain all the nodes + nodes = Hash.new + + # we put all the nodes into the hash, as it doesn't matter whether the node was + # added, deleted or modified - the tile will need updating anyway. + doc.find('//node').each do |node| + lat = node['lat'].to_f + if lat < -85 + lat = -85 + end + if lat > 85 + lat = 85 + end + point = Proj4::Point.new(Math::PI * node['lon'].to_f / 180, + Math::PI * lat / 180) + nodes[node['id'].to_i] = tile_from_latlon(point, MAX_ZOOM) + end + + # now we look for all the ways that have changed and put all of their nodes into + # the hash too. this will add too many nodes, as it is possible a long way will be + # changed at only a portion of its length. however, due to the non-local way that + # mapnik does text placement, it may stil not be enough. + # + # also, we miss cases where nodes are deleted from ways where that node is not + # itself deleted and the coverage of the point set isn't enough to encompass the + # change. + conn = PG::Connection.new(:host => DBHOST, :port => DBPORT, :dbname => DBNAME) + doc.find('//way/nd').each do |node| + node_id = node['ref'].to_i + unless nodes.include? node_id + # this is a node referenced but not added, modified or deleted, so it should + # still be in the postgis DB. + res = conn.query("select lon, lat from #{DBTABLE} where id=#{node_id};") + + # loop over results, adding tiles to the change set + res.each do |row| + point = Proj4::Point.new(row[0].to_f / 100.0, row[1].to_f / 100.0) + nodes[node_id] = tile_from_merc(point, MAX_ZOOM) + end + + # Discard results + res.clear + end + end + + # create a set of all the tiles at the maximum zoom level which are touched by + # any of the nodes we've collected. we'll create the tiles at other zoom levels + # by a simple recursion. + set = Set.new nodes.values + + # expire tiles and shrink to the set of parents + (MAX_ZOOM).downto(MIN_ZOOM) do |z| + # allow the block to work on the set, returning the set at the next + # zoom level + set = yield set + end + end +end diff --git a/cookbooks/tile/metadata.rb b/cookbooks/tile/metadata.rb new file mode 100644 index 000000000..6d261cf0d --- /dev/null +++ b/cookbooks/tile/metadata.rb @@ -0,0 +1,10 @@ +maintainer "OpenStreetMap Administrators" +maintainer_email "admins@openstreetmap.org" +license "Apache 2.0" +description "Installs and configures tile servers" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "1.0.0" +depends "apache" +depends "git" +depends "nodejs" +depends "postgresql" diff --git a/cookbooks/tile/recipes/default.rb b/cookbooks/tile/recipes/default.rb new file mode 100644 index 000000000..d60d52c2c --- /dev/null +++ b/cookbooks/tile/recipes/default.rb @@ -0,0 +1,385 @@ +# +# Cookbook Name:: tile +# Recipe:: default +# +# Copyright 2013, OpenStreetMap Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache" +include_recipe "git" +include_recipe "nodejs" +include_recipe "postgresql" + +blocks = data_bag_item("tile", "blocks") + +apache_module "alias" +apache_module "expires" +apache_module "headers" +apache_module "remoteip" +apache_module "rewrite" + +apache_module "tile" do + conf "tile.conf.erb" +end + +tilecaches = search(:node, "roles:tilecache") + +apache_site "default" do + action [ :disable ] +end + +apache_site "tile.openstreetmap.org" do + template "apache.erb" + variables :caches => tilecaches +end + +template "/etc/logrotate.d/apache2" do + source "logrotate.apache.erb" + owner "root" + group "root" + mode 0644 +end + +directory "/srv/tile.openstreetmap.org" do + owner "tile" + group "tile" + mode 0755 +end + +package "renderd" + +service "renderd" do + action [ :enable, :start ] + supports :status => false, :restart => true, :reload => false +end + +directory node[:tile][:tile_directory] do + owner "tile" + group "www-data" + mode 0775 +end + +if node[:tile][:tile_directory] != "/srv/tile.openstreetmap.org/tiles" + link "/srv/tile.openstreetmap.org/tiles" do + to node[:tile][:tile_directory] + end +end + +template "/etc/renderd.conf" do + source "renderd.conf.erb" + owner "root" + group "root" + mode 0644 + notifies :reload, resources(:service => "apache2") + notifies :restart, resources(:service => "renderd") +end + +remote_directory "/srv/tile.openstreetmap.org/html" do + source "html" + owner "tile" + group "tile" + mode 0755 + files_owner "tile" + files_group "tile" + files_mode 0644 +end + +directory "/srv/tile.openstreetmap.org/cgi-bin" do + owner "tile" + group "tile" + mode 0755 +end + +template "/srv/tile.openstreetmap.org/cgi-bin/export" do + source "export.erb" + owner "tile" + group "tile" + mode 0755 + variables :blocks => blocks +end + +directory "/srv/tile.openstreetmap.org/data" do + owner "tile" + group "tile" + mode 0755 +end + +node[:tile][:data].each do |name,data| + url = data[:url] + file = "/srv/tile.openstreetmap.org/data/#{File.basename(url)}" + directory = "/srv/tile.openstreetmap.org/data/#{data[:directory]}" + + directory directory do + owner "tile" + group "tile" + mode 0755 + end + + if file =~ /\.tgz$/ + package "tar" + + execute file do + action :nothing + command "tar -zxf #{file} -C #{directory}" + user "tile" + group "tile" + end + elsif file =~ /\.tar\.bz2$/ + package "tar" + + execute file do + action :nothing + command "tar -jxf #{file} -C #{directory}" + user "tile" + group "tile" + end + elsif file =~ /\.zip$/ + package "unzip" + + execute file do + action :nothing + command "unzip -qq #{file} -d #{directory}" + user "tile" + group "tile" + end + end + + if data[:processed] + original = "#{directory}/#{data[:original]}" + processed = "#{directory}/#{data[:processed]}" + + package "gdal-bin" + + execute processed do + action :nothing + command "ogr2ogr #{processed} #{original}" + user "tile" + group "tile" + subscribes :run, resources(:execute => file), :immediately + end + end + + remote_file file do + action :create_if_missing + source url + owner "tile" + group "tile" + mode 0644 + notifies :run, resources(:execute => file), :immediately + notifies :restart, resources(:service => "renderd") + end +end + +nodejs_package "carto" +nodejs_package "millstone" + +directory "/srv/tile.openstreetmap.org/styles" do + owner "tile" + group "tile" + mode 0755 +end + +node[:tile][:styles].each do |name,details| + directory = "/srv/tile.openstreetmap.org/styles/#{name}" + + git directory do + action :sync + repository details[:repository] + revision details[:revision] + user "tile" + group "tile" + end + + link "#{directory}/data" do + to "/srv/tile.openstreetmap.org/data" + owner "tile" + group "tile" + end + + execute "#{directory}/project.mml" do + command "carto project.mml > project.xml" + cwd directory + user "tile" + group "tile" + not_if do + File.exist?("#{directory}/project.xml") and + File.mtime("#{directory}/project.xml") >= File.mtime("#{directory}/project.mml") + end + notifies :restart, resources(:service => "renderd") + end +end + +package "postgis" + +postgresql_user "jburgess" do + cluster node[:tile][:database][:cluster] + superuser true +end + +postgresql_user "tomh" do + cluster node[:tile][:database][:cluster] + superuser true +end + +postgresql_user "tile" do + cluster node[:tile][:database][:cluster] +end + +postgresql_user "www-data" do + cluster node[:tile][:database][:cluster] +end + +postgresql_database "gis" do + cluster node[:tile][:database][:cluster] + owner "tile" +end + +postgresql_extension "postgis" do + cluster node[:tile][:database][:cluster] + database "gis" +end + +[ "geography_columns", + "planet_osm_nodes", + "planet_osm_rels", + "planet_osm_ways", + "raster_columns", + "raster_overviews", + "spatial_ref_sys" ].each do |table| + postgresql_table table do + cluster node[:tile][:database][:cluster] + database "gis" + owner "tile" + permissions "tile" => :all + end +end + +[ "geometry_columns", + "planet_osm_line", + "planet_osm_point", + "planet_osm_polygon", + "planet_osm_roads" ].each do |table| + postgresql_table table do + cluster node[:tile][:database][:cluster] + database "gis" + owner "tile" + permissions "tile" => :all, "www-data" => :select + end +end + +postgresql_munin "gis" do + cluster node[:tile][:database][:cluster] + database "gis" +end + +#if node[:tile][:node_file] +# file node[:tile][:node_file] do +# owner "tile" +# group "tile" +# mode 0664 +# end +#end + +package "osm2pgsql" +package "osmosis" + +package "ruby" +package "rubygems" + +package "libproj-dev" +package "libxml2-dev" +package "libpq-dev" + +gem_package "proj4rb" +gem_package "libxml-ruby" +gem_package "pg" + +remote_directory "/usr/local/lib/site_ruby" do + source "ruby" + owner "root" + group "root" + mode 0755 + files_owner "root" + files_group "root" + files_mode 0644 +end + +template "/usr/local/bin/expire-tiles" do + source "expire-tiles.erb" + owner "root" + group "root" + mode 0755 +end + +directory "/var/lib/replicate" do + owner "tile" + group "tile" + mode 0755 +end + +directory "/var/log/replicate" do + owner "tile" + group "tile" + mode 0755 +end + +template "/var/lib/replicate/configuration.txt" do + source "replicate.configuration.erb" + owner "tile" + group "tile" + mode 0644 +end + +template "/usr/local/bin/replicate" do + source "replicate.erb" + owner "root" + group "root" + mode 0755 +end + +template "/etc/init.d/replicate" do + source "replicate.init.erb" + owner "root" + group "root" + mode 0755 +end + +service "replicate" do + action [ :enable, :start ] + supports :restart => true + subscribes :restart, resources(:template => "/usr/local/bin/replicate") + subscribes :restart, resources(:template => "/etc/init.d/replicate") +end + +template "/etc/logrotate.d/replicate" do + source "replicate.logrotate.erb" + owner "root" + group "root" + mode 0644 +end + +munin_plugin "mod_tile_fresh" +munin_plugin "mod_tile_response" +munin_plugin "mod_tile_zoom" + +munin_plugin "renderd_processed" +munin_plugin "renderd_queue" +munin_plugin "renderd_zoom" +munin_plugin "renderd_zoom_time" + +munin_plugin "replication_delay" do + conf "munin.erb" +end + diff --git a/cookbooks/tile/templates/default/apache.erb b/cookbooks/tile/templates/default/apache.erb new file mode 100644 index 000000000..024c664be --- /dev/null +++ b/cookbooks/tile/templates/default/apache.erb @@ -0,0 +1,46 @@ +# DO NOT EDIT - This file is being maintained by Chef + + + # Basic server configuration + ServerName <%= node[:fqdn] %> + ServerAlias tile.openstreetmap.org + ServerAlias parent.tile.openstreetmap.org + ServerAdmin webmaster@openstreetmap.org + + # Configure location of static files and CGI scripts + DocumentRoot /srv/tile.openstreetmap.org/html + ScriptAlias /cgi-bin/ /srv/tile.openstreetmap.org/cgi-bin/ + + # Get the real remote IP for requests via a trusted proxy + RemoteIPHeader X-Forwarded-For +<% @caches.each do |cache| -%> +<% cache.ipaddresses(:role => :external) do |address| -%> + RemoteIPTrustedProxy <%= address %> +<% end -%> +<% end -%> + + # Setup logging + CustomLog /var/log/apache2/access.log combined + ErrorLog /var/log/apache2/error.log + BufferedLogs on + + # Enable the rewrite engine + RewriteEngine on + + # Rewrite tile requests to the default style + RewriteRule ^/(-?\d+)/(-?\d+)/(-?\d+)\.png$ /default/$1/$2/$3.png [PT,T=image/png,L] + + + + Options None + AllowOverride None + Order allow,deny + Allow from all + + + + Options ExecCGI + AllowOverride None + Order allow,deny + Allow from all + diff --git a/cookbooks/tile/templates/default/expire-tiles.erb b/cookbooks/tile/templates/default/expire-tiles.erb new file mode 100644 index 000000000..e9473a1a7 --- /dev/null +++ b/cookbooks/tile/templates/default/expire-tiles.erb @@ -0,0 +1,9 @@ +#!/usr/bin/ruby + +# DO NOT EDIT - This file is being maintained by Chef + +require 'expire' + +ARGV.each do |f| + Expire::expire(f) +end diff --git a/cookbooks/tile/templates/default/export.erb b/cookbooks/tile/templates/default/export.erb new file mode 100755 index 000000000..8dd209ba6 --- /dev/null +++ b/cookbooks/tile/templates/default/export.erb @@ -0,0 +1,150 @@ +#!/usr/bin/python -u +# -*- coding: utf-8 -*- + +import cairo +import cgi +import mapnik2 +import os +import shutil +import sys +import tempfile +import resource + +# Limit maximum CPU time +# The Postscript output format can sometimes take hours +resource.setrlimit(resource.RLIMIT_CPU,(180,180)) + +# Limit memory usage +# Some odd requests can cause extreme memory usage +resource.setrlimit(resource.RLIMIT_AS,(4000000000, 4000000000)) + +# Routine to output HTTP headers +def output_headers(content_type, filename = "", length = 0): + print "Content-Type: %s" % content_type + if filename: + print "Content-Disposition: attachment; filename=\"%s\"" % filename + if length: + print "Content-Length: %d" % length + print "" + +# Routine to output the contents of a file +def output_file(file): + file.seek(0) + shutil.copyfileobj(file, sys.stdout) + +# Routine to get the size of a file +def file_size(file): + return os.fstat(file.fileno()).st_size + +# Routine to report an error +def output_error(message): + output_headers("text/html") + print "" + print "" + print "Error" + print "" + print "" + print "

Error

" + print "

%s

" % message + print "" + print "" + +# Parse CGI parameters +form = cgi.FieldStorage() + +# Make sure we have a user agent +if not os.environ.has_key('HTTP_USER_AGENT'): + os.environ['HTTP_USER_AGENT'] = 'NONE' + +# Get the load average +loadavg = float(open("/proc/loadavg").readline().split(" ")[0]) + +# Process the request +if loadavg > 35.0: + # Abort if the load average on the machine is too high + print "Status: 503 Service Unavailable" + output_error("The load average on the server is too high at the moment. Please wait a few minutes before trying again.") +<% @blocks["user_agents"].each do |user_agent| -%> +elif os.environ['HTTP_USER_AGENT'] == '<%= user_agent %>': + # Block scraper + print "Status: 503 Service Unavailable" + output_error("The load average on the server is too high at the moment. Please wait a few minutes before trying again.") +<% end -%> +elif not form.has_key("bbox"): + # No bounding box specified + output_error("No bounding box specified") +elif not form.has_key("scale"): + # No scale specified + output_error("No scale specified") +elif not form.has_key("format"): + # No format specified + output_error("No format specified") +else: + # Create projection object + prj = mapnik2.Projection("+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs +over"); + + # Get the bounds of the area to render + bbox = [float(x) for x in form.getvalue("bbox").split(",")] + + if bbox[0] >= bbox[2] or bbox[1] >= bbox[3]: + # Bogus bounding box + output_error("Invalid bounding box") + else: + # Project the bounds to the map projection + bbox = mapnik2.forward_(mapnik2.Box2d(*bbox), prj) + + # Calculate the size of the final rendered image + scale = float(form.getvalue("scale")) + width = int(bbox.width() / scale / 0.00028) + height = int(bbox.height() / scale / 0.00028) + + # Limit the size of map we are prepared to produce + if width * height > 4000000: + # Map is too large (limit is approximately A2 size) + output_error("Map too large") + else: + # Create map + map = mapnik2.Map(width, height) + + # Load map configuration + mapnik2.load_map(map, "/home/jburgess/live/osm2.xml") + + # Zoom the map to the bounding box + map.zoom_to_box(bbox) + + # Render the map + if form.getvalue("format") == "png": + image = mapnik2.Image(map.width, map.height) + mapnik2.render(map, image) + png = image.tostring("png") + output_headers("image/png", "map.png", len(png)) + sys.stdout.write(png) + elif form.getvalue("format") == "jpeg": + image = mapnik2.Image(map.width, map.height) + mapnik2.render(map, image) + jpeg = image.tostring("jpeg") + output_headers("image/jpeg", "map.jpg", len(jpeg)) + sys.stdout.write(jpeg) + elif form.getvalue("format") == "svg": + file = tempfile.NamedTemporaryFile() + surface = cairo.SVGSurface(file.name, map.width, map.height) + mapnik2.render(map, surface) + surface.finish() + output_headers("image/svg+xml", "map.svg", file_size(file)) + output_file(file) + elif form.getvalue("format") == "pdf": + file = tempfile.NamedTemporaryFile() + surface = cairo.PDFSurface(file.name, map.width, map.height) + mapnik2.render(map, surface) + surface.finish() + output_headers("application/pdf", "map.pdf", file_size(file)) + output_file(file) + elif form.getvalue("format") == "ps": + file = tempfile.NamedTemporaryFile() + surface = cairo.PSSurface(file.name, map.width, map.height) + mapnik2.render(map, surface) + surface.finish() + output_headers("application/postscript", "map.ps", file_size(file)) + output_file(file) + else: + output_error("Unknown format '%s'" % form.getvalue("format")) diff --git a/cookbooks/tile/templates/default/logrotate.apache.erb b/cookbooks/tile/templates/default/logrotate.apache.erb new file mode 100644 index 000000000..9a82d4ca8 --- /dev/null +++ b/cookbooks/tile/templates/default/logrotate.apache.erb @@ -0,0 +1,16 @@ +# DO NOT EDIT - This file is being maintained by Chef + +/var/log/apache2/*.log { + daily + size 1G + missingok + rotate 52 + compress + delaycompress + notifempty + create 640 root adm + sharedscripts + postrotate + /usr/bin/service apache2 reload > /dev/null + endscript +} diff --git a/cookbooks/tile/templates/default/munin.erb b/cookbooks/tile/templates/default/munin.erb new file mode 100644 index 000000000..b789827b2 --- /dev/null +++ b/cookbooks/tile/templates/default/munin.erb @@ -0,0 +1,4 @@ +# DO NOT EDIT - This file is being maintained by Chef + +[<%= @name %>] +env.state /var/lib/replicate/state.txt diff --git a/cookbooks/tile/templates/default/renderd.conf.erb b/cookbooks/tile/templates/default/renderd.conf.erb new file mode 100644 index 000000000..ebd8c14eb --- /dev/null +++ b/cookbooks/tile/templates/default/renderd.conf.erb @@ -0,0 +1,19 @@ +# DO NOT EDIT - This file is being maintained by Chef + +[renderd] +socketname=/var/run/renderd/renderd.sock +num_threads=<%= node[:cpu][:total] - 2 %> +tile_dir=/srv/tile.openstreetmap.org/tiles +stats_file=/var/run/renderd/renderd.stats + +[mapnik] +plugins_dir=/usr/lib/mapnik/input +font_dir=/usr/share/fonts +font_dir_recurse=true +<% node[:tile][:styles].each do |name,details| -%> + +[<%= name %>] +URI=/<%= name %>/ +XML=/srv/tile.openstreetmap.org/styles/<%= name %>/project.xml +HOST=tile.openstreetmap.org +<% end -%> diff --git a/cookbooks/tile/templates/default/replicate.configuration.erb b/cookbooks/tile/templates/default/replicate.configuration.erb new file mode 100644 index 000000000..8dc015c0c --- /dev/null +++ b/cookbooks/tile/templates/default/replicate.configuration.erb @@ -0,0 +1,8 @@ +# DO NOT EDIT - This file is being maintained by Chef + +# The URL of the directory containing change files. +baseUrl=http://planet.openstreetmap.org/replication/minute + +# Defines the maximum time interval in seconds to download in a single invocation. +# Setting to 0 disables this feature. +maxInterval = 3600 diff --git a/cookbooks/tile/templates/default/replicate.erb b/cookbooks/tile/templates/default/replicate.erb new file mode 100644 index 000000000..862b6a5e7 --- /dev/null +++ b/cookbooks/tile/templates/default/replicate.erb @@ -0,0 +1,77 @@ +#!/bin/bash + +# DO NOT EDIT - This file is being maintained by Chef + +# Initialize timestamp with day of latest planet dump +# Setting to midnight ensures we get conistent data after first run +# osmosis --read-replication-interval-init + +# Send output to the log +exec > /var/log/replicate/replicate.log 2>&1 + +# Change to the replication state directory +cd /var/lib/replicate + +# Read in initial state +. state.txt + +# Loop indefinitely +while true +do + # Work out the name of the next file + file="changes-${sequenceNumber}.osm.gz" + + # Fetch the next set of changes + osmosis --read-replication-interval --simc --write-xml-change file="${file}" compressionMethod="gzip" + + # Check for errors + if [ $? -eq 0 ] + then + # Enable exit on error + set -e + + # Remember the previous sequence number + prevSequenceNumber=$sequenceNumber + + # Read in new state + . state.txt + + # Did we get any new data? + if [ "${sequenceNumber}" == "${prevSequenceNumber}" ] + then + # Log the lack of data + echo "No new data available. Sleeping..." + + # Remove file, it will just be an empty changeset + rm ${file} + + # Sleep for a short while + sleep 30 + else + # Log the new data + echo "Fetched new data from ${prevSequenceNumber} to ${sequenceNumber} into ${file}" + + # Apply the changes to the database +<% if node[:tile][:node_file] -%> + osm2pgsql --slim --append --flat-nodes=<%= node[:tile][:node_file] %> ${file} +<% else -%> + osm2pgsql --slim --append ${file} +<% end -%> + + # Expire tiles which are touched by the changes + /usr/local/bin/expire-tiles ${file} > /dev/null 2>&1 & + fi + + # Delete old downloads + find . -name 'changes-*.gz' -mmin +300 -exec rm -f {} \; + + # Disable exit on error + set +e + else + # Log our failure to fetch changes + echo "Failed to fetch changes - waiting a few minutes before retry" + + # Wait five minutes and have another go + sleep 300 + fi +done diff --git a/cookbooks/tile/templates/default/replicate.init.erb b/cookbooks/tile/templates/default/replicate.init.erb new file mode 100644 index 000000000..d2200f87a --- /dev/null +++ b/cookbooks/tile/templates/default/replicate.init.erb @@ -0,0 +1,24 @@ +#!/bin/bash + +# DO NOT EDIT - This file is being maintained by Chef + +start() { + start-stop-daemon --start --chuid tile --background --make-pidfile -pidfile /var/run/replicate.pid --exec /usr/local/bin/replicate +} + +stop() { + start-stop-daemon --stop --retry 300 --pidfile /var/run/replicate.pid --exec /usr/local/bin/replicate +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart) + stop || exit $? + start + ;; +esac diff --git a/cookbooks/tile/templates/default/replicate.logrotate.erb b/cookbooks/tile/templates/default/replicate.logrotate.erb new file mode 100644 index 000000000..a2878af92 --- /dev/null +++ b/cookbooks/tile/templates/default/replicate.logrotate.erb @@ -0,0 +1,10 @@ +# DO NOT EDIT - This file is being maintained by Chef + +/var/log/replicate/*.log { + compress + delaycompress + notifempty + postrotate + /usr/bin/service replicate restart + endscript +} diff --git a/cookbooks/tile/templates/default/tile.conf.erb b/cookbooks/tile/templates/default/tile.conf.erb new file mode 100644 index 000000000..9748774c2 --- /dev/null +++ b/cookbooks/tile/templates/default/tile.conf.erb @@ -0,0 +1,33 @@ +# DO NOT EDIT - This file is being maintained by Chef + +# Set location of renderd socket +ModTileRenderdSocketName /var/run/renderd/renderd.sock + +# Set location of tile directory +ModTileTileDir /srv/tile.openstreetmap.org/tiles + +# Time to wait for a re-render before serving a dirty tile +ModTileRequestTimeout 3 + +# Don't try and re-render dirty tiles if the load is higher than this +ModTileMaxLoadOld 36 + +# Don't try and render missing tiles if the load is higher than this +ModTileMaxLoadOld 72 + +# Maximum expiry to set on a tile +ModTileCacheDurationMax 604800 + +# Expiry time for dirty tiles that have been queued for re-rendering +ModTileCacheDurationDirty 900 + +# Minimum expiry time for fresh tiles +ModTileCacheDurationMinimum 10800 +ModTileCacheDurationMediumZoom 13 86400 +ModTileCacheDurationLowZoom 9 518400 + +# Factor controlling effect of last modification time on expiry +ModTileCacheLastModifiedFactor 0.20 + +# Load tile configuration +LoadTileConfigFile /etc/renderd.conf