rig 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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