solutious-rudy 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +75 -0
- data/LICENSE.txt +19 -0
- data/README.rdoc +36 -0
- data/Rakefile +68 -0
- data/bin/rudy +175 -0
- data/bin/rudy-ec2 +108 -0
- data/lib/aws_sdb.rb +3 -0
- data/lib/aws_sdb/error.rb +42 -0
- data/lib/aws_sdb/service.rb +215 -0
- data/lib/console.rb +385 -0
- data/lib/rudy.rb +210 -0
- data/lib/rudy/aws.rb +68 -0
- data/lib/rudy/aws/ec2.rb +304 -0
- data/lib/rudy/aws/s3.rb +3 -0
- data/lib/rudy/aws/simpledb.rb +53 -0
- data/lib/rudy/command/addresses.rb +46 -0
- data/lib/rudy/command/backups.rb +175 -0
- data/lib/rudy/command/base.rb +839 -0
- data/lib/rudy/command/config.rb +77 -0
- data/lib/rudy/command/deploy.rb +12 -0
- data/lib/rudy/command/disks.rb +213 -0
- data/lib/rudy/command/environment.rb +74 -0
- data/lib/rudy/command/groups.rb +61 -0
- data/lib/rudy/command/images.rb +99 -0
- data/lib/rudy/command/instances.rb +85 -0
- data/lib/rudy/command/machines.rb +170 -0
- data/lib/rudy/command/metadata.rb +41 -0
- data/lib/rudy/command/release.rb +174 -0
- data/lib/rudy/command/volumes.rb +66 -0
- data/lib/rudy/config.rb +93 -0
- data/lib/rudy/metadata.rb +26 -0
- data/lib/rudy/metadata/backup.rb +160 -0
- data/lib/rudy/metadata/disk.rb +138 -0
- data/lib/rudy/scm/svn.rb +68 -0
- data/lib/rudy/utils.rb +64 -0
- data/lib/storable.rb +280 -0
- data/lib/tryouts.rb +40 -0
- data/rudy.gemspec +76 -0
- data/support/mailtest +40 -0
- data/support/rudy-ec2-startup +200 -0
- data/tryouts/console_tryout.rb +91 -0
- metadata +188 -0
@@ -0,0 +1,85 @@
|
|
1
|
+
#
|
2
|
+
#
|
3
|
+
#
|
4
|
+
#
|
5
|
+
#
|
6
|
+
#
|
7
|
+
#
|
8
|
+
#
|
9
|
+
#
|
10
|
+
#
|
11
|
+
#
|
12
|
+
|
13
|
+
module Rudy
|
14
|
+
module Command
|
15
|
+
class Instances < Rudy::Command::Base
|
16
|
+
|
17
|
+
def restart_instances_valid?
|
18
|
+
raise "No instance ID provided" if @argv.filter.nil?
|
19
|
+
raise "No EC2 .pem keys provided" unless has_pem_keys?
|
20
|
+
raise "No SSH key provided for #{@global.user}!" unless has_keypair?
|
21
|
+
raise "No SSH key provided for root!" unless has_keypair?(:root)
|
22
|
+
|
23
|
+
|
24
|
+
@list = @ec2.instances.list(machine_group)
|
25
|
+
raise "#{@argv.filter} is not in the current machine group" unless @list.has_key?(@argv.filter)
|
26
|
+
|
27
|
+
raise "I will not help you destroy production!" if @global.environment == "prod" # TODO: use_caution?, locked?
|
28
|
+
|
29
|
+
exit unless are_you_sure?(5)
|
30
|
+
true
|
31
|
+
end
|
32
|
+
def restart_instances
|
33
|
+
puts "Restarting #{@argv.filter}!"
|
34
|
+
@ec2.instances.restart @argv.filter
|
35
|
+
end
|
36
|
+
|
37
|
+
def instances
|
38
|
+
filter = @argv.first
|
39
|
+
filter = machine_group if filter.nil? && !@option.all
|
40
|
+
if instance_id?(filter)
|
41
|
+
inst = @ec2.instances.get(filter)
|
42
|
+
raise "The instance #{filter} does not exist" if inst.empty?
|
43
|
+
list = {inst[:aws_instance_id] => inst}
|
44
|
+
else
|
45
|
+
raise "The security group #{filter} does not exist" if filter && !@ec2.groups.exists?(filter)
|
46
|
+
list = @ec2.instances.list(filter)
|
47
|
+
if list.empty?
|
48
|
+
msg = "There are no instances running"
|
49
|
+
msg << " in the group #{filter}" if filter
|
50
|
+
raise msg
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
list.each_pair do |id, inst|
|
55
|
+
print_instance inst
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
def destroy_instances_valid?
|
61
|
+
filter = argv.first
|
62
|
+
raise "No instance ID provided" if filter.nil?
|
63
|
+
raise "I will not help you destroy production!" if @global.environment == "prod" || filter =~ /^prod/
|
64
|
+
exit unless are_you_sure?
|
65
|
+
true
|
66
|
+
end
|
67
|
+
|
68
|
+
def destroy_instances
|
69
|
+
filter = argv.first
|
70
|
+
|
71
|
+
if @ec2.groups.exists?(filter)
|
72
|
+
list = @ec2.instances.list(filter)
|
73
|
+
raise "The group #{filter} has no running instances" if list.empty?
|
74
|
+
instance = list.keys.first
|
75
|
+
else
|
76
|
+
instance = filter
|
77
|
+
end
|
78
|
+
puts "Destroying #{instance}!"
|
79
|
+
@ec2.instances.destroy instance
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
@@ -0,0 +1,170 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Rudy
|
4
|
+
module Command
|
5
|
+
class Machines < Rudy::Command::Base
|
6
|
+
|
7
|
+
|
8
|
+
def shutdown_valid?
|
9
|
+
raise "No EC2 .pem keys provided" unless has_pem_keys?
|
10
|
+
raise "No SSH key provided for #{@global.user}!" unless has_keypair?
|
11
|
+
raise "No SSH key provided for root!" unless has_keypair?(:root)
|
12
|
+
|
13
|
+
@list = @ec2.instances.list(machine_group)
|
14
|
+
raise "No machines running in #{machine_group}" unless @list && !@list.empty?
|
15
|
+
|
16
|
+
raise "I will not help you ruin production!" if @global.environment == "prod" # TODO: use_caution?, locked?
|
17
|
+
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
def shutdown
|
23
|
+
puts "Shutting down #{machine_group}: #{@list.keys.join(', ')}".att(:bright)
|
24
|
+
switch_user("root")
|
25
|
+
puts "This command also affects the volumes attached to the instances! (according to your routines config)"
|
26
|
+
exit unless are_you_sure?(5)
|
27
|
+
|
28
|
+
execute_routines(@list.values, :shutdown, :before)
|
29
|
+
|
30
|
+
execute_disk_routines(@list.values, :shutdown)
|
31
|
+
|
32
|
+
|
33
|
+
puts $/, "Terminating instances...".att(:bright), $/
|
34
|
+
|
35
|
+
@ec2.instances.destroy @list.keys
|
36
|
+
sleep 5
|
37
|
+
|
38
|
+
execute_routines(@list.values, :shutdown, :after)
|
39
|
+
|
40
|
+
puts "Done!"
|
41
|
+
end
|
42
|
+
|
43
|
+
def startup_valid?
|
44
|
+
rig = @ec2.instances.list(machine_group)
|
45
|
+
raise "There is already an instance running in #{machine_group}" if rig && !rig.empty?
|
46
|
+
raise "No SSH key provided for #{keypairname}!" unless has_keypair?
|
47
|
+
true
|
48
|
+
end
|
49
|
+
def startup
|
50
|
+
puts "Starting a machine in #{machine_group}".att(:bright)
|
51
|
+
switch_user("root")
|
52
|
+
exit unless are_you_sure?(3)
|
53
|
+
|
54
|
+
#execute_routines([], :startup, :before_local)
|
55
|
+
|
56
|
+
@option.image ||= machine_image
|
57
|
+
|
58
|
+
puts "using AMI: #{@option.image}"
|
59
|
+
|
60
|
+
instances = @ec2.instances.create(@option.image, machine_group.to_s, File.basename(keypairpath), machine_data.to_yaml, @global.zone)
|
61
|
+
inst = instances.first
|
62
|
+
|
63
|
+
if @option.address ||= machine_address
|
64
|
+
puts "Associating #{@option.address} to #{inst[:aws_instance_id]}"
|
65
|
+
@ec2.addresses.associate(inst[:aws_instance_id], @option.address)
|
66
|
+
end
|
67
|
+
|
68
|
+
wait_for_machine(inst[:aws_instance_id])
|
69
|
+
inst = @ec2.instances.get(inst[:aws_instance_id])
|
70
|
+
|
71
|
+
#inst = @ec2.instances.list(machine_group).values
|
72
|
+
|
73
|
+
execute_disk_routines(inst, :startup)
|
74
|
+
execute_routines(inst, :startup, :after)
|
75
|
+
|
76
|
+
puts "Done!"
|
77
|
+
end
|
78
|
+
|
79
|
+
def restart_valid?
|
80
|
+
shutdown_valid?
|
81
|
+
end
|
82
|
+
def restart
|
83
|
+
puts "Restarting #{machine_group}: #{@list.keys.join(', ')}".att(:bright)
|
84
|
+
switch_user("root")
|
85
|
+
exit unless are_you_sure?(5)
|
86
|
+
|
87
|
+
@list.each do |id, inst|
|
88
|
+
execute_routines(@list.values, :restart, :before)
|
89
|
+
end
|
90
|
+
|
91
|
+
puts "Restarting instances: #{@list.keys.join(', ')}".att(:bright)
|
92
|
+
@ec2.instances.restart @list.keys
|
93
|
+
sleep 10 # Wait for state to change and SSH to shutdown
|
94
|
+
|
95
|
+
@list.keys.each do |id|
|
96
|
+
wait_for_machine(id)
|
97
|
+
end
|
98
|
+
|
99
|
+
execute_disk_routines(@list.values, :restart)
|
100
|
+
|
101
|
+
@list.each do |id, inst|
|
102
|
+
execute_routines(@list.values, :restart, :after)
|
103
|
+
end
|
104
|
+
|
105
|
+
puts "Done!"
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
def status_valid?
|
110
|
+
raise "No EC2 .pem keys provided" unless has_pem_keys?
|
111
|
+
raise "No SSH key provided for #{@global.user}!" unless has_keypair?
|
112
|
+
raise "No SSH key provided for root!" unless has_keypair?(:root)
|
113
|
+
|
114
|
+
@list = @ec2.instances.list(machine_group)
|
115
|
+
raise "No machines running in #{machine_group}" unless @list
|
116
|
+
true
|
117
|
+
end
|
118
|
+
def status
|
119
|
+
puts "There are no machines running in #{machine_group}" if @list.empty?
|
120
|
+
@list.each_pair do |id, inst|
|
121
|
+
print_instance inst
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
def update_valid?
|
127
|
+
raise "No EC2 .pem keys provided" unless has_pem_keys?
|
128
|
+
raise "No SSH key provided for #{@global.user}!" unless has_keypair?
|
129
|
+
raise "No SSH key provided for root!" unless has_keypair?(:root)
|
130
|
+
|
131
|
+
@scripts = %w[rudy-ec2-startup update-ec2-ami-tools randomize-root-password]
|
132
|
+
@scripts.collect! {|script| File.join(RUDY_HOME, 'support', script) }
|
133
|
+
@scripts.each do |script|
|
134
|
+
raise "Cannot find #{script}" unless File.exists?(script)
|
135
|
+
end
|
136
|
+
|
137
|
+
true
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
def update
|
142
|
+
puts "Updating Rudy "
|
143
|
+
switch_user("root")
|
144
|
+
|
145
|
+
exit unless are_you_sure?
|
146
|
+
scp do |scp|
|
147
|
+
@scripts.each do |script|
|
148
|
+
puts "Uploading #{File.basename(script)}"
|
149
|
+
scp.upload!(script, "/etc/init.d/")
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
ssh do |session|
|
154
|
+
@scripts.each do |script|
|
155
|
+
session.exec!("chmod 700 /etc/init.d/#{File.basename(script)}")
|
156
|
+
end
|
157
|
+
|
158
|
+
puts "Installing Rudy (#{Rudy::VERSION})"
|
159
|
+
session.exec!("mkdir -p /etc/ec2")
|
160
|
+
session.exec!("gem sources -a http://gems.github.com")
|
161
|
+
puts session.exec!("gem install --no-ri --no-rdoc rudy -v #{Rudy::VERSION}")
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Rudy
|
4
|
+
module Command
|
5
|
+
class Metadata < Rudy::Command::Base
|
6
|
+
|
7
|
+
|
8
|
+
# Print Rudy's metadata to STDOUT
|
9
|
+
def metadata
|
10
|
+
group_metadata.each_pair do |n,h|
|
11
|
+
puts n.att(:bright)
|
12
|
+
puts h.inspect, ""
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def destroy_metadata_valid?
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
def destroy_metadata
|
21
|
+
@sdb.domains.destroy(RUDY_DOMAIN)
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
def info
|
27
|
+
domains = @sdb.domains.list[:domains]
|
28
|
+
puts "Domains: #{domains.join(", ")}"
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def check_environment
|
33
|
+
raise "No Amazon keys provided!" unless has_keys?
|
34
|
+
raise "No SSH keypairs provided!" unless has_keypair?
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
@@ -0,0 +1,174 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Rudy
|
4
|
+
module Command
|
5
|
+
class Release < Rudy::Command::Base
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
def release_valid?
|
11
|
+
|
12
|
+
relroutine = @config.routines.find_deferred(@global.environment, @global.role, :release)
|
13
|
+
raise "No release routines defined for #{machine_group}" if relroutine.nil?
|
14
|
+
|
15
|
+
raise "No EC2 .pem keys provided" unless has_pem_keys?
|
16
|
+
raise "No SSH key provided for #{@global.user} in #{machine_group}!" unless has_keypair?
|
17
|
+
raise "No SSH key provided for root in #{machine_group}!" unless has_keypair?(:root)
|
18
|
+
|
19
|
+
@list = @ec2.instances.list(machine_group)
|
20
|
+
unless @list.empty?
|
21
|
+
msg = "#{machine_group} is in use, probably with another release. #{$/}"
|
22
|
+
msg << 'Sort it out and run "rudy shutdown" before continuing.'
|
23
|
+
raise msg
|
24
|
+
end
|
25
|
+
|
26
|
+
@scm, @scm_params = find_scm(:release)
|
27
|
+
|
28
|
+
raise "No SCM defined for release routine" unless @scm
|
29
|
+
raise "#{Dir.pwd} is not a working copy" unless @scm.working_copy?(Dir.pwd)
|
30
|
+
raise "There are local changes. Please revert or check them in." unless @scm.everything_checked_in?
|
31
|
+
raise "Invalid base URI (#{@scm_params[:base]})." unless @scm.valid_uri?(@scm_params[:base])
|
32
|
+
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
def rerelease_valid?
|
39
|
+
relroutine = @config.routines.find_deferred(@global.environment, @global.role, :rerelease)
|
40
|
+
raise "No rerelease routines defined for #{machine_group}" if relroutine.nil?
|
41
|
+
|
42
|
+
raise "No EC2 .pem keys provided" unless has_pem_keys?
|
43
|
+
raise "No SSH key provided for #{@global.user} in #{machine_group}!" unless has_keypair?
|
44
|
+
raise "No SSH key provided for root in #{machine_group}!" unless has_keypair?(:root)
|
45
|
+
|
46
|
+
@list = @ec2.instances.list(machine_group)
|
47
|
+
if @list.empty?
|
48
|
+
msg = "There are no machines running in #{machine_group}. #{$/}"
|
49
|
+
msg << 'You must run "rudy release" before you can rerelease.'
|
50
|
+
raise msg
|
51
|
+
end
|
52
|
+
|
53
|
+
@scm, @scm_params = find_scm(:rerelease)
|
54
|
+
|
55
|
+
raise "No SCM defined for release routine" unless @scm
|
56
|
+
raise "#{Dir.pwd} is not a working copy" unless @scm.working_copy?(Dir.pwd)
|
57
|
+
raise "There are local changes. Please revert or check them in." unless @scm.everything_checked_in?
|
58
|
+
raise "Invalid base URI (#{@scm_params[:base]})." unless @scm.valid_uri?(@scm_params[:base])
|
59
|
+
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
def rerelease
|
64
|
+
puts "Updating release from working copy".att(:bright)
|
65
|
+
|
66
|
+
tag, revision = @scm.local_info
|
67
|
+
puts "tag: #{tag}"
|
68
|
+
puts "rev: #{revision}"
|
69
|
+
|
70
|
+
execute_disk_routines(@list.values, :rerelease)
|
71
|
+
|
72
|
+
if @scm
|
73
|
+
|
74
|
+
puts "Running SCM command".att(:bright)
|
75
|
+
ssh do |session|
|
76
|
+
cmd = "svn #{@scm_params[:command]}"
|
77
|
+
puts "#{cmd}"
|
78
|
+
session.exec!("cd #{@scm_params[:path]}")
|
79
|
+
session.exec!(cmd)
|
80
|
+
puts "#{@scm_params[:command]} complete"
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
execute_routines(@list.values, :rerelease, :after)
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
# <li>Creates a release tag based on the working copy on your machine</li>
|
90
|
+
# <li>Starts a new stage instance</li>
|
91
|
+
# <li>Executes release routines</li>
|
92
|
+
def release
|
93
|
+
# TODO: store metadata about release with local username and hostname
|
94
|
+
puts "Creating release from working copy".att(:bright)
|
95
|
+
|
96
|
+
exit unless are_you_sure?
|
97
|
+
|
98
|
+
tag = @scm.create_release(@global.local_user, @option.msg)
|
99
|
+
puts "Done! (#{tag})"
|
100
|
+
|
101
|
+
if @option.switch
|
102
|
+
puts "Switching working copy to new tag".att(:bright)
|
103
|
+
@scm.switch_working_copy(tag)
|
104
|
+
end
|
105
|
+
|
106
|
+
@option.image ||= machine_image
|
107
|
+
|
108
|
+
switch_user("root")
|
109
|
+
|
110
|
+
puts "Starting #{machine_group}".att(:bright)
|
111
|
+
|
112
|
+
instances = @ec2.instances.create(@option.image, machine_group.to_s, File.basename(keypairpath), machine_data.to_yaml, @global.zone)
|
113
|
+
inst = instances.first
|
114
|
+
|
115
|
+
if @option.address ||= machine_address
|
116
|
+
puts "Associating #{@option.address} to #{inst[:aws_instance_id]}".att(:bright)
|
117
|
+
@ec2.addresses.associate(inst[:aws_instance_id], @option.address)
|
118
|
+
end
|
119
|
+
|
120
|
+
wait_for_machine(inst[:aws_instance_id])
|
121
|
+
inst = @ec2.instances.get(inst[:aws_instance_id])
|
122
|
+
|
123
|
+
#inst = @ec2.instances.list(machine_group).values
|
124
|
+
|
125
|
+
|
126
|
+
execute_disk_routines(inst, :release)
|
127
|
+
|
128
|
+
if @scm
|
129
|
+
|
130
|
+
puts "Running SCM command".att(:bright)
|
131
|
+
ssh do |session|
|
132
|
+
cmd = "svn #{@scm_params[:command]} #{tag} #{@scm_params[:path]}"
|
133
|
+
puts "#{cmd}"
|
134
|
+
session.exec!(cmd)
|
135
|
+
puts "#{@scm_params[:command]} complete"
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
execute_routines(inst, :release, :after)
|
141
|
+
|
142
|
+
print_instance inst
|
143
|
+
|
144
|
+
puts "Done!"
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
def find_scm(routine)
|
149
|
+
env, rol, att = @global.environment, @global.role
|
150
|
+
|
151
|
+
# Look for the source control engine, checking all known scm values.
|
152
|
+
# The available one will look like [environment][role][release][svn]
|
153
|
+
params = nil
|
154
|
+
scm_name = nil
|
155
|
+
SUPPORTED_SCM_NAMES.each do |v|
|
156
|
+
scm_name = v
|
157
|
+
params = @config.routines.find(env, rol, routine, scm_name)
|
158
|
+
break if params
|
159
|
+
end
|
160
|
+
|
161
|
+
if params
|
162
|
+
klass = eval "Rudy::SCM::#{scm_name.to_s.upcase}"
|
163
|
+
scm = klass.new(:base => params[:base])
|
164
|
+
end
|
165
|
+
|
166
|
+
[scm, params]
|
167
|
+
|
168
|
+
end
|
169
|
+
private :find_scm
|
170
|
+
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|