ec2ctl 0.7.9 → 0.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c9d5765abec98bc65ea4dd0c1276041db0c2766c
4
- data.tar.gz: 3ed14b3b91fa16fc850c5f1a567b9f3970df6e86
3
+ metadata.gz: 2db3d33906e470bb3a0e6b4a96e840feae7e0dfd
4
+ data.tar.gz: aac465cbdf09c95ce707088fd6dd705584088290
5
5
  SHA512:
6
- metadata.gz: bc349a68f3004dafea7daf1951f790b8da4ee30a8dd3434ec3429fb5b86a061eb52a42317e44f1081192d4d0e09a87b0ede2377d462e3e7ff82ae22af24f9967
7
- data.tar.gz: da45141f0d9338d1386c4d988741622df08ec521958b4f4146efa9e3bd50bd1d75c4f25c58fa9b9caaf33bcebf3c78067811f63de7bcfbf0af3d63a535ede5f6
6
+ metadata.gz: 14cadc9ca8e185e0b97547b05a155b0fb44ee2be7952430ae27199a292f3dc2594027e24c5fbdc71e3afcf46be780ccba3c2533da63febb972c2de37abcc8aa7
7
+ data.tar.gz: 19ab369c479627d591d68b44e943bcd6d3ce66ae66ec5fde155c634ec49f7afb0a411f1efe162a9b1d95487beaed8c4f26dccb50c900bace55b9bf8b940f8774
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.12.5
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at yamaguchi@cloudpack.jp. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Yoriki Yamaguchi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # EC2Ctl
2
2
 
3
- **!!! Version 0.7 or later is not compatible with 0.6 !!!**
3
+ A small command line tool for managing EC2/ELB.
4
4
 
5
5
  ## Installation
6
6
 
@@ -20,7 +20,37 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
 
23
- TODO: Write usage instructions here
23
+ ### `ec2 list`
24
+
25
+ List EC2 instances.
26
+
27
+ ### `ec2 execute`
28
+
29
+ Execute commands in the EC2 instances.
30
+
31
+ ### `elb list`
32
+
33
+ List load balancers.
34
+
35
+ ### `elb status`
36
+
37
+ Show the load balancer's status.
38
+
39
+ ### `elb attach`
40
+
41
+ Attach the instances to the load balancer.
42
+
43
+ ### `elb detach`
44
+
45
+ Detach the instances from the load balancer.
46
+
47
+ ### `elb execute`
48
+
49
+ Execute commands on instance(s) registered to a load balancer.
50
+
51
+ ### `elb graceful`
52
+
53
+ Sequencially deregister instance(s) from load balancer, execute commands, register it back to load balancer and wait until it's in `InService` state.
24
54
 
25
55
  ## Development
26
56
 
data/Rakefile CHANGED
@@ -1,6 +1 @@
1
1
  require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
-
4
- RSpec::Core::RakeTask.new(:spec)
5
-
6
- task :default => :spec
data/ec2ctl.gemspec CHANGED
@@ -4,26 +4,28 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'ec2ctl/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "ec2ctl"
8
- spec.version = EC2Ctl::VERSION
9
- spec.authors = ["y13i"]
10
- spec.email = ["email@y13i.com"]
11
-
12
- spec.summary = %q{A minimum tool for EC2 instances.}
13
- spec.description = %q{A minimum tool for EC2 instances.}
14
- spec.homepage = "https://github.com/y13i/ec2ctl"
7
+ spec.name = "ec2ctl"
8
+ spec.version = EC2Ctl::VERSION
9
+ spec.authors = ["Yoriki Yamaguchi"]
10
+ spec.email = ["email@y13i.com"]
11
+ spec.summary = %(A small command line tool for managing EC2/ELB.)
12
+ spec.description = %(A small command line tool for managing EC2/ELB.)
13
+ spec.homepage = "https://github.com/y13i/ec2ctl"
14
+ spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
17
  spec.bindir = "exe"
18
18
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_dependency "commander"
22
- spec.add_dependency "aws-sdk", ">= 2.0"
23
- spec.add_dependency "terminal-table"
21
+ spec.required_ruby_version = ">= 2.1.0"
22
+
23
+ spec.add_dependency "aws-sdk", "~> 2.3"
24
+ spec.add_dependency "commander", "~> 4.4"
25
+ spec.add_dependency "coderay", "~> 1.1"
26
+ spec.add_dependency "terminal-table", "~> 1.6"
24
27
 
25
- spec.add_development_dependency "bundler", "~> 1.10"
26
- spec.add_development_dependency "rake", "~> 10.0"
27
- spec.add_development_dependency "rspec"
28
- spec.add_development_dependency "pry"
28
+ spec.add_development_dependency "bundler", "~> 1.12"
29
+ spec.add_development_dependency "rake", "~> 10.0"
30
+ spec.add_development_dependency "pry", "~> 0.10"
29
31
  end
data/lib/ec2ctl/cli.rb CHANGED
@@ -1,108 +1,238 @@
1
1
  require "commander"
2
- require "json"
3
- require "yaml"
4
- require "terminal-table"
2
+ require "ec2ctl/logger"
5
3
 
6
4
  module EC2Ctl
7
5
  class CLI
8
6
  include Commander::Methods
9
7
 
8
+ OptionError = Class.new RuntimeError
9
+
10
10
  def run
11
- program :name, "EC2Ctl"
12
- program :version, EC2Ctl::VERSION
13
- program :description, "A minimum tool for EC2 instances."
11
+ program :name, self.class.to_s
12
+ program :version, VERSION
13
+ program :description, "A small command line tool for managing EC2/ELB."
14
+
15
+ global_option("-V", "--verbose", "Debug output.") {@verbose = true}
16
+ global_option("-P", "--pretty", "Pretty JSON output.") {@pretty = true}
17
+ global_option("-o", "--output #{EC2Ctl::Logger::VALID_FORMATS}", "Log output format.") {|v| @output_format = v.intern}
18
+ global_option("-p", "--profile PROFILE_NAME", "AWS profile name.") {|v| ENV["AWS_PROFILE"] = v}
19
+ global_option("-r", "--region REGION_NAME", "AWS region name.") {|v| ENV["AWS_REGION"] = v}
20
+
21
+ default_command :"ec2 list"
22
+
23
+ command :"ec2 list" do |c|
24
+ c.syntax = "ec2ctl ec2 list"
25
+ c.description = "List EC2 instances."
26
+
27
+ ec2_options c
28
+
29
+ c.action do |args, options|
30
+ options.default attributes: %w(instance_id tag:Name instance_type public_dns_name state.name)
31
+
32
+ invoke options do
33
+ @client.ec2_list
34
+ end
35
+ end
36
+ end
37
+
38
+ command :"ec2 execute" do |c|
39
+ c.syntax = "ec2ctl ec2 execute"
40
+ c.description = "Execute commands in the EC2 instances."
41
+
42
+ ec2_options c
43
+
44
+ c.option "-c", "--commands 'STRING1','STRING2',...", Array, "The commands to execute."
45
+
46
+ c.action do |args, options|
47
+ invoke options do
48
+ mandatory options, :commands
49
+ @client.ec2_execute
50
+ end
51
+ end
52
+ end
14
53
 
15
- global_option "-p", "--profile PROFILE", "Load AWS credentials from shared credentials file by specified name."
16
- global_option "-r", "--region REGION", "Specify AWS region."
54
+ command :"elb list" do |c|
55
+ c.syntax = "ec2ctl elb list"
56
+ c.description = "List load balancers."
17
57
 
18
- default_command :list
58
+ c.action do |args, options|
59
+ invoke options do
60
+ @client.elb_list
61
+ end
62
+ end
63
+ end
19
64
 
20
- command :list do |c|
21
- c.syntax = "list [options]"
22
- c.description = "List EC2 instances"
65
+ command :"elb status" do |c|
66
+ c.syntax = "ec2ctl elb status"
67
+ c.description = "Show the load balancer's status."
23
68
 
24
- c.option "-a", "--attributes STRING", String, "Attribute of EC2 instances separated by commas."
25
- c.option "-s", "--search STRING", String, "Search instance with given KEY=VALUE attributes."
26
- c.option "-o", "--order STRING", String, "Order list by specified attribute."
27
- c.option "-f", "--format STRING", String, "Output format (table/json/yaml)."
69
+ c.option "-b", "--load-balancer-name VALUE", String, "The name of the load balancer."
28
70
 
29
71
  c.action do |args, options|
30
- options.default(
31
- attributes: "instance_id,tag:Name,instance_type,private_ip_address,public_ip_address,state.name",
32
- format: "table",
33
- order: "tag:Name",
34
- )
35
-
36
- set_client(options)
37
-
38
- search = if options.search
39
- options.search.split(",").map {|s| s.split("=")}.inject Hash.new do |acc, pair|
40
- acc.merge pair.first => pair.last
41
- end
42
- else
43
- {}
72
+ invoke options do
73
+ mandatory options, :load_balancer_name
74
+ @client.elb_status
44
75
  end
76
+ end
77
+ end
78
+
79
+ command :"elb attach" do |c|
80
+ c.syntax = "ec2ctl elb attach"
81
+ c.description = "Attach the instances to the load balancer."
82
+
83
+ c.option "-b", "--load-balancer-name VALUE", String, "The name of the load balancer."
84
+ c.option "-i", "--instance-ids STRING1,STRING2", Array, "(Optional) The IDs of the instances to attach."
45
85
 
46
- attributes = (options.attributes.split(",") + search.keys).uniq
47
- instance_infos = client.instance_infos(attributes, search).sort_by {|i| i[options.order]}
48
-
49
- if instance_infos.empty?
50
- say "Instance not found."
51
- else
52
- case options.format
53
- when /table/i
54
- say tableize(attributes, instance_infos)
55
- when /markdown/i
56
- say tableize(attributes, instance_infos, :markdown)
57
- when /backlog/i
58
- say tableize(attributes, instance_infos, :backlog)
59
- when /json/i
60
- say JSON.pretty_generate(instance_infos)
61
- when /yaml/i
62
- say instance_infos.to_yaml
63
- end
86
+ c.action do |args, options|
87
+ invoke options do
88
+ mandatory options, :load_balancer_name, :instance_ids
89
+ @client.elb_attach
64
90
  end
65
91
  end
66
92
  end
67
93
 
68
- alias_command :ls, :list
94
+ command :"elb detach" do |c|
95
+ c.syntax = "ec2ctl elb detach"
96
+ c.description = "Detach the instances from the load balancer."
97
+
98
+ c.option "-b", "--load-balancer-name VALUE", String, "The name of the load balancer."
99
+ c.option "-i", "--instance-ids STRING1,STRING2", Array, "(Optional) The IDs of the instances to detach."
100
+
101
+ c.action do |args, options|
102
+ invoke options do
103
+ mandatory options, :load_balancer_name, :instance_ids
104
+ @client.elb_detach
105
+ end
106
+ end
107
+ end
108
+
109
+ command :"elb execute" do |c|
110
+ c.syntax = "ec2ctl elb execute"
111
+ c.description = "Execute commands on instance(s) registered to a load balancer."
112
+
113
+ elb_execute_options c
114
+
115
+ c.action do |args, options|
116
+ invoke options do
117
+ mandatory options, :load_balancer_name, :commands
118
+ @client.elb_execute
119
+ end
120
+ end
121
+ end
122
+
123
+ command :"elb graceful" do |c|
124
+ c.syntax = "ec2ctl elb graceful"
125
+ c.description = "Sequencially deregister instance(s) from load balancer, execute commands, register it back to load balancer and wait until it's in `InService` state."
126
+
127
+ elb_execute_options c
128
+
129
+ c.action do |args, options|
130
+ invoke options do
131
+ mandatory options, :load_balancer_name, :commands
132
+ @client.elb_graceful
133
+ end
134
+ end
135
+ end
69
136
 
70
137
  run!
71
138
  end
72
139
 
73
140
  private
74
141
 
75
- def tableize(attributes, instance_infos, mode = nil)
76
- table = Terminal::Table.new(
77
- headings: attributes,
78
- rows: instance_infos.map {|i| attributes.map {|a| i[a]}}
142
+ def init_client(options)
143
+ global_options = %i(
144
+ verbose
145
+ pretty
146
+ output
147
+ profile
148
+ region
149
+ trace
150
+ version
151
+ help
152
+ )
153
+
154
+ @client = EC2Ctl::Client.new options.__hash__.reject {|k, v| global_options.include? k}.merge(logger: logger)
155
+ end
156
+
157
+ def logger
158
+ @logger ||= EC2Ctl::Logger.new(
159
+ output_format: (@output_format || :json),
160
+ pretty: @pretty,
161
+ verbose: @verbose,
79
162
  )
163
+ end
164
+
165
+ def invoke(options, &block)
166
+ begin
167
+ debug_init options
168
+ init_client options
169
+ block.call
170
+ rescue => ex
171
+ logger.error(
172
+ error: {
173
+ class: ex.class,
174
+ message: ex.message
175
+ }
176
+ )
177
+
178
+ raise
179
+ end
180
+ end
80
181
 
81
- case mode
82
- when :markdown
83
- lines = table.to_s.lines[1..-2]
84
- lines[1].gsub!("+", "|")
85
- lines.join
86
- when :backlog
87
- lines = table.to_s.lines[1..-2]
88
- lines[0].sub!("\n", "h\n")
89
- lines[1] = nil
90
- lines.compact.join
91
- else
92
- table
182
+ # e.g.
183
+ #
184
+ # mandatory options, :load_balancer_name, :commands
185
+ #
186
+ def mandatory(options, *option_names)
187
+ option_names.each do |option_name|
188
+ fail OptionError, "Option `#{option_name}` is mandatory." if options.__send__(option_name).nil?
93
189
  end
94
190
  end
95
191
 
96
- def client
97
- @client
192
+ # only for debug output
193
+ def aws_env
194
+ {
195
+ AWS_ACCESS_KEY_ID: ("#{ENV["AWS_ACCESS_KEY_ID"][0, 5]}..." if ENV["AWS_ACCESS_KEY_ID"]),
196
+ AWS_SECRET_ACCESS_KEY: ("#{ENV["AWS_SECRET_ACCESS_KEY"][0, 5]}..." if ENV["AWS_SECRET_ACCESS_KEY"]),
197
+ AWS_PROFILE: ENV["AWS_PROFILE"],
198
+ AWS_REGION: ENV["AWS_REGION"],
199
+ }
98
200
  end
99
201
 
100
- def set_client(options)
101
- o = {}
102
- o[:profile] = options.profile if options.profile
103
- o[:region] = options.region if options.region
202
+ def debug_init(options)
203
+ logger.debug options: options.__hash__
204
+ logger.debug aws_env: aws_env
205
+ end
206
+
207
+ def elb_execute_options(_command)
208
+ _command.option "-b", "--load-balancer-name STRING", String, "The name of the load balancer."
209
+ _command.option "-c", "--commands 'STRING1','STRING2',...", Array, "The commands to execute."
210
+ _command.option "-P", "--platform-type Linux|Windows", String, "(Optional) Platform type: `Linux` or `Windows`. Default is `Linux`."
211
+ _command.option "--skip-ping-check", "(Optional) Skip SSM ping check."
212
+ _command.option "--skip-command-waits", "(Optional) Skip waiting command success."
213
+ _command.option "--wait-interval INTEGER", Integer, "(Optional) Waiting interval."
214
+ _command.option "--working-directory STRING", String, "(Optional) The path to the working directory on your instance."
215
+ _command.option "--execution-timeout INTEGER", Integer, "(Optional) The time in seconds for a command to be completed before it is considered to have failed. Default is 3600 (1 hour). Maximum is 28800 (8 hours)."
216
+ _command.option "--timeout-seconds INTEGER", Integer, "(Optional) If this time is reached and the command has not already started executing, it will not execute."
217
+ _command.option "--comment STRING", String, "(Optional) User-specified information about the command, such as a brief description of what the command should do."
218
+ _command.option "--output-s3-bucket-name STRING", String, "(Optional) The name of the S3 bucket where command execution responses should be stored."
219
+ _command.option "--output-s3-key-prefix STRING", String, "(Optional) The directory structure within the S3 bucket where the responses should be stored."
220
+ _command.option "--service-role-arn STRING", String, "(Optional) The IAM role that SSM uses to send notifications."
221
+ _command.option "--notification-arn STRING", String, "(Optional) An Amazon Resource Name (ARN) for a Simple Notification Service (SNS) topic."
222
+ _command.option "--notification-events STRING1,STRING2,...", Array, "(Optional) The different events for which you can receive notifications."
223
+ _command.option "--notification-type STRING", String, "(Optional) Command: Receive notification when the status of a command changes."
224
+ _command.option "--rolling-group-size INTEGER", Integer, "(Optional) The count of instances to register/deregister/execute simultaneously."
225
+ _command.option "--skip-draining-waits", "(Optional) Skip waiting connection draining after deregistering instances from load balancer."
226
+ _command.option "--skip-inservice-waits", "(Optional) Skip waiting `InService` state after registering instances to load balancer."
227
+ _command.option "--inservice-wait-timeout INTEGER", Integer, "(Optional) The time in seconds for instances to be `InService` state after registering to load balancer."
228
+ _command.option "-i", "--instance-ids STRING1,STRING2", Array, "(Optional) The IDs of the instances. If specified, the commands will be executed only on these instances."
229
+ end
104
230
 
105
- @client = EC2Ctl::Client.new(o)
231
+ def ec2_options(_command)
232
+ _command.option "-a", "--attributes KEY1,KEY2...", Array, "(Optional) The instance attribute keys to display."
233
+ _command.option "-f", "--filters KEY1=VALUE1,KEY2=VALUE2...", Array, "(Optional) The key-value pairs to filter instances."
234
+ _command.option "-s", "--search KEY1=VALUE1,KEY2=VALUE2...", Array, "(Optional) The key-value pairs to search instances by Regexp."
235
+ _command.option "-i", "--instance-ids STRING1,STRING2", Array, "(Optional) The IDs of the instances."
106
236
  end
107
237
  end
108
238
  end