rudy 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/rudy/scm/svn.rb CHANGED
@@ -6,41 +6,52 @@ module Rudy
6
6
  class SVN
7
7
  attr_accessor :base_uri
8
8
 
9
- def initialize(uri)
10
- @base_uri = uri
9
+ def initialize(args={:base => ''})
10
+ @base_uri = args[:base]
11
11
  end
12
12
 
13
- def create_release_tag
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_name}"
22
-
23
- puts "Creating tag: #{release_tag}"
24
- cmd = "svn copy -m 'Another Release by Rudy!' #{release} #{release_tag}"
13
+ def create_release(username=nil, msg=nil)
14
+ local_uri, local_revision = local_info
15
+ rtag = generate_release_tag_name(username)
16
+ release_uri = "#{@base_uri}/#{rtag}"
17
+ msg ||= 'Another Release by Rudy!'
18
+ msg.tr!("'", "\\'")
19
+ cmd = "svn copy -m '#{msg}' #{local_uri} #{release_uri}"
25
20
 
26
21
  `#{cmd} 2>&1`
27
22
 
28
- release_tag
23
+ release_uri
29
24
  end
30
25
 
31
- def generate_release_tag_name
26
+ def switch_working_copy(tag)
27
+ raise "Invalid release tag (#{tag})." unless valid_uri?(tag)
28
+ `svn switch #{tag}`
29
+ end
30
+
31
+ # rel-2009-03-05-user-rev
32
+ def generate_release_tag_name(username=nil)
32
33
  now = Time.now
33
34
  mon = now.mon.to_s.rjust(2, '0')
34
35
  day = now.day.to_s.rjust(2, '0')
35
- rev = "r01"
36
- criteria = ['rudy', now.year, mon, day, rev]
36
+ rev = "01"
37
+ criteria = ['rel', now.year, mon, day, rev]
38
+ criteria.insert(-2, username) if username
37
39
  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
+ # Keep incrementing the revision number until we find the next one.
41
+ tag.succ! while (valid_uri?("#{@base_uri}/#{tag}"))
40
42
  tag
41
43
  end
42
44
 
43
- def svn_dir?(path)
45
+ def local_info
46
+ ret = `svn info 2>&1`
47
+ # URL: http://some/uri/path
48
+ # Repository Root: http://some/uri
49
+ # Repository UUID: c5abe49d-53e4-4ea3-9314-89e1e25aa7e1
50
+ # Revision: 921
51
+ ret.scan(/URL: (http:.+?)\s*\n.+Revision: (\d+)/m).flatten
52
+ end
53
+
54
+ def working_copy?(path)
44
55
  (File.exists?(File.join(path, '.svn')))
45
56
  end
46
57
 
data/lib/rudy/utils.rb CHANGED
@@ -3,7 +3,6 @@ require 'socket'
3
3
  require 'open-uri'
4
4
  require 'date'
5
5
 
6
- require 'socket'
7
6
  require 'timeout'
8
7
 
9
8
  module Rudy
@@ -49,9 +48,9 @@ module Rudy
49
48
  end
50
49
 
51
50
 
52
- def service_available?(host, port)
51
+ def service_available?(host, port, wait=3)
53
52
  begin
54
- status = Timeout::timeout(3) do
53
+ status = Timeout::timeout(wait) do
55
54
  socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
56
55
  sockaddr = Socket.pack_sockaddr_in( port, host )
57
56
  socket.connect( sockaddr )
data/lib/storable.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  #--
2
2
  # TODO: Handle nested hashes and arrays.
3
3
  # TODO: to_xml, see: http://codeforpeople.com/lib/ruby/xx/xx-2.0.0/README
4
+ # TODO: Rename to Stuffany
4
5
  #++
5
6
 
6
7
  require 'yaml'
@@ -26,7 +27,10 @@ class Storable
26
27
  @format = v
27
28
  end
28
29
 
30
+ # TODO: from_args([HASH or ordered params])
31
+
29
32
  def init
33
+ # NOTE: I think this can be removed
30
34
  self.class.send(:class_variable_set, :@@field_names, []) unless class_variable_defined?(:@@field_names)
31
35
  self.class.send(:class_variable_set, :@@field_types, []) unless class_variable_defined?(:@@field_types)
32
36
  end
data/lib/tryouts.rb ADDED
@@ -0,0 +1,40 @@
1
+
2
+ require 'ostruct'
3
+
4
+ module Tryouts
5
+
6
+ def before(&b)
7
+ b.call
8
+ end
9
+ def after(&b)
10
+ at_exit &b
11
+ end
12
+
13
+
14
+ # tryout :name do
15
+ # ...
16
+ # end
17
+ def tryout(name, &b)
18
+ puts "Running#{@poop}: #{name}"
19
+ begin
20
+ b.call
21
+ puts $/*2
22
+ sleep 1
23
+ rescue Interrupt
24
+ end
25
+ end
26
+
27
+ # Ignore everything
28
+ def xtryout(name, &b)
29
+ end
30
+
31
+ # Is this wacky syntax useful for anything?
32
+ # t2 :set .
33
+ # run = "poop"
34
+ def t2(*args)
35
+ OpenStruct.new
36
+ end
37
+
38
+ end
39
+
40
+ include Tryouts
data/rudy.gemspec CHANGED
@@ -1,58 +1,74 @@
1
1
  @spec = Gem::Specification.new do |s|
2
2
  s.name = "rudy"
3
- s.version = "0.3.2"
4
- s.summary = "Rudy is a handy staging and deployment tool for Amazon EC2."
5
- s.description = "Rudy is a handy staging and deployment tool for Amazon EC2."
3
+ s.version = "0.4.0"
4
+ s.summary = "Your friend in staging and deploying with EC2."
5
+ s.description = s.summary
6
6
  s.author = "Delano Mandelbaum"
7
7
  s.email = "delano@solutious.com"
8
8
  s.homepage = "http://github.com/solutious/rudy"
9
9
 
10
10
  # = MANIFEST =
11
- # find {bin,lib,support,tryouts} -type f | grep -v git
11
+ # git ls-files
12
12
  s.files = %w(
13
13
  CHANGES.txt
14
14
  LICENSE.txt
15
15
  README.rdoc
16
16
  Rakefile
17
17
  bin/rudy
18
+ bin/rudy-ec2
18
19
  lib/aws_sdb.rb
19
20
  lib/aws_sdb/error.rb
20
21
  lib/aws_sdb/service.rb
21
22
  lib/console.rb
22
- lib/drydock.rb
23
23
  lib/rudy.rb
24
24
  lib/rudy/aws.rb
25
25
  lib/rudy/aws/ec2.rb
26
26
  lib/rudy/aws/s3.rb
27
27
  lib/rudy/aws/simpledb.rb
28
28
  lib/rudy/command/addresses.rb
29
+ lib/rudy/command/backups.rb
29
30
  lib/rudy/command/base.rb
31
+ lib/rudy/command/config.rb
32
+ lib/rudy/command/deploy.rb
30
33
  lib/rudy/command/disks.rb
31
34
  lib/rudy/command/environment.rb
32
35
  lib/rudy/command/groups.rb
33
36
  lib/rudy/command/images.rb
34
37
  lib/rudy/command/instances.rb
38
+ lib/rudy/command/machines.rb
35
39
  lib/rudy/command/metadata.rb
36
- lib/rudy/command/stage.rb
40
+ lib/rudy/command/release.rb
37
41
  lib/rudy/command/volumes.rb
42
+ lib/rudy/config.rb
38
43
  lib/rudy/metadata.rb
39
44
  lib/rudy/metadata/backup.rb
40
- lib/rudy/metadata/config.rb
41
45
  lib/rudy/metadata/disk.rb
42
- lib/rudy/metadata/environment.rb
43
46
  lib/rudy/scm/svn.rb
44
47
  lib/rudy/utils.rb
45
48
  lib/storable.rb
49
+ lib/tryouts.rb
46
50
  rudy.gemspec
51
+ support/mailtest
47
52
  support/rudy-ec2-startup
53
+ tryouts/console_tryout.rb
48
54
  )
49
55
  s.executables = %w[rudy]
50
56
 
51
57
  s.extra_rdoc_files = %w[README.rdoc LICENSE.txt]
52
58
  s.has_rdoc = true
53
- s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Rudy: Your friend in staging and deploying with EC2", "--main", "README.rdoc"]
59
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Rudy: #{s.summary}", "--main", "README.rdoc"]
54
60
  s.require_paths = %w[lib]
55
61
  s.rubygems_version = '1.1.1'
62
+
56
63
 
64
+ s.add_dependency 'drydock'
65
+ s.add_dependency 'caesars'
66
+ s.add_dependency 'net-ssh'
67
+ s.add_dependency 'net-scp'
68
+ s.add_dependency 'net-ssh-gateway'
69
+ s.add_dependency 'net-ssh-multi'
70
+ s.add_dependency 'highline'
71
+
72
+
57
73
  s.rubyforge_project = 'rudy'
58
74
  end
data/support/mailtest ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # A simple SMTP mailer.
4
+ # You can use this to test mail configuration between machines.
5
+
6
+ # Usage: mailtest hostname email
7
+
8
+ require 'net/smtp'
9
+
10
+ unless ARGV.size == 2
11
+ puts "Usage: mailtest HOSTNAME EMAIL"
12
+ exit 1
13
+ end
14
+
15
+ HOSTNAME = ARGV[0]
16
+ EMAIL = ARGV[1]
17
+
18
+ def send_email(from, from_alias, to, to_alias, subject, message)
19
+ msg = <<END_OF_MESSAGE
20
+ From: #{from_alias} <#{from}>
21
+ To: #{to_alias} <#{to}>
22
+ Subject: #{subject}
23
+
24
+ #{message}
25
+ END_OF_MESSAGE
26
+
27
+ Net::SMTP.start(HOSTNAME) do |smtp|
28
+ smtp.send_message msg, from, to
29
+ end
30
+ end
31
+
32
+ begin
33
+ spice = sprintf("%.3f", rand)
34
+ send_email(HOSTNAME, "Rudy", EMAIL, "Rudy's Friend", "Mail config (#{spice})", "You received this email via #{HOSTNAME}")
35
+ rescue => ex
36
+ puts "ERROR: #{ex.message}"
37
+ exit 1
38
+ end
39
+
40
+ puts "Success!"
@@ -33,7 +33,8 @@ METADATA = 'http://169.254.169.254/2008-02-01/meta-data'
33
33
 
34
34
  METADATAPARAMS = ['instance-id', 'instance-type']
35
35
 
36
- RUDY_CONFIG_FILE='.rudy'
36
+ RUDY_CONFIG_DIR=File.join('.rudy')
37
+ RUDY_CONFIG_FILE=File.join('config')
37
38
 
38
39
  module Rudy
39
40
  module EC2Startup
@@ -68,7 +69,11 @@ module Rudy
68
69
 
69
70
  config = YAML::load(config_yaml)
70
71
  config ||= {}
71
-
72
+
73
+ # Print to STDOUT and NOT to the log.
74
+ # We don't want sensitive config values available in the log
75
+ puts "CONFIG: " << config_yaml
76
+
72
77
  zone = config[:zone] ? config[:zone].downcase : "zone"
73
78
  environment = config[:environment] ? config[:environment].downcase : "env"
74
79
  role = config[:role] ? config[:role].downcase : "role"
@@ -85,9 +90,22 @@ module Rudy
85
90
  # TODO: How to we get the path to any user's home directory?
86
91
  # (when we're not running as that user.)
87
92
  config[:userdata].each_pair do |n,hash|
88
- fileparts = (n === "root") ? ['/root'] : ['/home', n]
89
- fileparts << RUDY_CONFIG_FILE
90
- filepath = File.join(fileparts)
93
+ fileparts = (n == :root) ? ['/root'] : [user_home_dir(n)]
94
+ fileparts << RUDY_CONFIG_DIR
95
+ dirpath = File.join(fileparts)
96
+ filepath = File.join(dirpath, RUDY_CONFIG_FILE)
97
+
98
+ # Backwards compatability
99
+ if !File.directory?(dirpath)
100
+ log "Deleting the deprecated #{dirpath} config"
101
+ File.unlink(dirpath)
102
+ end
103
+
104
+ unless File.exists?(dirpath)
105
+ log "Creating #{dirpath}"
106
+ Dir.mkdir(dirpath, 0700)
107
+ end
108
+
91
109
  log "Writing to #{filepath}"
92
110
  user_data = {
93
111
  :userdata => hash
@@ -97,15 +115,18 @@ module Rudy
97
115
  end
98
116
 
99
117
  if config[:hosts] && config[:hosts].is_a?(Hash)
100
- log "Updating /etc/hosts..."
101
- config[:hosts].each_pair do |name, address|
102
- # Respect existing entries
103
- next if read_file('/etc/hosts') =~ /#{name}/
118
+ unless read_file('/etc/hosts') =~ /----- RUDY SAYS -----/
119
+ log "Updating /etc/hosts..."
120
+ write_to_file("/etc/hosts", "\n# ----- RUDY SAYS -----\n", :append)
121
+ config[:hosts].each_pair do |name, address|
122
+ # Respect existing entries
123
+ next if read_file('/etc/hosts') =~ /#{name}/
104
124
 
105
- ip_address = (address !~ /^\d.+/) ? Resolv.getaddress(address) : address
125
+ ip_address = (address !~ /^\d.+/) ? Resolv.getaddress(address) : address
106
126
 
107
- log " ---> #{name}: #{ip_address}"
108
- write_to_file("/etc/hosts", "\n#{ip_address}\t#{name}\n")
127
+ log " ---> #{name}: #{ip_address}"
128
+ write_to_file("/etc/hosts", "\n#{ip_address}\t#{name}\n", :append)
129
+ end
109
130
  end
110
131
  end
111
132
 
@@ -143,7 +164,8 @@ module Rudy
143
164
  t_out = t.strftime("%H:%M:%S%p (%m/%d/%Y)")
144
165
  end
145
166
 
146
- def write_to_file (filename, s, type='a')
167
+ def write_to_file(filename, s, type)
168
+ type = (type == :append) ? 'a' : 'w'
147
169
  f = File.open(filename,type)
148
170
  f.puts s
149
171
  f.close
@@ -161,10 +183,14 @@ module Rudy
161
183
  end
162
184
  contents
163
185
  end
164
-
186
+
187
+ def user_home_dir(user)
188
+ (`su #{user} -c "echo \\$HOME"` || '').chomp
189
+ end
190
+
165
191
  def log(s)
166
192
  msg = "#{get_formatted_time}: #{s}"
167
- write_to_file(LOGFILE, msg)
193
+ write_to_file(LOGFILE, msg, :append)
168
194
  puts msg
169
195
  end
170
196
 
@@ -0,0 +1,91 @@
1
+ RUDY_HOME = File.join(File.dirname(__FILE__), '..')
2
+ RUDY_LIB = File.join(RUDY_HOME, 'lib')
3
+ $:.unshift RUDY_LIB # Put our local lib in first place
4
+
5
+ require 'yaml'
6
+ require 'date'
7
+
8
+ require 'tryouts'
9
+ require 'console'
10
+
11
+ raise "Sorry Ruby 1.9 only!" unless RUBY_VERSION =~ /1.9/
12
+
13
+ before do
14
+ @title = "RUDY v0.3"
15
+ @now_utc = Time.now.utc.strftime("%Y-%m-%d %H:%M:%S")
16
+ @props = {
17
+ :zone => "us-east-1b",
18
+ :environment => "stage",
19
+ :role =>"app",
20
+ :position => "01"
21
+ }
22
+ # PROMPT_COMMAND
23
+ end
24
+
25
+ after do
26
+ #Console.clear
27
+ end
28
+
29
+
30
+ tryout :positioned do
31
+ Console.print_at(@title, {:y => Cursor.y, :x => Cursor.x })
32
+ sleep 1
33
+ Console.print_at(@now_utc, {:y => Cursor.y, :x => Console.width, :minus => true})
34
+ puts
35
+ sleep 1
36
+ Console.print_left(@title)
37
+ sleep 1
38
+ Console.print_right(@now_utc)
39
+ puts
40
+ sleep 1
41
+ Console.print_spaced('1'*25, 2, 3, '4'*30, 5, 6)
42
+ puts
43
+ sleep 1
44
+ Console.print_center(Window.bar(50))
45
+
46
+ end
47
+
48
+ tryout :u_r_d_l do
49
+ puts
50
+ Cursor.up && print('.')
51
+ sleep 1
52
+ Cursor.right && print('.')
53
+ sleep 1
54
+ Cursor.left && Cursor.down && print('.')
55
+ sleep 1
56
+ Cursor.left(3) && print('.')
57
+ end
58
+
59
+ tryout :update_inplace do
60
+ [(0..11).to_a, (90..110).to_a].flatten.each do |i|
61
+ Console.print_at(i, {:y => Cursor.y, :x => 4 })
62
+ sleep 0.05
63
+ end
64
+
65
+ end
66
+
67
+
68
+ tryout :danger! do
69
+ win = Window.new(:width => 100, :height => 100)
70
+
71
+ # DEBUGGING: There is a threading bug where the values of props and the
72
+ # string to print are being shared. Make Console and class and give an instance
73
+ # to each thread. However, that could fuck up shit like Cursor.position.
74
+
75
+
76
+ win.static(:right, 0.2, {:y => 0}) do
77
+ Time.now.utc.strftime("%Y-%m-%d %H:%M:%S").colour(:blue, :white, :underline)
78
+ end
79
+ win.static(:left, 0.2) do
80
+ rand
81
+ end
82
+
83
+ win.join_threads
84
+
85
+ puts $/, "Done!"
86
+
87
+ end
88
+
89
+
90
+
91
+