ec2l 0.0.1 → 0.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.
- data/.travis.yml +3 -1
- data/Gemfile +1 -0
- data/README.md +35 -2
- data/lib/ec2l.rb +176 -12
- data/lib/ec2l/version.rb +1 -1
- data/test/test_ec2l.rb +85 -8
- metadata +4 -4
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
# Ec2l
|
2
2
|
|
3
3
|
[](https://travis-ci.org/yazgoo/ec2l)
|
4
|
+
[](https://codeclimate.com/github/yazgoo/ec2l)
|
5
|
+
[](https://codeclimate.com/github/yazgoo/ec2l)
|
6
|
+
[](http://inch-ci.org/github/yazgoo/ec2l)
|
7
|
+
[](http://badge.fury.io/rb/ec2l)
|
4
8
|
|
5
|
-
|
9
|
+
|
10
|
+
Ec2l aims to provide an efficient ruby developer oriented UI to EC2.
|
6
11
|
|
7
12
|
## Installation
|
8
13
|
|
@@ -20,7 +25,35 @@ Or install it yourself as:
|
|
20
25
|
|
21
26
|
## Usage
|
22
27
|
|
23
|
-
|
28
|
+
You can call public client methods directly from the ec2l command line,
|
29
|
+
though a better way is to use the shell, which relies on pry:
|
30
|
+
|
31
|
+
$ ec2l shell
|
32
|
+
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
...
|
36
|
+
[2] pry(#<Ec2l::Client>)> h # print help
|
37
|
+
Usage: action parameters...
|
38
|
+
available actions:
|
39
|
+
[
|
40
|
+
[0] associate(address, id) Ec2l::Client
|
41
|
+
[1] ins() Ec2l::Client
|
42
|
+
...
|
43
|
+
[2] pry(#<Ec2l::Client>)> show-doc ins # print documentation on ins method
|
44
|
+
...
|
45
|
+
Public: return virtual machines instances with few details
|
46
|
+
|
47
|
+
Examples
|
48
|
+
|
49
|
+
ins[0]
|
50
|
+
=> {:instanceId=>"i-deadbeef", :instanceState=>
|
51
|
+
{"code"=>"16", "name"=>"running"},
|
52
|
+
:ipAddress=>"10.1.1.2", :tagSet=>{:k=>"v"}}
|
53
|
+
|
54
|
+
Returns an array with instanceId, ipAddress, tagSet, instanceState in a hash
|
55
|
+
[7] pry(#<Ec2l::Client>)> ins # launch ins method
|
56
|
+
```
|
24
57
|
|
25
58
|
## Contributing
|
26
59
|
|
data/lib/ec2l.rb
CHANGED
@@ -2,30 +2,137 @@ require "ec2l/version"
|
|
2
2
|
require 'base64'
|
3
3
|
require 'awesome_print'
|
4
4
|
require 'pry'
|
5
|
+
# Public: Utilities to facilitate amazon EC2 administration
|
6
|
+
#
|
7
|
+
# Examples
|
8
|
+
#
|
9
|
+
# Ec2l::Client.new.i
|
10
|
+
# => {:instanceId=>"i-deadbeef", :instanceState=>
|
11
|
+
# {"code"=>"16", "name"=>"running"},
|
12
|
+
# :ipAddress=>"10.1.1.2", :tagSet=>{:k=>"v"}
|
13
|
+
# ...
|
5
14
|
module Ec2l
|
15
|
+
# Public: Client to use amazon EC2 apis
|
16
|
+
#
|
17
|
+
# Examples
|
18
|
+
#
|
19
|
+
# Client.new.instances(["ipAddress"])[0..1]
|
20
|
+
# => [{:ipAddress=>"10.1.1.2"}, {:ipAddress=>"10.1.1.1"}]
|
6
21
|
class Client
|
22
|
+
# Public: return virtual machines instances
|
23
|
+
#
|
24
|
+
# keep - what field you want to extract.
|
25
|
+
# valid fields names are documented in ec2 apis
|
26
|
+
# groups is an arranged shortcut for groupSet
|
27
|
+
# tagSet is also rearranged
|
28
|
+
#
|
29
|
+
# Examples
|
30
|
+
#
|
31
|
+
# instances(["ipAddress"])[0..1]
|
32
|
+
# => [{:ipAddress=>"10.1.1.2"}, {:ipAddress=>"10.1.1.1"}]
|
33
|
+
#
|
34
|
+
# Returns an array with all fields requested for each VM in a hash
|
7
35
|
def instances keep = ["instanceId", "ipAddress", "groups",
|
8
36
|
"launchType", "instanceType", "tagSet"]
|
9
37
|
@ec2.describe_instances.reservationSet.item.collect do |item|
|
10
|
-
|
38
|
+
rearrange_fields item, keep
|
11
39
|
item = item.instancesSet.item[0].reject{|k, v| not keep.include? k}
|
12
|
-
item["groups"] = group.item.map{|x| x.groupId } if not group.nil?
|
13
|
-
item["tagSet"] = to_hash(item["tagSet"].item) if item["tagSet"]
|
14
40
|
Hash[item.map { |k, v| [k.to_sym, v] }]
|
15
41
|
end
|
16
42
|
end
|
43
|
+
# Public: displays inforation about a VM instance
|
44
|
+
#
|
45
|
+
# id - the VM instance id
|
46
|
+
#
|
47
|
+
# Examples
|
48
|
+
#
|
49
|
+
# instance "i-deadbeef"
|
50
|
+
# => {"xmlns"=>"http://ec2.amazonaws.com/doc/2010-08-31/",
|
51
|
+
# "requestId"=>"89375055-16de-4ab7-84b5-5651670e7e3b",
|
52
|
+
# "reservationSet"=>
|
53
|
+
# {"item"=>
|
54
|
+
# ...
|
55
|
+
#
|
56
|
+
# Returns a hash with description of the instance
|
17
57
|
def instance(id) @ec2.describe_instances(instance_id: id) end
|
58
|
+
# Public: associate an elastic address with a VM instance
|
59
|
+
#
|
60
|
+
# address - the elastic IP address
|
61
|
+
# id - the VM instance id
|
62
|
+
#
|
63
|
+
# Examples
|
64
|
+
#
|
65
|
+
# associate '10.1.1.2', 'i-deadbeef'
|
66
|
+
#
|
67
|
+
# Returns info about the association
|
68
|
+
def associate address, id
|
69
|
+
@ec2.associate_address public_ip: address, instance_id: id
|
70
|
+
end
|
71
|
+
# Public: return a list of inforation about declared security groups
|
72
|
+
#
|
73
|
+
# Examples
|
74
|
+
#
|
75
|
+
# sgs[0..1]
|
76
|
+
# => [{"ownerId"=>"424242", "groupName"=>"sg1"},
|
77
|
+
# {"ownerId"=>"424242", "groupName"=>"sg2"}]
|
78
|
+
#
|
79
|
+
# Returns an array with for each SG, the ownerID and the groupName
|
18
80
|
def sgs
|
19
81
|
@ec2.describe_security_groups.securityGroupInfo.item.collect do |item|
|
20
82
|
item.reject { |k, v| not ["groupName", "ownerId"].include? k }
|
21
83
|
end
|
22
84
|
end
|
23
|
-
|
85
|
+
# Public: return virtual machines instances with few details
|
86
|
+
#
|
87
|
+
# Examples
|
88
|
+
#
|
89
|
+
# ins[0]
|
90
|
+
# => {:instanceId=>"i-deadbeef", :instanceState=>
|
91
|
+
# {"code"=>"16", "name"=>"running"},
|
92
|
+
# :ipAddress=>"10.1.1.2", :tagSet=>{:k=>"v"}}
|
93
|
+
#
|
94
|
+
# Returns an array with instanceId, ipAddress, tagSet, instanceState in a hash
|
95
|
+
def ins() instances ["instanceId", "ipAddress", "tagSet", "instanceState"] end
|
96
|
+
# Public: get system log
|
97
|
+
#
|
98
|
+
# id - VM instance id
|
99
|
+
#
|
100
|
+
# Examples
|
101
|
+
#
|
102
|
+
# log("i-deadbeef")[0..1]
|
103
|
+
# => ["Initializing cgroup subsys cpuset", "Initializing cgroup subsys cpu"]
|
104
|
+
#
|
105
|
+
# Returns an array containing the lines of the system log
|
24
106
|
def log id
|
25
|
-
|
107
|
+
Base64.decode64(@ec2.get_console_output(instance_id: id)["output"]).split("\r\n")
|
26
108
|
end
|
109
|
+
# Public: terminates a VM instance
|
110
|
+
#
|
111
|
+
# id - the VM instance id
|
112
|
+
#
|
113
|
+
# Examples
|
114
|
+
#
|
115
|
+
# terminate "i-deadbeef"
|
116
|
+
#
|
117
|
+
# Returns information about the termination status
|
27
118
|
def terminate(id) @ec2.terminate_instances(instance_id: id) end
|
119
|
+
# Public: opens up a pry shell
|
28
120
|
def shell() binding.pry end
|
121
|
+
# Public: update the credentials configuration
|
122
|
+
#
|
123
|
+
# creds - current credentials
|
124
|
+
#
|
125
|
+
# Examples
|
126
|
+
# update_configuration
|
127
|
+
# Will try and update configuration in /home/yazgoo/.awssecret
|
128
|
+
# access key (foo):
|
129
|
+
# secret access key (bar):
|
130
|
+
# entry point (default being https://aws.amazon.com if blank)
|
131
|
+
# (http://ec2.aws.com):
|
132
|
+
# => ["access key", "secret access key",
|
133
|
+
# "entry point ..."]
|
134
|
+
#
|
135
|
+
# Returns the description of the updated fields
|
29
136
|
def update_configuration creds = nil
|
30
137
|
puts "Will try and update configuration in #{@conf}"
|
31
138
|
creds = read_credentials if creds.nil?
|
@@ -33,19 +140,30 @@ module Ec2l
|
|
33
140
|
["access key", "secret access key",
|
34
141
|
"entry point (default being https://aws.amazon.com if blank)"
|
35
142
|
].each_with_index do |prompt, i|
|
36
|
-
|
37
|
-
line = $stdin.gets.chomp
|
38
|
-
line = creds[i] if line.empty? and creds.size > i
|
143
|
+
line = prompt_line prompt, creds[i]
|
39
144
|
f.puts line if not line.empty?
|
40
145
|
end
|
41
146
|
end
|
42
147
|
end
|
148
|
+
# Public: reads credentials from configuration file
|
149
|
+
#
|
150
|
+
# Examples
|
151
|
+
#
|
152
|
+
# read_credentials
|
153
|
+
# => ["accesskey", "scretkey", "https://entry.com"]
|
154
|
+
#
|
155
|
+
# Return a list with the credentials and entrypoint if defined
|
43
156
|
def read_credentials
|
44
157
|
creds = []
|
45
158
|
File.open(@conf) { |f| f.each_line { |l| creds << l.chomp } } if File.exists?(@conf)
|
46
159
|
creds
|
47
160
|
end
|
48
161
|
private
|
162
|
+
def prompt_line prompt, cred
|
163
|
+
printf "#{prompt} (#{creds? creds:""}): "
|
164
|
+
line = $stdin.gets.chomp
|
165
|
+
if line.empty? and creds.size > i then creds else line end
|
166
|
+
end
|
49
167
|
def to_hash array
|
50
168
|
Hash[array.collect { |i| [i["key"].to_sym, i["value"]] }]
|
51
169
|
end
|
@@ -55,18 +173,64 @@ private
|
|
55
173
|
update_configuration credentials
|
56
174
|
read_credentials
|
57
175
|
end
|
58
|
-
def
|
59
|
-
@conf = ENV['awssecret'] || "#{ENV['HOME']}/.awssecret"
|
60
|
-
credentials = load_credentials
|
176
|
+
def build_underlying_client credentials
|
61
177
|
ENV['EC2_URL'] = credentials[2] if credentials.size >= 3
|
62
178
|
require 'AWS' # *must* load AWS after setting EC2_URL
|
63
|
-
|
179
|
+
AWS::EC2::Base.new access_key_id: credentials[0],
|
64
180
|
secret_access_key: credentials[1]
|
65
181
|
end
|
182
|
+
def initialize
|
183
|
+
@conf = ENV['awssecret'] || "#{ENV['HOME']}/.awssecret"
|
184
|
+
@ec2 = build_underlying_client load_credentials
|
185
|
+
end
|
186
|
+
# Internal: try and find/launch the method on the
|
187
|
+
# underlying client called by the method name
|
188
|
+
# or by describe_#{name}
|
189
|
+
#
|
190
|
+
# Examples
|
191
|
+
#
|
192
|
+
# method_missing_ec2 :addresses
|
193
|
+
# => {"xmlns"=>"http://ec2.amazonaws.com/doc/2010-08-31/",
|
194
|
+
# ...
|
195
|
+
#
|
196
|
+
# Return the result of the underlying method call
|
197
|
+
def method_missing_ec2 method, *args, &block
|
198
|
+
[method, "describe_#{method.to_s}".to_sym].each do |meth|
|
199
|
+
if @ec2.public_methods.include? meth
|
200
|
+
return @ec2.send meth, *args, &block
|
201
|
+
end
|
202
|
+
end
|
203
|
+
nil
|
204
|
+
end
|
205
|
+
# Internal: try and call method_missing_ec2
|
206
|
+
# if its result is nil, display a list of the class
|
207
|
+
# public methods
|
208
|
+
#
|
209
|
+
# Examples
|
210
|
+
#
|
211
|
+
# h
|
212
|
+
# =>Usage: action parameters...
|
213
|
+
# available actions:
|
214
|
+
# [
|
215
|
+
# [0] associate(address, id) Ec2l::Client
|
216
|
+
# [1] i() Ec2l::Client
|
217
|
+
#
|
218
|
+
# Return the result of method_missing_ec2 or nil
|
66
219
|
def method_missing method, *args, &block
|
220
|
+
result = method_missing_ec2(method, *args, &block)
|
221
|
+
return result if not result.nil?
|
67
222
|
puts "Usage: action parameters...", "available actions:"
|
68
223
|
awesome_print (public_methods - "".public_methods)
|
69
224
|
nil
|
70
225
|
end
|
226
|
+
def rearrange_fields item, keep
|
227
|
+
it = item.instancesSet.item[0]
|
228
|
+
if keep.include? "groups" and item.groupSet
|
229
|
+
it["groups"] = item.groupSet.item.map{|x| x.groupId }
|
230
|
+
end
|
231
|
+
if keep.include? "tagSet" and it.tagSet
|
232
|
+
it["tagSet"] = to_hash(it.tagSet.item)
|
233
|
+
end
|
234
|
+
end
|
71
235
|
end
|
72
236
|
end
|
data/lib/ec2l/version.rb
CHANGED
data/test/test_ec2l.rb
CHANGED
@@ -1,20 +1,97 @@
|
|
1
|
+
require "codeclimate-test-reporter"
|
2
|
+
CodeClimate::TestReporter.start
|
1
3
|
require 'test/unit'
|
2
4
|
require 'tempfile'
|
5
|
+
require 'base64'
|
3
6
|
require 'ec2l'
|
7
|
+
class Hash
|
8
|
+
def method_missing method, *args, &block
|
9
|
+
method = method.to_s
|
10
|
+
if method.end_with? "="
|
11
|
+
self[method.to_s[0..-2]] = args[0]
|
12
|
+
else
|
13
|
+
self[method]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
class Client < Ec2l::Client
|
18
|
+
def build_underlying_client credentials
|
19
|
+
if credentials.size == 3 and credentials[2] == 'through'
|
20
|
+
super credentials
|
21
|
+
else
|
22
|
+
MockEc2.new
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
$instance_set = {"reservationSet" => {"item" => [{"instancesSet" => {
|
27
|
+
"item" => [{"instanceId" => "lol"}]}}]}}
|
28
|
+
class MockEc2
|
29
|
+
def method_missing method, *args, &block
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
def describe_instances stuff = nil
|
33
|
+
$instance_set
|
34
|
+
end
|
35
|
+
def describe_security_groups
|
36
|
+
{"securityGroupInfo" => {"item" => [{'groupName' => 'test'}] }}
|
37
|
+
end
|
38
|
+
def get_console_output stuff
|
39
|
+
{"output" => Base64.encode64("hello")}
|
40
|
+
end
|
41
|
+
end
|
4
42
|
class EC2lTest < Test::Unit::TestCase
|
5
|
-
def
|
43
|
+
def create_conf creds
|
6
44
|
conf = Tempfile.new "a"
|
7
|
-
conf.write "
|
45
|
+
conf.write creds.join "\n"
|
8
46
|
conf.close
|
9
|
-
|
10
|
-
|
47
|
+
conf.path
|
48
|
+
end
|
49
|
+
def setup
|
50
|
+
ENV['awssecret'] = create_conf ['a', 'b']
|
51
|
+
@cli = Client.new
|
11
52
|
end
|
12
53
|
def test_configuration
|
13
|
-
client = test_initialize
|
14
54
|
def $stdin.gets() 'same_old' end
|
15
|
-
|
16
|
-
creds =
|
17
|
-
assert creds.size == 3
|
55
|
+
@cli.update_configuration
|
56
|
+
creds = @cli.read_credentials
|
57
|
+
assert creds.size == 3, creds.to_s
|
18
58
|
creds.each { |cred| assert cred == 'same_old' }
|
59
|
+
ENV['awssecret'] = create_conf([])
|
60
|
+
Client.new.send :load_credentials
|
61
|
+
end
|
62
|
+
def test_method_missing
|
63
|
+
assert @cli.h.nil?
|
64
|
+
assert @cli.send(:method_missing_ec2, :h).nil?
|
65
|
+
end
|
66
|
+
def test_utilities
|
67
|
+
result = @cli.send :to_hash, [{"key" => "hello", "value" => "world"}]
|
68
|
+
assert result == {hello: "world"}
|
69
|
+
end
|
70
|
+
def test_basic_underlying_calls
|
71
|
+
assert @cli.associate(nil, nil).nil?
|
72
|
+
assert @cli.instance(nil) == $instance_set
|
73
|
+
assert @cli.log(nil) == ["hello"]
|
74
|
+
assert @cli.terminate(nil).nil?
|
75
|
+
end
|
76
|
+
def test_rearrange_fields
|
77
|
+
item = {"instancesSet" => {"item" => [{"tagSet" => {"item" => []}}]},
|
78
|
+
"groupSet" => {"item" => [{}] } }
|
79
|
+
@cli.send :rearrange_fields, item, ["groups", "tagSet"]
|
80
|
+
end
|
81
|
+
def test_complex_calls
|
82
|
+
assert @cli.describe_instances == $instance_set
|
83
|
+
instances = [{:instanceId=>"lol"}]
|
84
|
+
assert @cli.instances == instances
|
85
|
+
assert @cli.ins == instances
|
86
|
+
assert @cli.sgs == [{"groupName"=>"test"}]
|
87
|
+
end
|
88
|
+
def test_underlying_client_initialization
|
89
|
+
x = nil
|
90
|
+
begin
|
91
|
+
@cli.build_underlying_client ['a', 'b', 'through']
|
92
|
+
rescue Exception => e
|
93
|
+
x = e
|
94
|
+
end
|
95
|
+
assert not(x.nil?)
|
19
96
|
end
|
20
97
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ec2l
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-07-
|
12
|
+
date: 2014-07-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -125,7 +125,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
125
125
|
version: '0'
|
126
126
|
segments:
|
127
127
|
- 0
|
128
|
-
hash:
|
128
|
+
hash: 4414468867736333160
|
129
129
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
130
|
none: false
|
131
131
|
requirements:
|
@@ -134,7 +134,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
134
134
|
version: '0'
|
135
135
|
segments:
|
136
136
|
- 0
|
137
|
-
hash:
|
137
|
+
hash: 4414468867736333160
|
138
138
|
requirements: []
|
139
139
|
rubyforge_project:
|
140
140
|
rubygems_version: 1.8.23
|