website: initial commit
|
@ -0,0 +1,2 @@
|
||||||
|
https://github.com/heroku/heroku-buildpack-ruby.git
|
||||||
|
https://github.com/hashicorp/heroku-buildpack-middleman.git
|
|
@ -0,0 +1,14 @@
|
||||||
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
|
gem "less", "~> 2.5.0"
|
||||||
|
gem "middleman", "~> 3.3.2"
|
||||||
|
|
||||||
|
gem "middleman-minify-html", "~> 3.1.1"
|
||||||
|
gem "rack-contrib", "~> 1.1.0"
|
||||||
|
gem "redcarpet", "~> 3.0.0"
|
||||||
|
gem "therubyracer", "~> 0.12.0"
|
||||||
|
gem "thin", "~> 1.5.0"
|
||||||
|
|
||||||
|
group :development do
|
||||||
|
gem "highline", "~> 1.6.15"
|
||||||
|
end
|
|
@ -0,0 +1,132 @@
|
||||||
|
GEM
|
||||||
|
remote: https://rubygems.org/
|
||||||
|
specs:
|
||||||
|
activesupport (4.0.4)
|
||||||
|
i18n (~> 0.6, >= 0.6.9)
|
||||||
|
minitest (~> 4.2)
|
||||||
|
multi_json (~> 1.3)
|
||||||
|
thread_safe (~> 0.1)
|
||||||
|
tzinfo (~> 0.3.37)
|
||||||
|
chunky_png (1.3.0)
|
||||||
|
coffee-script (2.2.0)
|
||||||
|
coffee-script-source
|
||||||
|
execjs
|
||||||
|
coffee-script-source (1.7.0)
|
||||||
|
commonjs (0.2.7)
|
||||||
|
compass (0.12.5)
|
||||||
|
chunky_png (~> 1.2)
|
||||||
|
fssm (>= 0.2.7)
|
||||||
|
sass (~> 3.2.19)
|
||||||
|
compass-import-once (1.0.4)
|
||||||
|
sass (>= 3.2, < 3.5)
|
||||||
|
daemons (1.1.9)
|
||||||
|
erubis (2.7.0)
|
||||||
|
eventmachine (1.0.3)
|
||||||
|
execjs (2.0.2)
|
||||||
|
ffi (1.9.3)
|
||||||
|
fssm (0.2.10)
|
||||||
|
haml (4.0.5)
|
||||||
|
tilt
|
||||||
|
highline (1.6.21)
|
||||||
|
hike (1.2.3)
|
||||||
|
hooks (0.4.0)
|
||||||
|
uber (~> 0.0.4)
|
||||||
|
i18n (0.6.9)
|
||||||
|
json (1.8.1)
|
||||||
|
kramdown (1.3.3)
|
||||||
|
less (2.5.0)
|
||||||
|
commonjs (~> 0.2.7)
|
||||||
|
libv8 (3.16.14.3)
|
||||||
|
listen (1.3.1)
|
||||||
|
rb-fsevent (>= 0.9.3)
|
||||||
|
rb-inotify (>= 0.9)
|
||||||
|
rb-kqueue (>= 0.2)
|
||||||
|
middleman (3.3.2)
|
||||||
|
coffee-script (~> 2.2.0)
|
||||||
|
compass (>= 0.12.4)
|
||||||
|
compass-import-once (~> 1.0.4)
|
||||||
|
execjs (~> 2.0)
|
||||||
|
haml (>= 4.0.5)
|
||||||
|
kramdown (~> 1.2)
|
||||||
|
middleman-core (= 3.3.2)
|
||||||
|
middleman-sprockets (>= 3.1.2)
|
||||||
|
sass (>= 3.2.17, < 4.0)
|
||||||
|
uglifier (~> 2.5)
|
||||||
|
middleman-core (3.3.2)
|
||||||
|
activesupport (~> 4.0.1)
|
||||||
|
bundler (~> 1.1)
|
||||||
|
erubis
|
||||||
|
hooks (~> 0.3)
|
||||||
|
i18n (~> 0.6.9)
|
||||||
|
listen (~> 1.1)
|
||||||
|
padrino-helpers (~> 0.12.1)
|
||||||
|
rack (>= 1.4.5, < 2.0)
|
||||||
|
rack-test (~> 0.6.2)
|
||||||
|
thor (>= 0.15.2, < 2.0)
|
||||||
|
tilt (~> 1.4.1, < 2.0)
|
||||||
|
middleman-minify-html (3.1.1)
|
||||||
|
middleman-core (~> 3.0)
|
||||||
|
middleman-sprockets (3.3.2)
|
||||||
|
middleman-core (>= 3.2)
|
||||||
|
sprockets (~> 2.2)
|
||||||
|
sprockets-helpers (~> 1.1.0)
|
||||||
|
sprockets-sass (~> 1.0.0)
|
||||||
|
minitest (4.7.5)
|
||||||
|
multi_json (1.9.2)
|
||||||
|
padrino-helpers (0.12.1)
|
||||||
|
i18n (~> 0.6, >= 0.6.7)
|
||||||
|
padrino-support (= 0.12.1)
|
||||||
|
tilt (~> 1.4.1)
|
||||||
|
padrino-support (0.12.1)
|
||||||
|
activesupport (>= 3.1)
|
||||||
|
rack (1.5.2)
|
||||||
|
rack-contrib (1.1.0)
|
||||||
|
rack (>= 0.9.1)
|
||||||
|
rack-test (0.6.2)
|
||||||
|
rack (>= 1.0)
|
||||||
|
rb-fsevent (0.9.4)
|
||||||
|
rb-inotify (0.9.3)
|
||||||
|
ffi (>= 0.5.0)
|
||||||
|
rb-kqueue (0.2.2)
|
||||||
|
ffi (>= 0.5.0)
|
||||||
|
redcarpet (3.0.0)
|
||||||
|
ref (1.0.5)
|
||||||
|
sass (3.2.19)
|
||||||
|
sprockets (2.12.0)
|
||||||
|
hike (~> 1.2)
|
||||||
|
multi_json (~> 1.0)
|
||||||
|
rack (~> 1.0)
|
||||||
|
tilt (~> 1.1, != 1.3.0)
|
||||||
|
sprockets-helpers (1.1.0)
|
||||||
|
sprockets (~> 2.0)
|
||||||
|
sprockets-sass (1.0.3)
|
||||||
|
sprockets (~> 2.0)
|
||||||
|
tilt (~> 1.1)
|
||||||
|
therubyracer (0.12.1)
|
||||||
|
libv8 (~> 3.16.14.0)
|
||||||
|
ref
|
||||||
|
thin (1.5.1)
|
||||||
|
daemons (>= 1.0.9)
|
||||||
|
eventmachine (>= 0.12.6)
|
||||||
|
rack (>= 1.0.0)
|
||||||
|
thor (0.19.1)
|
||||||
|
thread_safe (0.3.3)
|
||||||
|
tilt (1.4.1)
|
||||||
|
tzinfo (0.3.39)
|
||||||
|
uber (0.0.4)
|
||||||
|
uglifier (2.5.0)
|
||||||
|
execjs (>= 0.3.0)
|
||||||
|
json (>= 1.8.0)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
ruby
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
highline (~> 1.6.15)
|
||||||
|
less (~> 2.5.0)
|
||||||
|
middleman (~> 3.3.2)
|
||||||
|
middleman-minify-html (~> 3.1.1)
|
||||||
|
rack-contrib (~> 1.1.0)
|
||||||
|
redcarpet (~> 3.0.0)
|
||||||
|
therubyracer (~> 0.12.0)
|
||||||
|
thin (~> 1.5.0)
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Proprietary License
|
||||||
|
|
||||||
|
This license is temporary while a more official one is drafted. However,
|
||||||
|
this should make it clear:
|
||||||
|
|
||||||
|
* The text contents of this website are MPL 2.0 licensed.
|
||||||
|
|
||||||
|
* The design contents of this website are proprietary and may not be reproduced
|
||||||
|
or reused in any way other than to run the Terraform website locally. The license
|
||||||
|
for the design is owned solely by HashiCorp, Inc.
|
|
@ -0,0 +1 @@
|
||||||
|
web: bundle exec thin start -p $PORT
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Terraform Website
|
||||||
|
|
||||||
|
This subdirectory contains the entire source for the [Terraform Website](http://www.terraform.io).
|
||||||
|
This is a [Middleman](http://middlemanapp.com) project, which builds a static
|
||||||
|
site from these source files.
|
||||||
|
|
||||||
|
## Contributions Welcome!
|
||||||
|
|
||||||
|
If you find a typo or you feel like you can improve the HTML, CSS, or
|
||||||
|
JavaScript, we welcome contributions. Feel free to open issues or pull
|
||||||
|
requests like any normal GitHub project, and we'll merge it in.
|
||||||
|
|
||||||
|
## Running the Site Locally
|
||||||
|
|
||||||
|
Running the site locally is simple. Clone this repo and run the following
|
||||||
|
commands:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ bundle
|
||||||
|
$ bundle exec middleman server
|
||||||
|
```
|
||||||
|
|
||||||
|
Then open up `localhost:4567`. Note that some URLs you may need to append
|
||||||
|
".html" to make them work (in the navigation and such).
|
|
@ -0,0 +1,24 @@
|
||||||
|
# -*- mode: ruby -*-
|
||||||
|
# vi: set ft=ruby :
|
||||||
|
|
||||||
|
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
|
||||||
|
VAGRANTFILE_API_VERSION = "2"
|
||||||
|
|
||||||
|
$script = <<SCRIPT
|
||||||
|
sudo apt-get -y update
|
||||||
|
sudo apt-get -y install curl
|
||||||
|
curl -sSL https://get.rvm.io | bash -s stable
|
||||||
|
. ~/.bashrc
|
||||||
|
. ~/.bash_profile
|
||||||
|
rvm install 2.0.0
|
||||||
|
rvm --default use 2.0.0
|
||||||
|
cd /vagrant
|
||||||
|
bundle
|
||||||
|
SCRIPT
|
||||||
|
|
||||||
|
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||||
|
config.vm.box = "chef/ubuntu-12.04"
|
||||||
|
config.vm.network "private_network", ip: "33.33.30.10"
|
||||||
|
config.vm.provision "shell", inline: $script, privileged: false
|
||||||
|
config.vm.synced_folder ".", "/vagrant", type: "rsync"
|
||||||
|
end
|
|
@ -0,0 +1,20 @@
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
# Configure Middleman
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
set :css_dir, 'stylesheets'
|
||||||
|
set :js_dir, 'javascripts'
|
||||||
|
set :images_dir, 'images'
|
||||||
|
|
||||||
|
# Use the RedCarpet Markdown engine
|
||||||
|
set :markdown_engine, :redcarpet
|
||||||
|
set :markdown,
|
||||||
|
:fenced_code_blocks => true,
|
||||||
|
:with_toc_data => true
|
||||||
|
|
||||||
|
# Build-specific configuration
|
||||||
|
configure :build do
|
||||||
|
activate :asset_hash
|
||||||
|
activate :minify_html
|
||||||
|
activate :minify_javascript
|
||||||
|
end
|
|
@ -0,0 +1,27 @@
|
||||||
|
require "rack"
|
||||||
|
require "rack/contrib/not_found"
|
||||||
|
require "rack/contrib/response_headers"
|
||||||
|
require "rack/contrib/static_cache"
|
||||||
|
require "rack/contrib/try_static"
|
||||||
|
|
||||||
|
# Properly compress the output if the client can handle it.
|
||||||
|
use Rack::Deflater
|
||||||
|
|
||||||
|
# Set the "forever expire" cache headers for these static assets. Since
|
||||||
|
# we hash the contents of the assets to determine filenames, this is safe
|
||||||
|
# to do.
|
||||||
|
use Rack::StaticCache,
|
||||||
|
:root => "build",
|
||||||
|
:urls => ["/images", "/javascripts", "/stylesheets"],
|
||||||
|
:duration => 2,
|
||||||
|
:versioning => false
|
||||||
|
|
||||||
|
# Try to find a static file that matches our request, since Middleman
|
||||||
|
# statically generates everything.
|
||||||
|
use Rack::TryStatic,
|
||||||
|
:root => "build",
|
||||||
|
:urls => ["/"],
|
||||||
|
:try => [".html", "index.html", "/index.html"]
|
||||||
|
|
||||||
|
# 404 if we reached this point. Sad times.
|
||||||
|
run Rack::NotFound.new(File.expand_path("../build/404.html", __FILE__))
|
|
@ -0,0 +1,4 @@
|
||||||
|
This file doesn't do anything, but we periodically update the number
|
||||||
|
below just to force being able to deploy the website again.
|
||||||
|
|
||||||
|
1
|
|
@ -0,0 +1,67 @@
|
||||||
|
require "net/http"
|
||||||
|
|
||||||
|
$terraform_files = {}
|
||||||
|
$terraform_os = []
|
||||||
|
|
||||||
|
if ENV["CONSUL_VERSION"]
|
||||||
|
raise "BINTRAY_API_KEY must be set." if !ENV["BINTRAY_API_KEY"]
|
||||||
|
http = Net::HTTP.new("dl.bintray.com", 80)
|
||||||
|
req = Net::HTTP::Get.new("/mitchellh/terraform/")
|
||||||
|
req.basic_auth "mitchellh", ENV["BINTRAY_API_KEY"]
|
||||||
|
response = http.request(req)
|
||||||
|
|
||||||
|
response.body.split("\n").each do |line|
|
||||||
|
next if line !~ /\/mitchellh\/terraform\/(#{Regexp.quote(ENV["CONSUL_VERSION"])}.+?)'/
|
||||||
|
filename = $1.to_s
|
||||||
|
os = filename.split("_")[1]
|
||||||
|
next if os == "SHA256SUMS"
|
||||||
|
next if os == "web"
|
||||||
|
|
||||||
|
$terraform_files[os] ||= []
|
||||||
|
$terraform_files[os] << filename
|
||||||
|
end
|
||||||
|
|
||||||
|
$terraform_os = ["darwin", "linux", "windows"] & $consul_files.keys
|
||||||
|
$terraform_os += $consul_files.keys
|
||||||
|
$terraform_os.uniq!
|
||||||
|
|
||||||
|
$terraform_files.each do |key, value|
|
||||||
|
value.sort!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module DownloadHelpers
|
||||||
|
def download_arch(file)
|
||||||
|
parts = file.split("_")
|
||||||
|
return "" if parts.length != 3
|
||||||
|
parts[2].split(".")[0]
|
||||||
|
end
|
||||||
|
|
||||||
|
def download_os_human(os)
|
||||||
|
if os == "darwin"
|
||||||
|
return "Mac OS X"
|
||||||
|
elsif os == "freebsd"
|
||||||
|
return "FreeBSD"
|
||||||
|
elsif os == "openbsd"
|
||||||
|
return "OpenBSD"
|
||||||
|
elsif os == "Linux"
|
||||||
|
return "Linux"
|
||||||
|
elsif os == "windows"
|
||||||
|
return "Windows"
|
||||||
|
else
|
||||||
|
return os
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def download_url(file)
|
||||||
|
"https://dl.bintray.com/mitchellh/terraform/#{file}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def ui_download_url
|
||||||
|
download_url("#{latest_version}_web_ui.zip")
|
||||||
|
end
|
||||||
|
|
||||||
|
def latest_version
|
||||||
|
ENV["CONSUL_VERSION"]
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,12 @@
|
||||||
|
module SidebarHelpers
|
||||||
|
# This helps by setting the "active" class for sidebar nav elements
|
||||||
|
# if the YAML frontmatter matches the expected value.
|
||||||
|
def sidebar_current(expected)
|
||||||
|
current = current_page.data.sidebar_current || ""
|
||||||
|
if current.start_with?(expected)
|
||||||
|
return " class=\"active\""
|
||||||
|
else
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,2 @@
|
||||||
|
# Source folder
|
||||||
|
node_modules/
|
|
@ -0,0 +1 @@
|
||||||
|
<h2>Page Not Found</h2>
|
|
@ -0,0 +1,69 @@
|
||||||
|
module.exports = function(grunt) {
|
||||||
|
|
||||||
|
// Configuration goes here
|
||||||
|
grunt.initConfig({
|
||||||
|
|
||||||
|
less: {
|
||||||
|
development:{
|
||||||
|
files: {
|
||||||
|
"stylesheets/main.css": "stylesheets/main.less"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
concat: {
|
||||||
|
options: {
|
||||||
|
separator: ';'
|
||||||
|
},
|
||||||
|
site: {
|
||||||
|
src: [
|
||||||
|
'javascripts/app/app.js',
|
||||||
|
'javascripts/app/util.js',
|
||||||
|
'javascripts/app/homepage.js'
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
dest: 'javascripts/app/deploy/site.js'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
uglify: {
|
||||||
|
app: {
|
||||||
|
files: {
|
||||||
|
'javascripts/app/deploy/site.min.js': ['javascripts/app/deploy/site.js']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
less: {
|
||||||
|
files: 'stylesheets/*.less',
|
||||||
|
tasks: ['less']
|
||||||
|
},
|
||||||
|
js: {
|
||||||
|
files: 'javascripts/app/*.js',
|
||||||
|
tasks: ['concat', 'uglify']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// Load plugins here
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-less');
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-concat');
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-connect');
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||||
|
grunt.loadNpmTasks('grunt-recess');
|
||||||
|
|
||||||
|
// JS distribution task.
|
||||||
|
grunt.registerTask('dist-js', ['concat', 'uglify']);
|
||||||
|
|
||||||
|
// Full distribution task.
|
||||||
|
grunt.registerTask('dist', ['dist-js']);
|
||||||
|
|
||||||
|
grunt.registerTask('default', ['watch']);
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,93 @@
|
||||||
|
---
|
||||||
|
page_title: "Community"
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="col-md-8 col-md-offset-2">
|
||||||
|
<div class="bs-docs-section">
|
||||||
|
<h1>Community</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Terraform is a new project with a growing community. Despite this,
|
||||||
|
there are active, dedicated users willing to help you through various
|
||||||
|
mediums.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>IRC:</strong> <code>#terraform</code> on Freenode
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Mailing list:</strong>
|
||||||
|
<a href="https://groups.google.com/group/terraform-tool">Terraform Google Group</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Bug Tracker:</strong>
|
||||||
|
<a href="https://github.com/hashicorp/terraform/issues">Issue tracker
|
||||||
|
on GitHub</a>. Please only use this for reporting bugs. Do not ask
|
||||||
|
for general help here. Use IRC or the mailing list for that.
|
||||||
|
|
||||||
|
<h1>People</h1>
|
||||||
|
<p>
|
||||||
|
The following people are some of the faces behind Terraform. They each
|
||||||
|
contribute to Terraform in some core way. Over time, faces may appear and
|
||||||
|
disappear from this list as contributors come and go.
|
||||||
|
</p>
|
||||||
|
<div class="people">
|
||||||
|
<div class="person">
|
||||||
|
<img class="pull-left" src="http://www.gravatar.com/avatar/11ba9630c9136eef9a70d26473d355d5.png?s=125">
|
||||||
|
<div class="bio">
|
||||||
|
<h3>Armon Dadgar (<a href="https://github.com/armon">@armon</a>)</h3>
|
||||||
|
<p>
|
||||||
|
Armon Dadgar is the creator of Terraform. He researched and developed
|
||||||
|
most of the internals of how Terraform works, including the
|
||||||
|
gossip layer, leader election, etc. Armon is also the creator of
|
||||||
|
<a href="https://github.com/hashicorp/serf">Serf</a>,
|
||||||
|
<a href="https://github.com/armon/statsite">Statsite</a>, and
|
||||||
|
<a href="https://github.com/armon/bloomd">Bloomd</a>.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="person">
|
||||||
|
<img class="pull-left" src="http://www.gravatar.com/avatar/54079122b67de9677c1f93933ce8b63a.png?s=125">
|
||||||
|
<div class="bio">
|
||||||
|
<h3>Mitchell Hashimoto (<a href="https://github.com/mitchellh">@mitchellh</a>)</h3>
|
||||||
|
<p>
|
||||||
|
Mitchell Hashimoto is a co-creator of Terraform. He primarily took
|
||||||
|
a management role in the creation of Terraform, guiding product
|
||||||
|
and user experience decisions on top of Armon's technical decisions.
|
||||||
|
Mitchell Hashimoto is also the creator of
|
||||||
|
<a href="http://www.vagrantup.com">Vagrant</a>,
|
||||||
|
<a href="http://www.packer.io">Packer</a>, and
|
||||||
|
<a href="http://www.serfdom.io">Serf</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="person">
|
||||||
|
<img class="pull-left" src="http://www.gravatar.com/avatar/2acc31dd6370a54b18f6755cd0710ce6.png?s=125">
|
||||||
|
<div class="bio">
|
||||||
|
<h3>Jack Pearkes (<a href="https://github.com/pearkes">@pearkes</a>)</h3>
|
||||||
|
<p>
|
||||||
|
Jack Pearkes created and maintains the Terraform web UI.
|
||||||
|
He is also a core committer to
|
||||||
|
<a href="http://www.packer.io">Packer</a> and maintains
|
||||||
|
many successful
|
||||||
|
<a href="https://github.com/pearkes">open source projects</a>
|
||||||
|
while also being an employee of
|
||||||
|
<a href="http://www.hashicorp.com">HashiCorp</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="person">
|
||||||
|
<img class="pull-left" src="//www.gravatar.com/avatar/1e87e6016a7c4f4ecbd2517d84058467.png?s=125">
|
||||||
|
<div class="bio">
|
||||||
|
<h3>William Tisäter (<a href="https://github.com/tiwilliam">@tiwilliam</a>)</h3>
|
||||||
|
<p>William Tisäter is a Terraform core committer. He is also maintainer of <a href="https://pypi.python.org/pypi/pygeoip">pygeoip</a> and build things daily at <a href="https://tictail.com">Tictail</a>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,98 @@
|
||||||
|
---
|
||||||
|
layout: "docs"
|
||||||
|
page_title: "Agent"
|
||||||
|
sidebar_current: "docs-agent-running"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Terraform Agent
|
||||||
|
|
||||||
|
The Terraform agent is the core process of Terraform. The agent maintains membership
|
||||||
|
information, registers services, runs checks, responds to queries
|
||||||
|
and more. The agent must run on every node that is part of a Terraform cluster.
|
||||||
|
|
||||||
|
Any Agent may run in one of two modes: client or server. A server
|
||||||
|
node takes on the additional responsibility of being part of the [consensus quorum](#).
|
||||||
|
These nodes take part in Raft, and provide strong consistency and availability in
|
||||||
|
the case of failure. The higher burden on the server nodes means that usually they
|
||||||
|
should be run on dedicated instances, as they are more resource intensive than a client
|
||||||
|
node. Client nodes make up the majority of the cluster, and they are very lightweight
|
||||||
|
as they maintain very little state and interface with the server nodes for most operations.
|
||||||
|
|
||||||
|
## Running an Agent
|
||||||
|
|
||||||
|
The agent is started with the `terraform agent` command. This command blocks,
|
||||||
|
running forever or until told to quit. The agent command takes a variety
|
||||||
|
of configuration options but the defaults are usually good enough. When
|
||||||
|
running `terraform agent`, you should see output similar to that below:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ terraform agent -data-dir=/tmp/terraform
|
||||||
|
==> Starting Terraform agent...
|
||||||
|
==> Starting Terraform agent RPC...
|
||||||
|
==> Terraform agent running!
|
||||||
|
Node name: 'Armons-MacBook-Air'
|
||||||
|
Datacenter: 'dc1'
|
||||||
|
Server: false (bootstrap: false)
|
||||||
|
Client Addr: 127.0.0.1 (HTTP: 8500, DNS: 8600, RPC: 8400)
|
||||||
|
Cluster Addr: 192.168.1.43 (LAN: 8301, WAN: 8302)
|
||||||
|
|
||||||
|
==> Log data will now stream in as it occurs:
|
||||||
|
|
||||||
|
[INFO] serf: EventMemberJoin: Armons-MacBook-Air.local 192.168.1.43
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
There are several important components that `terraform agent` outputs:
|
||||||
|
|
||||||
|
* **Node name**: This is a unique name for the agent. By default this
|
||||||
|
is the hostname of the machine, but you may customize it to whatever
|
||||||
|
you'd like using the `-node` flag.
|
||||||
|
|
||||||
|
* **Datacenter**: This is the datacenter the agent is configured to run
|
||||||
|
in. Terraform has first-class support for multiple datacenters, but to work efficiently
|
||||||
|
each node must be configured to correctly report its datacenter. The `-dc` flag
|
||||||
|
can be used to set the datacenter. For single-DC configurations, the agent
|
||||||
|
will default to "dc1".
|
||||||
|
|
||||||
|
* **Server**: This shows if the agent is running in the server or client mode.
|
||||||
|
Server nodes have the extra burden of participating in the consensus quorum,
|
||||||
|
storing cluster state, and handling queries. Additionally, a server may be
|
||||||
|
in "bootstrap" mode. The first server must be in this mode to allow additional
|
||||||
|
servers to join the cluster. Multiple servers cannot be in bootstrap mode,
|
||||||
|
otherwise the cluster state will be inconsistent.
|
||||||
|
|
||||||
|
* **Client Addr**: This is the address used for client interfaces to the agent.
|
||||||
|
This includes the ports for the HTTP, DNS, and RPC interfaces. The RPC
|
||||||
|
address is used for other `terraform` commands. Other Terraform commands such
|
||||||
|
as `terraform members` connect to a running agent and use RPC to query and
|
||||||
|
control the agent. By default, this binds only to localhost. If you
|
||||||
|
change this address or port, you'll have to specify an `-rpc-addr` to commands
|
||||||
|
such as `terraform members` so they know how to talk to the agent. This is also
|
||||||
|
the address other applications can use over [RPC to control Terraform](/docs/agent/rpc.html).
|
||||||
|
|
||||||
|
* **Cluster Addr**: This is the address and ports used for communication between
|
||||||
|
Terraform agents in a cluster. Every Terraform agent in a cluster does not have to
|
||||||
|
use the same port, but this address **MUST** be reachable by all other nodes.
|
||||||
|
|
||||||
|
## Stopping an Agent
|
||||||
|
|
||||||
|
An agent can be stopped in two ways: gracefully or forcefully. To gracefully
|
||||||
|
halt an agent, send the process an interrupt signal, which is usually
|
||||||
|
`Ctrl-C` from a terminal. When gracefully exiting, the agent first notifies
|
||||||
|
the cluster it intends to leave the cluster. This way, other cluster members
|
||||||
|
notify the cluster that the node has _left_.
|
||||||
|
|
||||||
|
Alternatively, you can force kill the agent by sending it a kill signal.
|
||||||
|
When force killed, the agent ends immediately. The rest of the cluster will
|
||||||
|
eventually (usually within seconds) detect that the node has died and will
|
||||||
|
notify the cluster that the node has _failed_.
|
||||||
|
|
||||||
|
It is especially important that a server node be allowed to gracefully leave,
|
||||||
|
so that there will be a minimal impact on availability as the server leaves
|
||||||
|
the consensus quorum.
|
||||||
|
|
||||||
|
For client agents, the difference between a node _failing_ and a node _leaving_
|
||||||
|
may not be important for your use case. For example, for a web server and load
|
||||||
|
balancer setup, both result in the same action: remove the web node
|
||||||
|
from the load balancer pool. But for other situations, you may handle
|
||||||
|
each scenario differently.
|
|
@ -0,0 +1,16 @@
|
||||||
|
---
|
||||||
|
layout: "docs"
|
||||||
|
page_title: "Commands: Agent"
|
||||||
|
sidebar_current: "docs-commands-agent"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Terraform Agent
|
||||||
|
|
||||||
|
The `terraform agent` command is the heart of Terraform: it runs the agent that
|
||||||
|
performs the important task of maintaining membership information,
|
||||||
|
running checks, announcing services, handling queries, etc.
|
||||||
|
|
||||||
|
Due to the power and flexibility of this command, the Terraform agent
|
||||||
|
is documented in its own section. See the [Terraform Agent](/docs/agent/basics.html)
|
||||||
|
section for more information on how to use this command and the
|
||||||
|
options it has.
|
|
@ -0,0 +1,119 @@
|
||||||
|
---
|
||||||
|
layout: "docs"
|
||||||
|
page_title: "Adding/Removing Servers"
|
||||||
|
sidebar_current: "docs-guides-servers"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Adding/Removing Servers
|
||||||
|
|
||||||
|
Terraform is designed to require minimal operator involvement, however any changes
|
||||||
|
to the set of Terraform servers must be handled carefully. To better understand
|
||||||
|
why, reading about the [consensus protocol](/docs/internals/consensus.html) will
|
||||||
|
be useful. In short, the Terraform servers perform leader election and replication.
|
||||||
|
For changes to be processed, a minimum quorum of servers (N/2)+1 must be available.
|
||||||
|
That means if there are 3 server nodes, at least 2 must be available.
|
||||||
|
|
||||||
|
In general, if you are ever adding and removing nodes simultaneously, it is better
|
||||||
|
to first add the new nodes and then remove the old nodes.
|
||||||
|
|
||||||
|
## Adding New Servers
|
||||||
|
|
||||||
|
Adding new servers is generally straightforward. After the initial server, no further
|
||||||
|
servers should ever be started with the `-bootstrap` flag. Instead, simply start the new
|
||||||
|
server with the `-server` flag. At this point, the server will not be a member of
|
||||||
|
any cluster, and should emit something like:
|
||||||
|
|
||||||
|
[WARN] raft: EnableSingleNode disabled, and no known peers. Aborting election.
|
||||||
|
|
||||||
|
This means that it does not know about any peers and is not configured to elect itself.
|
||||||
|
This is expected, and we can now add this node to the existing cluster using `join`.
|
||||||
|
From the new server, we can join any member of the existing cluster:
|
||||||
|
|
||||||
|
$ terraform join <Node Address>
|
||||||
|
Successfully joined cluster by contacting 1 nodes.
|
||||||
|
|
||||||
|
It is important to note that any node, including a non-server may be specified for
|
||||||
|
join. The gossip protocol is used to properly discover all the nodes in the cluster.
|
||||||
|
Once the node has joined, the existing cluster leader should log something like:
|
||||||
|
|
||||||
|
[INFO] raft: Added peer 127.0.0.2:8300, starting replication
|
||||||
|
|
||||||
|
This means that raft, the underlying consensus protocol, has added the peer and begun
|
||||||
|
replicating state. Since the existing cluster may be very far ahead, it can take some
|
||||||
|
time for the new node to catch up. To check on this, run `info` on the leader:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ terraform info
|
||||||
|
...
|
||||||
|
raft:
|
||||||
|
applied_index = 47244
|
||||||
|
commit_index = 47244
|
||||||
|
fsm_pending = 0
|
||||||
|
last_log_index = 47244
|
||||||
|
last_log_term = 21
|
||||||
|
last_snapshot_index = 40966
|
||||||
|
last_snapshot_term = 20
|
||||||
|
num_peers = 2
|
||||||
|
state = Leader
|
||||||
|
term = 21
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
This will provide various information about the state of Raft. In particular
|
||||||
|
the `last_log_index` shows the last log that is on disk. The same `info` command
|
||||||
|
can be run on the new server to see how far behind it is. Eventually the server
|
||||||
|
will be caught up, and the values should match.
|
||||||
|
|
||||||
|
It is best to add servers one at a time, allowing them to catch up. This avoids
|
||||||
|
the possibility of data loss in case the existing servers fail while bringing
|
||||||
|
the new servers up-to-date.
|
||||||
|
|
||||||
|
## Removing Servers
|
||||||
|
|
||||||
|
Removing servers must be done carefully to avoid causing an availability outage.
|
||||||
|
For a cluster of N servers, at least (N/2)+1 must be available for the cluster
|
||||||
|
to functions. See this [deployment table](/docs/internals/consensus.html#toc_3).
|
||||||
|
If you have 3 servers, and 1 of them is currently failed, removing any servers
|
||||||
|
will cause the cluster to become unavailable.
|
||||||
|
|
||||||
|
To avoid this, it may be necessary to first add new servers to the cluster,
|
||||||
|
increasing the failure tolerance of the cluster, and then to remove old servers.
|
||||||
|
Even if all 3 nodes are functioning, removing one leaves the cluster in a state
|
||||||
|
that cannot tolerate the failure of any node.
|
||||||
|
|
||||||
|
Once you have verified the existing servers are healthy, and that the cluster
|
||||||
|
can handle a node leaving, the actual process is simple. You simply issue a
|
||||||
|
`leave` command to the server.
|
||||||
|
|
||||||
|
The server leaving should contain logs like:
|
||||||
|
|
||||||
|
...
|
||||||
|
[INFO] terraform: server starting leave
|
||||||
|
...
|
||||||
|
[INFO] raft: Removed ourself, transitioning to follower
|
||||||
|
...
|
||||||
|
|
||||||
|
The leader should also emit various logs including:
|
||||||
|
|
||||||
|
...
|
||||||
|
[INFO] terraform: member 'node-10-0-1-8' left, deregistering
|
||||||
|
[INFO] raft: Removed peer 10.0.1.8:8300, stopping replication
|
||||||
|
...
|
||||||
|
|
||||||
|
At this point the node has been gracefully removed from the cluster, and
|
||||||
|
will shut down.
|
||||||
|
|
||||||
|
## Forced Removal
|
||||||
|
|
||||||
|
In some cases, it may not be possible to gracefully remove a server. For example,
|
||||||
|
if the server simply fails, then there is no ability to issue a leave. Instead,
|
||||||
|
the cluster will detect the failure and replication will continuously retry.
|
||||||
|
|
||||||
|
If the server can be recovered, it is best to bring it back online and then gracefully
|
||||||
|
leave the cluster. However, if this is not a possibility, then the `force-leave` command
|
||||||
|
can be used to force removal of a server.
|
||||||
|
|
||||||
|
This is done by invoking that command with the name of the failed node. At this point,
|
||||||
|
the cluster leader will mark the node as having left the cluster and it will stop attempting
|
||||||
|
to replicate.
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
---
|
||||||
|
layout: "docs"
|
||||||
|
page_title: "Documentation"
|
||||||
|
sidebar_current: "docs-home"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Terraform Documentation
|
||||||
|
|
||||||
|
Welcome to the Terraform documentation! This documentation is more of a reference
|
||||||
|
guide for all available features and options of Terraform. If you're just getting
|
||||||
|
started with Terraform, please start with the
|
||||||
|
[introduction and getting started guide](/intro/index.html) instead.
|
|
@ -0,0 +1,117 @@
|
||||||
|
---
|
||||||
|
layout: "docs"
|
||||||
|
page_title: "Terraform Architecture"
|
||||||
|
sidebar_current: "docs-internals-architecture"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Terraform Architecture
|
||||||
|
|
||||||
|
Terraform is a complex system that has many different moving parts. To help
|
||||||
|
users and developers of Terraform form a mental model of how it works, this
|
||||||
|
page documents the system architecture.
|
||||||
|
|
||||||
|
<div class="alert alert-block alert-warning">
|
||||||
|
<strong>Advanced Topic!</strong> This page covers technical details of
|
||||||
|
the internals of Terraform. You don't need to know these details to effectively
|
||||||
|
operate and use Terraform. These details are documented here for those who wish
|
||||||
|
to learn about them without having to go spelunking through the source code.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## Glossary
|
||||||
|
|
||||||
|
Before describing the architecture, we provide a glossary of terms to help
|
||||||
|
clarify what is being discussed:
|
||||||
|
|
||||||
|
* Agent - An agent is the long running daemon on every member of the Terraform cluster.
|
||||||
|
It is started by running `terraform agent`. The agent is able to run in either *client*,
|
||||||
|
or *server* mode. Since all nodes must be running an agent, it is simpler to refer to
|
||||||
|
the node as either being a client or server, but there are other instances of the agent. All
|
||||||
|
agents can run the DNS or HTTP interfaces, and are responsible for running checks and
|
||||||
|
keeping services in sync.
|
||||||
|
|
||||||
|
* Client - A client is an agent that forwards all RPCs to a server. The client is relatively
|
||||||
|
stateless. The only background activity a client performs is taking part of LAN gossip pool.
|
||||||
|
This has a minimal resource overhead and consumes only a small amount of network bandwidth.
|
||||||
|
|
||||||
|
* Server - An agent that is server mode. When in server mode, there is an expanded set
|
||||||
|
of responsibilities including participating in the Raft quorum, maintaining cluster state,
|
||||||
|
responding to RPC queries, WAN gossip to other datacenters, and forwarding queries to leaders
|
||||||
|
or remote datacenters.
|
||||||
|
|
||||||
|
* Datacenter - A datacenter seems obvious, but there are subtle details such as multiple
|
||||||
|
availability zones in EC2. We define a datacenter to be a networking environment that is
|
||||||
|
private, low latency, and high bandwidth. This excludes communication that would traverse
|
||||||
|
the public internet.
|
||||||
|
|
||||||
|
* Consensus - When used in our documentation we use consensus to mean agreement upon
|
||||||
|
the elected leader as well as agreement on the ordering of transactions. Since these
|
||||||
|
transactions are applied to a FSM, we implicitly include the consistency of a replicated
|
||||||
|
state machine. Consensus is described in more detail on [Wikipedia](http://en.wikipedia.org/wiki/Consensus_(computer_science)),
|
||||||
|
as well as our [implementation here](/docs/internals/consensus.html).
|
||||||
|
|
||||||
|
* Gossip - Terraform is built on top of [Serf](http://www.serfdom.io/), which provides a full
|
||||||
|
[gossip protocol](http://en.wikipedia.org/wiki/Gossip_protocol) that is used for multiple purposes.
|
||||||
|
Serf provides membership, failure detection, and event broadcast mechanisms. Our use of these
|
||||||
|
is described more in the [gossip documentation](/docs/internals/gossip.html). It is enough to know
|
||||||
|
gossip involves random node-to-node communication, primarily over UDP.
|
||||||
|
|
||||||
|
* LAN Gossip - This is used to mean that there is a gossip pool, containing nodes that
|
||||||
|
are all located on the same local area network or datacenter.
|
||||||
|
|
||||||
|
* WAN Gossip - This is used to mean that there is a gossip pool, containing servers that
|
||||||
|
are primary located in different datacenters and must communicate over the internet or
|
||||||
|
wide area network.
|
||||||
|
|
||||||
|
* RPC - RPC is short for a Remote Procedure Call. This is a request / response mechanism
|
||||||
|
allowing a client to make a request from a server.
|
||||||
|
|
||||||
|
## 10,000 foot view
|
||||||
|
|
||||||
|
From a 10,000 foot altitude the architecture of Terraform looks like this:
|
||||||
|
|
||||||
|
![Terraform Architecture](/images/terraform-arch.png)
|
||||||
|
|
||||||
|
Lets break down this image and describe each piece. First of all we can see
|
||||||
|
that there are two datacenters, one and two respectively. Terraform has first
|
||||||
|
class support for multiple datacenters and expects this to be the common case.
|
||||||
|
|
||||||
|
Within each datacenter we have a mixture of clients and servers. It is expected
|
||||||
|
that there be between three to five servers. This strikes a balance between
|
||||||
|
availability in the case of failure and performance, as consensus gets progressively
|
||||||
|
slower as more machines are added. However, there is no limit to the number of clients,
|
||||||
|
and they can easily scale into the thousands or tens of thousands.
|
||||||
|
|
||||||
|
All the nodes that are in a datacenter participate in a [gossip protocol](/docs/internals/gossip.html).
|
||||||
|
This means there is a gossip pool that contains all the nodes for a given datacenter. This serves
|
||||||
|
a few purposes: first, there is no need to configure clients with the addresses of servers,
|
||||||
|
discovery is done automatically. Second, the work of detecting node failures
|
||||||
|
is not placed on the servers but is distributed. This makes the failure detection much more
|
||||||
|
scalable than naive heartbeating schemes. Thirdly, it is used as a messaging layer to notify
|
||||||
|
when important events such as leader election take place.
|
||||||
|
|
||||||
|
The servers in each datacenter are all part of a single Raft peer set. This means that
|
||||||
|
they work together to elect a leader, which has extra duties. The leader is responsible for
|
||||||
|
processing all queries and transactions. Transactions must also be replicated to all peers
|
||||||
|
as part of the [consensus protocol](/docs/internals/consensus.html). Because of this requirement,
|
||||||
|
when a non-leader server receives an RPC request it forwards it to the cluster leader.
|
||||||
|
|
||||||
|
The server nodes also operate as part of a WAN gossip. This pool is different from the LAN pool,
|
||||||
|
as it is optimized for the higher latency of the internet, and is expected to only contain
|
||||||
|
other Terraform server nodes. The purpose of this pool is to allow datacenters to discover each
|
||||||
|
other in a low touch manner. Bringing a new datacenter online is as easy as joining the existing
|
||||||
|
WAN gossip. Because the servers are all operating in this pool, it also enables cross-datacenter requests.
|
||||||
|
When a server receives a request for a different datacenter, it forwards it to a random server
|
||||||
|
in the correct datacenter. That server may then forward to the local leader.
|
||||||
|
|
||||||
|
This results in a very low coupling between datacenters, but because of failure detection,
|
||||||
|
connection caching and multiplexing, cross-datacenter requests are relatively fast and reliable.
|
||||||
|
|
||||||
|
## Getting in depth
|
||||||
|
|
||||||
|
At this point we've covered the high level architecture of Terraform, but there are much
|
||||||
|
more details to each of the sub-systems. The [consensus protocol](/docs/internals/consensus.html) is
|
||||||
|
documented in detail, as is the [gossip protocol](/docs/internals/gossip.html). The [documentation](/docs/internals/security.html)
|
||||||
|
for the security model and protocols used are also available.
|
||||||
|
|
||||||
|
For other details, either terraformt the code, ask in IRC or reach out to the mailing list.
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
---
|
||||||
|
layout: "downloads"
|
||||||
|
page_title: "Download Terraform"
|
||||||
|
sidebar_current: "downloads-terraform"
|
||||||
|
---
|
||||||
|
|
||||||
|
<h1>Download Terraform</h1>
|
||||||
|
|
||||||
|
<section class="downloads">
|
||||||
|
<div class="description row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<p>
|
||||||
|
Below are all available downloads for the latest version of Terraform
|
||||||
|
(<%= latest_version %>). Please download the proper package for your
|
||||||
|
operating system and architecture. You can find SHA256 checksums
|
||||||
|
for packages <a href="https://dl.bintray.com/mitchellh/terraform/<%= latest_version %>_SHA256SUMS?direct">here</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% $terraform_os.each do |os| %>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12 download">
|
||||||
|
<div class="icon pull-left"><%= image_tag "/images/icons/icon_#{os}.png" %>
|
||||||
|
</div>
|
||||||
|
<div class="details">
|
||||||
|
<h2 class="os-name"><%= download_os_human(os) %></h2>
|
||||||
|
<ul>
|
||||||
|
<% $terraform_files[os].each do |file| %>
|
||||||
|
<li><a href="<%= download_url(file) %>"><%= download_arch(file) %></a></li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
<div class="clearfix">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12 poweredby">
|
||||||
|
<a href='http://www.bintray.com'>
|
||||||
|
<img src='http://www.bintray.com/docs/images/poweredByBintray_ColorTransparent.png'>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
|
@ -0,0 +1,33 @@
|
||||||
|
---
|
||||||
|
layout: "downloads"
|
||||||
|
page_title: "Download Terraform Web UI"
|
||||||
|
sidebar_current: "downloads-ui"
|
||||||
|
---
|
||||||
|
|
||||||
|
<h1>Download Terraform Web UI</h1>
|
||||||
|
|
||||||
|
<section class="downloads">
|
||||||
|
<div class="description row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<p>
|
||||||
|
From this page you can download the web UI for Terraform. This is
|
||||||
|
distributed as a separate ZIP package. You can view a
|
||||||
|
<a href="http://demo.terraform.io">demo of the web UI here</a> or
|
||||||
|
you can
|
||||||
|
<a href="/intro/getting-started/ui.html">read the docs on how to set up the UI here</a>.
|
||||||
|
</p>
|
||||||
|
<p class="center">
|
||||||
|
<a class="btn btn-default btn-lg" href="<%= ui_download_url %>">
|
||||||
|
Download Terraform Web UI <%= latest_version %></a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12 poweredby">
|
||||||
|
<a href='http://www.bintray.com'>
|
||||||
|
<img src='http://www.bintray.com/docs/images/poweredByBintray_ColorTransparent.png'>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
|
@ -0,0 +1,230 @@
|
||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<metadata></metadata>
|
||||||
|
<defs>
|
||||||
|
<font id="LeagueGothicRegular" horiz-adv-x="724" >
|
||||||
|
<font-face units-per-em="2048" ascent="1505" descent="-543" />
|
||||||
|
<missing-glyph horiz-adv-x="315" />
|
||||||
|
<glyph horiz-adv-x="0" />
|
||||||
|
<glyph horiz-adv-x="2048" />
|
||||||
|
<glyph unicode="
" horiz-adv-x="682" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="315" />
|
||||||
|
<glyph unicode="	" horiz-adv-x="315" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="315" />
|
||||||
|
<glyph unicode="!" horiz-adv-x="387" d="M74 1505h239l-55 -1099h-129zM86 0v227h215v-227h-215z" />
|
||||||
|
<glyph unicode=""" horiz-adv-x="329" d="M57 1505h215l-30 -551h-154z" />
|
||||||
|
<glyph unicode="#" horiz-adv-x="1232" d="M49 438l27 195h198l37 258h-196l26 194h197l57 420h197l-57 -420h260l57 420h197l-58 -420h193l-27 -194h-192l-37 -258h190l-26 -195h-191l-59 -438h-197l60 438h-261l-59 -438h-197l60 438h-199zM471 633h260l37 258h-260z" />
|
||||||
|
<glyph unicode="$" horiz-adv-x="692" d="M37 358l192 13q12 -186 129 -187q88 0 93 185q0 74 -61 175q-21 36 -34 53l-40 55q-28 38 -65.5 90t-70.5 101.5t-70.5 141.5t-37.5 170q4 293 215 342v131h123v-125q201 -23 235 -282l-192 -25q-14 129 -93 125q-80 -2 -84 -162q0 -102 94 -227l41 -59q30 -42 37 -52 t33 -48l37 -52q41 -57 68 -109l26 -55q43 -94 43 -186q-4 -338 -245 -369v-217h-123v221q-236 41 -250 352z" />
|
||||||
|
<glyph unicode="%" horiz-adv-x="1001" d="M55 911v437q0 110 82 156q33 18 90.5 18t97.5 -44t44 -87l4 -43v-437q0 -107 -81 -157q-32 -19 -77 -19q-129 0 -156 135zM158 0l553 1505h131l-547 -1505h-137zM178 911q-4 -55 37 -55q16 0 25.5 14.5t9.5 26.5v451q2 55 -35 55q-18 0 -27.5 -13.5t-9.5 -27.5v-451z M631 158v436q0 108 81 156q33 20 79 20q125 0 153 -135l4 -41v-436q0 -110 -80 -156q-32 -18 -90.5 -18t-98.5 43t-44 88zM754 158q-4 -57 37 -58q37 0 34 58v436q2 55 -34 55q-18 0 -27.5 -13t-9.5 -28v-450z" />
|
||||||
|
<glyph unicode="&" horiz-adv-x="854" d="M49 304q0 126 44 225.5t126 222.5q-106 225 -106 442v18q0 94 47 180q70 130 223 130q203 0 252 -215q14 -61 12 -113q0 -162 -205 -434q76 -174 148 -285q33 96 47 211l176 -33q-16 -213 -92 -358q55 -63 92 -76v-235q-23 0 -86 37.5t-123 101.5q-123 -139 -252 -139 t-216 97t-87 223zM263 325.5q1 -65.5 28.5 -107.5t78.5 -42t117 86q-88 139 -174 295q-18 -30 -34.5 -98t-15.5 -133.5zM305 1194q0 -111 55 -246q101 156 101 252q-2 2 0 15.5t-2 36t-11 42.5q-19 52 -61.5 52t-62 -38t-19.5 -75v-39z" />
|
||||||
|
<glyph unicode="'" horiz-adv-x="309" d="M45 1012l72 266h-72v227h215v-227l-113 -266h-102z" />
|
||||||
|
<glyph unicode="(" horiz-adv-x="561" d="M66 645q0 143 29.5 292.5t73.5 261.5q92 235 159 343l30 47l162 -84q-38 -53 -86.5 -148t-82.5 -189.5t-61.5 -238t-27.5 -284.5t26.5 -282.5t64.5 -240.5q80 -207 141 -296l26 -39l-162 -84q-41 61 -96 173t-94 217.5t-70.5 257t-31.5 294.5z" />
|
||||||
|
<glyph unicode=")" horiz-adv-x="561" d="M41 -213q36 50 85.5 147t83.5 190t61.5 236.5t27.5 284.5t-26.5 282.5t-64.5 240.5q-78 205 -140 298l-27 39l162 84q41 -61 96 -173.5t94 -217t71 -257.5t32 -296t-30 -292.5t-74 -260.5q-92 -233 -159 -342l-30 -47z" />
|
||||||
|
<glyph unicode="*" horiz-adv-x="677" d="M74 1251l43 148l164 -70l-19 176h154l-19 -176l164 70l43 -148l-172 -34l115 -138l-131 -80l-78 152l-76 -152l-131 80l115 138z" />
|
||||||
|
<glyph unicode="+" horiz-adv-x="1060" d="M74 649v172h370v346h172v-346h371v-172h-371v-346h-172v346h-370z" />
|
||||||
|
<glyph unicode="," horiz-adv-x="309" d="M45 0v227h215v-227l-113 -266h-102l72 266h-72z" />
|
||||||
|
<glyph unicode="-" horiz-adv-x="444" d="M74 455v194h297v-194h-297z" />
|
||||||
|
<glyph unicode="." horiz-adv-x="321" d="M53 0v227h215v-227h-215z" />
|
||||||
|
<glyph unicode="/" horiz-adv-x="720" d="M8 -147l543 1652h162l-537 -1652h-168z" />
|
||||||
|
<glyph unicode="0" d="M68 309v887q0 42 17 106t45 107t88.5 78t144 35t144 -34t88.5 -81q55 -93 60 -178l2 -33v-887q0 -42 -17 -106t-45 -107t-88.5 -77.5t-144 -34.5t-144 33.5t-88.5 81.5q-55 94 -60 175zM289 309q0 -46 19.5 -78t54 -32t53 27.5t18.5 56.5l2 26v887q0 46 -19.5 78.5 t-54 32.5t-53 -28t-18.5 -54l-2 -29v-887z" />
|
||||||
|
<glyph unicode="1" horiz-adv-x="475" d="M25 1180v141q129 25 205 130q16 21 30 54h133v-1505h-221v1180h-147z" />
|
||||||
|
<glyph unicode="2" horiz-adv-x="731" d="M55 0v219l39 62q25 39 88.5 152.5t112.5 220t91 241.5t44 238q0 184 -73.5 184t-73.5 -184v-105h-222v105q0 389 295 389t295 -375q0 -336 -346 -928h350v-219h-600z" />
|
||||||
|
<glyph unicode="3" horiz-adv-x="686" d="M45 1071q0 249 63 343q29 42 84.5 75t134.5 33t136 -31t84.5 -71t44.5 -92q22 -71 22 -130q0 -291 -108 -399q127 -100 127 -414q0 -68 -19.5 -145.5t-47 -128t-85 -89t-136.5 -38.5t-135 31.5t-86 75.5t-48 113q-23 91 -23 230h217q2 -150 17.5 -203t59.5 -53t56.5 50.5 t12.5 104.5t1 102t0 63q-6 82 -14 95l-18 33q-12 22 -29 29q-55 22 -108 25h-19v184q133 7 156 73q12 34 12 91v105q0 146 -29 177q-16 17 -40 17q-41 0 -52.5 -49t-13.5 -207h-217z" />
|
||||||
|
<glyph unicode="4" horiz-adv-x="684" d="M25 328v194l323 983h221v-983h103v-194h-103v-328h-202v328h-342zM213 522h154v516h-13z" />
|
||||||
|
<glyph unicode="5" horiz-adv-x="704" d="M74 438h221v-59q0 -115 14.5 -159t52 -44t53 45t15.5 156v336q0 111 -70 110q-33 0 -59.5 -40t-26.5 -70h-186v792h535v-219h-344v-313q74 55 127 51q78 0 133 -40t77 -100q35 -98 35 -171v-336q0 -393 -289 -393q-78 0 -133 29.5t-84.5 71.5t-46.5 109q-24 98 -24 244z " />
|
||||||
|
<glyph unicode="6" horiz-adv-x="700" d="M66 309v856q0 356 288.5 356.5t288.5 -356.5v-94h-221q0 162 -11.5 210t-53.5 48t-56 -37t-14 -127v-268q59 37 124.5 37t119 -36t75.5 -93q37 -92 37 -189v-307q0 -90 -42 -187q-26 -61 -89 -99.5t-157.5 -38.5t-158 38.5t-88.5 99.5q-42 98 -42 187zM287 244 q0 -20 17.5 -44t49 -24t50 24.5t18.5 43.5v450q0 18 -18.5 43t-49 25t-48 -20.5t-19.5 -41.5v-456z" />
|
||||||
|
<glyph unicode="7" horiz-adv-x="589" d="M8 1286v219h557v-221l-221 -1284h-229l225 1286h-332z" />
|
||||||
|
<glyph unicode="8" horiz-adv-x="696" d="M53 322v176q0 188 115 297q-102 102 -102 276v127q0 213 147 293q57 31 135 31t135.5 -31t84 -71t42.5 -93q21 -66 21 -129v-127q0 -174 -103 -276q115 -109 115 -297v-176q0 -222 -153 -306q-60 -32 -142 -32t-141.5 32.5t-88 73.5t-44.5 96q-21 69 -21 136zM269 422 q1 -139 16.5 -187.5t57.5 -48.5t59.5 30t21.5 71t4 158t-13.5 174t-66.5 57t-66.5 -57.5t-12.5 -196.5zM284 1116q-1 -123 11 -173t53 -50t53.5 50t12.5 170t-12.5 167t-51.5 47t-52 -44t-14 -167z" />
|
||||||
|
<glyph unicode="9" horiz-adv-x="700" d="M57 340v94h222q0 -162 11 -210t53 -48t56.5 37t14.5 127v283q-59 -37 -125 -37t-119 35.5t-76 92.5q-37 96 -37 189v293q0 87 43 188q25 60 88.5 99t157.5 39t157.5 -39t88.5 -99q43 -101 43 -188v-856q0 -356 -289 -356t-289 356zM279 825q0 -18 18 -42.5t49 -24.5 t48.5 20.5t19.5 40.5v443q0 20 -17.5 43.5t-49.5 23.5t-50 -24.5t-18 -42.5v-437z" />
|
||||||
|
<glyph unicode=":" horiz-adv-x="362" d="M74 0v227h215v-227h-215zM74 893v227h215v-227h-215z" />
|
||||||
|
<glyph unicode=";" horiz-adv-x="362" d="M74 0v227h215v-227l-113 -266h-102l71 266h-71zM74 893v227h215v-227h-215z" />
|
||||||
|
<glyph unicode="<" horiz-adv-x="1058" d="M74 649v160l911 475v-199l-698 -356l698 -356v-199z" />
|
||||||
|
<glyph unicode="=" horiz-adv-x="1058" d="M74 477v172h911v-172h-911zM74 864v172h911v-172h-911z" />
|
||||||
|
<glyph unicode=">" horiz-adv-x="1058" d="M74 174v199l698 356l-698 356v199l911 -475v-160z" />
|
||||||
|
<glyph unicode="?" horiz-adv-x="645" d="M25 1260q24 67 78 131q105 128 235 122q82 -2 138 -33.5t82 -81.5q46 -88 46 -170.5t-80 -219.5l-57 -96q-18 -32 -42 -106.5t-24 -143.5v-256h-190v256q0 102 24.5 195t48 140t65.5 118t50 105t-9 67.5t-60 34.5t-78 -48t-49 -98zM199 0h215v227h-215v-227z" />
|
||||||
|
<glyph unicode="@" horiz-adv-x="872" d="M66 303v889q0 97 73 200q39 56 117 93t184.5 37t184 -37t116.5 -93q74 -105 74 -200v-793h-164l-20 56q-14 -28 -46 -48t-67 -20q-145 0 -145 172v485q0 170 145 170q71 0 113 -67v45q0 51 -45 104.5t-145.5 53.5t-145.5 -53.5t-45 -104.5v-889q0 -53 44 -103t153.5 -50 t160.5 63l152 -86q-109 -143 -320 -143q-106 0 -184 35.5t-117 90.5q-73 102 -73 193zM535 573q0 -53 48 -53t48 53v455q0 53 -48 53t-48 -53v-455z" />
|
||||||
|
<glyph unicode="A" horiz-adv-x="765" d="M20 0l228 1505h270l227 -1505h-215l-41 307h-213l-40 -307h-216zM307 541h152l-64 475l-6 39h-12z" />
|
||||||
|
<glyph unicode="B" horiz-adv-x="745" d="M82 0v1505h194q205 0 304.5 -91t99.5 -308q0 -106 -29.5 -175t-107.5 -136q14 -5 47 -38.5t54 -71.5q52 -97 52 -259q0 -414 -342 -426h-272zM303 219q74 0 109 31q55 56 55 211t-63 195q-42 26 -93 26h-8v-463zM303 885q87 0 119 39q45 55 45 138t-14.5 124t-30.5 60.5 t-45 28.5q-35 11 -74 11v-401z" />
|
||||||
|
<glyph unicode="C" horiz-adv-x="708" d="M68 309v887q0 42 17 106t45 107t88.5 78t144 35t144 -34t88.5 -81q55 -93 60 -178l2 -33v-207h-206v207q-2 0 0 11.5t-3.5 27.5t-12.5 33q-17 39 -68 39q-70 -10 -78 -111v-887q0 -43 21.5 -76.5t59.5 -33.5t59.5 27.5t21.5 56.5v233h206v-207q0 -42 -17 -106t-45 -107 t-88.5 -77.5t-144 -34.5t-144 33.5t-88.5 81.5q-55 94 -60 175z" />
|
||||||
|
<glyph unicode="D" horiz-adv-x="761" d="M82 0v1505h174q270 0 346 -113q31 -46 50.5 -95.5t28.5 -139.5t12 -177t3 -228.5t-3 -228.5t-12 -176t-28.5 -138t-50.5 -95t-80 -68q-106 -46 -266 -46h-174zM303 221q117 0 140.5 78t23.5 399v111q0 322 -23.5 398.5t-140.5 76.5v-1063z" />
|
||||||
|
<glyph unicode="E" horiz-adv-x="628" d="M82 0v1505h506v-227h-285v-395h205v-242h-205v-414h285v-227h-506z" />
|
||||||
|
<glyph unicode="F" horiz-adv-x="616" d="M82 0v1505h526v-227h-305v-395h205v-228h-205v-655h-221z" />
|
||||||
|
<glyph unicode="G" horiz-adv-x="737" d="M67 271.5q0 26.5 1 37.5v887q0 42 17 106t45 107t88.5 78t144 35t144 -34t88.5 -81q55 -93 60 -178l2 -33v-231h-221v231q0 46 -19.5 78.5t-54 32.5t-53 -28t-18.5 -54l-2 -29v-905q0 -46 19.5 -78.5t54 -32.5t53 28t18.5 54l2 29v272h-88v187h309v-750h-131l-26 72 q-70 -88 -172 -88q-203 0 -250 213q-11 48 -11 74.5z" />
|
||||||
|
<glyph unicode="H" horiz-adv-x="778" d="M82 0v1505h221v-622h172v622h221v-1505h-221v655h-172v-655h-221z" />
|
||||||
|
<glyph unicode="I" horiz-adv-x="385" d="M82 0v1505h221v-1505h-221z" />
|
||||||
|
<glyph unicode="J" horiz-adv-x="423" d="M12 -14v217q4 0 12.5 -1t29 2t35.5 12t28.5 34.5t13.5 62.5v1192h221v-1226q0 -137 -74 -216q-74 -78 -223 -78h-4q-19 0 -39 1z" />
|
||||||
|
<glyph unicode="K" horiz-adv-x="768" d="M82 0v1505h221v-526h8l195 526h215l-203 -495l230 -1010h-216l-153 655l-6 31h-6l-64 -154v-532h-221z" />
|
||||||
|
<glyph unicode="L" horiz-adv-x="604" d="M82 0v1505h221v-1300h293v-205h-514z" />
|
||||||
|
<glyph unicode="M" horiz-adv-x="991" d="M82 0v1505h270l131 -688l11 -80h4l10 80l131 688h270v-1505h-204v1010h-13l-149 -1010h-94l-142 946l-8 64h-12v-1010h-205z" />
|
||||||
|
<glyph unicode="N" horiz-adv-x="808" d="M82 0v1505h197l215 -784l18 -70h12v854h203v-1505h-197l-215 784l-18 70h-12v-854h-203z" />
|
||||||
|
<glyph unicode="O" d="M68 309v887q0 42 17 106t45 107t88.5 78t144 35t144 -34t88.5 -81q55 -93 60 -178l2 -33v-887q0 -42 -17 -106t-45 -107t-88.5 -77.5t-144 -34.5t-144 33.5t-88.5 81.5q-55 94 -60 175zM289 309q0 -46 19.5 -78t54 -32t53 27.5t18.5 56.5l2 26v887q0 46 -19.5 78.5 t-54 32.5t-53 -28t-18.5 -54l-2 -29v-887z" />
|
||||||
|
<glyph unicode="P" horiz-adv-x="720" d="M82 0v1505h221q166 0 277.5 -105.5t111.5 -345t-111.5 -346t-277.5 -106.5v-602h-221zM303 827q102 0 134 45.5t32 175.5t-33 181t-133 51v-453z" />
|
||||||
|
<glyph unicode="Q" horiz-adv-x="729" d="M68 309v887q0 42 17 106t45 107t88.5 78t144 35t144 -34t88.5 -81q55 -93 60 -178l2 -33v-887q0 -94 -45 -182q33 -43 88 -53v-189q-160 0 -227 117q-55 -18 -125 -18t-130 33.5t-88 81.5q-55 94 -60 175zM289 309q0 -46 19.5 -78t54 -32t53 27.5t18.5 56.5l2 26v887 q0 46 -19.5 78.5t-54 32.5t-53 -28t-18.5 -54l-2 -29v-887z" />
|
||||||
|
<glyph unicode="R" horiz-adv-x="739" d="M82 0v1505h221q377 0 377 -434q0 -258 -123 -342l141 -729h-221l-115 635h-59v-635h-221zM303 840q117 0 149 98q15 49 15 125t-15.5 125t-45.5 68q-44 30 -103 30v-446z" />
|
||||||
|
<glyph unicode="S" horiz-adv-x="702" d="M37 422l217 20q0 -256 104 -256q90 0 91 166q0 59 -32 117.5t-45 79.5l-54 79q-40 58 -77 113t-73.5 117t-68 148.5t-31.5 162.5q0 139 71.5 245t216.5 108h10q88 0 152 -36t94 -100q54 -120 54 -264l-217 -20q0 217 -89 217q-75 -2 -75 -146q0 -59 23 -105 q32 -66 58 -104l197 -296q31 -49 67 -139.5t36 -166.5q0 -378 -306 -378h-2q-229 0 -290 188q-31 99 -31 250z" />
|
||||||
|
<glyph unicode="T" horiz-adv-x="647" d="M4 1278v227h639v-227h-209v-1278h-221v1278h-209z" />
|
||||||
|
<glyph unicode="U" horiz-adv-x="749" d="M80 309v1196h221v-1196q0 -46 19.5 -78t54.5 -32t53 27.5t18 56.5l3 26v1196h221v-1196q0 -42 -17.5 -106t-45 -107t-88 -77.5t-144.5 -34.5t-144.5 33.5t-88.5 81.5q-55 97 -60 175z" />
|
||||||
|
<glyph unicode="V" horiz-adv-x="716" d="M18 1505h215l111 -827l8 -64h13l118 891h215l-229 -1505h-221z" />
|
||||||
|
<glyph unicode="W" horiz-adv-x="1036" d="M25 1505h204l88 -782l5 -49h16l100 831h160l100 -831h17l92 831h205l-203 -1505h-172l-115 801h-8l-115 -801h-172z" />
|
||||||
|
<glyph unicode="X" horiz-adv-x="737" d="M16 0l244 791l-240 714h218l120 -381l7 -18h8l127 399h217l-240 -714l244 -791h-217l-127 449l-4 18h-8l-132 -467h-217z" />
|
||||||
|
<glyph unicode="Y" horiz-adv-x="700" d="M14 1505h217l111 -481l6 -14h4l6 14l111 481h217l-225 -864v-641h-221v641z" />
|
||||||
|
<glyph unicode="Z" horiz-adv-x="626" d="M20 0v238l347 1048h-297v219h536v-219l-352 -1067h352v-219h-586z" />
|
||||||
|
<glyph unicode="[" horiz-adv-x="538" d="M82 -213v1718h399v-196h-202v-1325h202v-197h-399z" />
|
||||||
|
<glyph unicode="\" horiz-adv-x="792" d="M8 1692h162l614 -1872h-168z" />
|
||||||
|
<glyph unicode="]" horiz-adv-x="538" d="M57 -16h203v1325h-203v196h400v-1718h-400v197z" />
|
||||||
|
<glyph unicode="^" horiz-adv-x="1101" d="M53 809l381 696h234l381 -696h-199l-299 543l-299 -543h-199z" />
|
||||||
|
<glyph unicode="_" horiz-adv-x="1210" d="M74 -154h1063v-172h-1063v172z" />
|
||||||
|
<glyph unicode="`" horiz-adv-x="1024" d="M293 1489h215l106 -184h-159z" />
|
||||||
|
<glyph unicode="a" horiz-adv-x="681" d="M49 235q0 131 34 212t83 124t98 73t88 50.5t43 36.5v123q0 102 -57 102q-41 0 -50 -42t-9 -84v-39h-207v47q0 123 80.5 211t190 88t184.5 -74t75 -180v-688q0 -109 14 -195h-202q-18 20 -19 90h-14q-20 -37 -65.5 -71.5t-102.5 -34.5t-110.5 60t-53.5 191zM252 291 q0 -104 57 -105q35 0 60.5 19.5t25.5 48.5v287q-143 -62 -143 -250z" />
|
||||||
|
<glyph unicode="b" horiz-adv-x="686" d="M82 0v1505h207v-458q88 90 165 90t117.5 -69t40.5 -150v-715q0 -82 -41 -150.5t-118 -68.5q-33 0 -74 22.5t-66 44.5l-24 23v-74h-207zM289 246q0 -29 19.5 -48.5t42 -19.5t39 19.5t16.5 48.5v628q0 29 -16.5 48.5t-39 19.5t-42 -21.5t-19.5 -46.5v-628z" />
|
||||||
|
<glyph unicode="c" horiz-adv-x="645" d="M66 315v490q0 332 264 332q137 0 201.5 -71t64.5 -251v-88h-207v135q0 51 -12 70.5t-47 19.5q-58 0 -58 -90v-604q0 -90 58 -90q35 0 47 19.5t12 70.5v156h207v-109q0 -180 -64.5 -250.5t-201.5 -70.5q-264 0 -264 331z" />
|
||||||
|
<glyph unicode="d" horiz-adv-x="686" d="M74 203v715q0 82 41 150.5t118 68.5q33 0 74 -22.5t66 -45.5l24 -22v458h207v-1505h-207v74q-88 -90 -165 -90t-117.5 68.5t-40.5 150.5zM281 246q0 -29 16 -48.5t38.5 -19.5t42 19.5t19.5 48.5v628q0 25 -19.5 46.5t-42 21.5t-38.5 -19.5t-16 -48.5v-628z" />
|
||||||
|
<glyph unicode="e" horiz-adv-x="659" d="M66 279v563q0 36 16 94.5t42 97.5t81 71t129 32q199 0 252 -197q14 -51 14 -92v-326h-342v-256q0 -59 39 -88q16 -12 37 -12q70 10 74 113v122h192v-129q0 -37 -16.5 -93t-41 -95t-79.5 -69.5t-130 -30.5t-130.5 30.5t-80.5 73.5q-49 87 -54 160zM258 684h150v158 q0 48 -19.5 81t-53.5 33t-53.5 -28.5t-21.5 -57.5l-2 -28v-158z" />
|
||||||
|
<glyph unicode="f" horiz-adv-x="475" d="M20 934v186h105v31q0 190 51 270q23 35 71 63.5t115 28.5l97 -14v-178q-27 8 -62 8q-65 0 -65 -175v-5v-29h104v-186h-104v-934h-207v934h-105z" />
|
||||||
|
<glyph unicode="g" horiz-adv-x="700" d="M12 -184q0 94 162 170q-125 35 -125 149q0 45 40 93t89 75q-51 35 -80.5 95.5t-34.5 105.5l-4 43v305q0 35 16.5 91t41 94t79 69t126.5 31q135 0 206 -103q102 102 170 103v-185q-72 0 -120 -24l10 -70v-317q0 -37 -17.5 -90.5t-42 -90t-79 -66.5t-104.5 -30t-62 2 q-29 -25 -29 -46t11 -33.5t42 -20.5t45.5 -10t65.5 -10.5t95 -21.5t89 -41q96 -60 96 -205t-103 -212q-100 -65 -250 -65h-9q-156 2 -240 50t-84 165zM213 -150q0 -77 132 -77h3q59 0 108.5 19t49.5 54t-20.5 52.5t-90.5 29.5l-106 21q-76 -43 -76 -99zM262 509 q0 -17 15.5 -45t44.5 -28q63 6 63 101v307q-2 0 0 10q1 3 1 7q0 8 -3 19q-4 15 -9 30q-11 36 -46 36t-50.5 -25.5t-15.5 -52.5v-359z" />
|
||||||
|
<glyph unicode="h" horiz-adv-x="690" d="M82 0v1505h207v-479l32 32q79 79 145.5 79t106 -69t39.5 -150v-918h-206v887q-1 49 -50 49q-41 0 -67 -53v-883h-207z" />
|
||||||
|
<glyph unicode="i" horiz-adv-x="370" d="M82 0v1120h207v-1120h-207zM82 1298v207h207v-207h-207z" />
|
||||||
|
<glyph unicode="j" horiz-adv-x="364" d="M-45 -182q29 -8 57 -8q64 0 64 142v1168h207v-1149q0 -186 -51 -266q-23 -35 -71 -62.5t-115 -27.5t-91 12v191zM76 1298v207h207v-207h-207z" />
|
||||||
|
<glyph unicode="k" horiz-adv-x="641" d="M82 0v1505h207v-714h10l113 329h186l-149 -364l188 -756h-199l-102 453l-4 16h-10l-33 -82v-387h-207z" />
|
||||||
|
<glyph unicode="l" horiz-adv-x="370" d="M82 0v1505h207v-1505h-207z" />
|
||||||
|
<glyph unicode="m" horiz-adv-x="1021" d="M82 0v1120h207v-94q2 0 33 30q80 81 139 81q100 0 139 -125q125 125 194.5 125t109.5 -69t40 -150v-918h-194v887q-1 49 -56 49q-41 0 -78 -53v-883h-194v887q0 49 -55 49q-41 0 -78 -53v-883h-207z" />
|
||||||
|
<glyph unicode="n" horiz-adv-x="690" d="M82 0v1120h207v-94l32 32q79 79 145.5 79t106 -69t39.5 -150v-918h-206v887q-1 49 -50 49q-41 0 -67 -53v-883h-207z" />
|
||||||
|
<glyph unicode="o" horiz-adv-x="657" d="M63 279v563q0 40 15.5 96.5t40 95.5t80 71t129.5 32q199 0 252 -197q14 -51 14 -92v-576q0 -102 -56 -188q-26 -39 -80.5 -69.5t-129 -30.5t-130 30.5t-80.5 73.5q-52 92 -52 160zM257 259q0 -17 9 -44q18 -49 62 -49q70 10 71 113v563l1 19q0 19 -10 45q-18 50 -62 50 q-68 -10 -70 -114v-563q1 -1 1 -4z" />
|
||||||
|
<glyph unicode="p" horiz-adv-x="686" d="M82 -385v1505h207v-73q88 90 165 90t117.5 -69t40.5 -150v-715q0 -82 -41 -150.5t-118 -68.5q-33 0 -74 22.5t-66 44.5l-24 23v-459h-207zM289 246q0 -25 19.5 -46.5t42 -21.5t39 19.5t16.5 48.5v628q0 29 -16.5 48.5t-39 19.5t-42 -19.5t-19.5 -48.5v-628z" />
|
||||||
|
<glyph unicode="q" horiz-adv-x="686" d="M74 203v715q0 82 41 150.5t118 68.5q33 0 74 -22.5t66 -45.5l24 -22v73h207v-1505h-207v459q-88 -90 -165 -90t-117.5 68.5t-40.5 150.5zM281 246q0 -29 16 -48.5t38.5 -19.5t42 21.5t19.5 46.5v628q0 29 -19.5 48.5t-42 19.5t-38.5 -19.5t-16 -48.5v-628z" />
|
||||||
|
<glyph unicode="r" horiz-adv-x="503" d="M82 0v1120h207v-125q8 41 58.5 91.5t148.5 50.5v-230q-34 11 -77 11t-86.5 -39t-43.5 -101v-778h-207z" />
|
||||||
|
<glyph unicode="s" horiz-adv-x="630" d="M37 326h192q0 -170 97 -170q71 0 71 131q0 78 -129 202q-68 66 -98.5 99t-64 101.5t-33.5 134t12 114.5t39 95q59 100 201 104h11q161 0 211 -105q42 -86 42 -198h-193q0 131 -67 131q-63 -2 -64 -131q0 -33 23.5 -73t45 -62.5t66.5 -65.5q190 -182 191 -342 q0 -123 -64.5 -215t-199.5 -92q-197 0 -260 170q-29 76 -29 172z" />
|
||||||
|
<glyph unicode="t" horiz-adv-x="501" d="M20 934v186h105v277h207v-277h141v-186h-141v-557q0 -184 65 -184l76 8v-203q-45 -14 -112 -14t-114.5 28.5t-70 64.5t-34.5 96q-17 79 -17 187v574h-105z" />
|
||||||
|
<glyph unicode="u" horiz-adv-x="690" d="M78 203v917h207v-887q0 -49 49 -49q41 0 67 54v882h207v-1120h-207v94l-31 -32q-78 -78 -145.5 -78t-107 68.5t-39.5 150.5z" />
|
||||||
|
<glyph unicode="v" horiz-adv-x="602" d="M16 1120h201l68 -649l8 -72h16l76 721h201l-183 -1120h-204z" />
|
||||||
|
<glyph unicode="w" horiz-adv-x="905" d="M20 1120h189l65 -585l9 -64h12l96 649h123l86 -585l10 -64h13l73 649h189l-166 -1120h-172l-80 535l-10 63h-8l-91 -598h-172z" />
|
||||||
|
<glyph unicode="x" horiz-adv-x="618" d="M16 0l193 578l-176 542h194l74 -262l6 -31h4l6 31l74 262h195l-176 -542l192 -578h-201l-84 283l-6 30h-4l-6 -30l-84 -283h-201z" />
|
||||||
|
<glyph unicode="y" horiz-adv-x="634" d="M25 1120h202l82 -688l4 -57h9l4 57l82 688h202l-198 -1204q-16 -127 -94 -222t-193 -95l-92 4v184q16 -4 49 -4q61 6 97 61.5t36 122.5z" />
|
||||||
|
<glyph unicode="z" horiz-adv-x="532" d="M12 0v168l285 764h-240v188h459v-168l-285 -764h285v-188h-504z" />
|
||||||
|
<glyph unicode="{" horiz-adv-x="688" d="M61 453v163q72 0 102 49.5t30 90.5v397q0 223 96 298t342 71v-172q-135 2 -188.5 -38t-53.5 -159v-397q0 -143 -127 -221q127 -82 127 -222v-397q0 -119 53.5 -159t188.5 -38v-172q-246 -4 -342 71t-96 298v397q0 57 -41 97.5t-91 42.5z" />
|
||||||
|
<glyph unicode="|" horiz-adv-x="356" d="M82 -512v2204h192v-2204h-192z" />
|
||||||
|
<glyph unicode="}" horiz-adv-x="688" d="M57 -281q135 -2 188.5 38t53.5 159v397q0 139 127 222q-127 78 -127 221v397q0 119 -53 159t-189 38v172q246 4 342.5 -71t96.5 -298v-397q0 -63 41 -101.5t90 -38.5v-163q-72 -4 -101.5 -52.5t-29.5 -87.5v-397q0 -223 -96.5 -298t-342.5 -71v172z" />
|
||||||
|
<glyph unicode="~" horiz-adv-x="1280" d="M113 1352q35 106 115 200q34 41 94.5 74t121 33t116.5 -18.5t82 -33t83 -51.5q106 -72 174 -71q109 0 178 153l13 29l135 -57q-63 -189 -206 -276q-56 -34 -120 -34q-121 0 -272 101q-115 74 -178.5 74t-113.5 -45.5t-69 -90.5l-18 -45z" />
|
||||||
|
<glyph unicode="¡" horiz-adv-x="387" d="M74 -385l55 1100h129l55 -1100h-239zM86 893v227h215v-227h-215z" />
|
||||||
|
<glyph unicode="¢" horiz-adv-x="636" d="M66 508v489q0 297 208 328v242h123v-244q98 -16 144.5 -88t46.5 -227v-88h-189v135q0 90 -72.5 90t-72.5 -90v-604q0 -90 72 -91q74 0 73 91v155h189v-108q0 -156 -46 -228.5t-145 -89.5v-303h-123v301q-209 31 -208 330z" />
|
||||||
|
<glyph unicode="£" horiz-adv-x="817" d="M4 63q8 20 23.5 53.5t70 91.5t117.5 68q37 111 37 189t-31 184h-188v137h147l-6 21q-78 254 -78 333t15.5 140t48.5 116q72 122 231 126q190 4 267 -126q65 -108 65 -276h-213q0 201 -115 197q-47 -2 -68.5 -51t-21.5 -139.5t70 -315.5l6 -25h211v-137h-174 q25 -100 24.5 -189t-57.5 -204q16 -8 44 -24q59 -35 89 -35q74 4 82 190l188 -22q-12 -182 -81.5 -281.5t-169.5 -99.5q-51 0 -143.5 51t-127.5 51t-63.5 -25.5t-40.5 -52.5l-12 -24z" />
|
||||||
|
<glyph unicode="¥" horiz-adv-x="720" d="M25 1505h217l110 -481l6 -14h4l7 14l110 481h217l-196 -753h147v-138h-176v-137h176v-137h-176v-340h-221v340h-176v137h176v137h-176v138h147z" />
|
||||||
|
<glyph unicode="¨" horiz-adv-x="1024" d="M272 1305v200h191v-200h-191zM561 1305v200h191v-200h-191z" />
|
||||||
|
<glyph unicode="©" horiz-adv-x="1644" d="M53 751.5q0 317.5 225.5 544t543 226.5t543.5 -226.5t226 -544t-226 -542.5t-543.5 -225t-543 225t-225.5 542.5zM172 751.5q0 -266.5 191.5 -458t457.5 -191.5t459 191.5t193 459t-191.5 459t-459 191.5t-459 -192.5t-191.5 -459zM627 487v531q0 122 97 174q40 22 95 22 q147 0 182 -147l7 -49v-125h-138v142q0 11 -12 28.5t-37 17.5q-47 -2 -49 -63v-531q0 -63 49 -63q53 2 49 63v125h138v-125q0 -68 -40 -127q-18 -26 -57 -47.5t-108.5 -21.5t-117.5 49t-54 98z" />
|
||||||
|
<glyph unicode="ª" horiz-adv-x="681" d="M49 235q0 131 34 212t83 124t98 73t88 50.5t43 36.5v123q0 102 -57 102q-41 0 -50 -42t-9 -84v-39h-207v47q0 123 80.5 211t190 88t184.5 -74t75 -180v-688q0 -109 14 -195h-202q-18 20 -19 90h-14q-20 -37 -65.5 -71.5t-102.5 -34.5t-110.5 60t-53.5 191zM252 291 q0 -104 57 -105q35 0 60.5 19.5t25.5 48.5v287q-143 -62 -143 -250z" />
|
||||||
|
<glyph unicode="­" horiz-adv-x="444" d="M74 455v194h297v-194h-297z" />
|
||||||
|
<glyph unicode="®" horiz-adv-x="1644" d="M53 751.5q0 317.5 225.5 544t543 226.5t543.5 -226.5t226 -544t-226 -542.5t-543.5 -225t-543 225t-225.5 542.5zM172 751.5q0 -266.5 191.5 -458t457.5 -191.5t459 191.5t193 459t-191.5 459t-459 191.5t-459 -192.5t-191.5 -459zM625 313v879h196q231 0 232 -258 q0 -76 -16.5 -125t-71.5 -96l106 -400h-151l-95 365h-55v-365h-145zM770 805h45q43 0 65.5 21.5t27.5 45t5 61.5t-5 62.5t-27.5 46t-65.5 21.5h-45v-258z" />
|
||||||
|
<glyph unicode="¯" horiz-adv-x="1024" d="M313 1315v162h398v-162h-398z" />
|
||||||
|
<glyph unicode="²" horiz-adv-x="731" d="M55 0v219l39 62q25 39 88.5 152.5t112.5 220t91 241.5t44 238q0 184 -73.5 184t-73.5 -184v-105h-222v105q0 389 295 389t295 -375q0 -336 -346 -928h350v-219h-600z" />
|
||||||
|
<glyph unicode="³" horiz-adv-x="686" d="M45 1071q0 249 63 343q29 42 84.5 75t134.5 33t136 -31t84.5 -71t44.5 -92q22 -71 22 -130q0 -291 -108 -399q127 -100 127 -414q0 -68 -19.5 -145.5t-47 -128t-85 -89t-136.5 -38.5t-135 31.5t-86 75.5t-48 113q-23 91 -23 230h217q2 -150 17.5 -203t59.5 -53t56.5 50.5 t12.5 104.5t1 102t0 63q-6 82 -14 95l-18 33q-12 22 -29 29q-55 22 -108 25h-19v184q133 7 156 73q12 34 12 91v105q0 146 -29 177q-16 17 -40 17q-41 0 -52.5 -49t-13.5 -207h-217z" />
|
||||||
|
<glyph unicode="´" horiz-adv-x="1024" d="M410 1305l106 184h215l-162 -184h-159z" />
|
||||||
|
<glyph unicode="·" horiz-adv-x="215" d="M0 649v228h215v-228h-215z" />
|
||||||
|
<glyph unicode="¸" horiz-adv-x="1024" d="M426 -111h172v-141l-45 -133h-104l40 133h-63v141z" />
|
||||||
|
<glyph unicode="¹" horiz-adv-x="475" d="M25 1180v141q129 25 205 130q16 21 30 54h133v-1505h-221v1180h-147z" />
|
||||||
|
<glyph unicode="º" horiz-adv-x="657" d="M63 279v563q0 40 15.5 96.5t40 95.5t80 71t129.5 32q199 0 252 -197q14 -51 14 -92v-576q0 -102 -56 -188q-26 -39 -80.5 -69.5t-129 -30.5t-130 30.5t-80.5 73.5q-52 92 -52 160zM257 259q0 -17 9 -44q18 -49 62 -49q70 10 71 113v563l1 19q0 19 -10 45q-18 50 -62 50 q-68 -10 -70 -114v-563q1 -1 1 -4z" />
|
||||||
|
<glyph unicode="¿" horiz-adv-x="645" d="M41 -106q0 82 80 219l57 95q18 32 42 106.5t24 144.5v256h190v-256q0 -102 -24.5 -195.5t-48 -140.5t-65.5 -118t-50 -104.5t9 -67.5t60 -35t78 48.5t49 98.5l179 -84q-24 -66 -78 -132q-104 -126 -236 -122q-163 4 -220 115q-46 90 -46 172zM231 893v227h215v-227h-215z " />
|
||||||
|
<glyph unicode="À" horiz-adv-x="765" d="M20 0l228 1505h270l227 -1505h-215l-41 307h-213l-40 -307h-216zM141 1823h215l107 -185h-160zM307 541h152l-64 475l-6 39h-12z" />
|
||||||
|
<glyph unicode="Á" horiz-adv-x="765" d="M20 0l228 1505h270l227 -1505h-215l-41 307h-213l-40 -307h-216zM293 1638l106 185h215l-161 -185h-160zM307 541h152l-64 475l-6 39h-12z" />
|
||||||
|
<glyph unicode="Â" horiz-adv-x="765" d="M20 0l228 1505h270l227 -1505h-215l-41 307h-213l-40 -307h-216zM133 1638l141 185h220l141 -185h-189l-63 72l-61 -72h-189zM307 541h152l-64 475l-6 39h-12z" />
|
||||||
|
<glyph unicode="Ã" horiz-adv-x="765" d="M20 0l228 1505h270l227 -1505h-215l-41 307h-213l-40 -307h-216zM184 1632v152q49 39 95.5 39t104.5 -18.5t100.5 -19.5t97.5 32v-152q-51 -39 -95.5 -39t-102.5 19.5t-98 19.5t-102 -33zM307 541h152l-64 475l-6 39h-12z" />
|
||||||
|
<glyph unicode="Ä" horiz-adv-x="765" d="M20 0l228 1505h270l227 -1505h-215l-41 307h-213l-40 -307h-216zM143 1638v201h191v-201h-191zM307 541h152l-64 475l-6 39h-12zM432 1638v201h191v-201h-191z" />
|
||||||
|
<glyph unicode="Å" horiz-adv-x="765" d="M20 0l228 1505h270l227 -1505h-215l-41 307h-213l-40 -307h-216zM231 1761.5q0 61.5 45.5 102.5t109 41t107.5 -41t44 -102.5t-44 -102.5t-107.5 -41t-109 41t-45.5 102.5zM307 541h152l-64 475l-6 39h-12zM309 1761.5q0 -28.5 23.5 -50t52.5 -21.5t52.5 21.5t23.5 50 t-23.5 50t-52.5 21.5t-52.5 -21.5t-23.5 -50z" />
|
||||||
|
<glyph unicode="Æ" horiz-adv-x="1099" d="M16 0l420 1505h623v-227h-285v-395h205v-242h-205v-414h285v-227h-506v307h-227l-90 -307h-220zM393 541h160v514h-10z" />
|
||||||
|
<glyph unicode="Ç" horiz-adv-x="708" d="M68 309v887q0 42 17 106t45 107t88.5 78t144 35t144 -34t88.5 -81q55 -93 60 -178l2 -33v-207h-206v207q-2 0 0 11.5t-3.5 27.5t-12.5 33q-17 39 -68 39q-70 -10 -78 -111v-887q0 -43 21.5 -76.5t59.5 -33.5t59.5 27.5t21.5 56.5v233h206v-207q0 -42 -17 -106t-45 -107 t-88.5 -77.5t-144 -34.5t-144 33.5t-88.5 81.5q-55 94 -60 175zM268 -111v-141h64l-41 -133h104l45 133v141h-172z" />
|
||||||
|
<glyph unicode="È" horiz-adv-x="628" d="M82 0v1505h506v-227h-285v-395h205v-242h-205v-414h285v-227h-506zM111 1823h215l106 -185h-160z" />
|
||||||
|
<glyph unicode="É" horiz-adv-x="628" d="M82 0v1505h506v-227h-285v-395h205v-242h-205v-414h285v-227h-506zM236 1638l106 185h215l-162 -185h-159z" />
|
||||||
|
<glyph unicode="Ê" horiz-adv-x="628" d="M82 0v1505h506v-227h-285v-395h205v-242h-205v-414h285v-227h-506zM84 1638l141 185h219l142 -185h-189l-63 72l-62 -72h-188z" />
|
||||||
|
<glyph unicode="Ë" horiz-adv-x="628" d="M82 0v1505h506v-227h-285v-395h205v-242h-205v-414h285v-227h-506zM94 1638v201h191v-201h-191zM383 1638v201h190v-201h-190z" />
|
||||||
|
<glyph unicode="Ì" horiz-adv-x="401" d="M-6 1823h215l106 -185h-159zM98 0v1505h221v-1505h-221z" />
|
||||||
|
<glyph unicode="Í" horiz-adv-x="401" d="M82 0v1505h221v-1505h-221zM86 1638l107 185h215l-162 -185h-160z" />
|
||||||
|
<glyph unicode="Î" horiz-adv-x="370" d="M-66 1638l142 185h219l141 -185h-188l-64 72l-61 -72h-189zM74 0v1505h221v-1505h-221z" />
|
||||||
|
<glyph unicode="Ï" horiz-adv-x="372" d="M-53 1638v201h190v-201h-190zM76 0v1505h221v-1505h-221zM236 1638v201h190v-201h-190z" />
|
||||||
|
<glyph unicode="Ð" horiz-adv-x="761" d="M20 655v228h62v622h174q270 0 346 -113q31 -46 50.5 -95.5t28.5 -139.5t12 -177t3 -228.5t-3 -228.5t-12 -176t-28.5 -138t-50.5 -95t-80 -68q-106 -46 -266 -46h-174v655h-62zM303 221q117 0 141.5 81t22.5 452q2 371 -22.5 450.5t-141.5 79.5v-401h84v-228h-84v-434z " />
|
||||||
|
<glyph unicode="Ñ" horiz-adv-x="808" d="M82 0v1505h197l215 -784l18 -70h12v854h203v-1505h-197l-215 784l-18 70h-12v-854h-203zM207 1632v152q49 39 95 39t104.5 -18.5t102.5 -19.5t95 32v-152q-51 -39 -95 -39t-102.5 19.5t-100 19.5t-99.5 -33z" />
|
||||||
|
<glyph unicode="Ò" d="M68 309v887q0 42 17 106t45 107t88.5 78t144 35t144 -34t88.5 -81q55 -93 60 -178l2 -33v-887q0 -42 -17 -106t-45 -107t-88.5 -77.5t-144 -34.5t-144 33.5t-88.5 81.5q-55 94 -60 175zM121 1823h215l106 -185h-159zM289 309q0 -46 19.5 -78t54 -32t53 27.5t18.5 56.5 l2 26v887q0 46 -19.5 78.5t-54 32.5t-53 -28t-18.5 -54l-2 -29v-887z" />
|
||||||
|
<glyph unicode="Ó" d="M68 309v887q0 42 17 106t45 107t88.5 78t144 35t144 -34t88.5 -81q55 -93 60 -178l2 -33v-887q0 -42 -17 -106t-45 -107t-88.5 -77.5t-144 -34.5t-144 33.5t-88.5 81.5q-55 94 -60 175zM285 1638l106 185h215l-162 -185h-159zM289 309q0 -46 19.5 -78t54 -32t53 27.5 t18.5 56.5l2 26v887q0 46 -19.5 78.5t-54 32.5t-53 -28t-18.5 -54l-2 -29v-887z" />
|
||||||
|
<glyph unicode="Ô" d="M68 309v887q0 42 17 106t45 107t88.5 78t144 35t144 -34t88.5 -81q55 -93 60 -178l2 -33v-887q0 -42 -17 -106t-45 -107t-88.5 -77.5t-144 -34.5t-144 33.5t-88.5 81.5q-55 94 -60 175zM113 1638l141 185h219l141 -185h-188l-64 72l-61 -72h-188zM289 309q0 -46 19.5 -78 t54 -32t53 27.5t18.5 56.5l2 26v887q0 46 -19.5 78.5t-54 32.5t-53 -28t-18.5 -54l-2 -29v-887z" />
|
||||||
|
<glyph unicode="Õ" d="M68 309v887q0 42 17 106t45 107t88.5 78t144 35t144 -34t88.5 -81q55 -93 60 -178l2 -33v-887q0 -42 -17 -106t-45 -107t-88.5 -77.5t-144 -34.5t-144 33.5t-88.5 81.5q-55 94 -60 175zM164 1632v152q49 39 95 39t104.5 -18.5t102.5 -19.5t95 32v-152q-51 -39 -95 -39 t-102.5 19.5t-100 19.5t-99.5 -33zM289 309q0 -46 19.5 -78t54 -32t53 27.5t18.5 56.5l2 26v887q0 46 -19.5 78.5t-54 32.5t-53 -28t-18.5 -54l-2 -29v-887z" />
|
||||||
|
<glyph unicode="Ö" d="M68 309v887q0 42 17 106t45 107t88.5 78t144 35t144 -34t88.5 -81q55 -93 60 -178l2 -33v-887q0 -42 -17 -106t-45 -107t-88.5 -77.5t-144 -34.5t-144 33.5t-88.5 81.5q-55 94 -60 175zM123 1638v201h190v-201h-190zM289 309q0 -46 19.5 -78t54 -32t53 27.5t18.5 56.5 l2 26v887q0 46 -19.5 78.5t-54 32.5t-53 -28t-18.5 -54l-2 -29v-887zM412 1638v201h190v-201h-190z" />
|
||||||
|
<glyph unicode="Ø" d="M59 -20l47 157q-36 74 -36 148l-2 24v887q0 42 17 106t45 107t88.5 78t148 35t153.5 -43l15 47h122l-45 -150q43 -84 43 -155l2 -25v-887q0 -42 -17 -106t-45 -107t-88.5 -77.5t-150.5 -34.5t-153 43l-15 -47h-129zM289 309q0 -46 19.5 -78t54 -32t53 27.5t18.5 56.5 l2 26v488zM289 727l147 479q-8 100 -74 101q-35 0 -53 -28t-18 -54l-2 -29v-469z" />
|
||||||
|
<glyph unicode="Ù" horiz-adv-x="749" d="M80 309q0 -42 17.5 -106t45 -107t88 -77.5t144.5 -34.5t144.5 33.5t88.5 81.5q55 97 60 175l2 35v1196h-221v-1196q0 -44 -19.5 -77t-54.5 -33t-53.5 27.5t-18.5 56.5l-2 26v1196h-221v-1196zM145 1823h215l107 -185h-160z" />
|
||||||
|
<glyph unicode="Ú" horiz-adv-x="749" d="M80 309q0 -42 17.5 -106t45 -107t88 -77.5t144.5 -34.5t144.5 33.5t88.5 81.5q55 97 60 175l2 35v1196h-221v-1196q0 -44 -19.5 -77t-54.5 -33t-53.5 27.5t-18.5 56.5l-2 26v1196h-221v-1196zM307 1638l107 185h215l-162 -185h-160z" />
|
||||||
|
<glyph unicode="Û" horiz-adv-x="749" d="M80 309q0 -42 17.5 -106t45 -107t88 -77.5t144.5 -34.5t144.5 33.5t88.5 81.5q55 97 60 175l2 35v1196h-221v-1196q0 -44 -19.5 -77t-54.5 -33t-53.5 27.5t-18.5 56.5l-2 26v1196h-221v-1196zM125 1638l141 185h219l142 -185h-189l-63 72l-62 -72h-188z" />
|
||||||
|
<glyph unicode="Ü" horiz-adv-x="749" d="M80 309v1196h221v-1196q0 -46 19.5 -78t54.5 -32t53 27.5t18 56.5l3 26v1196h221v-1196q0 -42 -17.5 -106t-45 -107t-88 -77.5t-144.5 -34.5t-144.5 33.5t-88.5 81.5q-55 97 -60 175zM135 1638v201h191v-201h-191zM424 1638v201h190v-201h-190z" />
|
||||||
|
<glyph unicode="Ý" horiz-adv-x="704" d="M16 1505l226 -864v-641h221v641l225 864h-217l-111 -481l-6 -14h-4l-6 14l-111 481h-217zM254 1638l106 185h215l-161 -185h-160z" />
|
||||||
|
<glyph unicode="Þ" d="M82 0v1505h219v-241h2q166 0 277.5 -105.5t111.5 -345.5t-111.5 -346.5t-277.5 -106.5v-360h-221zM303 586q102 0 134 45t32 175t-33 181t-133 51v-452z" />
|
||||||
|
<glyph unicode="ß" horiz-adv-x="733" d="M66 0v1235q0 123 70.5 205t206.5 82t204.5 -81t68.5 -197t-88 -181q152 -88 152 -488q0 -362 -87 -475q-46 -59 -102.5 -79.5t-144.5 -20.5v193q45 0 70 25q57 57 57 357q0 316 -57 377q-25 27 -70 27v141q35 0 60.5 33t25.5 84q0 100 -86 100q-74 0 -74 -102v-1235h-206 z" />
|
||||||
|
<glyph unicode="à" horiz-adv-x="681" d="M49 235q0 131 34 212t83 124t98 73t88 50.5t43 36.5v123q0 102 -57 102q-41 0 -50 -42t-9 -84v-39h-207v47q0 123 80.5 211t190 88t184.5 -74t75 -180v-688q0 -109 14 -195h-202q-18 20 -19 90h-14q-20 -37 -65.5 -71.5t-102.5 -34.5t-110.5 60t-53.5 191zM102 1489h215 l107 -184h-160zM252 291q0 -104 57 -105q35 0 60.5 19.5t25.5 48.5v287q-143 -62 -143 -250z" />
|
||||||
|
<glyph unicode="á" horiz-adv-x="681" d="M49 235q0 131 34 212t83 124t98 73t88 50.5t43 36.5v123q0 102 -57 102q-41 0 -50 -42t-9 -84v-39h-207v47q0 123 80.5 211t190 88t184.5 -74t75 -180v-688q0 -109 14 -195h-202q-18 20 -19 90h-14q-20 -37 -65.5 -71.5t-102.5 -34.5t-110.5 60t-53.5 191zM252 291 q0 -104 57 -105q35 0 60.5 19.5t25.5 48.5v287q-143 -62 -143 -250zM264 1305l107 184h215l-162 -184h-160z" />
|
||||||
|
<glyph unicode="â" horiz-adv-x="681" d="M49 235q0 131 34 212t83 124t98 73t88 50.5t43 36.5v123q0 102 -57 102q-41 0 -50 -42t-9 -84v-39h-207v47q0 123 80.5 211t190 88t184.5 -74t75 -180v-688q0 -109 14 -195h-202q-18 20 -19 90h-14q-20 -37 -65.5 -71.5t-102.5 -34.5t-110.5 60t-53.5 191zM90 1305 l141 184h220l141 -184h-189l-63 71l-61 -71h-189zM252 291q0 -104 57 -105q35 0 60.5 19.5t25.5 48.5v287q-143 -62 -143 -250z" />
|
||||||
|
<glyph unicode="ã" horiz-adv-x="681" d="M49 235q0 131 34 212t83 124t98 73t88 50.5t43 36.5v123q0 102 -57 102q-41 0 -50 -42t-9 -84v-39h-207v47q0 123 80.5 211t190 88t184.5 -74t75 -180v-688q0 -109 14 -195h-202q-18 20 -19 90h-14q-20 -37 -65.5 -71.5t-102.5 -34.5t-110.5 60t-53.5 191zM143 1305v151 q49 39 95.5 39t104.5 -18.5t97 -19.5t101 32v-152q-51 -39 -95.5 -39t-102.5 19.5t-99 19.5t-101 -32zM252 291q0 -104 57 -105q35 0 60.5 19.5t25.5 48.5v287q-143 -62 -143 -250z" />
|
||||||
|
<glyph unicode="ä" horiz-adv-x="681" d="M49 235q0 131 34 212t83 124t98 73t88 50.5t43 36.5v123q0 102 -57 102q-41 0 -50 -42t-9 -84v-39h-207v47q0 123 80.5 211t190 88t184.5 -74t75 -180v-688q0 -109 14 -195h-202q-18 20 -19 90h-14q-20 -37 -65.5 -71.5t-102.5 -34.5t-110.5 60t-53.5 191zM102 1305v200 h191v-200h-191zM252 291q0 -104 57 -105q35 0 60.5 19.5t25.5 48.5v287q-143 -62 -143 -250zM391 1305v200h191v-200h-191z" />
|
||||||
|
<glyph unicode="å" horiz-adv-x="681" d="M49 235q0 131 34 212t83 124t98 73t88 50.5t43 36.5v123q0 102 -57 102q-41 0 -50 -42t-9 -84v-39h-207v47q0 123 80.5 211t190 88t184.5 -74t75 -180v-688q0 -109 14 -195h-202q-18 20 -19 90h-14q-20 -37 -65.5 -71.5t-102.5 -34.5t-110.5 60t-53.5 191zM188 1421.5 q0 61.5 45.5 102.5t109 41t107.5 -41t44 -102.5t-44 -102.5t-107.5 -41t-109 41t-45.5 102.5zM252 291q0 -104 57 -105q35 0 60.5 19.5t25.5 48.5v287q-143 -62 -143 -250zM266 1421.5q0 -28.5 23.5 -50t52.5 -21.5t52.5 21.5t23.5 50t-23.5 50t-52.5 21.5t-52.5 -21.5 t-23.5 -50z" />
|
||||||
|
<glyph unicode="æ" horiz-adv-x="989" d="M49 235q0 131 34 212t83 124t98 73t88 50.5t43 36.5v123q0 102 -57 102q-41 0 -50 -42t-9 -84v-39h-207v47q0 123 80.5 211t197.5 88q84 0 152 -52q66 51 162 52q199 0 251 -197q14 -51 15 -92v-326h-342v-256q0 -60 38 -88q17 -12 38 -12q70 10 73 113v122h193v-129 q0 -37 -16.5 -93t-41 -95t-80 -69.5t-130.5 -30.5q-158 0 -226 131q-102 -131 -221 -131q-59 0 -112.5 60t-53.5 191zM252 291q0 -104 57 -105q35 0 60.5 19.5t25.5 48.5v287q-143 -62 -143 -250zM588 684h149v158q0 48 -19.5 81t-53 33t-53 -28.5t-21.5 -57.5l-2 -28v-158z " />
|
||||||
|
<glyph unicode="ç" horiz-adv-x="645" d="M66 315v490q0 332 264 332q137 0 201.5 -71t64.5 -251v-88h-207v135q0 51 -12 70.5t-47 19.5q-58 0 -58 -90v-604q0 -90 58 -90q35 0 47 19.5t12 70.5v156h207v-109q0 -180 -64.5 -250.5t-201.5 -70.5q-264 0 -264 331zM238 -111v-141h63l-41 -133h105l45 133v141h-172z " />
|
||||||
|
<glyph unicode="è" horiz-adv-x="659" d="M66 279v563q0 36 16 94.5t42 97.5t81 71t129 32q199 0 252 -197q14 -51 14 -92v-326h-342v-256q0 -59 39 -88q16 -12 37 -12q70 10 74 113v122h192v-129q0 -37 -16.5 -93t-41 -95t-79.5 -69.5t-130 -30.5t-130.5 30.5t-80.5 73.5q-49 87 -54 160zM102 1489h215l107 -184 h-160zM258 684h150v158q0 48 -19.5 81t-53.5 33t-53.5 -28.5t-21.5 -57.5l-2 -28v-158z" />
|
||||||
|
<glyph unicode="é" horiz-adv-x="659" d="M66 279v563q0 36 16 94.5t42 97.5t81 71t129 32q199 0 252 -197q14 -51 14 -92v-326h-342v-256q0 -59 39 -88q16 -12 37 -12q70 10 74 113v122h192v-129q0 -37 -16.5 -93t-41 -95t-79.5 -69.5t-130 -30.5t-130.5 30.5t-80.5 73.5q-49 87 -54 160zM258 684h150v158 q0 48 -19.5 81t-53.5 33t-53.5 -28.5t-21.5 -57.5l-2 -28v-158zM264 1305l107 184h215l-162 -184h-160z" />
|
||||||
|
<glyph unicode="ê" horiz-adv-x="659" d="M66 279v563q0 36 16 94.5t42 97.5t81 71t129 32q199 0 252 -197q14 -51 14 -92v-326h-342v-256q0 -59 39 -88q16 -12 37 -12q70 10 74 113v122h192v-129q0 -37 -16.5 -93t-41 -95t-79.5 -69.5t-130 -30.5t-130.5 30.5t-80.5 73.5q-49 87 -54 160zM80 1305l141 184h219 l142 -184h-189l-63 71l-62 -71h-188zM258 684h150v158q0 48 -19.5 81t-53.5 33t-53.5 -28.5t-21.5 -57.5l-2 -28v-158z" />
|
||||||
|
<glyph unicode="ë" horiz-adv-x="659" d="M66 279v563q0 36 16 94.5t42 97.5t81 71t129 32q199 0 252 -197q14 -51 14 -92v-326h-342v-256q0 -59 39 -88q16 -12 37 -12q70 10 74 113v122h192v-129q0 -37 -16.5 -93t-41 -95t-79.5 -69.5t-130 -30.5t-130.5 30.5t-80.5 73.5q-49 87 -54 160zM90 1305v200h191v-200 h-191zM258 684h150v158q0 48 -19.5 81t-53.5 33t-53.5 -28.5t-21.5 -57.5l-2 -28v-158zM379 1305v200h190v-200h-190z" />
|
||||||
|
<glyph unicode="ì" horiz-adv-x="370" d="M-33 1489h215l107 -184h-160zM82 0h207v1120h-207v-1120z" />
|
||||||
|
<glyph unicode="í" horiz-adv-x="370" d="M82 0h207v1120h-207v-1120zM82 1305l106 184h215l-161 -184h-160z" />
|
||||||
|
<glyph unicode="î" horiz-adv-x="370" d="M-66 1305l142 184h219l141 -184h-188l-64 71l-61 -71h-189zM82 0h207v1120h-207v-1120z" />
|
||||||
|
<glyph unicode="ï" horiz-adv-x="372" d="M-53 1305v200h190v-200h-190zM82 0v1120h207v-1120h-207zM236 1305v200h190v-200h-190z" />
|
||||||
|
<glyph unicode="ð" horiz-adv-x="673" d="M76 279v579q0 279 172 279q63 0 155 -78q-12 109 -51 203l-82 -72l-55 63l100 88l-45 66l109 100q25 -27 53 -61l94 82l56 -66l-101 -88q125 -201 125 -446v-656q0 -102 -56 -188q-26 -39 -80 -69.5t-129 -30.5t-130 30.5t-80 73.5q-53 91 -53 160zM270 267.5 q-2 -11.5 2 -29t10 -34.5q16 -38 58 -38q70 10 72 113v563q-2 0 0 11t-2 28.5t-10 34.5q-16 40 -60 40q-68 -10 -70 -114v-563q2 0 0 -11.5z" />
|
||||||
|
<glyph unicode="ñ" horiz-adv-x="690" d="M82 0v1120h207v-94l32 32q79 79 145.5 79t106 -69t39.5 -150v-918h-206v887q-1 49 -50 49q-41 0 -67 -53v-883h-207zM147 1305v151q49 39 95.5 39t105 -18.5t97 -19.5t100.5 32v-152q-51 -39 -95.5 -39t-102.5 19.5t-99 19.5t-101 -32z" />
|
||||||
|
<glyph unicode="ò" horiz-adv-x="657" d="M63 279v563q0 40 15.5 96.5t40 95.5t80 71t129.5 32q199 0 252 -197q14 -51 14 -92v-576q0 -102 -56 -188q-26 -39 -80.5 -69.5t-129 -30.5t-130 30.5t-80.5 73.5q-52 92 -52 160zM98 1489h215l107 -184h-160zM258 267.5q-2 -11.5 2 -29t10 -34.5q14 -38 58 -38 q70 10 71 113v563q-2 0 0 11t-2 28.5t-10 34.5q-15 40 -59 40q-68 -10 -70 -114v-563q2 0 0 -11.5z" />
|
||||||
|
<glyph unicode="ó" horiz-adv-x="657" d="M63 279v563q0 40 15.5 96.5t40 95.5t80 71t129.5 32q199 0 252 -197q14 -51 14 -92v-576q0 -102 -56 -188q-26 -39 -80.5 -69.5t-129 -30.5t-130 30.5t-80.5 73.5q-52 92 -52 160zM258 267.5q-2 -11.5 2 -29t10 -34.5q14 -38 58 -38q70 10 71 113v563q-2 0 0 11t-2 28.5 t-10 34.5q-15 40 -59 40q-68 -10 -70 -114v-563q2 0 0 -11.5zM260 1305l107 184h215l-162 -184h-160z" />
|
||||||
|
<glyph unicode="ô" horiz-adv-x="657" d="M63 279v563q0 40 15.5 96.5t40 95.5t80 71t129.5 32q199 0 252 -197q14 -51 14 -92v-576q0 -102 -56 -188q-26 -39 -80.5 -69.5t-129 -30.5t-130 30.5t-80.5 73.5q-52 92 -52 160zM78 1305l141 184h219l142 -184h-189l-63 71l-62 -71h-188zM258 267.5q-2 -11.5 2 -29 t10 -34.5q14 -38 58 -38q70 10 71 113v563q-2 0 0 11t-2 28.5t-10 34.5q-15 40 -59 40q-68 -10 -70 -114v-563q2 0 0 -11.5z" />
|
||||||
|
<glyph unicode="õ" horiz-adv-x="657" d="M63 279v563q0 40 15.5 96.5t40 95.5t80 71t129.5 32q199 0 252 -197q14 -51 14 -92v-576q0 -102 -56 -188q-26 -39 -80.5 -69.5t-129 -30.5t-130 30.5t-80.5 73.5q-52 92 -52 160zM131 1305v151q49 39 95.5 39t104.5 -18.5t98.5 -19.5t98.5 32v-152q-51 -39 -95 -39 t-102 19.5t-101 19.5t-99 -32zM258 267.5q-2 -11.5 2 -29t10 -34.5q14 -38 58 -38q70 10 71 113v563q-2 0 0 11t-2 28.5t-10 34.5q-15 40 -59 40q-68 -10 -70 -114v-563q2 0 0 -11.5z" />
|
||||||
|
<glyph unicode="ö" horiz-adv-x="657" d="M63 279v563q0 40 15.5 96.5t40 95.5t80 71t129.5 32q199 0 252 -197q14 -51 14 -92v-576q0 -102 -56 -188q-26 -39 -80.5 -69.5t-129 -30.5t-130 30.5t-80.5 73.5q-52 92 -52 160zM90 1305v200h191v-200h-191zM258 267.5q-2 -11.5 2 -29t10 -34.5q14 -38 58 -38 q70 10 71 113v563q-2 0 0 11t-2 28.5t-10 34.5q-15 40 -59 40q-68 -10 -70 -114v-563q2 0 0 -11.5zM379 1305v200h190v-200h-190z" />
|
||||||
|
<glyph unicode="ø" horiz-adv-x="657" d="M63 279v563q0 40 15.5 96.5t40 95.5t80 71t118 32t117.5 -19l21 80h75l-30 -121q88 -84 94 -229v-576q0 -102 -56 -188q-26 -39 -80.5 -69.5t-120.5 -30.5t-112 16l-20 -78h-80l31 121q-41 39 -64.5 97.5t-25.5 97.5zM258 436l125 486q-18 35 -55 34q-68 -10 -70 -114 v-406zM274 197q17 -31 54 -31q70 10 71 113v403z" />
|
||||||
|
<glyph unicode="ù" horiz-adv-x="690" d="M78 203v917h207v-887q0 -49 49 -49q41 0 67 54v882h207v-1120h-207v94l-31 -32q-78 -78 -145.5 -78t-107 68.5t-39.5 150.5zM113 1489h215l106 -184h-160z" />
|
||||||
|
<glyph unicode="ú" horiz-adv-x="690" d="M78 203v917h207v-887q0 -49 49 -49q41 0 67 54v882h207v-1120h-207v94l-31 -32q-78 -78 -145.5 -78t-107 68.5t-39.5 150.5zM274 1305l107 184h215l-162 -184h-160z" />
|
||||||
|
<glyph unicode="û" horiz-adv-x="690" d="M78 203v917h207v-887q0 -49 49 -49q41 0 67 54v882h207v-1120h-207v94l-31 -32q-78 -78 -145.5 -78t-107 68.5t-39.5 150.5zM94 1305l142 184h219l141 -184h-188l-64 71l-61 -71h-189z" />
|
||||||
|
<glyph unicode="ü" horiz-adv-x="690" d="M78 203v917h207v-887q0 -49 49 -49q41 0 67 54v882h207v-1120h-207v94l-31 -32q-78 -78 -145.5 -78t-107 68.5t-39.5 150.5zM106 1305v200h191v-200h-191zM395 1305v200h191v-200h-191z" />
|
||||||
|
<glyph unicode="ý" horiz-adv-x="634" d="M25 1120l190 -1153q0 -68 -36 -123t-97 -61l-49 4v-184q70 -4 92 -4q115 0 192.5 95t94.5 222l198 1204h-202l-82 -688l-4 -57h-9l-4 57l-82 688h-202zM231 1305l107 184h215l-162 -184h-160z" />
|
||||||
|
<glyph unicode="þ" horiz-adv-x="686" d="M82 -385v1890h207v-458q88 90 165 90t117.5 -69t40.5 -150v-715q0 -82 -41 -150.5t-118 -68.5q-33 0 -74 22.5t-66 44.5l-24 23v-459h-207zM289 246q0 -25 19.5 -46.5t42 -21.5t39 19.5t16.5 48.5v628q0 29 -16.5 48.5t-39 19.5t-42 -19.5t-19.5 -48.5v-628z" />
|
||||||
|
<glyph unicode="ÿ" horiz-adv-x="634" d="M25 1120h202l82 -688l4 -57h9l4 57l82 688h202l-198 -1204q-16 -127 -94 -222t-193 -95l-92 4v184q16 -4 49 -4q61 6 97 61.5t36 122.5zM78 1305v200h190v-200h-190zM367 1305v200h190v-200h-190z" />
|
||||||
|
<glyph unicode="Œ" horiz-adv-x="983" d="M68 309v887q0 41 17 101.5t45 100.5t88.5 73.5t143.5 33.5h580v-227h-285v-395h205v-242h-205v-414h285v-227h-580q-84 0 -144 31.5t-88 78.5q-55 91 -60 169zM289 309q0 -46 19.5 -78t54 -32t53 27.5t18.5 56.5l2 26v901q-6 96 -74 97q-35 0 -53 -28t-18 -54l-2 -29 v-887z" />
|
||||||
|
<glyph unicode="œ" horiz-adv-x="995" d="M63 279v563q0 40 15.5 96.5t40 95.5t80 71t145.5 32t156 -60q66 59 170 60q199 0 252 -197q14 -51 14 -92v-326h-342v-250q0 -46 22.5 -76t53.5 -30q70 10 73 113v122h193v-129q0 -37 -16.5 -93t-41 -95t-80 -69.5t-146 -30.5t-154.5 57q-68 -57 -156 -57t-143.5 30.5 t-80.5 73.5q-52 92 -52 160zM258 267.5q-2 -11.5 2 -29t10 -34.5q14 -38 58 -38q70 10 71 113v563q-2 0 0 11t-2 28.5t-10 34.5q-15 40 -59 40q-68 -10 -70 -114v-563q2 0 0 -11.5zM594 684h149v158q0 48 -19 81t-58 33t-55.5 -37.5t-16.5 -70.5v-164z" />
|
||||||
|
<glyph unicode="Ÿ" horiz-adv-x="704" d="M16 1505h217l111 -481l6 -14h4l6 14l111 481h217l-225 -864v-641h-221v641zM113 1638v201h190v-201h-190zM401 1638v201h191v-201h-191z" />
|
||||||
|
<glyph unicode="ˆ" horiz-adv-x="1021" d="M260 1305l141 184h220l141 -184h-189l-63 71l-61 -71h-189z" />
|
||||||
|
<glyph unicode="˜" horiz-adv-x="1024" d="M313 1305v151q49 39 95.5 39t104.5 -18.5t97 -19.5t101 32v-152q-51 -39 -95.5 -39t-102.5 19.5t-99 19.5t-101 -32z" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="952" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="1905" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="952" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="1905" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="635" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="476" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="317" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="317" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="238" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="381" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="105" />
|
||||||
|
<glyph unicode="‐" horiz-adv-x="444" d="M74 455v194h297v-194h-297z" />
|
||||||
|
<glyph unicode="‑" horiz-adv-x="444" d="M74 455v194h297v-194h-297z" />
|
||||||
|
<glyph unicode="‒" horiz-adv-x="444" d="M74 455v194h297v-194h-297z" />
|
||||||
|
<glyph unicode="–" horiz-adv-x="806" d="M74 649v195h659v-195h-659z" />
|
||||||
|
<glyph unicode="—" horiz-adv-x="972" d="M74 649v195h825v-195h-825z" />
|
||||||
|
<glyph unicode="‘" horiz-adv-x="309" d="M49 1012v227l113 266h102l-71 -266h71v-227h-215z" />
|
||||||
|
<glyph unicode="’" horiz-adv-x="309" d="M45 1012l72 266h-72v227h215v-227l-113 -266h-102z" />
|
||||||
|
<glyph unicode="‚" horiz-adv-x="309" d="M45 0v227h215v-227l-113 -266h-102l72 266h-72z" />
|
||||||
|
<glyph unicode="“" horiz-adv-x="624" d="M53 1012v227l113 266h102l-71 -266h71v-227h-215zM356 1012v227l113 266h102l-71 -266h71v-227h-215z" />
|
||||||
|
<glyph unicode="”" horiz-adv-x="624" d="M53 1012l72 266h-72v227h215v-227l-112 -266h-103zM356 1012l72 266h-72v227h215v-227l-112 -266h-103z" />
|
||||||
|
<glyph unicode="„" horiz-adv-x="624" d="M53 0v227h215v-227l-112 -266h-103l72 266h-72zM356 0v227h215v-227l-112 -266h-103l72 266h-72z" />
|
||||||
|
<glyph unicode="•" horiz-adv-x="663" d="M82 815q0 104 72.5 177t177 73t177.5 -72.5t73 -177t-73 -177.5t-177 -73t-177 73t-73 177z" />
|
||||||
|
<glyph unicode="…" horiz-adv-x="964" d="M53 0v227h215v-227h-215zM375 0v227h215v-227h-215zM696 0v227h215v-227h-215z" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="381" />
|
||||||
|
<glyph unicode="‹" horiz-adv-x="1058" d="M74 649v160l911 475v-199l-698 -356l698 -356v-199z" />
|
||||||
|
<glyph unicode="›" horiz-adv-x="1058" d="M74 174v199l698 356l-698 356v199l911 -475v-160z" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="476" />
|
||||||
|
<glyph unicode="€" horiz-adv-x="813" d="M53 547v137h107v137h-107v137h107v238q0 42 17.5 106t45 107t88 78t144.5 35t144 -34t88 -81q53 -90 61 -178l2 -33v-84h-207v84q-2 0 0 11.5t-3 27.5t-12 33q-18 39 -69 39q-70 -10 -78 -111v-238h233v-137h-233v-137h233v-137h-233v-238q0 -43 21.5 -76.5t59.5 -33.5 t58.5 27.5t20.5 56.5l2 26v84h207v-84q0 -38 -17.5 -104t-45.5 -109t-88 -77.5t-144 -34.5t-144.5 33.5t-88.5 81.5q-55 97 -60 175l-2 35v238h-107z" />
|
||||||
|
<glyph unicode="™" horiz-adv-x="937" d="M74 1401v104h321v-104h-104v-580h-113v580h-104zM440 821v684h138l67 -319h6l68 319h137v-684h-104v449l-78 -449h-51l-80 449v-449h-103z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1120" d="M0 0v1120h1120v-1120h-1120z" />
|
||||||
|
<glyph unicode="fi" horiz-adv-x="772" d="M20 934v186h105v31q0 172 31 231q16 31 42 67q53 71 181 71q59 0 127 -13l20 -2v-184q-41 12 -91 12t-69.5 -18.5t-25.5 -58.5q-8 -52 -8 -107v-29h358v-1120h-207v934h-151v-934h-207v934h-105z" />
|
||||||
|
<glyph unicode="fl" horiz-adv-x="772" d="M20 934v186h105v31q0 172 31 231q16 31 42 67q53 71 181 71q59 0 127 -13l20 -2h164v-1505h-207v1329q-37 4 -67.5 4t-50 -18.5t-25.5 -58.5q-8 -52 -8 -107v-29h104v-186h-104v-934h-207v934h-105z" />
|
||||||
|
<glyph unicode="ffi" horiz-adv-x="1320" d="M20 934v186h105v31q0 190 51 270q23 35 71 63.5t115 28.5l97 -14v-178q-27 8 -62 8q-66 0 -65 -180v-29h104v-186h-104v-934h-207v934h-105zM495 934v186h105v31q0 190 51 270q23 35 71 63.5t115 28.5l97 -14v-178q-27 8 -62 8q-66 0 -65 -180v-29h104v-186h-104v-934 h-207v934h-105zM1032 0v1120h207v-1120h-207zM1032 1298v207h207v-207h-207z" />
|
||||||
|
<glyph unicode="ffl" horiz-adv-x="1320" d="M20 934v186h105v31q0 190 51 270q23 35 71 63.5t115 28.5l97 -14v-178q-27 8 -62 8q-66 0 -65 -180v-29h104v-186h-104v-934h-207v934h-105zM495 934v186h105v31q0 190 51 270q23 35 71 63.5t115 28.5l97 -14v-178q-27 8 -62 8q-66 0 -65 -180v-29h104v-186h-104v-934 h-207v934h-105zM1032 0v1505h207v-1505h-207z" />
|
||||||
|
</font>
|
||||||
|
</defs></svg>
|
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 45 KiB |
After Width: | Height: | Size: 8.4 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 7.7 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 80 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 9.9 KiB |
After Width: | Height: | Size: 9.8 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 45 KiB |
After Width: | Height: | Size: 134 KiB |
After Width: | Height: | Size: 73 KiB |
After Width: | Height: | Size: 240 KiB |
After Width: | Height: | Size: 802 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 281 B |
After Width: | Height: | Size: 590 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 361 B |
After Width: | Height: | Size: 687 B |
After Width: | Height: | Size: 179 B |
After Width: | Height: | Size: 308 B |
After Width: | Height: | Size: 9.9 KiB |
|
@ -0,0 +1,182 @@
|
||||||
|
<!-- Main jumbotron for a primary marketing message or call to action -->
|
||||||
|
<div id="jumbotron-mask">
|
||||||
|
<div id="jumbotron">
|
||||||
|
<div class="container">
|
||||||
|
<div class="col-lg-6 col-md-6">
|
||||||
|
<h2 class="rls-l">
|
||||||
|
Service discovery and configuration made easy.
|
||||||
|
Distributed, highly available, and
|
||||||
|
datacenter-aware.
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div class="jumbo-logo-wrap col-lg-offset-1 col-lg-5 col-md-6 hidden-xs hidden-sm">
|
||||||
|
<div class="jumbo-logo"></div>
|
||||||
|
</div>
|
||||||
|
<!-- <p><a class="btn btn-primary btn-lg">Learn more »</a></p> -->
|
||||||
|
</div>
|
||||||
|
<div class="jumbotron-dots"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="features">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row double-row">
|
||||||
|
<div class="col-lg-6 col-md-6">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-5 col-md-5">
|
||||||
|
<span class="icn discovery"></span>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-7 col-md-7">
|
||||||
|
<h2>Service Discovery</h2>
|
||||||
|
<p>
|
||||||
|
Terraform makes it simple for services to register themselves
|
||||||
|
and to discover other services via a DNS or HTTP interface.
|
||||||
|
Register external services such as SaaS providers as well.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6 col-md-6">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-5 col-md-5">
|
||||||
|
<span class="icn health"></span>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-7 col-md-7">
|
||||||
|
<h2>Failure Detection</h2>
|
||||||
|
<p>Pairing service discovery with health checking prevents routing requests to unhealthy hosts and enables services to easily provide circuit breakers.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row double-row">
|
||||||
|
<div class="col-lg-6 col-md-6">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-5 col-md-5">
|
||||||
|
<span class="icn multi"></span>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-7 col-md-7">
|
||||||
|
<h2>Multi Datacenter</h2>
|
||||||
|
<p>Terraform scales to multiple datacenters out of the box with no complicated configuration. Look up services in other datacenters, or keep the request local.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6 col-md-6">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-5 col-md-5">
|
||||||
|
<span class="icn config"></span>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-7 col-md-7">
|
||||||
|
<h2>Key/Value Storage</h2>
|
||||||
|
<p>Flexible key/value store for dynamic configuration, feature flagging, coordination, leader election and more. Long poll for near-instant notification of configuration changes.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div> <!-- /container -->
|
||||||
|
</div> <!-- /features -->
|
||||||
|
|
||||||
|
<div id="demos">
|
||||||
|
<div class="container">
|
||||||
|
<div class="terminals row">
|
||||||
|
|
||||||
|
<div class="col-xs-12 col-lg-12 explantion">
|
||||||
|
<h2>DNS Query Interface</h2>
|
||||||
|
<p>
|
||||||
|
Look up services using Terraform's built-in DNS server. Support
|
||||||
|
existing infrastructure without any code change.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="terminal-item col-xs-12 col-lg-12">
|
||||||
|
<div class="terminal">
|
||||||
|
<header>
|
||||||
|
<h4>Terminal</h4>
|
||||||
|
<ul class='shell-dots'>
|
||||||
|
<li></li>
|
||||||
|
<li></li>
|
||||||
|
<li></li>
|
||||||
|
</ul>
|
||||||
|
</header>
|
||||||
|
<div class="terminal-window">
|
||||||
|
<div class="terminal">
|
||||||
|
<div class="display">
|
||||||
|
<p class="command"><span class="txt-r">admin@hashicorp</span>: dig web-frontend.service.terraform. ANY</p>
|
||||||
|
<p>; <<>> DiG 9.8.3-P1 <<>> web-frontend.service.terraform. ANY</p>
|
||||||
|
<p>;; global options: +cmd</p>
|
||||||
|
<p> </p>
|
||||||
|
<p>;; Got answer:</p>
|
||||||
|
<p>;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29981</p>
|
||||||
|
<p>;; flags: qr aa rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0</p>
|
||||||
|
<p> </p>
|
||||||
|
<p>;; QUESTION SECTION:</p>
|
||||||
|
<p>;web-frontend.service.terraform. IN ANY</p>
|
||||||
|
<p> </p>
|
||||||
|
<p>;; ANSWER SECTION:</p>
|
||||||
|
<p>web-frontend.service.terraform. 0 IN A <span class="txt-p">10.0.3.83</span></p>
|
||||||
|
<p>web-frontend.service.terraform. 0 IN A <span class="txt-p">10.0.1.109</span></p>
|
||||||
|
<p class="command"><span class="txt-r">admin@hashicorp</span>: <span class="cursor"> </span></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div> <!-- /.terminal-item -->
|
||||||
|
|
||||||
|
<div class="col-xs-12 col-lg-12 explantion">
|
||||||
|
<h2>Key Value Storage</h2>
|
||||||
|
<p>
|
||||||
|
Terraform provides a hierarchical key/value store with a simple HTTP API.
|
||||||
|
Managing configuration has never been simpler.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="terminal-item col-xs-12 col-lg-12">
|
||||||
|
<div class="terminal">
|
||||||
|
<header>
|
||||||
|
<h4>Terminal</h4>
|
||||||
|
<ul class='shell-dots'>
|
||||||
|
<li></li>
|
||||||
|
<li></li>
|
||||||
|
<li></li>
|
||||||
|
</ul>
|
||||||
|
</header>
|
||||||
|
<div class="terminal-window">
|
||||||
|
<div class="terminal">
|
||||||
|
<div class="display">
|
||||||
|
<p class="command"><span class="txt-r">admin@hashicorp</span>: curl -X PUT -d 'bar' http://localhost:8500/v1/kv/foo</p>
|
||||||
|
<p>true</p>
|
||||||
|
<p class="command"><span class="txt-r">admin@hashicorp</span>: curl http://localhost:8500/v1/kv/foo</p>
|
||||||
|
<p>[</p>
|
||||||
|
<p> {</p>
|
||||||
|
<p> "CreateIndex": 100,</p>
|
||||||
|
<p> "ModifyIndex": 200,</p>
|
||||||
|
<p> "Key": "foo",</p>
|
||||||
|
<p> "Flags": 0,</p>
|
||||||
|
<p> "Value": <span class="txt-p">"YmFy"</span></p>
|
||||||
|
<p> }</p>
|
||||||
|
<p>]</p>
|
||||||
|
<p class="command"><span class="txt-r">admin@hashicorp</span>: <span class="cursor"> </span></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div> <!-- /.terminal-item -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div><!-- /#demos -->
|
||||||
|
|
||||||
|
<div id="cta">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="intro">
|
||||||
|
<div class="left col-xs-12 col-sm-offset-2 col-sm-4">
|
||||||
|
<p>The intro and getting started guide contain
|
||||||
|
a simple and approachable walkthrough for running Terraform locally.</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-offset-5 col-xs-12 col-sm-6 col-sm-offset-0 right">
|
||||||
|
<a class="outline-btn purple" href="/intro/index.html">Read the intro »</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,125 @@
|
||||||
|
---
|
||||||
|
layout: "intro"
|
||||||
|
page_title: "Run the Agent"
|
||||||
|
sidebar_current: "gettingstarted-agent"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Run the Terraform Agent
|
||||||
|
|
||||||
|
After Terraform is installed, the agent must be run. The agent can either run
|
||||||
|
in a server or client mode. Each datacenter must have at least one server,
|
||||||
|
although 3 or 5 is recommended. A single server deployment is _**highly**_ discouraged
|
||||||
|
as data loss is inevitable in a failure scenario. [This guide](/docs/guides/bootstrapping.html)
|
||||||
|
covers bootstrapping a new datacenter. All other agents run in client mode, which
|
||||||
|
is a very lightweight process that registers services, runs health checks,
|
||||||
|
and forwards queries to servers. The agent must be run for every node that
|
||||||
|
will be part of the cluster.
|
||||||
|
|
||||||
|
## Starting the Agent
|
||||||
|
|
||||||
|
For simplicity, we'll run a single Terraform agent in server mode right now:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ terraform agent -server -bootstrap -data-dir /tmp/consul
|
||||||
|
==> WARNING: Bootstrap mode enabled! Do not enable unless necessary
|
||||||
|
==> WARNING: It is highly recommended to set GOMAXPROCS higher than 1
|
||||||
|
==> Starting Terraform agent...
|
||||||
|
==> Starting Terraform agent RPC...
|
||||||
|
==> Terraform agent running!
|
||||||
|
Node name: 'Armons-MacBook-Air'
|
||||||
|
Datacenter: 'dc1'
|
||||||
|
Server: true (bootstrap: true)
|
||||||
|
Client Addr: 127.0.0.1 (HTTP: 8500, DNS: 8600, RPC: 8400)
|
||||||
|
Cluster Addr: 10.1.10.38 (LAN: 8301, WAN: 8302)
|
||||||
|
|
||||||
|
==> Log data will now stream in as it occurs:
|
||||||
|
|
||||||
|
[INFO] serf: EventMemberJoin: Armons-MacBook-Air.local 10.1.10.38
|
||||||
|
[INFO] raft: Node at 10.1.10.38:8300 [Follower] entering Follower state
|
||||||
|
[INFO] terraform: adding server for datacenter: dc1, addr: 10.1.10.38:8300
|
||||||
|
[ERR] agent: failed to sync remote state: rpc error: No cluster leader
|
||||||
|
[WARN] raft: Heartbeat timeout reached, starting election
|
||||||
|
[INFO] raft: Node at 10.1.10.38:8300 [Candidate] entering Candidate state
|
||||||
|
[INFO] raft: Election won. Tally: 1
|
||||||
|
[INFO] raft: Node at 10.1.10.38:8300 [Leader] entering Leader state
|
||||||
|
[INFO] terraform: cluster leadership acquired
|
||||||
|
[INFO] terraform: New leader elected: Armons-MacBook-Air
|
||||||
|
[INFO] terraform: member 'Armons-MacBook-Air' joined, marking health alive
|
||||||
|
```
|
||||||
|
|
||||||
|
As you can see, the Terraform agent has started and has output some log
|
||||||
|
data. From the log data, you can see that our agent is running in server mode,
|
||||||
|
and has claimed leadership of the cluster. Additionally, the local member has
|
||||||
|
been marked as a healthy member of the cluster.
|
||||||
|
|
||||||
|
<div class="alert alert-block alert-warning">
|
||||||
|
<strong>Note for OS X Users:</strong> Terraform uses your hostname as the
|
||||||
|
default node name. If your hostname contains periods, DNS queries to
|
||||||
|
that node will not work with Terraform. To avoid this, explicitly set
|
||||||
|
the name of your node with the <code>-node</code> flag.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## Cluster Members
|
||||||
|
|
||||||
|
If you run `terraform members` in another terminal, you can see the members of
|
||||||
|
the Terraform cluster. You should only see one member (yourself). We'll cover
|
||||||
|
joining clusters in the next section.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ terraform members
|
||||||
|
Armons-MacBook-Air 10.1.10.38:8301 alive role=terraform,dc=dc1,vsn=1,vsn_min=1,vsn_max=1,port=8300,bootstrap=1
|
||||||
|
```
|
||||||
|
|
||||||
|
The output shows our own node, the address it is running on, its
|
||||||
|
health state, and some metadata associated with the node. Some important
|
||||||
|
metadata keys to recognize are the `role` and `dc` keys. These tell you
|
||||||
|
the service name and the datacenter that member is within. These can be
|
||||||
|
used to lookup nodes and services using the DNS interface, which is covered
|
||||||
|
shortly.
|
||||||
|
|
||||||
|
The output from the `members` command is generated based on the
|
||||||
|
[gossip protocol](/docs/internals/gossip.html) and is eventually consistent.
|
||||||
|
For a strongly consistent view of the world, use the
|
||||||
|
[HTTP API](/docs/agent/http.html), which forwards the request to the
|
||||||
|
Terraform servers:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ curl localhost:8500/v1/catalog/nodes
|
||||||
|
[{"Node":"Armons-MacBook-Air","Address":"10.1.10.38"}]
|
||||||
|
```
|
||||||
|
|
||||||
|
In addition to the HTTP API, the
|
||||||
|
[DNS interface](/docs/agent/dns.html) can be used to query the node. Note
|
||||||
|
that you have to make sure to point your DNS lookups to the Terraform agent's
|
||||||
|
DNS server, which runs on port 8600 by default. The format of the DNS
|
||||||
|
entries (such as "Armons-MacBook-Air.node.terraform") will be covered later.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ dig @127.0.0.1 -p 8600 Armons-MacBook-Air.node.terraform
|
||||||
|
...
|
||||||
|
|
||||||
|
;; QUESTION SECTION:
|
||||||
|
;Armons-MacBook-Air.node.terraform. IN A
|
||||||
|
|
||||||
|
;; ANSWER SECTION:
|
||||||
|
Armons-MacBook-Air.node.terraform. 0 IN A 10.1.10.38
|
||||||
|
```
|
||||||
|
|
||||||
|
## Stopping the Agent
|
||||||
|
|
||||||
|
You can use `Ctrl-C` (the interrupt signal) to gracefully halt the agent.
|
||||||
|
After interrupting the agent, you should see it leave the cluster gracefully
|
||||||
|
and shut down.
|
||||||
|
|
||||||
|
By gracefully leaving, Terraform notifies other cluster members that the
|
||||||
|
node _left_. If you had forcibly killed the agent process, other members
|
||||||
|
of the cluster would have detected that the node _failed_. When a member leaves,
|
||||||
|
its services and checks are removed from the catalog. When a member fails,
|
||||||
|
its health is simply marked as critical, but is not removed from the catalog.
|
||||||
|
Terraform will automatically try to reconnect to _failed_ nodes, which allows it
|
||||||
|
to recover from certain network conditions, while _left_ nodes are no longer contacted.
|
||||||
|
|
||||||
|
Additionally, if an agent is operating as a server, a graceful leave is important
|
||||||
|
to avoid causing a potential availability outage affecting the [consensus protocol](/docs/internals/consensus.html).
|
||||||
|
See the [guides section](/docs/guides/index.html) to safely add and remove servers.
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
---
|
||||||
|
layout: "intro"
|
||||||
|
page_title: "Registering Health Checks"
|
||||||
|
sidebar_current: "gettingstarted-checks"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Health Checks
|
||||||
|
|
||||||
|
We've now seen how simple it is to run Terraform, add nodes and services, and
|
||||||
|
query those nodes and services. In this section we will continue by adding
|
||||||
|
health checks to both nodes and services, a critical component of service
|
||||||
|
discovery that prevents using services that are unhealthy.
|
||||||
|
|
||||||
|
This page will build upon the previous page and assumes you have a
|
||||||
|
two node cluster running.
|
||||||
|
|
||||||
|
## Defining Checks
|
||||||
|
|
||||||
|
Similarly to a service, a check can be registered either by providing a
|
||||||
|
[check definition](/docs/agent/checks.html)
|
||||||
|
, or by making the appropriate calls to the
|
||||||
|
[HTTP API](/docs/agent/http.html).
|
||||||
|
|
||||||
|
We will use the check definition, because just like services, definitions
|
||||||
|
are the most common way to setup checks.
|
||||||
|
|
||||||
|
Create two definition files in the Terraform configuration directory of
|
||||||
|
the second node.
|
||||||
|
The first file will add a host-level check, and the second will modify the web
|
||||||
|
service definition to add a service-level check.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ echo '{"check": {"name": "ping", "script": "ping -c1 google.com >/dev/null", "interval": "30s"}}' >/etc/terraform.d/ping.json
|
||||||
|
|
||||||
|
$ echo '{"service": {"name": "web", "tags": ["rails"], "port": 80,
|
||||||
|
"check": {"script": "curl localhost:80 >/dev/null 2>&1", "interval": "10s"}}}' >/etc/terraform.d/web.json
|
||||||
|
```
|
||||||
|
|
||||||
|
The first definition adds a host-level check named "ping". This check runs
|
||||||
|
on a 30 second interval, invoking `ping -c1 google.com`. If the command
|
||||||
|
exits with a non-zero exit code, then the node will be flagged unhealthy.
|
||||||
|
|
||||||
|
The second command modifies the web service and adds a check that uses
|
||||||
|
curl every 10 seconds to verify that the web server is running.
|
||||||
|
|
||||||
|
Restart the second agent, or send a `SIGHUP` to it. We should now see the
|
||||||
|
following log lines:
|
||||||
|
|
||||||
|
```
|
||||||
|
==> Starting Terraform agent...
|
||||||
|
...
|
||||||
|
[INFO] agent: Synced service 'web'
|
||||||
|
[INFO] agent: Synced check 'service:web'
|
||||||
|
[INFO] agent: Synced check 'ping'
|
||||||
|
[WARN] Check 'service:web' is now critical
|
||||||
|
```
|
||||||
|
|
||||||
|
The first few log lines indicate that the agent has synced the new
|
||||||
|
definitions. The last line indicates that the check we added for
|
||||||
|
the `web` service is critical. This is because we're not actually running
|
||||||
|
a web server and the curl test is failing!
|
||||||
|
|
||||||
|
## Checking Health Status
|
||||||
|
|
||||||
|
Now that we've added some simple checks, we can use the HTTP API to check
|
||||||
|
them. First, we can look for any failing checks. You can run this curl
|
||||||
|
on either node:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ curl http://localhost:8500/v1/health/state/critical
|
||||||
|
[{"Node":"agent-two","CheckID":"service:web","Name":"Service 'web' check","Status":"critical","Notes":"","ServiceID":"web","ServiceName":"web"}]
|
||||||
|
```
|
||||||
|
|
||||||
|
We can see that there is only a single check in the `critical` state, which is
|
||||||
|
our `web` service check.
|
||||||
|
|
||||||
|
Additionally, we can attempt to query the web service using DNS. Terraform
|
||||||
|
will not return any results, since the service is unhealthy:
|
||||||
|
|
||||||
|
```
|
||||||
|
dig @127.0.0.1 -p 8600 web.service.terraform
|
||||||
|
...
|
||||||
|
|
||||||
|
;; QUESTION SECTION:
|
||||||
|
;web.service.terraform. IN A
|
||||||
|
```
|
||||||
|
|
||||||
|
This section should have shown that checks can be easily added. Check definitions
|
||||||
|
can be updated by changing configuration files and sending a `SIGHUP` to the agent.
|
||||||
|
Alternatively the HTTP API can be used to add, remove and modify checks dynamically.
|
||||||
|
The API allows for a "dead man's switch" or [TTL based check](/docs/agent/checks.html).
|
||||||
|
TTL checks can be used to integrate an application more tightly with Terraform, enabling
|
||||||
|
business logic to be evaluated as part of passing a check.
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
---
|
||||||
|
layout: "intro"
|
||||||
|
page_title: "Installing Terraform"
|
||||||
|
sidebar_current: "gettingstarted-install"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Install Terraform
|
||||||
|
|
||||||
|
Terraform must first be installed on every node that will be a member of a
|
||||||
|
Terraform cluster. To make installation easy, Terraform is distributed as a
|
||||||
|
[binary package](/downloads.html) for all supported platforms and
|
||||||
|
architectures. This page will not cover how to compile Terraform from
|
||||||
|
source.
|
||||||
|
|
||||||
|
## Installing Terraform
|
||||||
|
|
||||||
|
To install Terraform, find the [appropriate package](/downloads.html) for
|
||||||
|
your system and download it. Terraform is packaged as a "zip" archive.
|
||||||
|
|
||||||
|
After downloading Terraform, unzip the package. Copy the `terraform` binary to
|
||||||
|
somewhere on the PATH so that it can be executed. On Unix systems,
|
||||||
|
`~/bin` and `/usr/local/bin` are common installation directories,
|
||||||
|
depending on if you want to restrict the install to a single user or
|
||||||
|
expose it to the entire system. On Windows systems, you can put it wherever
|
||||||
|
you would like.
|
||||||
|
|
||||||
|
### OS X
|
||||||
|
|
||||||
|
If you are using [homebrew](http://brew.sh/#install) as a package manager,
|
||||||
|
than you can install terraform as simple as:
|
||||||
|
```
|
||||||
|
brew cask install terraform
|
||||||
|
```
|
||||||
|
|
||||||
|
if you are missing the [cask plugin](http://caskroom.io/) you can install it with:
|
||||||
|
```
|
||||||
|
brew install caskroom/cask/brew-cask
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verifying the Installation
|
||||||
|
|
||||||
|
After installing Terraform, verify the installation worked by opening a new
|
||||||
|
terminal session and checking that `terraform` is available. By executing
|
||||||
|
`terraform` you should see help output similar to that below:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ terraform
|
||||||
|
usage: terraform [--version] [--help] <command> [<args>]
|
||||||
|
|
||||||
|
Available commands are:
|
||||||
|
agent Runs a Terraform agent
|
||||||
|
force-leave Forces a member of the cluster to enter the "left" state
|
||||||
|
info Provides debugging information for operators
|
||||||
|
join Tell Terraform agent to join cluster
|
||||||
|
keygen Generates a new encryption key
|
||||||
|
leave Gracefully leaves the Terraform cluster and shuts down
|
||||||
|
members Lists the members of a Terraform cluster
|
||||||
|
monitor Stream logs from a Terraform agent
|
||||||
|
version Prints the Terraform version
|
||||||
|
```
|
||||||
|
|
||||||
|
If you get an error that `terraform` could not be found, then your PATH
|
||||||
|
environment variable was not setup properly. Please go back and ensure
|
||||||
|
that your PATH variable contains the directory where Terraform was installed.
|
||||||
|
|
||||||
|
Otherwise, Terraform is installed and ready to go!
|
|
@ -0,0 +1,121 @@
|
||||||
|
---
|
||||||
|
layout: "intro"
|
||||||
|
page_title: "Terraform Cluster"
|
||||||
|
sidebar_current: "gettingstarted-join"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Terraform Cluster
|
||||||
|
|
||||||
|
By this point, we've started our first agent and registered and queried
|
||||||
|
one or more services on that agent. This showed how easy it is to use
|
||||||
|
Terraform, but didn't show how this could be extended to a scalable production
|
||||||
|
service discovery infrastructure. On this page, we'll create our first
|
||||||
|
real cluster with multiple members.
|
||||||
|
|
||||||
|
When starting a Terraform agent, it begins without knowledge of any other node, and is
|
||||||
|
an isolated cluster of one. To learn about other cluster members, the agent must
|
||||||
|
_join_ an existing cluster. To join an existing cluster, only needs to know
|
||||||
|
about a _single_ existing member. After it joins, the agent will gossip with this
|
||||||
|
member and quickly discover the other members in the cluster. A Terraform
|
||||||
|
agent can join any other agent, it doesn't have to be an agent in server mode.
|
||||||
|
|
||||||
|
## Starting the Agents
|
||||||
|
|
||||||
|
To simulate a more realistic cluster, we are using a two node cluster in
|
||||||
|
Vagrant. The Vagrantfile can be found in the demo section of the repo
|
||||||
|
[here](https://github.com/hashicorp/terraform/tree/master/demo/vagrant-cluster).
|
||||||
|
|
||||||
|
We start the first agent on our first node and also specify a node name.
|
||||||
|
The node name must be unique and is how a machine is uniquely identified.
|
||||||
|
By default it is the hostname of the machine, but we'll manually override it.
|
||||||
|
We are also providing a bind address. This is the address that Terraform listens on,
|
||||||
|
and it *must* be accessible by all other nodes in the cluster. The first node
|
||||||
|
will act as our server in this cluster. We're still not making a cluster
|
||||||
|
of servers.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ terraform agent -server -bootstrap -data-dir /tmp/consul \
|
||||||
|
-node=agent-one -bind=172.20.20.10
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, in another terminal, start the second agent on the new node.
|
||||||
|
This time, we set the bind address to match the IP of the second node
|
||||||
|
as specified in the Vagrantfile. In production, you will generally want
|
||||||
|
to provide a bind address or interface as well.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ terraform agent -data-dir /tmp/consul -node=agent-two -bind=172.20.20.11
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
At this point, you have two Terraform agents running, one server and one client.
|
||||||
|
The two Terraform agents still don't know anything about each other, and are each part of their own
|
||||||
|
clusters (of one member). You can verify this by running `terraform members`
|
||||||
|
against each agent and noting that only one member is a part of each.
|
||||||
|
|
||||||
|
## Joining a Cluster
|
||||||
|
|
||||||
|
Now, let's tell the first agent to join the second agent by running
|
||||||
|
the following command in a new terminal:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ terraform join 172.20.20.11
|
||||||
|
Successfully joined cluster by contacting 1 nodes.
|
||||||
|
```
|
||||||
|
|
||||||
|
You should see some log output in each of the agent logs. If you read
|
||||||
|
carefully, you'll see that they received join information. If you
|
||||||
|
run `terraform members` against each agent, you'll see that both agents now
|
||||||
|
know about each other:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ terraform members
|
||||||
|
agent-one 172.20.20.10:8301 alive role=terraform,dc=dc1,vsn=1,vsn_min=1,vsn_max=1,port=8300,bootstrap=1
|
||||||
|
agent-two 172.20.20.11:8301 alive role=node,dc=dc1,vsn=1,vsn_min=1,vsn_max=1
|
||||||
|
```
|
||||||
|
|
||||||
|
<div class="alert alert-block alert-info">
|
||||||
|
<p><strong>Remember:</strong> To join a cluster, a Terraform agent needs to only
|
||||||
|
learn about <em>one existing member</em>. After joining the cluster, the
|
||||||
|
agents gossip with each other to propagate full membership information.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
In addition to using `terraform join` you can use the `-join` flag on
|
||||||
|
`terraform agent` to join a cluster as part of starting up the agent.
|
||||||
|
|
||||||
|
## Querying Nodes
|
||||||
|
|
||||||
|
Just like querying services, Terraform has an API for querying the
|
||||||
|
nodes themselves. You can do this via the DNS or HTTP API.
|
||||||
|
|
||||||
|
For the DNS API, the structure of the names is `NAME.node.terraform` or
|
||||||
|
`NAME.DATACENTER.node.terraform`. If the datacenter is omitted, Terraform
|
||||||
|
will only search the local datacenter.
|
||||||
|
|
||||||
|
From "agent-one", query "agent-two":
|
||||||
|
|
||||||
|
```
|
||||||
|
$ dig @127.0.0.1 -p 8600 agent-two.node.terraform
|
||||||
|
...
|
||||||
|
|
||||||
|
;; QUESTION SECTION:
|
||||||
|
;agent-two.node.terraform. IN A
|
||||||
|
|
||||||
|
;; ANSWER SECTION:
|
||||||
|
agent-two.node.terraform. 0 IN A 172.20.20.11
|
||||||
|
```
|
||||||
|
|
||||||
|
The ability to look up nodes in addition to services is incredibly
|
||||||
|
useful for system administration tasks. For example, knowing the address
|
||||||
|
of the node to SSH into is as easy as making it part of the Terraform cluster
|
||||||
|
and querying it.
|
||||||
|
|
||||||
|
## Leaving a Cluster
|
||||||
|
|
||||||
|
To leave the cluster, you can either gracefully quit an agent (using
|
||||||
|
`Ctrl-C`) or force kill one of the agents. Gracefully leaving allows
|
||||||
|
the node to transition into the _left_ state, otherwise other nodes
|
||||||
|
will detect it as having _failed_. The difference is covered
|
||||||
|
in more detail [here](/intro/getting-started/agent.html#toc_3).
|
|
@ -0,0 +1,118 @@
|
||||||
|
---
|
||||||
|
layout: "intro"
|
||||||
|
page_title: "Key/Value Data"
|
||||||
|
sidebar_current: "gettingstarted-kv"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Key/Value Data
|
||||||
|
|
||||||
|
In addition to providing service discovery and integrated health checking,
|
||||||
|
Terraform provides an easy to use Key/Value store. This can be used to hold
|
||||||
|
dynamic configuration, assist in service coordination, build leader election,
|
||||||
|
and anything else a developer can think to build. The
|
||||||
|
[HTTP API](/docs/agent/http.html) fully documents the features of the K/V store.
|
||||||
|
|
||||||
|
This page assumes you have at least one Terraform agent already running.
|
||||||
|
|
||||||
|
## Simple Usage
|
||||||
|
|
||||||
|
To demonstrate how simple it is to get started, we will manipulate a few keys
|
||||||
|
in the K/V store.
|
||||||
|
|
||||||
|
Querying the agent we started in a prior page, we can first verify that
|
||||||
|
there are no existing keys in the k/v store:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ curl -v http://localhost:8500/v1/kv/?recurse
|
||||||
|
* About to connect() to localhost port 8500 (#0)
|
||||||
|
* Trying 127.0.0.1... connected
|
||||||
|
> GET /v1/kv/?recurse HTTP/1.1
|
||||||
|
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
|
||||||
|
> Host: localhost:8500
|
||||||
|
> Accept: */*
|
||||||
|
>
|
||||||
|
< HTTP/1.1 404 Not Found
|
||||||
|
< X-Terraform-Index: 1
|
||||||
|
< Date: Fri, 11 Apr 2014 02:10:28 GMT
|
||||||
|
< Content-Length: 0
|
||||||
|
< Content-Type: text/plain; charset=utf-8
|
||||||
|
<
|
||||||
|
* Connection #0 to host localhost left intact
|
||||||
|
* Closing connection #0
|
||||||
|
```
|
||||||
|
|
||||||
|
Since there are no keys, we get a 404 response back.
|
||||||
|
Now, we can put a few example keys:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ curl -X PUT -d 'test' http://localhost:8500/v1/kv/web/key1
|
||||||
|
true
|
||||||
|
$ curl -X PUT -d 'test' http://localhost:8500/v1/kv/web/key2?flags=42
|
||||||
|
true
|
||||||
|
$ curl -X PUT -d 'test' http://localhost:8500/v1/kv/web/sub/key3
|
||||||
|
true
|
||||||
|
$ curl http://localhost:8500/v1/kv/?recurse
|
||||||
|
[{"CreateIndex":97,"ModifyIndex":97,"Key":"web/key1","Flags":0,"Value":"dGVzdA=="},
|
||||||
|
{"CreateIndex":98,"ModifyIndex":98,"Key":"web/key2","Flags":42,"Value":"dGVzdA=="},
|
||||||
|
{"CreateIndex":99,"ModifyIndex":99,"Key":"web/sub/key3","Flags":0,"Value":"dGVzdA=="}]
|
||||||
|
```
|
||||||
|
|
||||||
|
Here we have created 3 keys, each with the value of "test". Note that the
|
||||||
|
`Value` field returned is base64 encoded to allow non-UTF8
|
||||||
|
characters. For the "web/key2" key, we set a `flag` value of 42. All keys
|
||||||
|
support setting a 64bit integer flag value. This is opaque to Terraform but can
|
||||||
|
be used by clients for any purpose.
|
||||||
|
|
||||||
|
After setting the values, we then issued a GET request to retrieve multiple
|
||||||
|
keys using the `?recurse` parameter.
|
||||||
|
|
||||||
|
You can also fetch a single key just as easily:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ curl http://localhost:8500/v1/kv/web/key1
|
||||||
|
[{"CreateIndex":97,"ModifyIndex":97,"Key":"web/key1","Flags":0,"Value":"dGVzdA=="}]
|
||||||
|
```
|
||||||
|
|
||||||
|
Deleting keys is simple as well. We can delete a single key by specifying the
|
||||||
|
full path, or we can recursively delete all keys under a root using "?recurse":
|
||||||
|
|
||||||
|
```
|
||||||
|
$ curl -X DELETE http://localhost:8500/v1/kv/web/sub?recurse
|
||||||
|
$ curl http://localhost:8500/v1/kv/web?recurse
|
||||||
|
[{"CreateIndex":97,"ModifyIndex":97,"Key":"web/key1","Flags":0,"Value":"dGVzdA=="},
|
||||||
|
{"CreateIndex":98,"ModifyIndex":98,"Key":"web/key2","Flags":42,"Value":"dGVzdA=="}]
|
||||||
|
```
|
||||||
|
|
||||||
|
A key can be updated by setting a new value by issuing the same PUT request.
|
||||||
|
Additionally, Terraform provides a Check-And-Set operation, enabling atomic
|
||||||
|
key updates. This is done by providing the `?cas=` paramter with the last
|
||||||
|
`ModifyIndex` value from the GET request. For example, suppose we wanted
|
||||||
|
to update "web/key1":
|
||||||
|
|
||||||
|
```
|
||||||
|
$ curl -X PUT -d 'newval' http://localhost:8500/v1/kv/web/key1?cas=97
|
||||||
|
true
|
||||||
|
$ curl -X PUT -d 'newval' http://localhost:8500/v1/kv/web/key1?cas=97
|
||||||
|
false
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, the first CAS update succeeds because the last modify time is 97.
|
||||||
|
However the second operation fails because the `ModifyIndex` is no longer 97.
|
||||||
|
|
||||||
|
We can also make use of the `ModifyIndex` to wait for a key's value to change.
|
||||||
|
For example, suppose we wanted to wait for key2 to be modified:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ curl "http://localhost:8500/v1/kv/web/key2?index=101&wait=5s"
|
||||||
|
[{"CreateIndex":98,"ModifyIndex":101,"Key":"web/key2","Flags":42,"Value":"dGVzdA=="}]
|
||||||
|
```
|
||||||
|
|
||||||
|
By providing "?index=" we are asking to wait until the key has a `ModifyIndex` greater
|
||||||
|
than 101. However the "?wait=5s" parameter restricts the query to at most 5 seconds,
|
||||||
|
returning the current, unchanged value. This can be used to efficiently wait for
|
||||||
|
key modifications. Additionally, this same technique can be used to wait for a list
|
||||||
|
of keys, waiting only until any of the keys has a newer modification time.
|
||||||
|
|
||||||
|
This is only a few example of what the API supports. For full documentation, please
|
||||||
|
reference the [HTTP API](/docs/agent/http.html).
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
layout: "intro"
|
||||||
|
page_title: "Next Steps"
|
||||||
|
sidebar_current: "gettingstarted-nextsteps"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Next Steps
|
||||||
|
|
||||||
|
That concludes the getting started guide for Terraform. Hopefully you're able to
|
||||||
|
see that while Terraform is simple to use, it has a powerful set of features.
|
||||||
|
We've covered the basics for all of these features in this guide.
|
||||||
|
|
||||||
|
Terraform is designed to be friendly to both the DevOps community and
|
||||||
|
application developers, making it perfect for modern, elastic infrastructures.
|
||||||
|
|
||||||
|
As a next step, the following resources are available:
|
||||||
|
|
||||||
|
* [Documentation](/docs/index.html) - The documentation is an in-depth reference
|
||||||
|
guide to all the features of Terraform, including technical details about the
|
||||||
|
internals of how Terraform operates.
|
||||||
|
|
||||||
|
* [Guides](/docs/guides/index.html) - This section provides various getting
|
||||||
|
started guides with Terraform, including how to bootstrap a new datacenter.
|
||||||
|
|
||||||
|
* [Examples](https://github.com/hashicorp/terraform/tree/master/demo) -
|
||||||
|
The work-in-progress examples folder within the GitHub
|
||||||
|
repository for Terraform contains functional examples of various use cases
|
||||||
|
of Terraform to help you get started with exactly what you need.
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
---
|
||||||
|
layout: "intro"
|
||||||
|
page_title: "Registering Services"
|
||||||
|
sidebar_current: "gettingstarted-services"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Registering Services
|
||||||
|
|
||||||
|
In the previous page, we ran our first agent, saw the cluster members, and
|
||||||
|
queried that node. On this page, we'll register our first service and query
|
||||||
|
that service. We're not yet running a cluster of Terraform agents.
|
||||||
|
|
||||||
|
## Defining a Service
|
||||||
|
|
||||||
|
A service can be registered either by providing a
|
||||||
|
[service definition](/docs/agent/services.html),
|
||||||
|
or by making the appropriate calls to the
|
||||||
|
[HTTP API](/docs/agent/http.html).
|
||||||
|
|
||||||
|
We're going to start by registering a service using a service definition,
|
||||||
|
since this is the most common way that services are registered. We'll be
|
||||||
|
building on what we covered in the
|
||||||
|
[previous page](/intro/getting-started/agent.html).
|
||||||
|
|
||||||
|
First, create a directory for Terraform configurations. A good directory
|
||||||
|
is typically `/etc/terraform.d`. Terraform loads all configuration files in the
|
||||||
|
configuration directory.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ sudo mkdir /etc/terraform.d
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, we'll write a service definition configuration file. We'll
|
||||||
|
pretend we have a service named "web" running on port 80. Additionally,
|
||||||
|
we'll give it some tags, which we can use as additional ways to query
|
||||||
|
it later.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ echo '{"service": {"name": "web", "tags": ["rails"], "port": 80}}' \
|
||||||
|
>/etc/terraform.d/web.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, restart the agent we're running, providing the configuration directory:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ terraform agent -server -bootstrap -data-dir /tmp/consul -config-dir /etc/consul.d
|
||||||
|
==> Starting Terraform agent...
|
||||||
|
...
|
||||||
|
[INFO] agent: Synced service 'web'
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll notice in the output that it "synced" the web service. This means
|
||||||
|
that it loaded the information from the configuration.
|
||||||
|
|
||||||
|
If you wanted to register multiple services, you create multiple service
|
||||||
|
definition files in the Terraform configuration directory.
|
||||||
|
|
||||||
|
## Querying Services
|
||||||
|
|
||||||
|
Once the agent is started and the service is synced, we can query that
|
||||||
|
service using either the DNS or HTTP API.
|
||||||
|
|
||||||
|
### DNS API
|
||||||
|
|
||||||
|
Let's first query it using the DNS API. For the DNS API, the DNS name
|
||||||
|
for services is `NAME.service.terraform`. All DNS names are always in the
|
||||||
|
`terraform` namespace. The `service` subdomain on that tells Terraform we're
|
||||||
|
querying services, and the `NAME` is the name of the service. For the
|
||||||
|
web service we registered, that would be `web.service.terraform`:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ dig @127.0.0.1 -p 8600 web.service.terraform
|
||||||
|
...
|
||||||
|
|
||||||
|
;; QUESTION SECTION:
|
||||||
|
;web.service.terraform. IN A
|
||||||
|
|
||||||
|
;; ANSWER SECTION:
|
||||||
|
web.service.terraform. 0 IN A 172.20.20.11
|
||||||
|
```
|
||||||
|
|
||||||
|
As you can see, an A record was returned with the IP address of the node that
|
||||||
|
the service is available on. A records can only hold IP addresses. You can
|
||||||
|
also use the DNS API to retrieve the entire address/port pair using SRV
|
||||||
|
records:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ dig @127.0.0.1 -p 8600 web.service.terraform SRV
|
||||||
|
...
|
||||||
|
|
||||||
|
;; QUESTION SECTION:
|
||||||
|
;web.service.terraform. IN SRV
|
||||||
|
|
||||||
|
;; ANSWER SECTION:
|
||||||
|
web.service.terraform. 0 IN SRV 1 1 80 agent-one.node.dc1.consul.
|
||||||
|
|
||||||
|
;; ADDITIONAL SECTION:
|
||||||
|
agent-one.node.dc1.terraform. 0 IN A 172.20.20.11
|
||||||
|
```
|
||||||
|
|
||||||
|
The SRV record returned says that the web service is running on port 80
|
||||||
|
and exists on the node `agent-one.node.dc1.terraform.`. An additional section
|
||||||
|
is returned by the DNS with the A record for that node.
|
||||||
|
|
||||||
|
Finally, we can also use the DNS API to filter services by tags. The
|
||||||
|
format for tag-based service queries is `TAG.NAME.service.terraform`. In
|
||||||
|
the example below, we ask Terraform for all web services with the "rails"
|
||||||
|
tag. We get a response since we registered our service with that tag.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ dig @127.0.0.1 -p 8600 rails.web.service.terraform
|
||||||
|
...
|
||||||
|
|
||||||
|
;; QUESTION SECTION:
|
||||||
|
;rails.web.service.terraform. IN A
|
||||||
|
|
||||||
|
;; ANSWER SECTION:
|
||||||
|
rails.web.service.terraform. 0 IN A 172.20.20.11
|
||||||
|
```
|
||||||
|
|
||||||
|
### HTTP API
|
||||||
|
|
||||||
|
In addition to the DNS API, the HTTP API can be used to query services:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ curl http://localhost:8500/v1/catalog/service/web
|
||||||
|
[{"Node":"agent-one","Address":"172.20.20.11","ServiceID":"web","ServiceName":"web","ServiceTags":["rails"],"ServicePort":80}]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Updating Services
|
||||||
|
|
||||||
|
Service definitions can be updated by changing configuration files and
|
||||||
|
sending a `SIGHUP` to the agent. This lets you update services without
|
||||||
|
any downtime or unavailability to service queries.
|
||||||
|
|
||||||
|
Alternatively the HTTP API can be used to add, remove, and modify services
|
||||||
|
dynamically.
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
---
|
||||||
|
layout: "intro"
|
||||||
|
page_title: "Web UI"
|
||||||
|
sidebar_current: "gettingstarted-ui"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Terraform Web UI
|
||||||
|
|
||||||
|
Terraform comes with support for a
|
||||||
|
[beautiful, functional web UI](http://demo.terraform.io) out of the box.
|
||||||
|
This UI can be used for viewing all services and nodes, viewing all
|
||||||
|
health checks and their current status, and for reading and setting
|
||||||
|
key/value data. The UI automatically supports multi-datacenter.
|
||||||
|
|
||||||
|
For ease of deployment, the UI is
|
||||||
|
[distributed](/downloads_web_ui.html)
|
||||||
|
as static HTML and JavaScript.
|
||||||
|
You don't need a separate web server to run the web UI. The Terraform
|
||||||
|
agent itself can be configured to serve the UI.
|
||||||
|
|
||||||
|
## Screenshot and Demo
|
||||||
|
|
||||||
|
You can view a live demo of the Terraform Web UI
|
||||||
|
[here](http://demo.terraform.io).
|
||||||
|
|
||||||
|
While the live demo is able to access data from all datacenters,
|
||||||
|
we've also setup demo endpoints in the specific datacenters:
|
||||||
|
[AMS2](http://ams2.demo.terraform.io) (Amsterdam),
|
||||||
|
[SFO1](http://sfo1.demo.terraform.io) (San Francisco),
|
||||||
|
and [NYC1](http://nyc1.demo.terraform.io) (New York).
|
||||||
|
|
||||||
|
A screenshot of one page of the demo is shown below so you can get an
|
||||||
|
idea of what the web UI is like. Click the screenshot for the full size.
|
||||||
|
|
||||||
|
<div class="center">
|
||||||
|
<a href="/images/terraform_web_ui.png">
|
||||||
|
<img src="/images/terraform_web_ui.png">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## Set Up
|
||||||
|
|
||||||
|
To set up the web UI,
|
||||||
|
[download the web UI package](/downloads_web_ui.html)
|
||||||
|
and unzip it to a directory somewhere on the server where the Terraform agent
|
||||||
|
is also being run. Then, just append the `-ui-dir` to the `terraform agent`
|
||||||
|
command pointing to the directory where you unzipped the UI (the
|
||||||
|
directory with the `index.html` file):
|
||||||
|
|
||||||
|
```
|
||||||
|
$ terraform agent -ui-dir /path/to/ui
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
The UI is available at the `/ui` path on the same port as the HTTP API.
|
||||||
|
By default this is `http://localhost:8500/ui`.
|
|
@ -0,0 +1,75 @@
|
||||||
|
---
|
||||||
|
layout: "intro"
|
||||||
|
page_title: "Introduction"
|
||||||
|
sidebar_current: "what"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Introduction to Terraform
|
||||||
|
|
||||||
|
Welcome to the intro guide to Terraform! This guide is the best place to start
|
||||||
|
with Terraform. We cover what Terraform is, what problems it can solve, how it compares
|
||||||
|
to existing software, and a quick start for using Terraform. If you are already familiar
|
||||||
|
with the basics of Terraform, the [documentation](/docs/index.html) provides more
|
||||||
|
of a reference for all available features.
|
||||||
|
|
||||||
|
## What is Terraform?
|
||||||
|
|
||||||
|
Terraform has multiple components, but as a whole, it is a tool for discovering
|
||||||
|
and configuring services in your infrastructure. It provides several
|
||||||
|
key features:
|
||||||
|
|
||||||
|
* **Service Discovery**: Clients of Terraform can _provide_ a service, such as
|
||||||
|
`api` or `mysql`, and other clients can use Terraform to _discover_ providers
|
||||||
|
of a given service. Using either DNS or HTTP, applications can easily find
|
||||||
|
the services they depend upon.
|
||||||
|
|
||||||
|
* **Health Checking**: Terraform clients can provide any number of health checks,
|
||||||
|
either associated with a given service ("is the webserver returning 200 OK"), or
|
||||||
|
with the local node ("is memory utilization below 90%"). This information can be
|
||||||
|
used by an operator to monitor cluster health, and it is used by the service
|
||||||
|
discovery components to route traffic away from unhealthy hosts.
|
||||||
|
|
||||||
|
* **Key/Value Store**: Applications can make use of Terraform's hierarchical key/value
|
||||||
|
store for any number of purposes including: dynamic configuration, feature flagging,
|
||||||
|
coordination, leader election, etc. The simple HTTP API makes it easy to use.
|
||||||
|
|
||||||
|
* **Multi Datacenter**: Terraform supports multiple datacenters out of the box. This
|
||||||
|
means users of Terraform do not have to worry about building additional layers of
|
||||||
|
abstraction to grow to multiple regions.
|
||||||
|
|
||||||
|
Terraform is designed to be friendly to both the DevOps community and
|
||||||
|
application developers, making it perfect for modern, elastic infrastructures.
|
||||||
|
|
||||||
|
## Basic Architecture of Terraform
|
||||||
|
|
||||||
|
Terraform is a distributed, highly available system. There is an
|
||||||
|
[in-depth architecture overview](/docs/internals/architecture.html) available,
|
||||||
|
but this section will cover the basics so you can get an understanding
|
||||||
|
of how Terraform works. This section will purposely omit details to quickly
|
||||||
|
provide an overview of the architecture.
|
||||||
|
|
||||||
|
Every node that provides services to Terraform runs a _Terraform agent_. Running
|
||||||
|
an agent is not required for discovering other services or getting/setting
|
||||||
|
key/value data. The agent is responsible for health checking the services
|
||||||
|
on the node as well as the node itself.
|
||||||
|
|
||||||
|
The agents talk to one or more _Terraform servers_. The Terraform servers are
|
||||||
|
where data is stored and replicated. The servers themselves elect a leader.
|
||||||
|
While Terraform can function with one server, 3 to 5 is recommended to avoid
|
||||||
|
data loss scenarios. A cluster of Terraform servers is recommended for each
|
||||||
|
datacenter.
|
||||||
|
|
||||||
|
Components of your infrastructure that need to discover other services
|
||||||
|
or nodes can query any of the Terraform servers _or_ any of the Terraform agents.
|
||||||
|
The agents forward queries to the servers automatically.
|
||||||
|
|
||||||
|
Each datacenter runs a cluster of Terraform servers. When a cross-datacenter
|
||||||
|
service discovery or configuration request is made, the local Terraform servers
|
||||||
|
forward the request to the remote datacenter and return the result.
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
See the page on [how Terraform compares to other software](/intro/vs/index.html)
|
||||||
|
to see how it fits into your existing infrastructure. Or continue onwards with
|
||||||
|
the [getting started guide](/intro/getting-started/install.html) to get
|
||||||
|
Terraform up and running and see how it works.
|
|
@ -0,0 +1,42 @@
|
||||||
|
---
|
||||||
|
layout: "intro"
|
||||||
|
page_title: "Terraform vs. Chef, Puppet, etc."
|
||||||
|
sidebar_current: "vs-other-chef"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Terraform vs. Chef, Puppet, etc.
|
||||||
|
|
||||||
|
It is not uncommon to find people using Chef, Puppet, and other configuration
|
||||||
|
management tools to build service discovery mechanisms. This is usually
|
||||||
|
done by querying global state to construct configuration files on each
|
||||||
|
node during a periodic convergence run.
|
||||||
|
|
||||||
|
Unfortunately, this approach has
|
||||||
|
a number of pitfalls. The configuration information is static,
|
||||||
|
and cannot update any more frequently than convergence runs. Generally this
|
||||||
|
is on the interval of many minutes or hours. Additionally, there is no
|
||||||
|
mechanism to incorporate the system state in the configuration. Nodes which
|
||||||
|
are unhealthy may receive traffic exacerbating issues further. Using this
|
||||||
|
approach also makes supporting multiple datacenters challenging as a central
|
||||||
|
group of servers must manage all datacenters.
|
||||||
|
|
||||||
|
Terraform is designed specifically as a service discovery tool. As such,
|
||||||
|
it is much more dynamic and responsive to the state of the cluster. Nodes
|
||||||
|
can register and deregister the services they provide, enabling dependent
|
||||||
|
applications and services to rapidly discover all providers. By using the
|
||||||
|
integrated health checking, Terraform can route traffic away from unhealthy
|
||||||
|
nodes, allowing systems and services to gracefully recover. Static configuration
|
||||||
|
that may be provided by configuration management tools can be moved into the
|
||||||
|
dynamic key/value store. This allows application configuration to be updated
|
||||||
|
without a slow convergence run. Lastly, because each datacenter runs indepedently,
|
||||||
|
supporting multiple datacenters is no different than a single datacenter.
|
||||||
|
|
||||||
|
That said, Terraform is not a replacement for configuration management tools.
|
||||||
|
These tools are still critical to setup applications and even to
|
||||||
|
configure Terraform itself. Static provisioning is best managed
|
||||||
|
by existing tools, while dynamic state and discovery is better managed by
|
||||||
|
Terraform. The separation of configuration management and cluster management
|
||||||
|
also has a number of advantageous side effects: Chef recipes and Puppet manifests
|
||||||
|
become simpler without global state, periodic runs are no longer required for service
|
||||||
|
or configuration changes, and the infrastructure can become immutable since config management
|
||||||
|
runs require no global state.
|
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
layout: "intro"
|
||||||
|
page_title: "Terraform vs. Custom Solutions"
|
||||||
|
sidebar_current: "vs-other-custom"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Terraform vs. Custom Solutions
|
||||||
|
|
||||||
|
As a code base grows, a monolithic app usually evolves into a Service Oriented Architecture (SOA).
|
||||||
|
A universal pain point for SOA is service discovery and configuration. In many
|
||||||
|
cases, this leads to organizations building home grown solutions.
|
||||||
|
It is an undisputed fact that distributed systems are hard; building one is error prone and time consuming.
|
||||||
|
Most systems cut corners by introducing single points of failure such
|
||||||
|
as a single Redis or RDBMS to maintain cluster state. These solutions may work in the short term,
|
||||||
|
but they are rarely fault tolerant or scalable. Besides these limitations,
|
||||||
|
they require time and resources to build and maintain.
|
||||||
|
|
||||||
|
Terraform provides the core set of features needed by a SOA out of the box. By using Terraform,
|
||||||
|
organizations can leverage open source work to reduce their time and resource commitment to
|
||||||
|
re-inventing the wheel and focus on their business applications.
|
||||||
|
|
||||||
|
Terraform is built on well-cited research, and is designed with the constraints of
|
||||||
|
distributed systems in mind. At every step, Terraform takes efforts to provide a robust
|
||||||
|
and scalable solution for organizations of any size.
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
---
|
||||||
|
layout: "intro"
|
||||||
|
page_title: "Terraform vs. Other Software"
|
||||||
|
sidebar_current: "vs-other"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Terraform vs. Other Software
|
||||||
|
|
||||||
|
The problems Terraform solves are varied, but each individual feature has been
|
||||||
|
solved by many different systems. Although there is no single system that provides
|
||||||
|
all the features of Terraform, there are other options available to solve some of these problems.
|
||||||
|
In this section, we compare Terraform to some other options. In most cases, Terraform is not
|
||||||
|
mutually exclusive with any other system.
|
||||||
|
|
||||||
|
Use the navigation to the left to read the comparison of Terraform to specific
|
||||||
|
systems.
|
|
@ -0,0 +1,49 @@
|
||||||
|
---
|
||||||
|
layout: "intro"
|
||||||
|
page_title: "Terraform vs. Nagios, Sensu"
|
||||||
|
sidebar_current: "vs-other-nagios-sensu"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Terraform vs. Nagios, Sensu
|
||||||
|
|
||||||
|
Nagios and Sensu are both tools built for monitoring. They are used
|
||||||
|
to quickly notify operators when an issue occurs.
|
||||||
|
|
||||||
|
Nagios uses a group of central servers that are configured to perform
|
||||||
|
checks on remote hosts. This design makes it difficult to scale Nagios,
|
||||||
|
as large fleets quickly reach the limit of vertical scaling, and Nagios
|
||||||
|
does not easily scale horizontally. Nagios is also notoriously
|
||||||
|
difficult to use with modern DevOps and configuration management tools,
|
||||||
|
as local configurations must be updated when remote servers are added
|
||||||
|
or removed.
|
||||||
|
|
||||||
|
Sensu has a much more modern design, relying on local agents to run
|
||||||
|
checks and pushing results to an AMQP broker. A number of servers
|
||||||
|
ingest and handle the result of the health checks from the broker. This model
|
||||||
|
is more scalable than Nagios, as it allows for much more horizontal scaling,
|
||||||
|
and a weaker coupling between the servers and agents. However, the central broker
|
||||||
|
has scaling limits, and acts as a single point of failure in the system.
|
||||||
|
|
||||||
|
Terraform provides the same health checking abilities as both Nagios and Sensu,
|
||||||
|
is friendly to modern DevOps, and avoids the scaling issues inherent in the
|
||||||
|
other systems. Terraform runs all checks locally, like Sensu, avoiding placing
|
||||||
|
a burden on central servers. The status of checks is maintained by the Terraform
|
||||||
|
servers, which are fault tolerant and have no single point of failure.
|
||||||
|
Lastly, Terraform can scale to vastly more checks because it relies on edge triggered
|
||||||
|
updates. This means that an update is only triggered when a check transitions
|
||||||
|
from "passing" to "failing" or vice versa.
|
||||||
|
|
||||||
|
In a large fleet, the majority of checks are passing, and even the minority
|
||||||
|
that are failing are persistent. By capturing changes only, Terraform reduces
|
||||||
|
the amount of networking and compute resources used by the health checks,
|
||||||
|
allowing the system to be much more scalable.
|
||||||
|
|
||||||
|
An astute reader may notice that if a Terraform agent dies, then no edge triggered
|
||||||
|
updates will occur. From the perspective of other nodes all checks will appear
|
||||||
|
to be in a steady state. However, Terraform guards against this as well. The
|
||||||
|
[gossip protocol](/docs/internals/gossip.html) used between clients and servers
|
||||||
|
integrates a distributed failure detector. This means that if a Terraform agent fails,
|
||||||
|
the failure will be detected, and thus all checks being run by that node can be
|
||||||
|
assumed failed. This failure detector distributes the work among the entire cluster,
|
||||||
|
and critically enables the edge triggered architecture to work.
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
---
|
||||||
|
layout: "intro"
|
||||||
|
page_title: "Terraform vs. Serf"
|
||||||
|
sidebar_current: "vs-other-serf"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Terraform vs. Serf
|
||||||
|
|
||||||
|
[Serf](http://www.serfdom.io) is a node discovery and orchestration tool and is the only
|
||||||
|
tool discussed so far that is built on an eventually consistent gossip model,
|
||||||
|
with no centralized servers. It provides a number of features, including group
|
||||||
|
membership, failure detection, event broadcasts and a query mechanism. However,
|
||||||
|
Serf does not provide any high-level features such as service discovery, health
|
||||||
|
checking or key/value storage. To clarify, the discovery feature of Serf is at a node
|
||||||
|
level, while Terraform provides a service and node level abstraction.
|
||||||
|
|
||||||
|
Terraform is a complete system providing all of those features. In fact, the internal
|
||||||
|
[gossip protocol](/docs/internals/gossip.html) used within Terraform, is powered by
|
||||||
|
the Serf library. Terraform leverages the membership and failure detection features,
|
||||||
|
and builds upon them.
|
||||||
|
|
||||||
|
The health checking provided by Serf is very low level, and only indicates if the
|
||||||
|
agent is alive. Terraform extends this to provide a rich health checking system,
|
||||||
|
that handles liveness, in addition to arbitrary host and service-level checks.
|
||||||
|
Health checks are integrated with a central catalog that operators can easily
|
||||||
|
query to gain insight into the cluster.
|
||||||
|
|
||||||
|
The membership provided by Serf is at a node level, while Terraform focuses
|
||||||
|
on the service level abstraction, with a single node to multiple service model.
|
||||||
|
This can be simulated in Serf using tags, but it is much more limited, and does
|
||||||
|
not provide useful query interfaces. Terraform also makes use of a strongly consistent
|
||||||
|
Catalog, while Serf is only eventually consistent.
|
||||||
|
|
||||||
|
In addition to the service level abstraction and improved health checking,
|
||||||
|
Terraform provides a key/value store and support for multiple datacenters.
|
||||||
|
Serf can run across the WAN but with degraded performance. Terraform makes use
|
||||||
|
of [multiple gossip pools](/docs/internals/architecture.html), so that
|
||||||
|
the performance of Serf over a LAN can be retained while still using it over
|
||||||
|
a WAN for linking together multiple datacenters.
|
||||||
|
|
||||||
|
Terraform is opinionated in its usage, while Serf is a more flexible and
|
||||||
|
general purpose tool. Terraform uses a CP architecture, favoring consistency over
|
||||||
|
availability. Serf is a AP system, and sacrifices consistency for availability.
|
||||||
|
This means Terraform cannot operate if the central servers cannot form a quorum,
|
||||||
|
while Serf will continue to function under almost all circumstances.
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
---
|
||||||
|
layout: "intro"
|
||||||
|
page_title: "Terraform vs. SkyDNS"
|
||||||
|
sidebar_current: "vs-other-skydns"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Terraform vs. SkyDNS
|
||||||
|
|
||||||
|
SkyDNS is a relatively new tool designed to solve service discovery.
|
||||||
|
It uses multiple central servers that are strongly consistent and
|
||||||
|
fault tolerant. Nodes register services using an HTTP API, and
|
||||||
|
queries can be made over HTTP or DNS to perform discovery.
|
||||||
|
|
||||||
|
Terraform is very similar, but provides a superset of features. Terraform
|
||||||
|
also relies on multiple central servers to provide strong consistency
|
||||||
|
and fault tolerance. Nodes can use an HTTP API or use an agent to
|
||||||
|
register services, and queries are made over HTTP or DNS.
|
||||||
|
|
||||||
|
However, the systems differ in many ways. Terraform provides a much richer
|
||||||
|
health checking framework, with support for arbitrary checks and
|
||||||
|
a highly scalable failure detection scheme. SkyDNS relies on naive
|
||||||
|
heartbeating and TTLs, which have known scalability issues. Additionally,
|
||||||
|
the heartbeat only provides a limited liveness check, versus the rich
|
||||||
|
health checks that Terraform is capable of.
|
||||||
|
|
||||||
|
Multiple datacenters can be supported by using "regions" in SkyDNS,
|
||||||
|
however the data is managed and queried from a single cluster. If servers
|
||||||
|
are split between datacenters the replication protocol will suffer from
|
||||||
|
very long commit times. If all the SkyDNS servers are in a central datacenter, then
|
||||||
|
connectivity issues can cause entire datacenters to lose availability.
|
||||||
|
Additionally, even without a connectivity issue, query performance will
|
||||||
|
suffer as requests must always be performed in a remote datacenter.
|
||||||
|
|
||||||
|
Terraform supports multiple datacenters out of the box, and it purposely
|
||||||
|
scopes the managed data to be per-datacenter. This means each datacenter
|
||||||
|
runs an independent cluster of servers. Requests are forwarded to remote
|
||||||
|
datacenters if necessary. This means requests for services within a datacenter
|
||||||
|
never go over the WAN, and connectivity issues between datacenters do not
|
||||||
|
affect availability within a datacenter. Additionally, the unavailability
|
||||||
|
of one datacenter does not affect the service discovery of services
|
||||||
|
in any other datacenter.
|
|
@ -0,0 +1,57 @@
|
||||||
|
---
|
||||||
|
layout: "intro"
|
||||||
|
page_title: "Terraform vs. SmartStack"
|
||||||
|
sidebar_current: "vs-other-smartstack"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Terraform vs. SmartStack
|
||||||
|
|
||||||
|
SmartStack is another tool which tackles the service discovery problem.
|
||||||
|
It has a rather unique architecture, and has 4 major components: ZooKeeper,
|
||||||
|
HAProxy, Synapse, and Nerve. The ZooKeeper servers are responsible for storing cluster
|
||||||
|
state in a consistent and fault tolerant manner. Each node in the SmartStack
|
||||||
|
cluster then runs both Nerves and Synapses. The Nerve is responsible for running
|
||||||
|
health checks against a service, and registering with the ZooKeeper servers.
|
||||||
|
Synapse queries ZooKeeper for service providers and dynamically configures
|
||||||
|
HAProxy. Finally, clients speak to HAProxy, which does health checking and
|
||||||
|
load balancing across service providers.
|
||||||
|
|
||||||
|
Terraform is a much simpler and more contained system, as it does not rely on any external
|
||||||
|
components. Terraform uses an integrated [gossip protocol](/docs/internals/gossip.html)
|
||||||
|
to track all nodes and perform server discovery. This means that server addresses
|
||||||
|
do not need to be hardcoded and updated fleet wide on changes, unlike SmartStack.
|
||||||
|
|
||||||
|
Service registration for both Terraform and Nerves can be done with a configuration file,
|
||||||
|
but Terraform also supports an API to dynamically change the services and checks that are in use.
|
||||||
|
|
||||||
|
For discovery, SmartStack clients must use HAProxy, requiring that Synapse be
|
||||||
|
configured with all desired endpoints in advance. Terraform clients instead
|
||||||
|
use the DNS or HTTP APIs without any configuration needed in advance. Terraform
|
||||||
|
also provides a "tag" abstraction, allowing services to provide metadata such
|
||||||
|
as versions, primary/secondary designations, or opaque labels that can be used for
|
||||||
|
filtering. Clients can then request only the service providers which have
|
||||||
|
matching tags.
|
||||||
|
|
||||||
|
The systems also differ in how they manage health checking.
|
||||||
|
Nerve's performs local health checks in a manner similar to Terraform agents.
|
||||||
|
However, Terraform maintains separate catalog and health systems, which allow
|
||||||
|
operators to see which nodes are in each service pool, as well as providing
|
||||||
|
insight into failing checks. Nerve simply deregisters nodes on failed checks,
|
||||||
|
providing limited operator insight. Synapse also configures HAProxy to perform
|
||||||
|
additional health checks. This causes all potential service clients to check for
|
||||||
|
liveness. With large fleets, this N-to-N style health checking may be prohibitively
|
||||||
|
expensive.
|
||||||
|
|
||||||
|
Terraform generally provides a much richer health checking system. Terraform supports
|
||||||
|
Nagios style plugins, enabling a vast catalog of checks to be used. It also
|
||||||
|
allows for service and host-level checks. There is also a "dead man's switch"
|
||||||
|
check that allows applications to easily integrate custom health checks. All of this
|
||||||
|
is also integrated into a Health and Catalog system with APIs enabling operator
|
||||||
|
to gain insight into the broader system.
|
||||||
|
|
||||||
|
In addition to the service discovery and health checking, Terraform also provides
|
||||||
|
an integrated key/value store for configuration and multi-datacenter support.
|
||||||
|
While it may be possible to configure SmartStack for multiple datacenters,
|
||||||
|
the central ZooKeeper cluster would be a serious impediment to a fault tolerant
|
||||||
|
deployment.
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
---
|
||||||
|
layout: "intro"
|
||||||
|
page_title: "Terraform vs. ZooKeeper, doozerd, etcd"
|
||||||
|
sidebar_current: "vs-other-zk"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Terraform vs. ZooKeeper, doozerd, etcd
|
||||||
|
|
||||||
|
ZooKeeper, doozerd and etcd are all similar in their architecture.
|
||||||
|
All three have server nodes that require a quorum of nodes to operate (usually a simple majority).
|
||||||
|
They are strongly consistent, and expose various primitives that can be used
|
||||||
|
through client libraries within applications to build complex distributed systems.
|
||||||
|
|
||||||
|
Terraform works in a similar way within a single datacenter with only server nodes.
|
||||||
|
In each datacenter, Terraform servers require a quorum to operate
|
||||||
|
and provide strong consistency. However, Terraform has native support for multiple datacenters,
|
||||||
|
as well as a more complex gossip system that links server nodes and clients.
|
||||||
|
|
||||||
|
If any of these systems are used for pure key/value storage, then they all
|
||||||
|
roughly provide the same semantics. Reads are strongly consistent, and availability
|
||||||
|
is sacrificed for consistency in the face of a network partition. However, the differences
|
||||||
|
become more apparent when these systems are used for advanced cases.
|
||||||
|
|
||||||
|
The semantics provided by these systems are attractive for building
|
||||||
|
service discovery systems. ZooKeeper et al. provide only a primitive K/V store,
|
||||||
|
and require that application developers build their own system to provide service
|
||||||
|
discovery. Terraform provides an opinionated framework for service discovery, and
|
||||||
|
eliminates the guess work and development effort. Clients simply register services
|
||||||
|
and then perform discovery using a DNS or HTTP interface. Other systems
|
||||||
|
require a home-rolled solution.
|
||||||
|
|
||||||
|
A compelling service discovery framework must incorporate health checking and the
|
||||||
|
possibility of failures as well. It is not useful to know that Node A
|
||||||
|
provides the Foo service if that node has failed or the service crashed. Naive systems
|
||||||
|
make use of heartbeating, using periodic updates and TTLs. These schemes require work linear
|
||||||
|
to the number of nodes and place the demand on a fixed number of servers. Additionally, the
|
||||||
|
failure detection window is at least as long as the TTL. ZooKeeper provides ephemeral
|
||||||
|
nodes which are K/V entries that are removed when a client disconnects. These are more
|
||||||
|
sophisticated than a heartbeat system, but also have inherent scalability issues and add
|
||||||
|
client side complexity. All clients must maintain active connections to the ZooKeeper servers,
|
||||||
|
and perform keep-alives. Additionally, this requires "thick clients", which are difficult
|
||||||
|
to write and often result in difficult to debug issues.
|
||||||
|
|
||||||
|
Terraform uses a very different architecture for health checking. Instead of only
|
||||||
|
having server nodes, Terraform clients run on every node in the cluster.
|
||||||
|
These clients are part of a [gossip pool](/docs/internals/gossip.html), which
|
||||||
|
serves several functions including distributed health checking. The gossip protocol implements
|
||||||
|
an efficient failure detector that can scale to clusters of any size without concentrating
|
||||||
|
the work on any select group of servers. The clients also enable a much richer set of health checks to be run locally,
|
||||||
|
whereas ZooKeeper ephemeral nodes are a very primitive check of liveness. Clients can check that
|
||||||
|
a web server is returning 200 status codes, that memory utilization is not critical, there is sufficient
|
||||||
|
disk space, etc. The Terraform clients expose a simple HTTP interface and avoid exposing the complexity
|
||||||
|
of the system is to clients in the same way as ZooKeeper.
|
||||||
|
|
||||||
|
Terraform provides first class support for service discovery, health checking,
|
||||||
|
K/V storage, and multiple datacenters. To support anything more than simple K/V storage,
|
||||||
|
all these other systems require additional tools and libraries to be built on
|
||||||
|
top. By using client nodes, Terraform provides a simple API that only requires thin clients.
|
||||||
|
Additionally, the API can be avoided entirely by using configuration files and the
|
||||||
|
DNS interface to have a complete service discovery solution with no development at all.
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
//
|
||||||
|
// app.js
|
||||||
|
//
|
||||||
|
|
||||||
|
var APP = (function() {
|
||||||
|
|
||||||
|
function initialize (){
|
||||||
|
APP.Utils.runIfClassNamePresent('page-home', initHome);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initHome() {
|
||||||
|
APP.Homepage.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
//api
|
||||||
|
return {
|
||||||
|
initialize: initialize
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
|
@ -0,0 +1,101 @@
|
||||||
|
//
|
||||||
|
// app.js
|
||||||
|
//
|
||||||
|
|
||||||
|
var APP = (function() {
|
||||||
|
|
||||||
|
function initialize (){
|
||||||
|
APP.Utils.runIfClassNamePresent('page-home', initHome);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initHome() {
|
||||||
|
APP.Homepage.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
//api
|
||||||
|
return {
|
||||||
|
initialize: initialize
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
||||||
|
;//
|
||||||
|
// util.js
|
||||||
|
//
|
||||||
|
var APP = APP || {};
|
||||||
|
|
||||||
|
APP.Utils = (function () {
|
||||||
|
return {
|
||||||
|
//check for mobile user agents
|
||||||
|
isMobile : (function(){
|
||||||
|
if( navigator.userAgent.match(/Android/i)
|
||||||
|
|| navigator.userAgent.match(/webOS/i)
|
||||||
|
|| navigator.userAgent.match(/iPhone/i)
|
||||||
|
//|| navigator.userAgent.match(/iPad/i)
|
||||||
|
|| navigator.userAgent.match(/iPod/i)
|
||||||
|
|| navigator.userAgent.match(/BlackBerry/i)
|
||||||
|
|| navigator.userAgent.match(/Windows Phone/i)
|
||||||
|
){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})(),
|
||||||
|
|
||||||
|
runIfClassNamePresent: function(selector, initFunction) {
|
||||||
|
var elms = document.getElementsByClassName(selector);
|
||||||
|
if (elms.length > 0) {
|
||||||
|
initFunction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}());;//homepage.js
|
||||||
|
|
||||||
|
var APP = APP || {};
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
APP.Homepage = (function () {
|
||||||
|
return {
|
||||||
|
|
||||||
|
ui : null,
|
||||||
|
|
||||||
|
init: function () {
|
||||||
|
var _this = this;
|
||||||
|
|
||||||
|
//cache elements
|
||||||
|
this.ui = {
|
||||||
|
$doc: $(window),
|
||||||
|
$hero: $('#jumbotron'),
|
||||||
|
$collapse: $('.navbar-collapse')
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addEventListeners();
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
addEventListeners: function(){
|
||||||
|
var _this = this;
|
||||||
|
|
||||||
|
if(APP.Utils.isMobile)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_this.ui.$doc.scroll(function() {
|
||||||
|
|
||||||
|
//if collapseable menu is open dont do parrallax. It looks wonky. Bootstrap conflict
|
||||||
|
if( _this.ui.$collapse.hasClass('in'))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var top = _this.ui.$doc.scrollTop(),
|
||||||
|
speedAdj = (top*0.8),
|
||||||
|
speedAdjOffset = speedAdj - top;
|
||||||
|
|
||||||
|
_this.ui.$hero.css('webkitTransform', 'translate(0, '+ speedAdj +'px)');
|
||||||
|
_this.ui.$hero.find('.container').css('webkitTransform', 'translate(0, '+ speedAdjOffset +'px)');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
|
||||||
|
}(jQuery, this));
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
var APP=function(){function a(){APP.Utils.runIfClassNamePresent("page-home",b)}function b(){APP.Homepage.init()}return{initialize:a}}(),APP=APP||{};APP.Utils=function(){return{isMobile:function(){return navigator.userAgent.match(/Android/i)||navigator.userAgent.match(/webOS/i)||navigator.userAgent.match(/iPhone/i)||navigator.userAgent.match(/iPod/i)||navigator.userAgent.match(/BlackBerry/i)||navigator.userAgent.match(/Windows Phone/i)?!0:!1}(),runIfClassNamePresent:function(a,b){var c=document.getElementsByClassName(a);c.length>0&&b()}}}();var APP=APP||{};!function(){APP.Homepage=function(){return{ui:null,init:function(){this.ui={$doc:$(window),$hero:$("#jumbotron"),$collapse:$(".navbar-collapse")},this.addEventListeners()},addEventListeners:function(){var a=this;APP.Utils.isMobile||a.ui.$doc.scroll(function(){if(!a.ui.$collapse.hasClass("in")){var b=a.ui.$doc.scrollTop(),c=.8*b,d=c-b;a.ui.$hero.css("webkitTransform","translate(0, "+c+"px)"),a.ui.$hero.find(".container").css("webkitTransform","translate(0, "+d+"px)")}})}}}()}(jQuery,this);
|
|
@ -0,0 +1,49 @@
|
||||||
|
//homepage.js
|
||||||
|
|
||||||
|
var APP = APP || {};
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
APP.Homepage = (function () {
|
||||||
|
return {
|
||||||
|
|
||||||
|
ui : null,
|
||||||
|
|
||||||
|
init: function () {
|
||||||
|
var _this = this;
|
||||||
|
|
||||||
|
//cache elements
|
||||||
|
this.ui = {
|
||||||
|
$doc: $(window),
|
||||||
|
$hero: $('#jumbotron'),
|
||||||
|
$collapse: $('.navbar-collapse')
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addEventListeners();
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
addEventListeners: function(){
|
||||||
|
var _this = this;
|
||||||
|
|
||||||
|
if(APP.Utils.isMobile)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_this.ui.$doc.scroll(function() {
|
||||||
|
|
||||||
|
//if collapseable menu is open dont do parrallax. It looks wonky. Bootstrap conflict
|
||||||
|
if( _this.ui.$collapse.hasClass('in'))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var top = _this.ui.$doc.scrollTop(),
|
||||||
|
speedAdj = (top*0.8),
|
||||||
|
speedAdjOffset = speedAdj - top;
|
||||||
|
|
||||||
|
_this.ui.$hero.css('webkitTransform', 'translate(0, '+ speedAdj +'px)');
|
||||||
|
_this.ui.$hero.find('.container').css('webkitTransform', 'translate(0, '+ speedAdjOffset +'px)');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
|
||||||
|
}(jQuery, this));
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
//
|
||||||
|
// util.js
|
||||||
|
//
|
||||||
|
var APP = APP || {};
|
||||||
|
|
||||||
|
APP.Utils = (function () {
|
||||||
|
return {
|
||||||
|
//check for mobile user agents
|
||||||
|
isMobile : (function(){
|
||||||
|
if( navigator.userAgent.match(/Android/i)
|
||||||
|
|| navigator.userAgent.match(/webOS/i)
|
||||||
|
|| navigator.userAgent.match(/iPhone/i)
|
||||||
|
//|| navigator.userAgent.match(/iPad/i)
|
||||||
|
|| navigator.userAgent.match(/iPod/i)
|
||||||
|
|| navigator.userAgent.match(/BlackBerry/i)
|
||||||
|
|| navigator.userAgent.match(/Windows Phone/i)
|
||||||
|
){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})(),
|
||||||
|
|
||||||
|
runIfClassNamePresent: function(selector, initFunction) {
|
||||||
|
var elms = document.getElementsByClassName(selector);
|
||||||
|
if (elms.length > 0) {
|
||||||
|
initFunction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}());
|
|
@ -0,0 +1,158 @@
|
||||||
|
/**
|
||||||
|
* Classy - classy classes for JavaScript
|
||||||
|
*
|
||||||
|
* :copyright: (c) 2011 by Armin Ronacher.
|
||||||
|
* :license: BSD.
|
||||||
|
*/
|
||||||
|
|
||||||
|
;(function(undefined) {
|
||||||
|
var
|
||||||
|
CLASSY_VERSION = '1.4',
|
||||||
|
root = this,
|
||||||
|
old_class = root.Class,
|
||||||
|
disable_constructor = false;
|
||||||
|
|
||||||
|
/* we check if $super is in use by a class if we can. But first we have to
|
||||||
|
check if the JavaScript interpreter supports that. This also matches
|
||||||
|
to false positives later, but that does not do any harm besides slightly
|
||||||
|
slowing calls down. */
|
||||||
|
var probe_super = (function(){$super();}).toString().indexOf('$super') > 0;
|
||||||
|
function usesSuper(obj) {
|
||||||
|
return !probe_super || /\B\$super\b/.test(obj.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper function to set the attribute of something to a value or
|
||||||
|
removes it if the value is undefined. */
|
||||||
|
function setOrUnset(obj, key, value) {
|
||||||
|
if (value === undefined)
|
||||||
|
delete obj[key];
|
||||||
|
else
|
||||||
|
obj[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* gets the own property of an object */
|
||||||
|
function getOwnProperty(obj, name) {
|
||||||
|
return Object.prototype.hasOwnProperty.call(obj, name)
|
||||||
|
? obj[name] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* instanciate a class without calling the constructor */
|
||||||
|
function cheapNew(cls) {
|
||||||
|
disable_constructor = true;
|
||||||
|
var rv = new cls;
|
||||||
|
disable_constructor = false;
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the base class we export */
|
||||||
|
var Class = function() {};
|
||||||
|
|
||||||
|
/* restore the global Class name and pass it to a function. This allows
|
||||||
|
different versions of the classy library to be used side by side and
|
||||||
|
in combination with other libraries. */
|
||||||
|
Class.$noConflict = function() {
|
||||||
|
try {
|
||||||
|
setOrUnset(root, 'Class', old_class);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
// fix for IE that does not support delete on window
|
||||||
|
root.Class = old_class;
|
||||||
|
}
|
||||||
|
return Class;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* what version of classy are we using? */
|
||||||
|
Class.$classyVersion = CLASSY_VERSION;
|
||||||
|
|
||||||
|
/* extend functionality */
|
||||||
|
Class.$extend = function(properties) {
|
||||||
|
var super_prototype = this.prototype;
|
||||||
|
|
||||||
|
/* disable constructors and instanciate prototype. Because the
|
||||||
|
prototype can't raise an exception when created, we are safe
|
||||||
|
without a try/finally here. */
|
||||||
|
var prototype = cheapNew(this);
|
||||||
|
|
||||||
|
/* copy all properties of the includes over if there are any */
|
||||||
|
if (properties.__include__)
|
||||||
|
for (var i = 0, n = properties.__include__.length; i != n; ++i) {
|
||||||
|
var mixin = properties.__include__[i];
|
||||||
|
for (var name in mixin) {
|
||||||
|
var value = getOwnProperty(mixin, name);
|
||||||
|
if (value !== undefined)
|
||||||
|
prototype[name] = mixin[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy class vars from the superclass */
|
||||||
|
properties.__classvars__ = properties.__classvars__ || {};
|
||||||
|
if (prototype.__classvars__)
|
||||||
|
for (var key in prototype.__classvars__)
|
||||||
|
if (!properties.__classvars__[key]) {
|
||||||
|
var value = getOwnProperty(prototype.__classvars__, key);
|
||||||
|
properties.__classvars__[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy all properties over to the new prototype */
|
||||||
|
for (var name in properties) {
|
||||||
|
var value = getOwnProperty(properties, name);
|
||||||
|
if (name === '__include__' ||
|
||||||
|
value === undefined)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
prototype[name] = typeof value === 'function' && usesSuper(value) ?
|
||||||
|
(function(meth, name) {
|
||||||
|
return function() {
|
||||||
|
var old_super = getOwnProperty(this, '$super');
|
||||||
|
this.$super = super_prototype[name];
|
||||||
|
try {
|
||||||
|
return meth.apply(this, arguments);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
setOrUnset(this, '$super', old_super);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})(value, name) : value
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dummy constructor */
|
||||||
|
var rv = function() {
|
||||||
|
if (disable_constructor)
|
||||||
|
return;
|
||||||
|
var proper_this = root === this ? cheapNew(arguments.callee) : this;
|
||||||
|
if (proper_this.__init__)
|
||||||
|
proper_this.__init__.apply(proper_this, arguments);
|
||||||
|
proper_this.$class = rv;
|
||||||
|
return proper_this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy all class vars over of any */
|
||||||
|
for (var key in properties.__classvars__) {
|
||||||
|
var value = getOwnProperty(properties.__classvars__, key);
|
||||||
|
if (value !== undefined)
|
||||||
|
rv[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy prototype and constructor over, reattach $extend and
|
||||||
|
return the class */
|
||||||
|
rv.prototype = prototype;
|
||||||
|
rv.constructor = rv;
|
||||||
|
rv.$extend = Class.$extend;
|
||||||
|
rv.$withData = Class.$withData;
|
||||||
|
return rv;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* instanciate with data functionality */
|
||||||
|
Class.$withData = function(data) {
|
||||||
|
var rv = cheapNew(this);
|
||||||
|
for (var key in data) {
|
||||||
|
var value = getOwnProperty(data, key);
|
||||||
|
if (value !== undefined)
|
||||||
|
rv[key] = value;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* export the class */
|
||||||
|
root.Class = Class;
|
||||||
|
})();
|
|
@ -0,0 +1,8 @@
|
||||||
|
/*
|
||||||
|
HTML5 Shiv v3.6.2pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
|
||||||
|
*/
|
||||||
|
(function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag();
|
||||||
|
a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/\w+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x<style>article,aside,figcaption,figure,footer,header,hgroup,nav,section{display:block}mark{background:#FF0;color:#000}</style>";
|
||||||
|
c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="<xyz></xyz>";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode||
|
||||||
|
"undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",version:"3.6.2pre",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);if(g)return a.createDocumentFragment();
|
||||||
|
for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d<h;d++)c.createElement(e[d]);return c}};l.html5=e;q(f)})(this,document);
|
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright (C) 2010 Google Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fileoverview
|
||||||
|
* Registers a language handler for the Go language..
|
||||||
|
* <p>
|
||||||
|
* Based on the lexical grammar at
|
||||||
|
* http://golang.org/doc/go_spec.html#Lexical_elements
|
||||||
|
* <p>
|
||||||
|
* Go uses a minimal style for highlighting so the below does not distinguish
|
||||||
|
* strings, keywords, literals, etc. by design.
|
||||||
|
* From a discussion with the Go designers:
|
||||||
|
* <pre>
|
||||||
|
* On Thursday, July 22, 2010, Mike Samuel <...> wrote:
|
||||||
|
* > On Thu, Jul 22, 2010, Rob 'Commander' Pike <...> wrote:
|
||||||
|
* >> Personally, I would vote for the subdued style godoc presents at http://golang.org
|
||||||
|
* >>
|
||||||
|
* >> Not as fancy as some like, but a case can be made it's the official style.
|
||||||
|
* >> If people want more colors, I wouldn't fight too hard, in the interest of
|
||||||
|
* >> encouragement through familiarity, but even then I would ask to shy away
|
||||||
|
* >> from technicolor starbursts.
|
||||||
|
* >
|
||||||
|
* > Like http://golang.org/pkg/go/scanner/ where comments are blue and all
|
||||||
|
* > other content is black? I can do that.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @author mikesamuel@gmail.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
PR['registerLangHandler'](
|
||||||
|
PR['createSimpleLexer'](
|
||||||
|
[
|
||||||
|
// Whitespace is made up of spaces, tabs and newline characters.
|
||||||
|
[PR['PR_PLAIN'], /^[\t\n\r \xA0]+/, null, '\t\n\r \xA0'],
|
||||||
|
// Not escaped as a string. See note on minimalism above.
|
||||||
|
[PR['PR_PLAIN'], /^(?:\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)|\'(?:[^\'\\]|\\[\s\S])+(?:\'|$)|`[^`]*(?:`|$))/, null, '"\'']
|
||||||
|
],
|
||||||
|
[
|
||||||
|
// Block comments are delimited by /* and */.
|
||||||
|
// Single-line comments begin with // and extend to the end of a line.
|
||||||
|
[PR['PR_COMMENT'], /^(?:\/\/[^\r\n]*|\/\*[\s\S]*?\*\/)/],
|
||||||
|
[PR['PR_PLAIN'], /^(?:[^\/\"\'`]|\/(?![\/\*]))+/i]
|
||||||
|
]),
|
||||||
|
['go']);
|
|
@ -0,0 +1,30 @@
|
||||||
|
!function(){var q=null;window.PR_SHOULD_USE_CONTINUATION=!0;
|
||||||
|
(function(){function S(a){function d(e){var b=e.charCodeAt(0);if(b!==92)return b;var a=e.charAt(1);return(b=r[a])?b:"0"<=a&&a<="7"?parseInt(e.substring(1),8):a==="u"||a==="x"?parseInt(e.substring(2),16):e.charCodeAt(1)}function g(e){if(e<32)return(e<16?"\\x0":"\\x")+e.toString(16);e=String.fromCharCode(e);return e==="\\"||e==="-"||e==="]"||e==="^"?"\\"+e:e}function b(e){var b=e.substring(1,e.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),e=[],a=
|
||||||
|
b[0]==="^",c=["["];a&&c.push("^");for(var a=a?1:0,f=b.length;a<f;++a){var h=b[a];if(/\\[bdsw]/i.test(h))c.push(h);else{var h=d(h),l;a+2<f&&"-"===b[a+1]?(l=d(b[a+2]),a+=2):l=h;e.push([h,l]);l<65||h>122||(l<65||h>90||e.push([Math.max(65,h)|32,Math.min(l,90)|32]),l<97||h>122||e.push([Math.max(97,h)&-33,Math.min(l,122)&-33]))}}e.sort(function(e,a){return e[0]-a[0]||a[1]-e[1]});b=[];f=[];for(a=0;a<e.length;++a)h=e[a],h[0]<=f[1]+1?f[1]=Math.max(f[1],h[1]):b.push(f=h);for(a=0;a<b.length;++a)h=b[a],c.push(g(h[0])),
|
||||||
|
h[1]>h[0]&&(h[1]+1>h[0]&&c.push("-"),c.push(g(h[1])));c.push("]");return c.join("")}function s(e){for(var a=e.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),c=a.length,d=[],f=0,h=0;f<c;++f){var l=a[f];l==="("?++h:"\\"===l.charAt(0)&&(l=+l.substring(1))&&(l<=h?d[l]=-1:a[f]=g(l))}for(f=1;f<d.length;++f)-1===d[f]&&(d[f]=++x);for(h=f=0;f<c;++f)l=a[f],l==="("?(++h,d[h]||(a[f]="(?:")):"\\"===l.charAt(0)&&(l=+l.substring(1))&&l<=h&&
|
||||||
|
(a[f]="\\"+d[l]);for(f=0;f<c;++f)"^"===a[f]&&"^"!==a[f+1]&&(a[f]="");if(e.ignoreCase&&m)for(f=0;f<c;++f)l=a[f],e=l.charAt(0),l.length>=2&&e==="["?a[f]=b(l):e!=="\\"&&(a[f]=l.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return a.join("")}for(var x=0,m=!1,j=!1,k=0,c=a.length;k<c;++k){var i=a[k];if(i.ignoreCase)j=!0;else if(/[a-z]/i.test(i.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){m=!0;j=!1;break}}for(var r={b:8,t:9,n:10,v:11,
|
||||||
|
f:12,r:13},n=[],k=0,c=a.length;k<c;++k){i=a[k];if(i.global||i.multiline)throw Error(""+i);n.push("(?:"+s(i)+")")}return RegExp(n.join("|"),j?"gi":"g")}function T(a,d){function g(a){var c=a.nodeType;if(c==1){if(!b.test(a.className)){for(c=a.firstChild;c;c=c.nextSibling)g(c);c=a.nodeName.toLowerCase();if("br"===c||"li"===c)s[j]="\n",m[j<<1]=x++,m[j++<<1|1]=a}}else if(c==3||c==4)c=a.nodeValue,c.length&&(c=d?c.replace(/\r\n?/g,"\n"):c.replace(/[\t\n\r ]+/g," "),s[j]=c,m[j<<1]=x,x+=c.length,m[j++<<1|1]=
|
||||||
|
a)}var b=/(?:^|\s)nocode(?:\s|$)/,s=[],x=0,m=[],j=0;g(a);return{a:s.join("").replace(/\n$/,""),d:m}}function H(a,d,g,b){d&&(a={a:d,e:a},g(a),b.push.apply(b,a.g))}function U(a){for(var d=void 0,g=a.firstChild;g;g=g.nextSibling)var b=g.nodeType,d=b===1?d?a:g:b===3?V.test(g.nodeValue)?a:d:d;return d===a?void 0:d}function C(a,d){function g(a){for(var j=a.e,k=[j,"pln"],c=0,i=a.a.match(s)||[],r={},n=0,e=i.length;n<e;++n){var z=i[n],w=r[z],t=void 0,f;if(typeof w==="string")f=!1;else{var h=b[z.charAt(0)];
|
||||||
|
if(h)t=z.match(h[1]),w=h[0];else{for(f=0;f<x;++f)if(h=d[f],t=z.match(h[1])){w=h[0];break}t||(w="pln")}if((f=w.length>=5&&"lang-"===w.substring(0,5))&&!(t&&typeof t[1]==="string"))f=!1,w="src";f||(r[z]=w)}h=c;c+=z.length;if(f){f=t[1];var l=z.indexOf(f),B=l+f.length;t[2]&&(B=z.length-t[2].length,l=B-f.length);w=w.substring(5);H(j+h,z.substring(0,l),g,k);H(j+h+l,f,I(w,f),k);H(j+h+B,z.substring(B),g,k)}else k.push(j+h,w)}a.g=k}var b={},s;(function(){for(var g=a.concat(d),j=[],k={},c=0,i=g.length;c<i;++c){var r=
|
||||||
|
g[c],n=r[3];if(n)for(var e=n.length;--e>=0;)b[n.charAt(e)]=r;r=r[1];n=""+r;k.hasOwnProperty(n)||(j.push(r),k[n]=q)}j.push(/[\S\s]/);s=S(j)})();var x=d.length;return g}function v(a){var d=[],g=[];a.tripleQuotedStrings?d.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?d.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,
|
||||||
|
q,"'\"`"]):d.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&g.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var b=a.hashComments;b&&(a.cStyleComments?(b>1?d.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):d.push(["com",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),g.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,q])):d.push(["com",
|
||||||
|
/^#[^\n\r]*/,q,"#"]));a.cStyleComments&&(g.push(["com",/^\/\/[^\n\r]*/,q]),g.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));if(b=a.regexLiterals){var s=(b=b>1?"":"\n\r")?".":"[\\S\\s]";g.push(["lang-regex",RegExp("^(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*("+("/(?=[^/*"+b+"])(?:[^/\\x5B\\x5C"+b+"]|\\x5C"+s+"|\\x5B(?:[^\\x5C\\x5D"+b+"]|\\x5C"+
|
||||||
|
s+")*(?:\\x5D|$))+/")+")")])}(b=a.types)&&g.push(["typ",b]);b=(""+a.keywords).replace(/^ | $/g,"");b.length&&g.push(["kwd",RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),q]);d.push(["pln",/^\s+/,q," \r\n\t\u00a0"]);b="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(b+="(?!s*/)");g.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,
|
||||||
|
q],["pun",RegExp(b),q]);return C(d,g)}function J(a,d,g){function b(a){var c=a.nodeType;if(c==1&&!x.test(a.className))if("br"===a.nodeName)s(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((c==3||c==4)&&g){var d=a.nodeValue,i=d.match(m);if(i)c=d.substring(0,i.index),a.nodeValue=c,(d=d.substring(i.index+i[0].length))&&a.parentNode.insertBefore(j.createTextNode(d),a.nextSibling),s(a),c||a.parentNode.removeChild(a)}}function s(a){function b(a,c){var d=
|
||||||
|
c?a.cloneNode(!1):a,e=a.parentNode;if(e){var e=b(e,1),g=a.nextSibling;e.appendChild(d);for(var i=g;i;i=g)g=i.nextSibling,e.appendChild(i)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),d;(d=a.parentNode)&&d.nodeType===1;)a=d;c.push(a)}for(var x=/(?:^|\s)nocode(?:\s|$)/,m=/\r\n?|\n/,j=a.ownerDocument,k=j.createElement("li");a.firstChild;)k.appendChild(a.firstChild);for(var c=[k],i=0;i<c.length;++i)b(c[i]);d===(d|0)&&c[0].setAttribute("value",d);var r=j.createElement("ol");
|
||||||
|
r.className="linenums";for(var d=Math.max(0,d-1|0)||0,i=0,n=c.length;i<n;++i)k=c[i],k.className="L"+(i+d)%10,k.firstChild||k.appendChild(j.createTextNode("\u00a0")),r.appendChild(k);a.appendChild(r)}function p(a,d){for(var g=d.length;--g>=0;){var b=d[g];F.hasOwnProperty(b)?D.console&&console.warn("cannot override language handler %s",b):F[b]=a}}function I(a,d){if(!a||!F.hasOwnProperty(a))a=/^\s*</.test(d)?"default-markup":"default-code";return F[a]}function K(a){var d=a.h;try{var g=T(a.c,a.i),b=g.a;
|
||||||
|
a.a=b;a.d=g.d;a.e=0;I(d,b)(a);var s=/\bMSIE\s(\d+)/.exec(navigator.userAgent),s=s&&+s[1]<=8,d=/\n/g,x=a.a,m=x.length,g=0,j=a.d,k=j.length,b=0,c=a.g,i=c.length,r=0;c[i]=m;var n,e;for(e=n=0;e<i;)c[e]!==c[e+2]?(c[n++]=c[e++],c[n++]=c[e++]):e+=2;i=n;for(e=n=0;e<i;){for(var p=c[e],w=c[e+1],t=e+2;t+2<=i&&c[t+1]===w;)t+=2;c[n++]=p;c[n++]=w;e=t}c.length=n;var f=a.c,h;if(f)h=f.style.display,f.style.display="none";try{for(;b<k;){var l=j[b+2]||m,B=c[r+2]||m,t=Math.min(l,B),A=j[b+1],G;if(A.nodeType!==1&&(G=x.substring(g,
|
||||||
|
t))){s&&(G=G.replace(d,"\r"));A.nodeValue=G;var L=A.ownerDocument,o=L.createElement("span");o.className=c[r+1];var v=A.parentNode;v.replaceChild(o,A);o.appendChild(A);g<l&&(j[b+1]=A=L.createTextNode(x.substring(t,l)),v.insertBefore(A,o.nextSibling))}g=t;g>=l&&(b+=2);g>=B&&(r+=2)}}finally{if(f)f.style.display=h}}catch(u){D.console&&console.log(u&&u.stack||u)}}var D=window,y=["break,continue,do,else,for,if,return,while"],E=[[y,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
|
||||||
|
"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],M=[E,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],N=[E,"abstract,assert,boolean,byte,extends,final,finally,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"],
|
||||||
|
O=[N,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,internal,into,is,let,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where"],E=[E,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],P=[y,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],
|
||||||
|
Q=[y,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],W=[y,"as,assert,const,copy,drop,enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv,pub,pure,ref,self,static,struct,true,trait,type,unsafe,use"],y=[y,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],R=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/,
|
||||||
|
V=/\S/,X=v({keywords:[M,O,E,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",P,Q,y],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),F={};p(X,["default-code"]);p(C([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",
|
||||||
|
/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);p(C([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],
|
||||||
|
["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);p(C([],[["atv",/^[\S\s]+/]]),["uq.val"]);p(v({keywords:M,hashComments:!0,cStyleComments:!0,types:R}),["c","cc","cpp","cxx","cyc","m"]);p(v({keywords:"null,true,false"}),["json"]);p(v({keywords:O,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:R}),
|
||||||
|
["cs"]);p(v({keywords:N,cStyleComments:!0}),["java"]);p(v({keywords:y,hashComments:!0,multiLineStrings:!0}),["bash","bsh","csh","sh"]);p(v({keywords:P,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py","python"]);p(v({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:2}),["perl","pl","pm"]);p(v({keywords:Q,
|
||||||
|
hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb","ruby"]);p(v({keywords:E,cStyleComments:!0,regexLiterals:!0}),["javascript","js"]);p(v({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);p(v({keywords:W,cStyleComments:!0,multilineStrings:!0}),["rc","rs","rust"]);
|
||||||
|
p(C([],[["str",/^[\S\s]+/]]),["regex"]);var Y=D.PR={createSimpleLexer:C,registerLangHandler:p,sourceDecorator:v,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:D.prettyPrintOne=function(a,d,g){var b=document.createElement("div");b.innerHTML="<pre>"+a+"</pre>";b=b.firstChild;g&&J(b,g,!0);K({h:d,j:g,c:b,i:1});
|
||||||
|
return b.innerHTML},prettyPrint:D.prettyPrint=function(a,d){function g(){for(var b=D.PR_SHOULD_USE_CONTINUATION?c.now()+250:Infinity;i<p.length&&c.now()<b;i++){for(var d=p[i],j=h,k=d;k=k.previousSibling;){var m=k.nodeType,o=(m===7||m===8)&&k.nodeValue;if(o?!/^\??prettify\b/.test(o):m!==3||/\S/.test(k.nodeValue))break;if(o){j={};o.replace(/\b(\w+)=([\w%+\-.:]+)/g,function(a,b,c){j[b]=c});break}}k=d.className;if((j!==h||e.test(k))&&!v.test(k)){m=!1;for(o=d.parentNode;o;o=o.parentNode)if(f.test(o.tagName)&&
|
||||||
|
o.className&&e.test(o.className)){m=!0;break}if(!m){d.className+=" prettyprinted";m=j.lang;if(!m){var m=k.match(n),y;if(!m&&(y=U(d))&&t.test(y.tagName))m=y.className.match(n);m&&(m=m[1])}if(w.test(d.tagName))o=1;else var o=d.currentStyle,u=s.defaultView,o=(o=o?o.whiteSpace:u&&u.getComputedStyle?u.getComputedStyle(d,q).getPropertyValue("white-space"):0)&&"pre"===o.substring(0,3);u=j.linenums;if(!(u=u==="true"||+u))u=(u=k.match(/\blinenums\b(?::(\d+))?/))?u[1]&&u[1].length?+u[1]:!0:!1;u&&J(d,u,o);r=
|
||||||
|
{h:m,c:d,j:u,i:o};K(r)}}}i<p.length?setTimeout(g,250):"function"===typeof a&&a()}for(var b=d||document.body,s=b.ownerDocument||document,b=[b.getElementsByTagName("pre"),b.getElementsByTagName("code"),b.getElementsByTagName("xmp")],p=[],m=0;m<b.length;++m)for(var j=0,k=b[m].length;j<k;++j)p.push(b[m][j]);var b=q,c=Date;c.now||(c={now:function(){return+new Date}});var i=0,r,n=/\blang(?:uage)?-([\w.]+)(?!\S)/,e=/\bprettyprint\b/,v=/\bprettyprinted\b/,w=/pre|xmp/i,t=/^code$/i,f=/^(?:pre|code|xmp)$/i,
|
||||||
|
h={};g()}};typeof define==="function"&&define.amd&&define("google-code-prettify",[],function(){return Y})})();}()
|
|
@ -0,0 +1,6 @@
|
||||||
|
/*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */
|
||||||
|
/*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */
|
||||||
|
window.matchMedia=window.matchMedia||function(a){"use strict";var c,d=a.documentElement,e=d.firstElementChild||d.firstChild,f=a.createElement("body"),g=a.createElement("div");return g.id="mq-test-1",g.style.cssText="position:absolute;top:-100em",f.style.background="none",f.appendChild(g),function(a){return g.innerHTML='­<style media="'+a+'"> #mq-test-1 { width: 42px; }</style>',d.insertBefore(f,e),c=42===g.offsetWidth,d.removeChild(f),{matches:c,media:a}}}(document);
|
||||||
|
|
||||||
|
/*! Respond.js v1.1.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */
|
||||||
|
(function(a){"use strict";function x(){u(!0)}var b={};a.respond=b,b.update=function(){},b.mediaQueriesSupported=a.matchMedia&&a.matchMedia("only all").matches,b.mediaQueriesSupported;var q,r,t,c=a.document,d=c.documentElement,e=[],f=[],g=[],h={},i=30,j=c.getElementsByTagName("head")[0]||d,k=c.getElementsByTagName("base")[0],l=j.getElementsByTagName("link"),m=[],n=function(){for(var b=0;l.length>b;b++){var c=l[b],d=c.href,e=c.media,f=c.rel&&"stylesheet"===c.rel.toLowerCase();d&&f&&!h[d]&&(c.styleSheet&&c.styleSheet.rawCssText?(p(c.styleSheet.rawCssText,d,e),h[d]=!0):(!/^([a-zA-Z:]*\/\/)/.test(d)&&!k||d.replace(RegExp.$1,"").split("/")[0]===a.location.host)&&m.push({href:d,media:e}))}o()},o=function(){if(m.length){var a=m.shift();v(a.href,function(b){p(b,a.href,a.media),h[a.href]=!0,setTimeout(function(){o()},0)})}},p=function(a,b,c){var d=a.match(/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi),g=d&&d.length||0;b=b.substring(0,b.lastIndexOf("/"));var h=function(a){return a.replace(/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,"$1"+b+"$2$3")},i=!g&&c;b.length&&(b+="/"),i&&(g=1);for(var j=0;g>j;j++){var k,l,m,n;i?(k=c,f.push(h(a))):(k=d[j].match(/@media *([^\{]+)\{([\S\s]+?)$/)&&RegExp.$1,f.push(RegExp.$2&&h(RegExp.$2))),m=k.split(","),n=m.length;for(var o=0;n>o;o++)l=m[o],e.push({media:l.split("(")[0].match(/(only\s+)?([a-zA-Z]+)\s?/)&&RegExp.$2||"all",rules:f.length-1,hasquery:l.indexOf("(")>-1,minw:l.match(/\(min\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:l.match(/\(max\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},s=function(){var a,b=c.createElement("div"),e=c.body,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",e||(e=f=c.createElement("body"),e.style.background="none"),e.appendChild(b),d.insertBefore(e,d.firstChild),a=b.offsetWidth,f?d.removeChild(e):e.removeChild(b),a=t=parseFloat(a)},u=function(a){var b="clientWidth",h=d[b],k="CSS1Compat"===c.compatMode&&h||c.body[b]||h,m={},n=l[l.length-1],o=(new Date).getTime();if(a&&q&&i>o-q)return clearTimeout(r),r=setTimeout(u,i),void 0;q=o;for(var p in e)if(e.hasOwnProperty(p)){var v=e[p],w=v.minw,x=v.maxw,y=null===w,z=null===x,A="em";w&&(w=parseFloat(w)*(w.indexOf(A)>-1?t||s():1)),x&&(x=parseFloat(x)*(x.indexOf(A)>-1?t||s():1)),v.hasquery&&(y&&z||!(y||k>=w)||!(z||x>=k))||(m[v.media]||(m[v.media]=[]),m[v.media].push(f[v.rules]))}for(var B in g)g.hasOwnProperty(B)&&g[B]&&g[B].parentNode===j&&j.removeChild(g[B]);for(var C in m)if(m.hasOwnProperty(C)){var D=c.createElement("style"),E=m[C].join("\n");D.type="text/css",D.media=C,j.insertBefore(D,n.nextSibling),D.styleSheet?D.styleSheet.cssText=E:D.appendChild(c.createTextNode(E)),g.push(D)}},v=function(a,b){var c=w();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))},w=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}();n(),b.update=n,a.addEventListener?a.addEventListener("resize",x,!1):a.attachEvent&&a.attachEvent("onresize",x)})(this);
|
|
@ -0,0 +1,35 @@
|
||||||
|
<div id="footer">
|
||||||
|
<div class="container">
|
||||||
|
<div class="footer-links">
|
||||||
|
<ul class="main-links nav navbar-nav rls-sb">
|
||||||
|
<li class="li-under"><a href="/intro/index.html">Intro</a></li>
|
||||||
|
<li class="active li-under"><a href="/docs/index.html">Docs</a></li>
|
||||||
|
<li class="li-under"><a href="/community.html">Community</a></li>
|
||||||
|
<li class="li-under"><a href="http://demo.terraform.io/">Demo</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<ul class="buttons nav navbar-nav rls-sb">
|
||||||
|
<li class="first download outline-btn purple"><a href="/downloads.html">Download</a></li>
|
||||||
|
<li class="github outline-btn purple"><a href="https://github.com/hashicorp/terraform">Github</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="footer-logo">
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
<div class="footer-hashi os">
|
||||||
|
<span>© 2014. A <a href="http://www.hashicorp.com">HashiCorp</a> Project.</span>
|
||||||
|
<a class="hashi-logo" href="http://www.hashicorp.com"><img src="/images/footer-hashicorp-logo.png"></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/javascripts/lib/jquery-2.0.3.min.js"></script>
|
||||||
|
<script src="/javascripts/lib/bootstrap.min.js"></script>
|
||||||
|
<script src="/javascripts/app/deploy/site.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
APP.initialize();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,72 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta name="description" content="Service discovery and configuration made easy. Distributed, highly available, and datacenter-aware.">
|
||||||
|
|
||||||
|
<link rel="shortcut icon" href="/images/favicon.png">
|
||||||
|
|
||||||
|
<title><%= current_page.data.page_title ? "#{current_page.data.page_title} - " : "" %>Terraform</title>
|
||||||
|
|
||||||
|
<!-- Bootstrap core CSS -->
|
||||||
|
<%= stylesheet_link_tag "bootstrap.min" %>
|
||||||
|
<%= stylesheet_link_tag "main" %>
|
||||||
|
|
||||||
|
<!-- google fonts -->
|
||||||
|
<!-- <link href='http://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700' rel='stylesheet' type='text/css'>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- typekit -->
|
||||||
|
<script type="text/javascript" src="//use.typekit.net/kgv0shi.js"></script>
|
||||||
|
<script type="text/javascript">try{Typekit.load();}catch(e){}</script>
|
||||||
|
|
||||||
|
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||||
|
<!--[if lt IE 9]>
|
||||||
|
<%= javascript_include_tag "html5shiv", "respond.min" %>
|
||||||
|
<![endif]-->
|
||||||
|
|
||||||
|
<%= yield_content :head %>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||||
|
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||||
|
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||||
|
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||||
|
|
||||||
|
ga('create', 'UA-50021714-1', 'terraform.io');
|
||||||
|
ga('send', 'pageview');
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body class="page-<%= current_page.data.page_title ? "#{current_page.data.page_title} layout-#{current_page.data.layout} page-sub" : "home layout-#{current_page.data.layout}" %>">
|
||||||
|
<div id="header" class="<%= current_page.data.page_title == "home" ? "" : "navbar-static-top" %>">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-header">
|
||||||
|
<button class="navbar-toggle" type="button" data-toggle="collapse" data-target=".bs-navbar-collapse">
|
||||||
|
<span class="sr-only">Toggle navigation</span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
</button>
|
||||||
|
<a class="navbar-brand logo" href="/">
|
||||||
|
<span></span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav class="collapse navbar-collapse bs-navbar-collapse" role="navigation">
|
||||||
|
<ul class="buttons nav navbar-nav navbar-right rls-sb">
|
||||||
|
<li class="first download outline-btn"><a href="/downloads.html">Download</a></li>
|
||||||
|
<li class="github outline-btn"><a href="https://github.com/hashicorp/terraform">Github</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<ul class="main-links nav navbar-nav navbar-right rls-sb">
|
||||||
|
<li class="first li-under"><a href="/intro/index.html">Intro</a></li>
|
||||||
|
<li class="li-under"><a href="/docs/index.html">Docs</a></li>
|
||||||
|
<li class="li-under"><a href="/community.html">Community</a></li>
|
||||||
|
<li class="li-under"><a href="http://demo.terraform.io/">Demo</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,169 @@
|
||||||
|
<% wrap_layout :inner do %>
|
||||||
|
<% content_for :sidebar do %>
|
||||||
|
<div class="docs-sidebar hidden-print affix-top" role="complementary">
|
||||||
|
<ul class="nav docs-sidenav">
|
||||||
|
<li<%= sidebar_current("docs-home") %>>
|
||||||
|
<a href="/docs/index.html">Documentation Home</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-upgrading") %>>
|
||||||
|
<a href="/docs/upgrading.html">Upgrading and Compatibility</a>
|
||||||
|
<ul class="nav">
|
||||||
|
<li<%= sidebar_current("docs-upgrading-upgrading") %>>
|
||||||
|
<a href="/docs/upgrading.html">Upgrading Terraform</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-upgrading-compat") %>>
|
||||||
|
<a href="/docs/compatibility.html">Compatibility Promise</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-internals") %>>
|
||||||
|
<a href="/docs/internals/index.html">Terraform Internals</a>
|
||||||
|
<ul class="nav">
|
||||||
|
<li<%= sidebar_current("docs-internals-architecture") %>>
|
||||||
|
<a href="/docs/internals/architecture.html">Architecture</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-internals-consensus") %>>
|
||||||
|
<a href="/docs/internals/consensus.html">Consensus Protocol</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-internals-gossip") %>>
|
||||||
|
<a href="/docs/internals/gossip.html">Gossip Protocol</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-internals-sessions") %>>
|
||||||
|
<a href="/docs/internals/sessions.html">Sessions</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-internals-security") %>>
|
||||||
|
<a href="/docs/internals/security.html">Security Model</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-internals-jepsen") %>>
|
||||||
|
<a href="/docs/internals/jepsen.html">Jepsen Testing</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-commands") %>>
|
||||||
|
<a href="/docs/commands/index.html">Terraform Commands (CLI)</a>
|
||||||
|
<ul class="nav">
|
||||||
|
<li<%= sidebar_current("docs-commands-agent") %>>
|
||||||
|
<a href="/docs/commands/agent.html">agent</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-commands-forceleave") %>>
|
||||||
|
<a href="/docs/commands/force-leave.html">force-leave</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-commands-join") %>>
|
||||||
|
<a href="/docs/commands/join.html">join</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-commands-keygen") %>>
|
||||||
|
<a href="/docs/commands/keygen.html">keygen</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-commands-leave") %>>
|
||||||
|
<a href="/docs/commands/leave.html">leave</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-commands-members") %>>
|
||||||
|
<a href="/docs/commands/members.html">members</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-commands-monitor") %>>
|
||||||
|
<a href="/docs/commands/monitor.html">monitor</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-commands-info") %>>
|
||||||
|
<a href="/docs/commands/info.html">info</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-agent") %>>
|
||||||
|
<a href="/docs/agent/basics.html">Terraform Agent</a>
|
||||||
|
<ul class="nav">
|
||||||
|
<li<%= sidebar_current("docs-agent-running") %>>
|
||||||
|
<a href="/docs/agent/basics.html">Running and Stopping</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-agent-dns") %>>
|
||||||
|
<a href="/docs/agent/dns.html">DNS Interface</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-agent-http") %>>
|
||||||
|
<a href="/docs/agent/http.html">HTTP API</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-agent-config") %>>
|
||||||
|
<a href="/docs/agent/options.html">Configuration</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-agent-services") %>>
|
||||||
|
<a href="/docs/agent/services.html">Service Definitions</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-agent-checks") %>>
|
||||||
|
<a href="/docs/agent/checks.html">Check Definitions</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-agent-encryption") %>>
|
||||||
|
<a href="/docs/agent/encryption.html">Encryption</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-agent-rpc") %>>
|
||||||
|
<a href="/docs/agent/rpc.html">RPC Protocol</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-agent-telemetry") %>>
|
||||||
|
<a href="/docs/agent/telemetry.html">Telemetry</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-guides") %>>
|
||||||
|
<a href="/docs/guides/index.html">Guides</a>
|
||||||
|
<ul class="nav">
|
||||||
|
<li<%= sidebar_current("docs-guides-servers") %>>
|
||||||
|
<a href="/docs/guides/servers.html">Adding/Removing Servers</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-guides-bootstrapping") %>>
|
||||||
|
<a href="/docs/guides/bootstrapping.html">Bootstrapping</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-guides-dns-cache") %>>
|
||||||
|
<a href="/docs/guides/dns-cache.html">DNS Caching</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-guides-forwarding") %>>
|
||||||
|
<a href="/docs/guides/forwarding.html">DNS Forwarding</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-guides-external") %>>
|
||||||
|
<a href="/docs/guides/external.html">External Services</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-guides-leader") %>>
|
||||||
|
<a href="/docs/guides/leader-election.html">Leader Election</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-guides-datacenters") %>>
|
||||||
|
<a href="/docs/guides/datacenters.html">Multiple Datacenters</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-guides-outage") %>>
|
||||||
|
<a href="/docs/guides/outage.html">Outage Recovery</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= yield %>
|
||||||
|
<% end %>
|
|
@ -0,0 +1,17 @@
|
||||||
|
<% wrap_layout :inner do %>
|
||||||
|
<% content_for :sidebar do %>
|
||||||
|
<div class="docs-sidebar hidden-print affix-top" role="complementary">
|
||||||
|
<ul class="nav docs-sidenav">
|
||||||
|
<li<%= sidebar_current("downloads-terraform") %>>
|
||||||
|
<a href="/downloads.html">Download Terraform</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("downloads-ui") %>>
|
||||||
|
<a href="/downloads_web_ui.html">Download Web UI</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= yield %>
|
||||||
|
<% end %>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<% wrap_layout :layout do %>
|
||||||
|
<div class="container">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<%= yield_content :sidebar %>
|
||||||
|
</div> <!-- /col-md-4 -->
|
||||||
|
<div class="col-md-8" role="main">
|
||||||
|
<div class="bs-docs-section">
|
||||||
|
<%= yield %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
|
@ -0,0 +1,84 @@
|
||||||
|
<% wrap_layout :inner do %>
|
||||||
|
<% content_for :sidebar do %>
|
||||||
|
<div class="docs-sidebar hidden-print affix-top" role="complementary">
|
||||||
|
<ul class="nav docs-sidenav">
|
||||||
|
<li<%= sidebar_current("what") %>>
|
||||||
|
<a href="/intro/index.html">What is Terraform?</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("vs-other") %>>
|
||||||
|
<a href="/intro/vs/index.html">Terraform vs. Other Software</a>
|
||||||
|
<ul class="nav">
|
||||||
|
<li<%= sidebar_current("vs-other-zk") %>>
|
||||||
|
<a href="/intro/vs/zookeeper.html">ZooKeeper, doozerd, etcd</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("vs-other-chef") %>>
|
||||||
|
<a href="/intro/vs/chef-puppet.html">Chef, Puppet, etc.</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("vs-other-nagios-sensu") %>>
|
||||||
|
<a href="/intro/vs/nagios-sensu.html">Nagios, Sensu</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("vs-other-skydns") %>>
|
||||||
|
<a href="/intro/vs/skydns.html">SkyDNS</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("vs-other-smartstack") %>>
|
||||||
|
<a href="/intro/vs/smartstack.html">SmartStack</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("vs-other-serf") %>>
|
||||||
|
<a href="/intro/vs/serf.html">Serf</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("vs-other-custom") %>>
|
||||||
|
<a href="/intro/vs/custom.html">Custom Solutions</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("gettingstarted") %>>
|
||||||
|
<a href="/intro/getting-started/install.html">Getting Started</a>
|
||||||
|
<ul class="nav">
|
||||||
|
<li<%= sidebar_current("gettingstarted-install") %>>
|
||||||
|
<a href="/intro/getting-started/install.html">Install Terraform</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("gettingstarted-agent") %>>
|
||||||
|
<a href="/intro/getting-started/agent.html">Run the Agent</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("gettingstarted-services") %>>
|
||||||
|
<a href="/intro/getting-started/services.html">Services</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("gettingstarted-join") %>>
|
||||||
|
<a href="/intro/getting-started/join.html">Terraform Cluster</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("gettingstarted-checks") %>>
|
||||||
|
<a href="/intro/getting-started/checks.html">Health Checks</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("gettingstarted-kv") %>>
|
||||||
|
<a href="/intro/getting-started/kv.html">Key/Value Data</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("gettingstarted-ui") %>>
|
||||||
|
<a href="/intro/getting-started/ui.html">Web UI</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("gettingstarted-nextsteps") %>>
|
||||||
|
<a href="/intro/getting-started/next-steps.html">Next Steps</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= yield %>
|
||||||
|
<% end %>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<%= partial "layouts/header" %>
|
||||||
|
<%= yield %>
|
||||||
|
<%= partial "layouts/footer" %>
|