solutious-rudy 0.4.0
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.
- 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
|
+
|