toquen 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ docs
2
+ *.gem
3
+ *.rbc
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in toquen.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Brian Muller
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,87 @@
1
+ # Toquen
2
+
3
+ **Toquen** combines [Capistrano 3](http://www.capistranorb.com), [Chef](http://www.getchef.com), and [AWS instance tags](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html) into one bundle of joy. Instance roles are stored in AWS tags and **Toquen** can suck those out, put them into data bags for chef, and create stages in capistrano. You can then selectively run chef on individual servers or whole roles that contain many servers with simple commands.
4
+
5
+ ## Installation
6
+
7
+ Generally, it's easiest if you start off in an empty directory. First, create a file named *Gemfile* that contains these lines:
8
+
9
+ ```ruby
10
+ source 'http://rubygems.org'
11
+ gem 'toquen'
12
+ ```
13
+
14
+ Then, create a file named *Capfile* that contains the following line:
15
+
16
+ ```ruby
17
+ require 'toquen'
18
+ ```
19
+
20
+ And then on the command line execute:
21
+
22
+ $ bundle
23
+ $ cap toquen_install
24
+
25
+ This will create a config directory with a file named *deploy.rb*. Edit this file, setting the location of your AWS key, AWS credentials, and chef cookbooks/data bags/roles.
26
+
27
+ Then, in AWS, create an [AWS instance tag](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html) named "Roles" for each instance, using a space separated list of chef roles as the value. The "Name" tag must also be set or the instance will be ignored.
28
+
29
+ Then, run:
30
+
31
+ $ cap update_roles
32
+
33
+ This will create a data_bag named *servers* that contains one item per server name, as well as create stages per server and role for use in capistrano.
34
+
35
+ ## Server Bootstrapping
36
+ Bootstrapping a server will perform all of the following:
37
+
38
+ 1. Update all packages (assuming a Debian/Ubuntu system)
39
+ 1. Set ruby 1.9.3 as the default ruby
40
+ 1. Install rubygems
41
+ 1. Install the chef and bundler gems
42
+ 1. Reboot
43
+
44
+ You can bootstrap a single server by using:
45
+
46
+ $ cap server-<server name> update_roles
47
+
48
+ Or a all the servers with a given role with:
49
+
50
+ $ cap <role name> update_roles
51
+
52
+ A lockfile is created after the first bootstrapping so that the full bootstrap process is only run once per server.
53
+
54
+ ## Running Chef-Solo
55
+ You can run chef-solo for a single server by using:
56
+
57
+ $ cap server-<server name> cook
58
+
59
+ Or a all the servers with a given role with:
60
+
61
+ $ cap <role name> cook
62
+
63
+ ## Updating Roles
64
+ If you change the roles of any servers you will need to run:
65
+
66
+ $ bundle exec cap update_roles
67
+
68
+ This will update the *servers* data_bag as well as the capistrano stages.
69
+
70
+ ## Additional Configuration
71
+ If you want to use a different tag name (or you like commas as a delimiter) you can specify your own role extractor by placing the following in either your Capfile or config/deploy.rb:
72
+
73
+ ```ruby
74
+ Toquen.config.aws_roles_extractor = lambda { |inst| (inst.tags["MyRoles"] || "").split(",") }
75
+ ```
76
+
77
+ By default, instance information is only pulled out of the default region (us-east-1), but you can specify mutiple alternative regions:
78
+
79
+ ```ruby
80
+ set :aws_regions, ['us-west-1', 'us-west-2']
81
+ ```
82
+
83
+ You can also manually specify the version of rubygems you want installed (default is 2.1.11):
84
+
85
+ ```ruby
86
+ set :rubygems_version, "2.1.11"
87
+ ```
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rdoc/task'
3
+
4
+ RDoc::Task.new("doc") { |rdoc|
5
+ rdoc.title = "Capistrano + AWS + Chef-Solo"
6
+ rdoc.rdoc_dir = 'docs'
7
+ rdoc.rdoc_files.include('README.md')
8
+ rdoc.rdoc_files.include('lib/**/*.rb')
9
+ }
@@ -0,0 +1,17 @@
1
+ require "toquen/version"
2
+ require "toquen/aws"
3
+ require "toquen/capistrano"
4
+
5
+ module Toquen
6
+ class Config
7
+ attr_accessor :aws_roles_extractor
8
+
9
+ def initialize
10
+ @aws_roles_extractor = lambda { |inst| (inst.tags["Roles"] || "").split }
11
+ end
12
+ end
13
+
14
+ def self.config
15
+ @config ||= Config.new
16
+ end
17
+ end
@@ -0,0 +1,35 @@
1
+ require 'aws'
2
+
3
+ module Toquen
4
+ class AWSProxy
5
+ def initialize
6
+ @key_id = fetch(:aws_access_key_id)
7
+ @key = fetch(:aws_secret_access_key)
8
+ @regions = fetch(:aws_regions, ['us-east-1'])
9
+ end
10
+
11
+ def server_details
12
+ filter @regions.map { |region| server_details_in(region) }.flatten
13
+ end
14
+
15
+ def filter(details)
16
+ details.select { |detail|
17
+ not detail[:name].nil? and detail[:roles].length > 0
18
+ }
19
+ end
20
+
21
+ def server_details_in(region)
22
+ AWS.config(:access_key_id => @key_id, :secret_access_key => @key, :region => region)
23
+ AWS::EC2.new.instances.map do |i|
24
+ {
25
+ :id => i.tags["Name"],
26
+ :internal_ip => i.private_ip_address,
27
+ :external_ip => i.public_ip_address,
28
+ :name => i.tags["Name"],
29
+ :roles => Toquen.config.aws_roles_extractor.call(i)
30
+ }
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,141 @@
1
+ require 'capistrano/setup'
2
+ require 'capistrano/console'
3
+
4
+ desc "update local cache of servers and roles"
5
+ task :update_roles do
6
+ load Pathname.new fetch(:deploy_config_path, 'config/deploy.rb')
7
+ roles = Hash.new([])
8
+
9
+ aws = Toquen::AWSProxy.new
10
+ aws.server_details.each do |details|
11
+ run_locally { info "Updating local details for #{details[:name]} (#{details[:external_ip]})" }
12
+
13
+ open("#{fetch(:chef_data_bags_path)}/servers/#{details[:name]}.json", 'w') { |f|
14
+ f.write JSON.dump(details)
15
+ }
16
+ details[:roles].each { |role| roles[role] += [details[:external_ip]] }
17
+ roles['all'] += [details[:external_ip]]
18
+
19
+ open("config/deploy/server-#{details[:name]}.rb", 'w') { |f|
20
+ f.write("# This file will be overwritten by toquen! Don't put anything here.\n")
21
+ f.write("set :stage, 'server-#{details[:name]}'.intern\n")
22
+ (details[:roles] + ["server-#{details[:name]}"]).each { |role|
23
+ f.write("role '#{role}'.intern, %w{#{details[:external_ip]}}\n")
24
+ }
25
+ f.write("set :filter, :roles => %w{server-#{details[:name]}}\n")
26
+ }
27
+ end
28
+
29
+ roles.keys.each do |name|
30
+ open("config/deploy/#{name}.rb", 'w') { |f|
31
+ f.write("# This file will be overwritten by toquen! Don't put anything here.\n")
32
+ f.write("set :stage, '#{name}'.intern\n")
33
+ roles.each { |n,ips|
34
+ f.write("role '#{n}'.intern, %w{#{ips.reject(&:nil?).join(' ')}}\n")
35
+ }
36
+ f.write("set :filter, :roles => %w{#{name}}\n")
37
+ }
38
+ end
39
+ end
40
+
41
+ desc "bootstrap a server so that it can run chef"
42
+ task :bootstrap do
43
+ rgems = "rubygems-#{fetch(:rubygems_version, '2.1.11')}"
44
+ on roles(:all), in: :parallel do |host|
45
+ info "Bootstrapping #{host}..."
46
+ code = <<-EOF
47
+ #!/bin/bash
48
+ if [ -e "/home/#{fetch(:ssh_options)[:user]}/bootstrap.lock" ]; then exit 0; fi
49
+ DEBIAN_FRONTEND=noninteractive apt-get -y update
50
+ DEBIAN_FRONTEND=noninteractive apt-get -y upgrade
51
+ DEBIAN_FRONTEND=noninteractive apt-get -y dist-upgrade
52
+ DEBIAN_FRONTEND=noninteractive apt-get -y install ruby1.9.3 ruby-dev automake make
53
+ update-alternatives --set ruby /usr/bin/ruby1.9.1
54
+ cd /usr/src
55
+ rm -rf rubygems*
56
+ wget -q http://production.cf.rubygems.org/rubygems/#{rgems}.tgz
57
+ tar -zxf #{rgems}.tgz
58
+ cd /usr/src/#{rgems}
59
+ ruby setup.rb
60
+ gem install --no-rdoc --no-ri chef bundler
61
+ touch /home/#{fetch(:ssh_options)[:user]}/bootstrap.lock
62
+ echo "Rebooting now, standby..."
63
+ reboot
64
+ EOF
65
+ fname = "/home/#{fetch(:ssh_options)[:user]}/bootstrap.sh"
66
+ upload! StringIO.new(code), fname
67
+ sudo "sh #{fname}"
68
+ end
69
+ end
70
+
71
+ desc "Update cookbooks/data bags/roles on server"
72
+ task :update_kitchen do
73
+ kitchen = "/home/#{fetch(:ssh_options)[:user]}/kitchen"
74
+ lkitchen = "/tmp/toquen/kitchen"
75
+ user = fetch(:ssh_options)[:user]
76
+ key = fetch(:ssh_options)[:keys].first
77
+
78
+ run_locally do
79
+ info "Building kitchen locally..."
80
+ execute [
81
+ "rm -rf #{lkitchen}",
82
+ "mkdir -p #{lkitchen}",
83
+ "ln -s #{File.expand_path(fetch(:chef_cookbooks_path))} #{lkitchen}",
84
+ "ln -s #{File.expand_path(fetch(:chef_data_bags_path))} #{lkitchen}",
85
+ "ln -s #{File.expand_path(fetch(:chef_roles_path))} #{lkitchen}"
86
+ ].join(" && ")
87
+ end
88
+
89
+ open("#{lkitchen}/chef_config.rb", 'w') { |f|
90
+ f.write("file_cache_path '/var/chef-solo'\n")
91
+ f.write("cookbook_path '#{kitchen}/cookbooks'\n")
92
+ f.write("data_bag_path '#{kitchen}/data_bags'\n")
93
+ f.write("role_path '#{kitchen}/roles'\n")
94
+ }
95
+
96
+ on roles(:all), in: :parallel do |host|
97
+ run_locally do
98
+ info "Sending kitchen to #{host}..."
99
+ execute "rsync -avzk --delete -e 'ssh -i #{key}' #{lkitchen} #{user}@#{host}:/home/#{fetch(:ssh_options)[:user]}"
100
+ end
101
+ end
102
+ end
103
+
104
+ desc "Run chef for servers"
105
+ task :cook do
106
+ on roles(:all), in: :parallel do |host|
107
+ info "Chef is now cooking on #{host}..."
108
+ roles = host.properties.roles.reject { |r| r.to_s.start_with?('server-') }
109
+ roles = roles.map { |r| "\"role[#{r}]\"" }.join(',')
110
+ info "Roles for #{host}: #{roles}"
111
+ tfile = "/home/#{fetch(:ssh_options)[:user]}/chef.json"
112
+ upload! StringIO.new("{ \"run_list\": [ #{roles} ] }"), tfile
113
+ execute "sudo chef-solo -c kitchen/chef_config.rb -j #{tfile}"
114
+ end
115
+ end
116
+ before :cook, :update_kitchen
117
+
118
+ desc "install toquen capistrano setup to current directory"
119
+ task :toquen_install do
120
+ unless Dir.exists?('config')
121
+ puts "Creating config directory..."
122
+ Dir.mkdir('config')
123
+ end
124
+ unless Dir.exists?('config/deploy')
125
+ puts "Creating config/deploy directory..."
126
+ Dir.mkdir('config/deploy')
127
+ end
128
+ if not File.exists?('config/deploy.rb')
129
+ puts "Initializing config/deploy.rb configuration file..."
130
+ FileUtils.cp File.expand_path("../templates/deploy.rb", __FILE__), 'config/deploy.rb'
131
+ end
132
+ end
133
+
134
+ module Capistrano
135
+ module TaskEnhancements
136
+ alias_method :original_default_tasks, :default_tasks
137
+ def default_tasks
138
+ original_default_tasks + %w{toquen_install update_roles}
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,14 @@
1
+ # Set your AWS access key id and secret. This should be provisioned in
2
+ # Amazon AWS if you haven't already set it up.
3
+ set :aws_access_key_id, ""
4
+ set :aws_secret_access_key, ""
5
+
6
+ # Set the location of your SSH key. You can give a list of files, but
7
+ # the first key given will be the one used to upload your chef files to
8
+ # each server.
9
+ set :ssh_options, { :keys => ["./mykey.pem"], :user => "ubuntu" }
10
+
11
+ # Set the location of your cookbooks/data bags/roles for Chef
12
+ set :chef_cookbooks_path, 'kitchen/cookbooks'
13
+ set :chef_data_bags_path, 'kitchen/data_bags'
14
+ set :chef_roles_path, 'kitchen/roles'
@@ -0,0 +1,3 @@
1
+ module Toquen
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'toquen/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "toquen"
8
+ gem.version = Toquen::VERSION
9
+ gem.authors = ["Brian Muller"]
10
+ gem.email = ["bamuller@gmail.com"]
11
+ gem.description = "Toquen: Capistrano + AWS + Chef-Solo"
12
+ gem.summary = "Toquen: Capistrano + AWS + Chef-Solo"
13
+ gem.homepage = "https://github.com/bmuller/toquen"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+ gem.add_dependency('capistrano', '~> 3.0.1')
20
+ gem.add_dependency('aws-sdk')
21
+ gem.add_development_dependency("rdoc")
22
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: toquen
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Brian Muller
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2013-12-15 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: capistrano
17
+ requirement: &id001 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: 3.0.1
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: aws-sdk
28
+ requirement: &id002 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *id002
37
+ - !ruby/object:Gem::Dependency
38
+ name: rdoc
39
+ requirement: &id003 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: "0"
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *id003
48
+ description: "Toquen: Capistrano + AWS + Chef-Solo"
49
+ email:
50
+ - bamuller@gmail.com
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ extra_rdoc_files: []
56
+
57
+ files:
58
+ - .gitignore
59
+ - Gemfile
60
+ - LICENSE.txt
61
+ - README.md
62
+ - Rakefile
63
+ - lib/toquen.rb
64
+ - lib/toquen/aws.rb
65
+ - lib/toquen/capistrano.rb
66
+ - lib/toquen/templates/deploy.rb
67
+ - lib/toquen/version.rb
68
+ - toquen.gemspec
69
+ homepage: https://github.com/bmuller/toquen
70
+ licenses: []
71
+
72
+ post_install_message:
73
+ rdoc_options: []
74
+
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ hash: 695670950495976934
83
+ segments:
84
+ - 0
85
+ version: "0"
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ hash: 695670950495976934
92
+ segments:
93
+ - 0
94
+ version: "0"
95
+ requirements: []
96
+
97
+ rubyforge_project:
98
+ rubygems_version: 1.8.24
99
+ signing_key:
100
+ specification_version: 3
101
+ summary: "Toquen: Capistrano + AWS + Chef-Solo"
102
+ test_files: []
103
+