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 +104 -0
- data/lib/stem/instance.rb +15 -13
- data/lib/stem/userdata.rb +69 -0
- data/lib/stem.rb +2 -0
- metadata +9 -5
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
|
-
"
|
20
|
-
"
|
21
|
-
"
|
22
|
-
"
|
23
|
-
"
|
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
|
-
|
26
|
-
|
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 =
|
106
|
-
images["imagesSet"].
|
107
|
-
|
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
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:
|
4
|
+
hash: 19
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
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
|
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
|