rudy 0.2

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.
@@ -0,0 +1,61 @@
1
+
2
+
3
+ module Rudy
4
+ module Command
5
+ class Images < Rudy::Command::Base
6
+
7
+
8
+ def print_images
9
+ @ec2.images.list.each do |img|
10
+ print_image img
11
+ end
12
+ end
13
+
14
+ def create_image
15
+
16
+ if @user != "root"
17
+ puts "This command will be run as root"
18
+ @user = "root" # We need to be root for this operation!
19
+ end
20
+
21
+ raise "No EC2 .pem keys provided" unless has_pem_keys?
22
+ raise "No SSH key provided for #{keypairname}!" unless has_keypair?(keypairname)
23
+ raise "SSH key provided but cannot be found! (#{keypairpath})" unless File.exists?(keypairpath)
24
+
25
+ machine_list = @ec2.instances.list(machine_group)
26
+ machine = machine_list.values.first # NOTE: Only one machine per group, for now...
27
+
28
+ raise "There's no machine running in #{machine_group}" unless machine
29
+ raise "The primary machine in #{machine_group} is not in a running state" unless machine[:aws_state] == 'running'
30
+
31
+ puts "The new image will be based on #{machine_group}_01"
32
+
33
+ unless @account
34
+ puts "Enter your 12 digit Amazon account number:"
35
+ @account = gets.chomp
36
+ end
37
+
38
+ unless @image_name
39
+ puts "Enter the image name:"
40
+ @image_name = gets.chomp
41
+ end
42
+
43
+ unless @bucket_name
44
+ puts "Enter the S3 bucket that will store the image:"
45
+ @bucket_name = gets.chomp
46
+ end
47
+
48
+ scp machine[:dns_name], keypairpath, user, @ec2_cert, "/mnt/"
49
+ scp machine[:dns_name], keypairpath, user, @ec2_private_key, "/mnt/"
50
+
51
+ ssh machine[:dns_name], keypairpath, user, "ec2-bundle-vol -r i386 -p #{@image_name} -k /mnt/pk-*pem -c /mnt/cert*pem -u #{@account}"
52
+ ssh machine[:dns_name], keypairpath, user, "ec2-upload-bundle -b #{@bucket_name} -m /tmp/#{@image_name}.manifest.xml -a #{@access_key} -s #{@secret_key}"
53
+
54
+ # Why did I not use the Ruby API?
55
+ sh "ec2-register -K #{@ec2_private_key} -C #{@ec2_cert} #{@bucket_name}/#{@image_name}.manifest.xml"
56
+ end
57
+
58
+ end
59
+ end
60
+ end
61
+
@@ -0,0 +1,109 @@
1
+
2
+
3
+ module Rudy
4
+ module Command
5
+ class Instances < Rudy::Command::Base
6
+
7
+ def print_instances(filter=nil)
8
+ filter = machine_group if filter.nil? && !@all
9
+ if instance_id?(filter)
10
+ inst = @ec2.instances.get(filter)
11
+ raise "The instance #{filter} does not exist" if inst.empty?
12
+ list = {inst[:aws_instance_id] => inst}
13
+ else
14
+ raise "The security group #{filter} does not exist" if filter && !@ec2.groups.exists?(filter)
15
+ list = @ec2.instances.list(filter)
16
+ if list.empty?
17
+ msg = "There are no instances running"
18
+ msg << " in the group #{filter}" if filter
19
+ raise msg
20
+ end
21
+ end
22
+
23
+ list.each_pair do |id, inst|
24
+ print_instance inst
25
+ end
26
+
27
+ end
28
+
29
+ def destroy_instances(filter=nil)
30
+ raise "No instance ID or group name provided" if filter.nil?
31
+ raise "I will not help you destroy production!" if @environment == "prod" || filter =~ /^prod/
32
+ if @ec2.groups.exists?(filter)
33
+ list = @ec2.instances.list(filter)
34
+ raise "The group #{filter} has no running instances" if list.empty?
35
+ instance = list.keys.first
36
+ else
37
+ instance = filter
38
+ end
39
+ puts "Destroying #{instance}!"
40
+ @ec2.instances.destroy instance
41
+ end
42
+
43
+ def start_instance
44
+ @image ||= machine_image
45
+
46
+ @user = "root"
47
+
48
+ rig = @ec2.instances.list(machine_group)
49
+ raise "There is already an instance running in #{machine_group}" unless rig.empty?
50
+ raise "No SSH key provided for #{keypairname}!" unless has_keypair?(keypairname)
51
+ raise "SSH key provided but cannot be found! (#{keypairpath})" unless File.exists?(keypairpath)
52
+
53
+ user_data = {
54
+ :dbmaster => "localhost",
55
+ :role => @role,
56
+ :env => @environment
57
+ }
58
+
59
+ puts "Starting an instance in #{machine_group}"
60
+ instances = @ec2.instances.create(@image, machine_group.to_s, File.basename(keypairpath), user_data.to_yaml, @zone)
61
+ inst = instances.first
62
+ id, state = inst[:aws_instance_id], inst[:aws_state]
63
+
64
+ if @address
65
+ puts "Associating #{@address} to #{id}"
66
+ @ec2.addresses.associate(id, @address)
67
+ end
68
+
69
+ print "Waiting for #{id} to become available"
70
+
71
+ while @ec2.instances.pending?(id)
72
+ sleep 2
73
+ print '.'
74
+ STDOUT.flush
75
+ end
76
+
77
+ machine = @ec2.instances.get(id)
78
+
79
+ puts " It's up!"
80
+ print "Waiting for SSH daemon at #{machine[:dns_name]}"
81
+ while !Rudy::Utils.service_available?(machine[:dns_name], 22)
82
+ print '.'
83
+ STDOUT.flush
84
+ end
85
+ puts " It's up!"
86
+
87
+ print "Looking for disk metadata for #{machine[:aws_availability_zone]}... "
88
+ disks = Rudy::MetaData::Disk.list(@sdb, machine[:aws_availability_zone], @environment, @role, @position)
89
+
90
+ if disks.empty?
91
+ puts "None"
92
+ else
93
+ puts "#{disks.size} disk(s)."
94
+ disks.each do |disk|
95
+
96
+ do_dirty_disk_volume_deeds(disk, machine)
97
+ end
98
+ end
99
+
100
+ puts
101
+ ssh machine[:dns_name], keypairpath, user, "df -h" # Display current mounts
102
+ puts
103
+ puts "Done!"
104
+ end
105
+
106
+ end
107
+ end
108
+ end
109
+
@@ -0,0 +1,57 @@
1
+
2
+
3
+ module Rudy
4
+ module Command
5
+ class Metadata < Rudy::Command::Base
6
+
7
+
8
+ # Update Rudy's metadata for a running instance
9
+ def update_metadata(instances)
10
+ attributes = {
11
+ 'role' => role,
12
+ 'environment' => environment,
13
+ 'instances' => instances
14
+ }
15
+ puts "Putting #{instances.join(', ')} into #{machine_group}"
16
+
17
+ @sdb.store(RUDY_DOMAIN, "group_#{machine_group}", attributes)
18
+
19
+ p group_metadata
20
+ end
21
+
22
+ # Print Rudy's metadata to STDOUT
23
+ def print_metadata(instances=[])
24
+ p group_metadata
25
+ #query = "['instances' = '#{instances.first}'] union ['group' = '#{machine}']"
26
+ #
27
+ # p @sdb.get_attributes(RUDY_DOMAIN, "instances_#{machine}")
28
+ # p @sdb.query(RUDY_DOMAIN, query)
29
+ end
30
+
31
+ def destroy_metadata
32
+ @sdb.domains.destroy(RUDY_DOMAIN)
33
+ end
34
+
35
+ def setup
36
+ puts "Checking environment..."
37
+ check_environment
38
+
39
+ puts "Creating SimpleDB domain called #{RUDY_DOMAIN}"
40
+ @sdb.domains.create(RUDY_DOMAIN)
41
+ end
42
+
43
+ def info
44
+ domains = @sdb.domains.list[:domains]
45
+ puts "Domains: #{domains.join(", ")}"
46
+ end
47
+
48
+ def check_environment
49
+ raise "No Amazon keys provided!" unless has_keys?
50
+ raise "No SSH keypairs provided!" unless has_keypairs?
51
+ true
52
+ end
53
+
54
+ end
55
+ end
56
+ end
57
+
@@ -0,0 +1,43 @@
1
+
2
+
3
+ module Rudy
4
+ module Command
5
+ class Release < Rudy::Command::Base
6
+
7
+
8
+ def create_release
9
+
10
+ if @user != "root"
11
+ #puts "This command will be run as root"
12
+ @user = "root" # We need to be root for this operation!
13
+ end
14
+
15
+ raise "No EC2 .pem keys provided" unless has_pem_keys?
16
+ raise "No SSH key provided for #{keypairname}!" unless has_keypair?(keypairname)
17
+ raise "SSH key provided but cannot be found! (#{keypairpath})" unless File.exists?(keypairpath)
18
+
19
+ raise "The security group #{filter} does not exist" if machine_group && !@ec2.groups.exists?(machine_group)
20
+ list = @ec2.instances.list(machine_group)
21
+ raise "Please start an instance in #{machine_group} before releasing!" if list.empty?
22
+ puts "Creating release from: #{Dir.pwd}"
23
+
24
+ tag = @scm.create_release
25
+
26
+ machine = list.values.first # NOTE: we're assuming there's only one machine
27
+
28
+ #basename = File.basename(@rscripts[keypairname])
29
+ #scp machine[:dns_name], keypairpath, user, @rscripts[keypairname], "/mnt/"
30
+ #ssh machine[:dns_name], keypairpath, user, "chmod 755 /mnt/#{basename} && /mnt/#{basename} #{tag}"
31
+
32
+ @user = "rudy"
33
+ basename = File.basename(@rscripts[keypairname])
34
+
35
+ scp machine[:dns_name], keypairpath, user, @rscripts[keypairname], "~/"
36
+ ssh machine[:dns_name], keypairpath, user, "chmod 755 ~/#{basename} && ~/#{basename} test #{@access_key} #{@secret_key} r1ll1r1ll1"
37
+
38
+ end
39
+
40
+ end
41
+ end
42
+ end
43
+
@@ -0,0 +1,13 @@
1
+
2
+
3
+ module Rudy
4
+ module Command
5
+ class Volumes < Rudy::Command::Base
6
+
7
+ def print_volumes
8
+ y @ec2.volumes.list
9
+ end
10
+ end
11
+ end
12
+ end
13
+
@@ -0,0 +1,142 @@
1
+
2
+
3
+ module Rudy
4
+ class MetaData
5
+ attr_accessor :sdb
6
+
7
+ def initalize(sdb)
8
+ @sdb = sdb
9
+ end
10
+
11
+ end
12
+ class MetaData
13
+ class Disk < Storable
14
+
15
+ field :awsid
16
+
17
+ field :environment
18
+ field :role
19
+ field :path
20
+ field :position
21
+
22
+ field :zone
23
+ field :region
24
+ field :device
25
+ field :backups => Array
26
+ field :size
27
+
28
+ def initialize
29
+ @device = "/dev/sdh"
30
+ @zone = DEFAULT_ZONE
31
+ @region = DEFAULT_REGION
32
+ @backups = []
33
+
34
+ end
35
+
36
+ def name
37
+ Disk.generate_name(@zone, @environment, @role, @position, @path)
38
+ end
39
+
40
+ def valid?
41
+ @zone && @environment && @role && @position && @path
42
+ end
43
+
44
+ def to_query(more=[], remove=[])
45
+ criteria = [:zone, :environment, :role, :position, :path, *more]
46
+ criteria -= [*remove].flatten
47
+ query = []
48
+ criteria.each do |n|
49
+ query << "['#{n}' = '#{self.send(n.to_sym)}']"
50
+ end
51
+ query.join(" intersection ")
52
+ end
53
+
54
+ def to_s
55
+ str = ""
56
+ field_names.each do |key|
57
+ str << sprintf(" %22s: %s#{$/}", key, self.send(key.to_sym))
58
+ end
59
+ str
60
+ end
61
+
62
+ def Disk.generate_name(zon, env, rol, pos, pat, sep=File::SEPARATOR)
63
+ pos = pos.to_s.rjust 2, '0'
64
+ dirs = pat.split sep if pat
65
+ dirs.shift while dirs && (dirs[0].nil? || dirs[0].empty?)
66
+ ["disk", zon, env, rol, pos, *dirs].join(RUDY_DELIM)
67
+ end
68
+
69
+ def Disk.get(sdb, name)
70
+ disk = sdb.get_attributes(RUDY_DOMAIN, name)
71
+ raise "Disk #{name} does not exist!" unless disk && disk.has_key?(:attributes)
72
+ Rudy::MetaData::Disk.from_hash(disk[:attributes])
73
+ end
74
+
75
+ def Disk.destroy(sdb, name)
76
+ disk = Disk.get(sdb, name) # get raises an exception if the disk doesn't exist
77
+ sdb.destroy(RUDY_DOMAIN, name)
78
+ true # wtf: RightAws::SimpleDB doesn't tell us whether it succeeds. We'll assume!
79
+ end
80
+
81
+ def Disk.save(sdb, disk)
82
+ sdb.store(RUDY_DOMAIN, disk.name, disk.to_hash, :replace)
83
+ end
84
+
85
+ def Disk.is_defined?(sdb, disk)
86
+ # We don't care about the path, but we do care about the device
87
+ # which is not part of the disk's name.
88
+ query = disk.to_query(:device, :path)
89
+ !sdb.query_with_attributes(RUDY_DOMAIN, query).empty?
90
+ end
91
+
92
+ def Disk.update_volume(sdb, ec2, disk, machine)
93
+
94
+ disk = Disk.get(sdb, disk) if disk.is_a?(String)
95
+ raise "You must provide a disk name or obect" unless disk.is_a?(Rudy::MetaData::Disk)
96
+
97
+
98
+ # Make sure the volume is still running
99
+ disk.awsid = nil if disk.awsid && !ec2.volumes.exists?(disk.awsid)
100
+
101
+
102
+ # Otherwise we need to start one
103
+ unless disk.awsid
104
+ puts "No active EBS volume found for #{disk.name}"
105
+
106
+ backup = disk.backups.last
107
+ if backup
108
+ puts "We'll use the most recent backup..."
109
+ volume = ec2.volumes.create(disk.zone, disk.size, backup)
110
+ puts "Attaching #{disk.awsid} to #{id} (#{disk.device})"
111
+ ec2.volumes.attach(machine[:aws_instance_id], disk.awsid, disk.device)
112
+ else
113
+ puts "We'll create one from scratch..."
114
+ volume = ec2.volumes.create(disk.zone, disk.size, nil)
115
+ disk.awsid = volume[:aws_id]
116
+ puts "Saving disk metadata"
117
+ Disk.save(sdb, disk)
118
+ end
119
+ puts ""
120
+ end
121
+
122
+ disk
123
+ end
124
+
125
+ def Disk.list(sdb, zon, env=nil, rol=nil, pos=nil)
126
+ query = ''
127
+ query << "['zone' = '#{zon}']" if zon
128
+ query << " intersection ['environment' = '#{env}']" if env
129
+ query << " intersection ['role' = '#{rol}']" if rol
130
+ query << " intersection ['position' = '#{pos}']" if pos
131
+
132
+ list = []
133
+ sdb.query_with_attributes(RUDY_DOMAIN, query).each_pair do |name, hash|
134
+ list << Rudy::MetaData::Disk.from_hash(hash)
135
+ end
136
+ list
137
+ end
138
+ end
139
+
140
+ end
141
+
142
+ end
File without changes
@@ -0,0 +1,57 @@
1
+
2
+ require 'date'
3
+
4
+ module Rudy
5
+ module SCM
6
+ class SVN
7
+ attr_accessor :base_uri
8
+
9
+ def initialize(uri)
10
+ @base_uri = uri
11
+ end
12
+
13
+ def create_release
14
+ raise "There are local changes. Please revert or check them in!" unless everything_checked_in?
15
+ raise "Invalid base URI (#{@base_uri}). Check RUDY_SVN_BASE." unless valid_uri?(@base_uri)
16
+ raise "You must run this command from SVN you want to release from!" unless svn_dir?(Dir.pwd)
17
+
18
+ re = `svn info`.match /^URL:\s+(.+)$/
19
+ release = re[1] if re
20
+
21
+ release_tag = "#{@base_uri}/#{generate_release_tag}"
22
+
23
+ puts "Creating tag: #{release_tag}"
24
+ cmd = "svn copy -m 'Another Release by Rudy!' #{release} #{release_tag}"
25
+
26
+ `#{cmd}`
27
+
28
+ release_tag
29
+ end
30
+
31
+ def generate_release_tag
32
+ now = Time.now
33
+ mon = now.mon.to_s.rjust(2, '0')
34
+ day = now.day.to_s.rjust(2, '0')
35
+ rev = "r01"
36
+ criteria = ['rudy', now.year, mon, day, rev]
37
+ tag = criteria.join(RUDY_DELIM)
38
+ # Keep incrementing the revision number until we find the next one.
39
+ tag.succ! while (valid_uri?("#{@base_uri}/#{tag}"))
40
+ tag
41
+ end
42
+
43
+ def svn_dir?(path)
44
+ (File.exists?(File.join(path, '.svn')))
45
+ end
46
+
47
+ def valid_uri?(uri)
48
+ ret = `svn info #{uri} 2>1&` || '' # Valid SVN URIs will return some info
49
+ (ret =~ /Repository UUID/) ? true : false
50
+ end
51
+
52
+ def everything_checked_in?
53
+ `svn diff . 2>1&` == '' # svn diff should return nothing
54
+ end
55
+ end
56
+ end
57
+ end