ec2ctl 0.1.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 +7 -0
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +52 -0
- data/Rakefile +1 -0
- data/bin/ec2ctl +4 -0
- data/ec2ctl.gemspec +30 -0
- data/lib/ec2ctl/version.rb +3 -0
- data/lib/ec2ctl.rb +198 -0
- metadata +167 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bea0a185379f61297a427c2c7efc2af67df6b763
|
4
|
+
data.tar.gz: 6135a1d00da6cbb6df57a8f184d24dc8d87104d3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8729284c92f26b5220aab5925715b3056696a3a03fd116254dd8920e2d3d548baa0f1b5595b185fc3b3b4b11c63ef04979f044171c3bff3c6a97e3313e839949
|
7
|
+
data.tar.gz: e4451be5017a641a415e2f5da2e8463ac2daf598c32d0d4025b1683d4dae3895e16308de8353148b5cfc738a82c43e80233c7f5a93285e7e638ee39dded91c63
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 y13i
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Ec2ctl
|
2
|
+
|
3
|
+
**!!! WORK IN PROGRESS !!!**
|
4
|
+
|
5
|
+
TODO: Add spec. etc...
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'ec2ctl'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install ec2ctl
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
TODO: Write more usage instructions here
|
24
|
+
|
25
|
+
```
|
26
|
+
Commands:
|
27
|
+
ec2ctl add NAME ELB_NAME # Add instance to ELB. Instance will be searched by Name tag.
|
28
|
+
ec2ctl help [COMMAND] # Describe available commands or one specific command
|
29
|
+
ec2ctl lb ELB_NAME # Show load balancer registered instance statuses.
|
30
|
+
ec2ctl list PATTERN # List all instances in region. Specify PATTERN to filter results by name.
|
31
|
+
ec2ctl remove NAME ELB_NAME # Remove instance from ELB. Instance will be searched by Name tag.
|
32
|
+
ec2ctl ssh NAME COMMAND # Execute remote command to specified instance by given name.
|
33
|
+
ec2ctl start NAME # Start instance. Instance will be searched by Name tag.
|
34
|
+
ec2ctl status NAME # Show instance status. Instance will be searched by Name tag.
|
35
|
+
ec2ctl stop NAME # Stop instance. Instance will be searched by Name tag.
|
36
|
+
|
37
|
+
Options:
|
38
|
+
k, [--access-key-id=ACCESS_KEY_ID]
|
39
|
+
s, [--secret-access-key=SECRET_ACCESS_KEY]
|
40
|
+
r, [--region=REGION]
|
41
|
+
[--profile=PROFILE]
|
42
|
+
[--color], [--no-color]
|
43
|
+
# Default: true
|
44
|
+
```
|
45
|
+
|
46
|
+
## Contributing
|
47
|
+
|
48
|
+
1. Fork it ( http://github.com/y13i/ec2ctl/fork )
|
49
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
50
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
51
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
52
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/ec2ctl
ADDED
data/ec2ctl.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'ec2ctl/version'
|
5
|
+
|
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
|
+
spec.summary = %q{Yet another handy EC2 tools.}
|
12
|
+
spec.description = %q{Can start/stop instances, add/remove to load balancer, remote command via ssh.}
|
13
|
+
spec.homepage = "https://github.com/y13i/ec2ctl"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "thor"
|
22
|
+
spec.add_dependency "aws-sdk"
|
23
|
+
spec.add_dependency "net-ssh"
|
24
|
+
spec.add_dependency "colorize"
|
25
|
+
spec.add_dependency "terminal-table"
|
26
|
+
spec.add_dependency "unindent"
|
27
|
+
|
28
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
29
|
+
spec.add_development_dependency "rake"
|
30
|
+
end
|
data/lib/ec2ctl.rb
ADDED
@@ -0,0 +1,198 @@
|
|
1
|
+
require "ec2ctl/version"
|
2
|
+
|
3
|
+
require "thor"
|
4
|
+
require "aws-sdk"
|
5
|
+
require "net/ssh"
|
6
|
+
require "net/http"
|
7
|
+
require "colorize"
|
8
|
+
require "terminal-table"
|
9
|
+
require "timeout"
|
10
|
+
require "unindent"
|
11
|
+
|
12
|
+
module Ec2ctl
|
13
|
+
class CLI < Thor
|
14
|
+
own_region = begin
|
15
|
+
timeout 3 do
|
16
|
+
Net::HTTP.get("169.254.169.254", "/latest/meta-data/placement/availability-zone")[0..-2]
|
17
|
+
end
|
18
|
+
rescue
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
|
22
|
+
class_option :access_key_id, default: nil, aliases: [:k]
|
23
|
+
class_option :secret_access_key, default: nil, aliases: [:s]
|
24
|
+
class_option :region, default: own_region, aliases: [:r]
|
25
|
+
class_option :profile
|
26
|
+
class_option :color, default: true, type: :boolean
|
27
|
+
|
28
|
+
desc "list PATTERN", "List all instances in region. Specify PATTERN to filter results by name."
|
29
|
+
def list pattern = ""
|
30
|
+
rows = []
|
31
|
+
|
32
|
+
ec2.instances.each do |instance|
|
33
|
+
name = instance.tags["Name"]
|
34
|
+
next if !pattern.empty? and !name.match pattern
|
35
|
+
|
36
|
+
rows << [
|
37
|
+
instance.id,
|
38
|
+
name,
|
39
|
+
instance.instance_type,
|
40
|
+
instance.private_ip_address,
|
41
|
+
instance.public_ip_address,
|
42
|
+
status_colorize(instance.status),
|
43
|
+
]
|
44
|
+
end
|
45
|
+
|
46
|
+
puts Terminal::Table.new headings: ["ID", "Name", "Type", "Private IP", "Public IP", "Status"], rows: rows
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "status NAME", "Show instance status. Instance will be searched by Name tag."
|
50
|
+
def status name
|
51
|
+
instance = find_instance_by_name name
|
52
|
+
tags = instance.tags.to_h
|
53
|
+
|
54
|
+
puts <<-EOS.unindent
|
55
|
+
* Name: #{tags["Name"]}
|
56
|
+
* Status: #{status_colorize instance.status}
|
57
|
+
* Instance Type: #{instance.instance_type}
|
58
|
+
* Private IP: #{instance.private_ip_address}
|
59
|
+
* Public IP: #{instance.public_ip_address}
|
60
|
+
* Availability Zone: #{instance.availability_zone}
|
61
|
+
* Other Tags: #{tags.reject {|k, v| k == "Name"}.inspect}
|
62
|
+
EOS
|
63
|
+
end
|
64
|
+
|
65
|
+
desc "start NAME", "Start instance. Instance will be searched by Name tag."
|
66
|
+
def start name
|
67
|
+
instance = find_instance_by_name name
|
68
|
+
abort "Instance is not stopped!" unless instance.status == :stopped
|
69
|
+
instance.start
|
70
|
+
sleep 2
|
71
|
+
puts "Instance is now #{status_colorize instance.status}."
|
72
|
+
end
|
73
|
+
|
74
|
+
desc "stop NAME", "Stop instance. Instance will be searched by Name tag."
|
75
|
+
def stop name
|
76
|
+
instance = find_instance_by_name name
|
77
|
+
abort "Instance is not running!" unless instance.status == :running
|
78
|
+
instance.stop
|
79
|
+
sleep 2
|
80
|
+
puts "Instance is now #{status_colorize instance.status}."
|
81
|
+
end
|
82
|
+
|
83
|
+
option :user, default: "ec2-user", aliases: [:u]
|
84
|
+
option :port, default: 22, aliases: [:p]
|
85
|
+
option :identity_file, default: "~/.ssh/id_rsa", aliases: [:i]
|
86
|
+
option :passphrase, default: nil, aliases: [:P]
|
87
|
+
option :via_public_ip, default: false, type: :boolean
|
88
|
+
desc "ssh NAME COMMAND", "Execute remote command to specified instance by given name."
|
89
|
+
def ssh name, command
|
90
|
+
instance = find_instance_by_name name
|
91
|
+
ip_address = options[:via_public_ip] ? instance.public_ip_address : instance.private_ip_address
|
92
|
+
|
93
|
+
ssh_options = {
|
94
|
+
keys: [options[:identity_file]],
|
95
|
+
passphrase: options[:passphrase],
|
96
|
+
}
|
97
|
+
|
98
|
+
Net::SSH.start ip_address, options[:user], ssh_options do |ssh|
|
99
|
+
puts ssh.exec!(command)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
desc "add NAME ELB_NAME", "Add instance to ELB. Instance will be searched by Name tag."
|
104
|
+
def add name, elb_name
|
105
|
+
instance = find_instance_by_name name
|
106
|
+
load_balancer = find_load_balancer_by_name elb_name
|
107
|
+
|
108
|
+
abort "Instance is already registered to the load balancer!" if load_balancer.instances.include? instance
|
109
|
+
|
110
|
+
puts "Adding instance to the load balancer..."
|
111
|
+
load_balancer.instances.add instance
|
112
|
+
end
|
113
|
+
|
114
|
+
desc "remove NAME ELB_NAME", "Remove instance from ELB. Instance will be searched by Name tag."
|
115
|
+
def remove name, elb_name
|
116
|
+
instance = find_instance_by_name name
|
117
|
+
load_balancer = find_load_balancer_by_name elb_name
|
118
|
+
|
119
|
+
abort "Instance is not registered to the load balancer!" unless load_balancer.instances.include? instance
|
120
|
+
|
121
|
+
puts "Removing instance from the load balancer..."
|
122
|
+
load_balancer.instances.remove instance
|
123
|
+
end
|
124
|
+
|
125
|
+
desc "lb ELB_NAME", "Show load balancer registered instance statuses."
|
126
|
+
def lb elb_name
|
127
|
+
load_balancer = find_load_balancer_by_name elb_name
|
128
|
+
instances = load_balancer.instances
|
129
|
+
rows = []
|
130
|
+
|
131
|
+
load_balancer.instances.health.each do |instance_health|
|
132
|
+
next unless instances.any? {|instance| instance.id == instance_health[:instance].id}
|
133
|
+
|
134
|
+
rows << [
|
135
|
+
instance_health[:instance].tags["Name"],
|
136
|
+
instance_health[:instance].id,
|
137
|
+
instance_health[:description],
|
138
|
+
instance_health[:state],
|
139
|
+
]
|
140
|
+
end
|
141
|
+
|
142
|
+
puts Terminal::Table.new headings: ["Name", "Instance ID", "Description", "State"], rows: rows
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
def aws_config
|
148
|
+
hash = {}
|
149
|
+
|
150
|
+
if options[:profile]
|
151
|
+
provider = AWS::Core::CredentialProviders::SharedCredentialFileProvider.new profile_name: options[:profile]
|
152
|
+
hash.update credential_provider: provider
|
153
|
+
else
|
154
|
+
hash.update access_key_id: options[:access_key_id], secret_access_key: options[:secret_access_key] if options[:access_key_id] && options[:secret_access_key]
|
155
|
+
end
|
156
|
+
|
157
|
+
hash.update region: options[:region] if options[:region]
|
158
|
+
hash
|
159
|
+
end
|
160
|
+
|
161
|
+
def ec2
|
162
|
+
AWS::EC2.new aws_config
|
163
|
+
end
|
164
|
+
|
165
|
+
def elb
|
166
|
+
AWS::ELB.new aws_config
|
167
|
+
end
|
168
|
+
|
169
|
+
def status_colorize status
|
170
|
+
return status unless options[:color]
|
171
|
+
|
172
|
+
color = case status
|
173
|
+
when :pending, :stopping then :magenta
|
174
|
+
when :running then :green
|
175
|
+
when :stopped then :light_blue
|
176
|
+
when :shutting_down then :yellow
|
177
|
+
when :terminated then :red
|
178
|
+
else :default
|
179
|
+
end
|
180
|
+
|
181
|
+
status.to_s.colorize color
|
182
|
+
end
|
183
|
+
|
184
|
+
def find_instance_by_name name
|
185
|
+
instance = ec2.instances.with_tag("Name", name).first
|
186
|
+
abort "No instance found!" unless instance
|
187
|
+
puts "Instance found: " + instance.inspect
|
188
|
+
instance
|
189
|
+
end
|
190
|
+
|
191
|
+
def find_load_balancer_by_name name
|
192
|
+
load_balancer = elb.load_balancers[name]
|
193
|
+
abort "No load balancer found!" unless load_balancer
|
194
|
+
puts "Load balancer found: " + load_balancer.inspect
|
195
|
+
load_balancer
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
metadata
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ec2ctl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- y13i
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-08-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: aws-sdk
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: net-ssh
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: colorize
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: terminal-table
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: unindent
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: bundler
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.5'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.5'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rake
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: Can start/stop instances, add/remove to load balancer, remote command
|
126
|
+
via ssh.
|
127
|
+
email:
|
128
|
+
- email@y13i.com
|
129
|
+
executables:
|
130
|
+
- ec2ctl
|
131
|
+
extensions: []
|
132
|
+
extra_rdoc_files: []
|
133
|
+
files:
|
134
|
+
- ".gitignore"
|
135
|
+
- Gemfile
|
136
|
+
- LICENSE.txt
|
137
|
+
- README.md
|
138
|
+
- Rakefile
|
139
|
+
- bin/ec2ctl
|
140
|
+
- ec2ctl.gemspec
|
141
|
+
- lib/ec2ctl.rb
|
142
|
+
- lib/ec2ctl/version.rb
|
143
|
+
homepage: https://github.com/y13i/ec2ctl
|
144
|
+
licenses:
|
145
|
+
- MIT
|
146
|
+
metadata: {}
|
147
|
+
post_install_message:
|
148
|
+
rdoc_options: []
|
149
|
+
require_paths:
|
150
|
+
- lib
|
151
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
152
|
+
requirements:
|
153
|
+
- - ">="
|
154
|
+
- !ruby/object:Gem::Version
|
155
|
+
version: '0'
|
156
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - ">="
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '0'
|
161
|
+
requirements: []
|
162
|
+
rubyforge_project:
|
163
|
+
rubygems_version: 2.2.2
|
164
|
+
signing_key:
|
165
|
+
specification_version: 4
|
166
|
+
summary: Yet another handy EC2 tools.
|
167
|
+
test_files: []
|