rig 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +22 -0
  4. data/README.md +67 -0
  5. data/Rakefile +2 -0
  6. data/bin/rig +15 -0
  7. data/lib/rig.rb +2 -0
  8. data/lib/rig/capistrano.rb +54 -0
  9. data/lib/rig/chef.rb +53 -0
  10. data/lib/rig/cloud.rb +4 -0
  11. data/lib/rig/cloud/balancer.rb +40 -0
  12. data/lib/rig/cloud/connection.rb +40 -0
  13. data/lib/rig/cloud/dns.rb +56 -0
  14. data/lib/rig/cloud/environment.rb +214 -0
  15. data/lib/rig/cloud/instance.rb +64 -0
  16. data/lib/rig/cloud/userdata.rb +111 -0
  17. data/lib/rig/command.rb +42 -0
  18. data/lib/rig/command/abstract.rb +36 -0
  19. data/lib/rig/command/balancer/destroy.rb +16 -0
  20. data/lib/rig/command/balancer/list.rb +21 -0
  21. data/lib/rig/command/balancer/main.rb +13 -0
  22. data/lib/rig/command/balancer/view.rb +19 -0
  23. data/lib/rig/command/dns/create.rb +19 -0
  24. data/lib/rig/command/dns/destroy.rb +16 -0
  25. data/lib/rig/command/dns/edit.rb +20 -0
  26. data/lib/rig/command/dns/list.rb +21 -0
  27. data/lib/rig/command/dns/main.rb +13 -0
  28. data/lib/rig/command/environment/create.rb +24 -0
  29. data/lib/rig/command/environment/destroy.rb +18 -0
  30. data/lib/rig/command/environment/list.rb +30 -0
  31. data/lib/rig/command/environment/main.rb +15 -0
  32. data/lib/rig/command/instance/create.rb +27 -0
  33. data/lib/rig/command/instance/destroy.rb +20 -0
  34. data/lib/rig/command/instance/list.rb +25 -0
  35. data/lib/rig/command/instance/main.rb +16 -0
  36. data/lib/rig/command/instance/ssh.rb +32 -0
  37. data/lib/rig/command/instance/tag/get.rb +41 -0
  38. data/lib/rig/command/instance/tag/main.rb +14 -0
  39. data/lib/rig/command/instance/tag/remove.rb +44 -0
  40. data/lib/rig/command/instance/tag/set.rb +47 -0
  41. data/lib/rig/command/instance/view.rb +20 -0
  42. data/lib/rig/command/keypair/list.rb +14 -0
  43. data/lib/rig/command/keypair/main.rb +11 -0
  44. data/lib/rig/command/main.rb +48 -0
  45. data/lib/rig/command/options.rb +97 -0
  46. data/lib/rig/config.rb +68 -0
  47. data/lib/rig/version.rb +10 -0
  48. data/rig.gemspec +39 -0
  49. metadata +196 -0
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rig.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Shawn Catanzarite
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,67 @@
1
+ # Rig
2
+
3
+ Command line cloud provisioning tool.
4
+
5
+ Using the Fog (http://fog.io/) gem, provide a suite of tools for managing provisioning from the command line.
6
+
7
+ Code is still in alpha. I'm working on documentation and refactoring things to be more generalized.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'rig'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install rig
22
+
23
+ ## Usage
24
+
25
+ $ rig -h
26
+
27
+ The rig command line uses Clamp to handle options parsing and such. Clamp automatically generates help information.
28
+
29
+ # Managing Environments
30
+
31
+ ## Instances
32
+
33
+ The `rig instance` command allows you to manage instances in your accounts.
34
+
35
+ To view all of your instances in your account, run:
36
+
37
+ ```
38
+ rig instance list
39
+ ```
40
+
41
+ Generally, you will not create instances manually, instance creation should be handled by the environment commands.
42
+
43
+ ## Environments
44
+
45
+ The `rig environment` command allows you to manage environments in your accounts.
46
+
47
+ # Developer Notes
48
+
49
+ ## Filters
50
+
51
+ Notes about handling filters on #all methods of Fog.
52
+
53
+ full filters list can be found at:
54
+ http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeInstances.html
55
+
56
+ pass an array to a filter to do an OR type query
57
+ the below means running or stopped:
58
+
59
+ filters["instance-state-name"] = %w{running stopped}
60
+
61
+ ## Contributing
62
+
63
+ 1. Fork it
64
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
65
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
66
+ 4. Push to the branch (`git push origin my-new-feature`)
67
+ 5. Create new Pull Request
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/bin/rig ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rig'
3
+ require 'rig/command'
4
+
5
+ Rig::Command::Main.run
6
+
7
+ #begin
8
+ # Rig::Command::Main.run
9
+ #rescue Exception => e
10
+ # puts e.message
11
+ # if $DEBUG
12
+ # puts $!
13
+ # puts $@
14
+ # end
15
+ #end
@@ -0,0 +1,2 @@
1
+ require 'rig/version'
2
+ require 'rig/config'
@@ -0,0 +1,54 @@
1
+ # integration with capistrano
2
+ # have some helpers to use rig to get the
3
+ # host lists for capistrano to use.
4
+
5
+ require 'rig'
6
+ require 'rig/cloud'
7
+ require 'capistrano'
8
+
9
+ module Rig
10
+ module Capistrano
11
+ def servers
12
+ env = ENV['ENVIRONMENT'] || Rig.config.environment
13
+ role = ENV['ROLE'] || Rig.config.role
14
+ servers = Rig::Cloud::Environment.find(env).servers
15
+ list = servers.select { |s| s.tags['Role'] == role }
16
+
17
+ raise "Rig could not find any servers matching environment=#{env} and role=#{role}" unless list && list.count > 0
18
+ list.each do |s|
19
+ server "#{s.tags['Name']}.#{Rig.config.dns.zone}", :web, :app
20
+ end
21
+ rescue => e
22
+ puts "*** servers not found: #{e.message}"
23
+ end
24
+ end
25
+ end
26
+
27
+ ::Capistrano.plugin :rig, Rig::Capistrano
28
+
29
+ configuration = Capistrano::Configuration.respond_to?(:instance) ?
30
+ Capistrano::Configuration.instance(:must_exist) :
31
+ Capistrano.configuration(:must_exist)
32
+
33
+ configuration.load do
34
+
35
+ puts " * reading rig information..."
36
+
37
+ set :environments, Rig::Cloud::Environment.list
38
+ set :servers, Rig::Cloud::Instance.running
39
+ set :rigroles, servers.collect { |s| s.tags['Role'] }.compact
40
+
41
+ Rig.config.environment = ARGV[0]
42
+ Rig.config.role = ARGV[1]
43
+
44
+ begin
45
+ [ARGV[0], ARGV[1]].each do |arg|
46
+ task arg do
47
+ nil
48
+ end
49
+ end
50
+ rescue
51
+ nil
52
+ end
53
+
54
+ end
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+
3
+ require 'chef'
4
+ require 'chef/config'
5
+ require 'chef/api_client'
6
+ require 'chef/node'
7
+ require 'chef/environment'
8
+
9
+ module Rig
10
+ module Chef
11
+ class << self
12
+ def configure
13
+ @chef ||= ::Chef::Config.from_file(File.expand_path(Rig.config.chef.knife))
14
+ end
15
+
16
+ def environment_create(name)
17
+ configure
18
+ env = ::Chef::Environment.new
19
+ env.name(name)
20
+ env.description("created by Rig")
21
+ env.save
22
+ true
23
+ rescue => e
24
+ puts "*** chef exception: #{e.message}"
25
+ false
26
+ end
27
+
28
+ def environment_destroy(name)
29
+ configure
30
+ env = ::Chef::Environment.load(name) rescue nil
31
+ env.destroy if env
32
+ true
33
+ rescue => e
34
+ puts "*** chef exception: #{e.message}"
35
+ false
36
+ end
37
+
38
+ def client_delete(name)
39
+ configure
40
+ # TODO: better error handling
41
+ client = ::Chef::ApiClient.load(name) rescue nil
42
+ client.destroy if client
43
+ end
44
+
45
+ def node_delete(name)
46
+ configure
47
+ # TODO: better error handling
48
+ node = ::Chef::Node.load(name) rescue nil
49
+ node.destroy if node
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,4 @@
1
+ require 'rig'
2
+ require 'rig/chef' if Rig.config.chef
3
+
4
+ Dir["#{File.dirname(__FILE__)}/cloud/*.rb"].each {|file| require file.gsub(/\.rb/,'')}
@@ -0,0 +1,40 @@
1
+
2
+ module Rig
3
+ module Cloud
4
+ class Balancer
5
+ class << self
6
+ def all
7
+ Rig::Cloud.balancer.load_balancers.all
8
+ end
9
+
10
+ def new(name)
11
+ Rig::Cloud.balancer.load_balancers.new({ :id => name })
12
+ end
13
+
14
+ def create(name)
15
+ b = self.new(name)
16
+ b.save if b
17
+ puts ".. created: #{name}"
18
+ b
19
+ end
20
+
21
+ def find(name)
22
+ Rig::Cloud.balancer.load_balancers.get(name)
23
+ end
24
+
25
+ def find_by_environment(name)
26
+ self.all.select {|e| e.id =~ /^#{name}/}
27
+ end
28
+
29
+ def destroy(list)
30
+ list = [*list]
31
+ lbs = list.map {|e| e.kind_of?(String) ? self.find(e) : e}
32
+ lbs.each do |lb|
33
+ puts ".. destroying: #{lb.id}"
34
+ lb.destroy if lb
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ require 'rig'
2
+ require 'fog'
3
+
4
+ module Rig
5
+ module Cloud
6
+ class << self
7
+ def compute
8
+ @compute ||= begin
9
+ Fog::Compute.new(
10
+ :provider => Rig.config.provider,
11
+ :aws_access_key_id => Rig.config.key,
12
+ :aws_secret_access_key => Rig.config.secret,
13
+ :region => Rig.config.region
14
+ )
15
+ end
16
+ end
17
+ def balancer
18
+ @balancer ||= begin
19
+ #Fog::Compute.new(
20
+ Fog::AWS::ELB.new(
21
+ #:provider => Rig.config.provider,
22
+ :aws_access_key_id => Rig.config.key,
23
+ :aws_secret_access_key => Rig.config.secret,
24
+ :region => Rig.config.region
25
+ )
26
+ end
27
+ end
28
+ def dns
29
+ @dns ||= begin
30
+ Fog::DNS.new(
31
+ :provider => Rig.config.provider,
32
+ :aws_access_key_id => Rig.config.key,
33
+ :aws_secret_access_key => Rig.config.secret
34
+ #:region => Rig.config.region
35
+ )
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,56 @@
1
+ require 'rig'
2
+ require 'ipaddress'
3
+
4
+ module Rig
5
+ module Cloud
6
+ class Dns
7
+ class << self
8
+ def all
9
+ Rig::Cloud.dns.zones.all
10
+ end
11
+
12
+ def zone(zone)
13
+ Rig::Cloud.dns.zones.find(zone).first
14
+ end
15
+
16
+ def records(zone)
17
+ zone(zone).records
18
+ end
19
+
20
+ def zone_create(name)
21
+ Rig::Cloud.dns.zones.create(
22
+ :domain => name
23
+ )
24
+ end
25
+
26
+ def create(name, value)
27
+ zone = zone(Rig.config.dns.zone)
28
+
29
+ if IPAddress.valid?(value)
30
+ #A record
31
+ o = {:name => name, :value => value, :type => 'A', :ttl => 86400}
32
+ else
33
+ #CNAME record
34
+ #name = name.gsub(/\.#{Rig.config.dns.zone}/,'')
35
+ o = {:name => name, :value => value, :type => 'CNAME', :ttl => 300}
36
+ end
37
+ zone.records.create(o)
38
+ end
39
+
40
+ def destroy(name)
41
+ zone = zone(Rig.config.dns.zone)
42
+ name = name.gsub(/\.$/, '')
43
+ # cant seem to make zone.records#get or zone.records#find work correctly
44
+ list = zone.records.all.select {|e| e.attributes[:name] == "#{name}."}
45
+ record = list.first
46
+ if record
47
+ puts ".. destroying zone: #{name}"
48
+ record.destroy
49
+ else
50
+ puts "record not found: #{name}"
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,214 @@
1
+ module Rig
2
+ module Cloud
3
+ class Environment
4
+ attr_reader :servers
5
+ attr_reader :balancers
6
+ attr_reader :name
7
+ attr_reader :template
8
+ attr_reader :ami
9
+ attr_reader :region
10
+ attr_reader :flavor
11
+
12
+ TEMPLATES = {
13
+ :solo => {
14
+ :groups => %w{app-server db-inqcloud-dev},
15
+ :instances => [
16
+ :solo => {
17
+ :flavor => 'c1.medium',
18
+ :count => 1,
19
+ :balance => true,
20
+ :primary => true
21
+ }
22
+ ]
23
+ },
24
+ :multi => {
25
+ :groups => %w{app-server db-inqcloud-dev},
26
+ :instances => [
27
+ :harvester => { :flavor => 'c1.large', :count => 3, :ami => "ami-faketest" },
28
+ :queue => { :flavor => 'c1.medium', :count => 3 },
29
+ :cache => { :flavor => 'm1.medium', :count => 3 },
30
+ :app => { :flavor => 'c1.large', :count => 3, :balance => true, :primary => true }
31
+ ]
32
+ }
33
+ }
34
+
35
+ class << self
36
+ def list
37
+ filters = { }
38
+
39
+ list = Rig::Cloud.compute.servers.all(filters)
40
+ list.inject([]) { |a, e| a << e.tags["Environment"] }.compact.uniq.sort
41
+ end
42
+
43
+ def find(name, template=nil, opts={ })
44
+ env = self.new(name, template, opts)
45
+ env.reload
46
+ env
47
+ end
48
+
49
+ def create(name, template, opts={ })
50
+ env = self.new(name, template, opts)
51
+ env.save
52
+ env
53
+ end
54
+
55
+ def destroy(name, template=nil, opts={ })
56
+ env = self.find(name, template, opts)
57
+ puts "destroying: #{name}"
58
+ env.destroy
59
+ true
60
+ rescue => e
61
+ puts "exception: #{e.message}"
62
+ puts e.backtrace.join("\n")
63
+ false
64
+ end
65
+ end
66
+
67
+ def initialize(name, template=nil, opts={ })
68
+ @options = {
69
+ :region => Rig.config.region,
70
+ }.merge(opts)
71
+
72
+ @name = name
73
+ @template = template
74
+ @region = @options[:region]
75
+ @ami = @options[:ami] || Rig.config.ami[@region] || Rig.config.ami
76
+ @flavor = @options[:flavor] # || Rig.config.flavor[@region] || Rig.config.flavor
77
+ @servers = []
78
+ @balancers = []
79
+ end
80
+
81
+ def save
82
+ puts "creating instances"
83
+
84
+ template_name = @template.to_sym
85
+ template = TEMPLATES[template_name]
86
+ raise "unknown template #{template}" unless template
87
+
88
+ template[:instances].each do |inst|
89
+ setinstances = []
90
+ inst.each do |role, tmpl|
91
+ count = tmpl[:count]
92
+ ami = tmpl[:ami] || @ami
93
+ flavor = tmpl[:flavor] || @flavor
94
+ keypair = Rig.config.keypair
95
+ count.times do |i|
96
+ n = "#{role}#{i+1}.#{name}.env"
97
+ chef = Rig.config.chef ? true : false
98
+
99
+ o = {
100
+ :image_id => ami,
101
+ :flavor_id => flavor,
102
+ :key_name => keypair,
103
+ :groups => tmpl[:groups] || template[:groups],
104
+ :user_data => Rig::Cloud.userdata(n, role, name, :chef => chef)
105
+ }
106
+
107
+ server = Rig::Cloud::Instance.create(o, 'Name' => n, 'Environment' => name, 'Role' => role)
108
+ @servers << server
109
+ setinstances << server
110
+ end
111
+
112
+ if tmpl[:balance]
113
+ puts "creating balancer"
114
+ balancer = Rig::Cloud::Balancer.create("#{name}-#{role}")
115
+ balancer.register_instances(setinstances.collect { |e| e.id })
116
+ balancer.save
117
+
118
+ @balancers << balancer
119
+
120
+ # set dns for balancer for this role (<role>.<env>.env.inqlabs.com)
121
+ # example:
122
+ # if there are 3 servers with the harvester role
123
+ # in the environment test
124
+ # then the dns would be harvester.test.env.inqlabs.com
125
+ puts ".. dns: #{role}.#{name}.env.#{Rig.config.dns.zone} #{balancer.attributes[:dns_name]}"
126
+ Rig::Cloud::Dns.create("#{role}.#{name}.env.#{Rig.config.dns.zone}", balancer.attributes[:dns_name])
127
+
128
+ # set primary dns for environment (<name>.env.inqlabs.com)
129
+ # to point to this load balancer
130
+ if tmpl[:primary]
131
+ puts ".. primary: #{name}.env.#{Rig.config.dns.zone} #{balancer.attributes[:dns_name]}"
132
+ Rig::Cloud::Dns.create("#{name}.env.#{Rig.config.dns.zone}", balancer.attributes[:dns_name])
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ #print "waiting"
139
+ #@servers.first.wait_for { print "."; attributes[:public_ip_address] }
140
+ #puts
141
+
142
+ # if we've got a chef config, use it
143
+ if Rig.config.chef
144
+ puts "creating chef environment"
145
+ Rig::Chef.environment_create(name)
146
+ end
147
+
148
+ # all of this crap to be absolutely sure that the instance has an ip and dns
149
+ puts "sleeping 3"
150
+ sleep 3
151
+ reload
152
+
153
+ puts "associating servers' DNS"
154
+ @servers.each do |server|
155
+ record = "#{server.tags['Name']}.#{Rig.config.dns.zone}"
156
+ dns = server.attributes[:dns_name]
157
+ ip = server.attributes[:public_ip_address]
158
+
159
+ puts ".. #{record} dns=#{dns} ip=#{ip}"
160
+ if dns
161
+ Rig::Cloud::Dns.create(record, dns)
162
+ else
163
+ puts "server #{server.id} doesn't have public_ip_address"
164
+ end
165
+ end
166
+ rescue => e
167
+ puts "error creating environment: #{e.message}"
168
+ destroy
169
+ raise "error creating environment: #{e.message}"
170
+ end
171
+
172
+ def destroy
173
+ puts "removing dns from servers"
174
+ @servers.each do |server|
175
+ Rig::Cloud::Dns.destroy("#{server.tags['Name']}.#{Rig.config.dns.zone}") if server.tags['Name']
176
+ end
177
+
178
+ roles = @servers.collect {|s| s.tags['Role'] }.compact
179
+ roles.each do |r|
180
+ puts "removing role balancer dns"
181
+ Rig::Cloud::Dns.destroy("#{r}.#{name}.env.#{Rig.config.dns.zone}")
182
+ end
183
+
184
+ puts "removing primary dns"
185
+ Rig::Cloud::Dns.destroy("#{name}.env.#{Rig.config.dns.zone}")
186
+
187
+ puts "destroying servers"
188
+ Rig::Cloud::Instance.destroy(@servers)
189
+
190
+ puts "destroying balancers"
191
+ Rig::Cloud::Balancer.destroy(@balancers)
192
+
193
+ if Rig.config.chef
194
+ puts "destroying chef environment"
195
+ Rig::Chef.environment_destroy(name)
196
+ end
197
+ end
198
+
199
+ def reload
200
+ find_servers
201
+ find_balancers
202
+ end
203
+
204
+ private
205
+ def find_servers
206
+ @servers = Rig::Cloud::Instance.find_by_environment(@name) || []
207
+ end
208
+
209
+ def find_balancers
210
+ @balancers = Rig::Cloud::Balancer.find_by_environment(@name) || []
211
+ end
212
+ end
213
+ end
214
+ end