stem 0.2.1 → 0.3.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/lib/stem/group.rb ADDED
@@ -0,0 +1,104 @@
1
+ module Stem
2
+ module Group
3
+ include Util
4
+ extend self
5
+
6
+ ## Example Rules
7
+
8
+ ## icmp://1.2.3.4/32
9
+ ## icmp://GroupName
10
+ ## icmp://GroupName@UserId
11
+ ## icmp://@UserId
12
+ ## tcp://0.0.0.0/0:22
13
+ ## tcp://0.0.0.0/0:22-23
14
+ ## tcp://10.0.0.0/8: (this imples 0-65535
15
+ ## udp://GroupName:4567
16
+ ## udp://GroupName@UserID:4567-9999
17
+
18
+ def get(name)
19
+ swirl.call("DescribeSecurityGroups", "GroupName.1" => name)["securityGroupInfo"].first
20
+ rescue Swirl::InvalidRequest => e
21
+ raise e unless e.message =~ /The security group '\S+' does not exist/
22
+ nil
23
+ end
24
+
25
+ def create(name, rules = nil)
26
+ create!(name, rules)
27
+ true
28
+ rescue Swirl::InvalidRequest => e
29
+ raise e unless e.message =~ /The security group '\S+' already exists/
30
+ false
31
+ end
32
+
33
+ def create!(name, rules = nil)
34
+ description = {}
35
+ swirl.call "CreateSecurityGroup", "GroupName" => name, "GroupDescription" => "%%" + description.to_json
36
+ auth(name, rules) if rules
37
+ end
38
+
39
+ def destroy(name)
40
+ destroy!(name)
41
+ true
42
+ rescue Swirl::InvalidRequest => e
43
+ puts "===> #{e.class}"
44
+ puts "===> #{e.message}"
45
+ puts "#{e.backtrace.join("\n")}"
46
+ false
47
+ end
48
+
49
+ def destroy!(name)
50
+ swirl.call "DeleteSecurityGroup", "GroupName" => name
51
+ end
52
+
53
+ def auth(name, rules)
54
+ index = 0
55
+ args = rules.inject({"GroupName" => name}) do |i,rule|
56
+ index += 1;
57
+ rule_hash = gen_authorize(index, rule)
58
+ i.merge(rule_hash)
59
+ end
60
+ swirl.call "AuthorizeSecurityGroupIngress", args
61
+ end
62
+
63
+ def gen_authorize_target(index, target)
64
+ if target =~ /^\d+\.\d+\.\d+.\d+\/\d+$/
65
+ { "IpPermissions.#{index}.IpRanges.1.CidrIp" => target }
66
+ elsif target =~ /^(\w+)@(\w+)$/
67
+ { "IpPermissions.#{index}.Groups.1.GroupName" => $1,
68
+ "IpPermissions.#{index}.Groups.1.UserId" => $2 }
69
+ elsif target =~ /^@(\w+)$/
70
+ { "IpPermissions.#{index}.Groups.1.UserId" => $1 }
71
+ else
72
+ { "IpPermissions.#{index}.Groups.1.GroupName" => target }
73
+ end
74
+ end
75
+
76
+ def gen_authorize_ports(index, ports)
77
+ if ports =~ /^(\d+)-(\d+)$/
78
+ { "IpPermissions.#{index}.FromPort" => $1,
79
+ "IpPermissions.#{index}.ToPort" => $2 }
80
+ elsif ports =~ /^(\d+)$/
81
+ { "IpPermissions.#{index}.FromPort" => $1,
82
+ "IpPermissions.#{index}.ToPort" => $1 }
83
+ elsif ports == ""
84
+ { "IpPermissions.#{index}.FromPort" => "0",
85
+ "IpPermissions.#{index}.ToPort" => "65535" }
86
+ else
87
+ raise "bad ports: #{rule}"
88
+ end
89
+ end
90
+
91
+ def gen_authorize(index, rule)
92
+ if rule =~ /icmp:\/\/(.+)/
93
+ { "IpPermissions.#{index}.IpProtocol" => "icmp",
94
+ "IpPermissions.#{index}.FromPort" => "-1",
95
+ "IpPermissions.#{index}.ToPort" => "-1" }.merge(gen_authorize_target(index,$1))
96
+ elsif rule =~ /(tcp|udp):\/\/(.*):(.*)/
97
+ { "IpPermissions.#{index}.IpProtocol" => $1 }.merge(gen_authorize_target(index,$2)).merge(gen_authorize_ports(index,$3))
98
+ else
99
+ raise "bad rule: #{rule}"
100
+ end
101
+ end
102
+ end
103
+ end
104
+
data/lib/stem/instance.rb CHANGED
@@ -16,15 +16,15 @@ module Stem
16
16
  throw "No AMI specified." unless ami
17
17
 
18
18
  opt = {
19
- "MinCount" => "1",
20
- "MaxCount" => "1",
21
- "KeyName" => config["key_name"] || "default",
22
- "InstanceType" => config["instance_type"] || "m1.small",
23
- "ImageId" => ami
19
+ "SecurityGroup.#" => config["groups"] || [],
20
+ "MinCount" => "1",
21
+ "MaxCount" => "1",
22
+ "KeyName" => config["key_name"] || "default",
23
+ "InstanceType" => config["instance_type"] || "m1.small",
24
+ "ImageId" => ami
24
25
  }
25
- if config["availability_zone"]
26
- opt.merge! "Placement.AvailabilityZone" => config["availability_zone"]
27
- end
26
+
27
+ opt.merge! "Placement.AvailabilityZone" => config["availability_zone"] if config["availability_zone"]
28
28
 
29
29
  if config["volumes"]
30
30
  devices = []
@@ -46,11 +46,11 @@ module Stem
46
46
 
47
47
  response = swirl.call "RunInstances", opt
48
48
 
49
- puts "Success!"
50
49
  response["instancesSet"].each do |i|
51
50
  return i["instanceId"]
52
51
  end
53
52
  end
53
+
54
54
  def restart instance_id
55
55
  swirl.call "RebootInstances", "InstanceId" => instance_id
56
56
  end
@@ -93,18 +93,20 @@ module Stem
93
93
  puts "Instances"
94
94
  puts "------------------------------------------"
95
95
  instances["reservationSet"].each do |r|
96
+ groups = r["groupSet"].map { |g| g["groupId"] }.join(",")
96
97
  r["instancesSet"].each do |i|
97
98
  name = lookup[i["imageId"]]
98
- puts "%-15s %-15s %-15s %s" % [ i["instanceId"], i["ipAddress"] || "no ip", i["instanceState"]["name"], name ]
99
+ puts "%-15s %-15s %-15s %-20s %s" % [ i["instanceId"], i["ipAddress"] || "no ip", i["instanceState"]["name"], groups, name ]
99
100
  end
100
101
  end
101
102
 
102
103
  puts "------------------------------------------"
103
104
  puts "AMIs"
104
105
  puts "------------------------------------------"
105
- images = c.call "DescribeImages", "Owner" => "self"
106
- images["imagesSet"].each do
107
- puts "%-15s %s" % [ img["name"], img["imageId"] ]
106
+ images = swirl.call "DescribeImages", "Owner" => "self"
107
+ iwidth = images["imagesSet"].map { |img| img["name"].length }.max + 1
108
+ images["imagesSet"].each do |img|
109
+ puts "%-#{iwidth}s %s" % [ img["name"], img["imageId"] ]
108
110
  end
109
111
  end
110
112
  end
@@ -0,0 +1,69 @@
1
+ require 'tmpdir'
2
+ require 'fileutils'
3
+
4
+ module Stem
5
+ module Userdata
6
+ CREATE_ONLY = File::CREAT|File::EXCL|File::WRONLY
7
+
8
+ extend self
9
+
10
+ def compile(path, opts = {})
11
+ raise "No absolute paths please" if path.index("/") == 0
12
+ raise "must be a directory" unless File.directory?(path)
13
+ Dir.mktmpdir do |tmp_path|
14
+ # trailing dot copies directory contents to match actual cp -r semantics... go figure
15
+ FileUtils.cp_r("#{path}/.", tmp_path)
16
+ Dir.chdir tmp_path do
17
+ process_erb(opts[:erb_binding])
18
+ process_mustache(opts[:mustache_vars])
19
+ raise "must contain a userdata.sh" unless File.exists?("userdata.sh")
20
+ make_zip_shell
21
+ end
22
+ end
23
+ end
24
+
25
+ # todo: make this & process_mustache both use binding
26
+ def process_erb(binding)
27
+ Dir["**/*.erb.stem"].each do |file|
28
+ raise "must pass :erb_binding when using .erb.stem files" unless binding
29
+ require 'erb'
30
+ puts "erb ... #{file}"
31
+ File.open(file.gsub(/.erb.stem$/,""), CREATE_ONLY) do |fff|
32
+ fff.write ERB.new(File.read(file), 0, '<>').result(binding)
33
+ end
34
+ end
35
+ end
36
+
37
+ def process_mustache(vars)
38
+ Dir["**/*.mustache.stem"].each do |file|
39
+ raise "must pass :mustache_vars when using .mustache.stem files" unless vars
40
+ require 'mustache'
41
+ puts "mustache ... #{file}"
42
+ File.open(file.gsub(/.mustache.stem$/,""), CREATE_ONLY) do |fff|
43
+ fff.write Mustache.render(File.read(file), vars)
44
+ end
45
+ end
46
+ end
47
+
48
+ def make_zip_shell
49
+ # We'll comment outside here, to keep from wasting valuable userdata bytes.
50
+ # we decompress into /root/userdata, then run userdata.sh
51
+ header = <<-'SHELL'
52
+ #!/bin/bash
53
+ exec >> /var/log/userdata.log 2>&1
54
+ echo BOOTING `date`
55
+ UD=~/userdata
56
+ mkdir -p $UD
57
+ tail -n +HEADER_LINES "$0" | tar -jx -C $UD -f -
58
+ cd $UD
59
+ exec bash userdata.sh
60
+ SHELL
61
+ process_header(header) + %x{tar --exclude *.stem -cv - . | bzip2 --best -}
62
+ end
63
+
64
+ def process_header(shell)
65
+ shell.gsub(/HEADER_LINES/, (shell.split("\n").size + 1).to_s).gsub(/^ +/,'')
66
+ end
67
+ end
68
+ end
69
+
data/lib/stem.rb CHANGED
@@ -5,6 +5,8 @@ require 'json'
5
5
 
6
6
  require 'stem/cli'
7
7
  require 'stem/util'
8
+ require 'stem/group'
9
+ require 'stem/userdata'
8
10
  require 'stem/instance'
9
11
  require 'stem/image'
10
12
  require 'stem/ip'
metadata CHANGED
@@ -1,21 +1,22 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stem
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 19
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 2
9
- - 1
10
- version: 0.2.1
8
+ - 3
9
+ - 0
10
+ version: 0.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Peter van Hardenberg
14
+ - Orion Henry
14
15
  autorequire:
15
16
  bindir: bin
16
17
  cert_chain: []
17
18
 
18
- date: 2010-08-12 00:00:00 -07:00
19
+ date: 2010-10-08 00:00:00 -07:00
19
20
  default_executable:
20
21
  dependencies:
21
22
  - !ruby/object:Gem::Dependency
@@ -37,6 +38,7 @@ dependencies:
37
38
  description: minimalist EC2 instance management
38
39
  email:
39
40
  - pvh@heroku.com
41
+ - orion@heroku.com
40
42
  executables:
41
43
  - stem
42
44
  extensions: []
@@ -47,9 +49,11 @@ files:
47
49
  - LICENSE
48
50
  - README.md
49
51
  - lib/stem/cli.rb
52
+ - lib/stem/group.rb
50
53
  - lib/stem/image.rb
51
54
  - lib/stem/instance.rb
52
55
  - lib/stem/ip.rb
56
+ - lib/stem/userdata.rb
53
57
  - lib/stem/util.rb
54
58
  - lib/stem.rb
55
59
  - bin/stem