ec2-host 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,112 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
+ <head>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6
+ <title>
7
+ Top Level Namespace
8
+
9
+ &mdash; Documentation by YARD 0.8.7.6
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
16
+
17
+ <script type="text/javascript" charset="utf-8">
18
+ hasFrames = window.top.frames.main ? true : false;
19
+ relpath = '';
20
+ framesUrl = "frames.html#!top-level-namespace.html";
21
+ </script>
22
+
23
+
24
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
25
+
26
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
27
+
28
+
29
+ </head>
30
+ <body>
31
+ <div id="header">
32
+ <div id="menu">
33
+
34
+ <a href="_index.html">Index</a> &raquo;
35
+
36
+
37
+ <span class="title">Top Level Namespace</span>
38
+
39
+
40
+ <div class="noframes"><span class="title">(</span><a href="." target="_top">no frames</a><span class="title">)</span></div>
41
+ </div>
42
+
43
+ <div id="search">
44
+
45
+ <a class="full_list_link" id="class_list_link"
46
+ href="class_list.html">
47
+ Class List
48
+ </a>
49
+
50
+ <a class="full_list_link" id="method_list_link"
51
+ href="method_list.html">
52
+ Method List
53
+ </a>
54
+
55
+ <a class="full_list_link" id="file_list_link"
56
+ href="file_list.html">
57
+ File List
58
+ </a>
59
+
60
+ </div>
61
+ <div class="clear"></div>
62
+ </div>
63
+
64
+ <iframe id="search_frame"></iframe>
65
+
66
+ <div id="content"><h1>Top Level Namespace
67
+
68
+
69
+
70
+ </h1>
71
+
72
+ <dl class="box">
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+ </dl>
82
+ <div class="clear"></div>
83
+
84
+ <h2>Defined Under Namespace</h2>
85
+ <p class="children">
86
+
87
+
88
+
89
+
90
+ <strong class="classes">Classes:</strong> <span class='object_link'><a href="EC2.html" title="EC2 (class)">EC2</a></span>
91
+
92
+
93
+ </p>
94
+
95
+
96
+
97
+
98
+
99
+
100
+
101
+
102
+
103
+ </div>
104
+
105
+ <div id="footer">
106
+ Generated on Thu Nov 24 10:25:36 2016 by
107
+ <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
108
+ 0.8.7.6 (ruby-2.3.2).
109
+ </div>
110
+
111
+ </body>
112
+ </html>
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |gem|
2
2
  gem.name = "ec2-host"
3
- gem.version = '0.3.1'
3
+ gem.version = '0.4.0'
4
4
  gem.author = ['Naotoshi Seo']
5
5
  gem.email = ['sonots@gmail.com']
6
6
  gem.homepage = 'https://github.com/sonots/ec2-host'
@@ -14,7 +14,6 @@ Gem::Specification.new do |gem|
14
14
  gem.require_paths = ["lib"]
15
15
 
16
16
  gem.add_runtime_dependency 'aws-sdk'
17
- gem.add_runtime_dependency 'hashie'
18
17
  gem.add_runtime_dependency 'dotenv'
19
18
 
20
19
  gem.add_development_dependency 'yard'
@@ -1,6 +1,6 @@
1
1
  AWS_ACCESS_KEY_ID=xxxxxxx
2
2
  AWS_SECRET_ACCESS_KEY=xxxxxx
3
- AWS_REGION=ap-northeast-1
3
+ AWS_DEFAULT_REGION=ap-northeast-1
4
4
  HOSTNAME_TAG=Name
5
5
  ROLES_TAG=Roles
6
6
  ROLE_TAG_DELIMITER=:
@@ -3,5 +3,5 @@ require_relative 'ec2/host/hash_util'
3
3
  require_relative 'ec2/host/config'
4
4
  require_relative 'ec2/host/host_data'
5
5
  require_relative 'ec2/host/role_data'
6
- require_relative 'ec2/host/client_util'
6
+ require_relative 'ec2/host/ec2_client'
7
7
  require_relative 'ec2/host'
@@ -40,7 +40,7 @@ class EC2
40
40
 
41
41
  # @return [Host::Data] representing myself
42
42
  def self.me
43
- new(instance_id: ClientUtil.instance_id).each do |d|
43
+ new(instance_id: ec2_client.instance_id).each do |d|
44
44
  return d
45
45
  end
46
46
  raise 'Not Found'
@@ -48,11 +48,19 @@ class EC2
48
48
 
49
49
  # Configure EC2::Host
50
50
  #
51
- # @params [Hash] params see EC2::Host::Config for configurable parameters
51
+ # @param [Hash] params see EC2::Host::Config for configurable parameters
52
52
  def self.configure(params = {})
53
53
  Config.configure(params)
54
54
  end
55
55
 
56
+ def self.ec2_client
57
+ @ec2_client ||= EC2Client.new
58
+ end
59
+
60
+ def ec2_client
61
+ self.class.ec2_client
62
+ end
63
+
56
64
  attr_reader :conditions, :options
57
65
 
58
66
  # @param [Array of Hash, or Hash] conditions (and options)
@@ -92,7 +100,7 @@ class EC2
92
100
  # @yieldparam [Host::Data] data entry
93
101
  def each(&block)
94
102
  @conditions.each do |condition|
95
- search(ClientUtil.instances(condition), condition, &block)
103
+ search(ec2_client.instances(condition), condition, &block)
96
104
  end
97
105
  return self
98
106
  end
@@ -101,7 +109,7 @@ class EC2
101
109
 
102
110
  def search(instances, condition)
103
111
  instances.each do |i|
104
- d = EC2::Host::HostData.initialize(i)
112
+ d = EC2::Host::HostData.new(i)
105
113
  next unless d.match?(condition)
106
114
  yield d
107
115
  end
@@ -10,7 +10,7 @@ class EC2
10
10
  :aws_profile,
11
11
  :aws_access_key_id,
12
12
  :aws_secret_access_key,
13
- :aws_credentials_file,
13
+ :aws_credential_file,
14
14
  :log_level,
15
15
  :hostname_tag,
16
16
  :roles_tag,
@@ -29,24 +29,31 @@ class EC2
29
29
  end
30
30
 
31
31
  def self.aws_region
32
- @aws_region ||= ENV['AWS_REGION'] || config.fetch('AWS_REGION')
32
+ @aws_region ||=
33
+ ENV['AWS_REGION'] || config.fetch('AWS_REGION', nil) || # ref. old aws cli
34
+ ENV['AWS_DEFAULT_REGION'] || config.fetch('AWS_DEFAULT_REGION') # ref. aws cli and terraform
33
35
  end
34
36
 
35
37
  def self.aws_profile
36
- @aws_profile ||= ENV['AWS_PROFILE'] || config.fetch('AWS_PROFILE', 'default')
38
+ @aws_profile ||=
39
+ ENV['AWS_PROFILE'] || config.fetch('AWS_PROFILE', 'nil') || # ref. old aws cli
40
+ ENV['AWS_DEFAULT_PROFILE'] || config.fetch('AWS_DEFAULT_PROFILE', 'default') # ref. aws cli and terraform
37
41
  end
38
42
 
39
43
  def self.aws_access_key_id
44
+ # ref. aws cli and terraform
40
45
  @aws_access_key_id ||= ENV['AWS_ACCESS_KEY_ID'] || config.fetch('AWS_ACCESS_KEY_ID', nil)
41
46
  end
42
47
 
43
48
  def self.aws_secret_access_key
49
+ # ref. aws cli and terraform
44
50
  @aws_secret_access_key ||= ENV['AWS_SECRET_ACCESS_KEY'] || config.fetch('AWS_SECRET_ACCESS_KEY', nil)
45
51
  end
46
52
 
47
- # this is not an official aws sdk environment variable
48
- def self.aws_credentials_file
49
- @aws_credentials_file ||= ENV['AWS_CREDENTIALS_FILE'] || config.fetch('AWS_CREDENTIALS_FILE', nil)
53
+ def self.aws_credential_file
54
+ @aws_credential_file ||=
55
+ ENV['AWS_CREDENTIALS_FILE'] || config.fetch('AWS_CREDENTIALS_FILE', nil) ||
56
+ ENV['AWS_CREDENTIAL_FILE'] || config.fetch('AWS_CREDENTIAL_FILE', nil) # ref. aws cli (supported lately)
50
57
  end
51
58
 
52
59
  def self.log_level
@@ -79,15 +86,6 @@ class EC2
79
86
 
80
87
  # private
81
88
 
82
- def self.aws_credentials
83
- @aws_credentials ||=
84
- if aws_access_key_id and aws_secret_access_key
85
- Aws::Credentials.new(aws_access_key_id, aws_secret_access_key)
86
- else
87
- Aws::SharedCredentials.new(profile_name: aws_profile, path: aws_credentials_file)
88
- end
89
- end
90
-
91
89
  def self.optional_array_options
92
90
  @optional_array_options ||= Hash[optional_array_tags.map {|tag|
93
91
  [StringUtil.singularize(StringUtil.underscore(tag)), tag]
@@ -0,0 +1,60 @@
1
+ require 'net/http'
2
+ require 'aws-sdk'
3
+
4
+ class EC2
5
+ class Host
6
+ class EC2Client
7
+ def instances(condition)
8
+ filters = build_filters(condition)
9
+ instances =
10
+ if filters
11
+ ec2.describe_instances(filters: filters)
12
+ elsif instance_id = condition[:instance_id]
13
+ ec2.describe_instances(instance_ids: Array(instance_id))
14
+ else
15
+ ec2.describe_instances
16
+ end
17
+ instances.reservations.map(&:instances).flatten
18
+ end
19
+
20
+ def instance_id
21
+ return @instance_id if @instance_id
22
+ begin
23
+ http_conn = Net::HTTP.new('169.254.169.254')
24
+ http_conn.open_timeout = 5
25
+ @instance_id = http_conn.start {|http| http.get('/latest/meta-data/instance-id').body }
26
+ rescue Net::OpenTimeout
27
+ raise "HTTP connection to 169.254.169.254 is timeout. Probably, not an EC2 instance?"
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def ec2
34
+ @ec2 ||= Aws::EC2::Client.new(region: Config.aws_region, credentials: credentials)
35
+ end
36
+
37
+ def credentials
38
+ if Config.aws_access_key_id and Config.aws_secret_access_key
39
+ Aws::Credentials.new(Config.aws_access_key_id, Config.aws_secret_access_key)
40
+ else
41
+ Aws::SharedCredentials.new(profile_name: Config.aws_profile, path: Config.aws_credential_file)
42
+ end
43
+ end
44
+
45
+ def build_filters(condition)
46
+ if role = (condition[:role] || condition[:usage]) and role.size == 1
47
+ [{name: "tag:#{Config.roles_tag}", values: ["*#{role.first}*"]}]
48
+ elsif role1 = (condition[:role1] || condition[:usage1]) and role1.size == 1
49
+ [{name: "tag:#{Config.roles_tag}", values: ["*#{role1.first}*"]}]
50
+ elsif role2 = (condition[:role2] || condition[:usage2]) and role2.size == 1
51
+ [{name: "tag:#{Config.roles_tag}", values: ["*#{role2.first}*"]}]
52
+ elsif role3 = (condition[:role3] || condition[:usage3]) and role3.size == 1
53
+ [{name: "tag:#{Config.roles_tag}", values: ["*#{role3.first}*"]}]
54
+ else
55
+ nil
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -1,98 +1,93 @@
1
- require 'forwardable'
2
- require 'hashie/mash'
3
1
  require 'json'
4
2
 
5
3
  class EC2
6
4
  class Host
7
5
  # Represents each host
8
- class HostData < Hashie::Mash
6
+ class HostData
7
+ attr_reader :instance
8
+
9
9
  # :hostname, # tag:Name or hostname part of private_dns_name
10
10
  # :roles, # tag:Roles.split(',') such as web:app1,db:app1
11
11
  # :region, # ENV['AWS_REGION'],
12
12
  # :instance, # Aws::EC2::Types::Instance itself
13
13
  #
14
14
  # and OPTIONAL_ARRAY_TAGS, OPTIONAL_STRING_TAGS
15
+ def initialize(instance)
16
+ @instance = instance
17
+ end
15
18
 
16
- extend Forwardable
17
- def_delegators :instance,
18
- :instance_id,
19
- :private_ip_address,
20
- :public_ip_address,
21
- :launch_time
22
- def state; self[:instance].state.name; end
23
- def monitoring; self[:instance].monitoring.state; end
24
-
25
- alias_method :ip, :private_ip_address
26
- alias_method :start_date, :launch_time
27
- def usages; self[:roles]; end
28
-
29
- def self.initialize(instance)
30
- d = self.new
31
- d.instance = instance
32
- d.set_hostname
33
- d.set_roles
34
- d.set_region
35
- d.set_string_tags
36
- d.set_array_tags
37
- d
19
+ def hostname
20
+ return @hostname if @hostname
21
+ @hostname = find_string_tag(Config.hostname_tag)
22
+ @hostname = instance.private_dns_name.split('.').first if @hostname.empty?
23
+ @hostname
38
24
  end
39
25
 
40
- # match with condition or not
41
- #
42
- # @param [Hash] condition search parameters
43
- def match?(condition)
44
- return false if !condition[:state] and (terminated? or shutting_down?)
45
- return false unless role_match?(condition)
46
- condition = HashUtil.except(condition,
47
- :role, :role1, :role2, :role3,
48
- :usage, :usage1, :usage2, :usage3
49
- )
50
- condition.each do |key, values|
51
- v = get_value(key)
52
- if v.is_a?(Array)
53
- return false unless v.find {|_| values.include?(_) }
54
- else
55
- return false unless values.include?(v)
56
- end
57
- end
58
- true
26
+ def roles
27
+ return @roles if @roles
28
+ roles = find_array_tag(Config.roles_tag)
29
+ @roles = roles.map {|role| EC2::Host::RoleData.build(role) }
59
30
  end
60
31
 
61
- def inspect
62
- sprintf "#<Aws::Host::HostData %s>", info
32
+ def region
33
+ Config.aws_region
63
34
  end
64
35
 
65
- def info
66
- if self[:hostname] and self[:status] and self[:roles] and self[:tags] and self[:service]
67
- # special treatment for DeNA ;)
68
- info = "#{self[:hostname]}:#{self[:status]}"
69
- info << "(#{self[:roles].join(' ')})" unless self[:roles].empty?
70
- info << "[#{self[:tags].join(' ')}]" unless self[:tags].empty?
71
- info << "{#{self[:service]}}" unless self[:service].empty?
72
- info
73
- else
74
- to_hash.to_s
36
+ Config.optional_string_tags.each do |tag|
37
+ field = StringUtil.underscore(tag)
38
+ define_method(field) do
39
+ instance_variable_get("@#{field}") || instance_variable_set("@#{field}", find_string_tag(tag))
75
40
  end
76
41
  end
77
42
 
78
- def to_hash
79
- HashUtil.except(self, :instance).to_h.merge(
80
- instance_id: instance_id,
81
- private_ip_address: private_ip_address,
82
- public_ip_address: public_ip_address,
83
- launch_time: launch_time,
84
- state: state,
85
- monitoring: monitoring,
86
- )
43
+ Config.optional_array_tags.each do |tag|
44
+ field = StringUtil.underscore(tag)
45
+ define_method(field) do
46
+ instance_variable_get("@#{field}") || instance_variable_set("@#{field}", find_array_tag(tag))
47
+ end
87
48
  end
88
49
 
89
- # private
50
+ def instance_id
51
+ instance.instance_id
52
+ end
90
53
 
91
- # "instance.instance_id" => self.send("instance").send("instance_id")
92
- def get_value(key)
93
- v = self
94
- key.to_s.split('.').each {|k| v = v[k] || v.send(k) }
95
- v
54
+ def instance_type
55
+ instance.instance_type
56
+ end
57
+
58
+ def private_ip_address
59
+ instance.private_ip_address
60
+ end
61
+
62
+ def public_ip_address
63
+ instance.public_ip_address
64
+ end
65
+
66
+ def launch_time
67
+ instance.launch_time
68
+ end
69
+
70
+ def state
71
+ instance.state.name
72
+ end
73
+
74
+ def monitoring
75
+ instance.monitoring.state
76
+ end
77
+
78
+ # compatibility with dino-host
79
+ def ip
80
+ private_ip_address
81
+ end
82
+
83
+ # compatibility with dino-host
84
+ def start_date
85
+ launch_time
86
+ end
87
+
88
+ # compatibility with dino-host
89
+ def usages
90
+ roles
96
91
  end
97
92
 
98
93
  def terminated?
@@ -119,49 +114,59 @@ class EC2
119
114
  state == "pending"
120
115
  end
121
116
 
122
- def role_match?(condition)
123
- # usage is an alias of role
124
- if role = (condition[:role] || condition[:usage])
125
- role1, role2, role3 = role.first.split(':')
126
- else
127
- role1 = (condition[:role1] || condition[:usage1] || []).first
128
- role2 = (condition[:role2] || condition[:usage2] || []).first
129
- role3 = (condition[:role3] || condition[:usage3] || []).first
130
- end
131
- if role1
132
- return false unless self[:roles].find {|role| role.match?(role1, role2, role3) }
133
- end
117
+ # match with condition or not
118
+ #
119
+ # @param [Hash] condition search parameters
120
+ def match?(condition)
121
+ return false if !condition[:state] and (terminated? or shutting_down?)
122
+ return false unless role_match?(condition)
123
+ return false unless instance_match?(condition)
134
124
  true
135
125
  end
136
126
 
137
- def set_hostname
138
- self[:hostname] = find_string_tag(Config.hostname_tag)
139
- self[:hostname] = self[:instance].private_dns_name.split('.').first if self[:hostname].empty?
140
- end
141
-
142
- def set_roles
143
- roles = find_array_tag(Config.roles_tag)
144
- self[:roles] = roles.map {|role| EC2::Host::RoleData.initialize(role) }
145
- end
146
-
147
- def set_region
148
- self[:region] = Config.aws_region
149
- end
150
-
151
- def set_string_tags
127
+ def to_hash
128
+ params = {
129
+ "hostname" => hostname,
130
+ "roles" => roles,
131
+ }
152
132
  Config.optional_string_tags.each do |tag|
153
133
  field = StringUtil.underscore(tag)
154
- self[field] = find_string_tag(tag)
134
+ params[field] = send(field)
155
135
  end
156
- end
157
-
158
- def set_array_tags
159
136
  Config.optional_array_tags.each do |tag|
160
137
  field = StringUtil.underscore(tag)
161
- self[field] = find_array_tag(tag)
138
+ params[field] = send(field)
162
139
  end
140
+ params.merge!(
141
+ "region" => region,
142
+ "instance_id" => instance_id,
143
+ "instance_type" => instance_type,
144
+ "private_ip_address" => private_ip_address,
145
+ "public_ip_address" => public_ip_address,
146
+ "launch_time" => launch_time,
147
+ "state" => state,
148
+ "monitoring" => monitoring,
149
+ )
163
150
  end
164
151
 
152
+ def info
153
+ if self.class.display_short_info?
154
+ info = "#{hostname}:#{status}"
155
+ info << "(#{roles.join(',')})" unless roles.empty?
156
+ info << "[#{tags.join(',')}]" unless tags.empty?
157
+ info << "{#{service}}" unless service.empty?
158
+ info
159
+ else
160
+ to_hash.to_s
161
+ end
162
+ end
163
+
164
+ def inspect
165
+ sprintf "#<Aws::Host::HostData %s>", info
166
+ end
167
+
168
+ private
169
+
165
170
  def find_string_tag(key)
166
171
  v = instance.tags.find {|tag| tag.key == key }
167
172
  v ? v.value : ''
@@ -171,6 +176,54 @@ class EC2
171
176
  v = instance.tags.find {|tag| tag.key == key }
172
177
  v ? v.value.split(Config.array_tag_delimiter) : []
173
178
  end
179
+
180
+ def role_match?(condition)
181
+ # usage is an alias of role
182
+ if role = (condition[:role] || condition[:usage])
183
+ role1, role2, role3 = role.first.split(':')
184
+ else
185
+ role1 = (condition[:role1] || condition[:usage1] || []).first
186
+ role2 = (condition[:role2] || condition[:usage2] || []).first
187
+ role3 = (condition[:role3] || condition[:usage3] || []).first
188
+ end
189
+ if role1
190
+ return false unless roles.find {|role| role.match?(role1, role2, role3) }
191
+ end
192
+ true
193
+ end
194
+
195
+ def instance_match?(condition)
196
+ condition = HashUtil.except(condition, :role, :role1, :role2, :role3, :usage, :usage1, :usage2, :usage3)
197
+ condition.each do |key, values|
198
+ v = instance_variable_recursive_get(key)
199
+ if v.is_a?(Array)
200
+ return false unless v.find {|_| values.include?(_) }
201
+ else
202
+ return false unless values.include?(v)
203
+ end
204
+ end
205
+ true
206
+ end
207
+
208
+ # "instance.instance_id" => self.instance.instance_id
209
+ def instance_variable_recursive_get(key)
210
+ v = self
211
+ key.to_s.split('.').each {|k| v = v.send(k) }
212
+ v
213
+ end
214
+
215
+ # compatibility with dono-host
216
+ #
217
+ # If Service,Status,Tags tags are defined
218
+ #
219
+ # OPTIONAL_STRING_TAGS=Service,Status
220
+ # OPTIONAL_ARRAY_TAGS=Tags
221
+ #
222
+ # show in short format, otherwise, same with to_hash.to_s
223
+ def self.display_short_info?
224
+ return @display_short_info unless @display_short_info.nil?
225
+ @display_short_info = method_defined?(:service) and method_defined?(:status) and method_defined?(:tags)
226
+ end
174
227
  end
175
228
  end
176
229
  end