├── .gitignore
├── .ruby-gemset
├── .ruby-version
├── CHANGELOG.md
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── cap-ec2.gemspec
└── lib
└── cap-ec2
├── capistrano.rb
├── ec2-handler.rb
├── status-table.rb
├── tasks
└── ec2.rake
├── utils.rb
└── version.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | *.rbc
3 | .bundle
4 | .config
5 | .yardoc
6 | Gemfile.lock
7 | InstalledFiles
8 | _yardoc
9 | coverage
10 | doc/
11 | lib/bundler/man
12 | pkg
13 | rdoc
14 | spec/reports
15 | test/tmp
16 | test/version_tmp
17 | tmp
18 |
--------------------------------------------------------------------------------
/.ruby-gemset:
--------------------------------------------------------------------------------
1 | cap-ec2
2 |
--------------------------------------------------------------------------------
/.ruby-version:
--------------------------------------------------------------------------------
1 | 2.2.0
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Cap-EC2 changelog
2 |
3 | ## 1.1.2
4 |
5 | * Allow using aws-sdk v3 [@magnusvk](https://github.com/magnusvk)
6 | * Fix NoMethodError undefined method call for Hash [@ur5us](https://github.com/ur5us)
7 | * Allow tag value delimiter to be configurable [@erez-rabih](https://github.com/erez-rabih)
8 |
9 | ## 1.1.1
10 |
11 | Require aws-sdk v2 instead of v1
12 |
13 | * Require aws-sdk v2 instead of v1 [@hajder](https://github.com/hajder)
14 |
15 | ## 1.1.0
16 |
17 | Upgrade to AWS's v2 SDK
18 |
19 | * Upgrade to AWS's v2 SDK [@kylev](https://github.com/kylev)
20 |
21 | ## 1.0.0
22 |
23 | Cap-EC2 is pretty stable, and the rate of PRs has decreased, so I've
24 | decided to bump the version to 1.0.0.
25 |
26 | * Remove the require of `capistrano/setup`, so that people can make
27 | use of `capistrano-multiconfig`. [@ashleybrown](https://github.com/ashleybrown)
28 |
29 | ## 0.0.19
30 |
31 | * Stop using the `colored` gem, switch to `colorize` instead. [@freakphp][https://github.com/freakphp]
32 |
33 | ## 0.0.18
34 |
35 | * Update gemspec to explicitly use the AWS v1 SDK. [@Tomtomgo](https://github.com/Tomtomgo)
36 | * Fix available roles for newer Capistrano versions. [@AmirKremer](https://github.com/AmirKremer)
37 |
38 | ## 0.0.17
39 |
40 | * Provide access to EC2 server tags within Capistrano recipes [@eightbitraptor](https://github.com/eightbitraptor)
41 | * Fix sorting of servers when there is no Name tag [@johnf](https://github.com/johnf)
42 |
43 | ## 0.0.16
44 |
45 | * Don't colorize status table output if STDOUT is not a TTY. [@jcoglan](https://github.com/jcoglan)
46 |
47 | ## 0.0.15
48 |
49 | * Add `ec2_filter_by_status_ok?` to filter out instances that aren't returning `OK`
50 | for their EC2 status checks. [@tomconroy](https://github.com/tomconroy)
51 |
52 | ## 0.0.14
53 |
54 | * Fix issue when tag was present in EC2 but had no value. [@tomconroy](https://github.com/tomconroy)
55 |
56 | ## 0.0.13
57 |
58 | * Use AWS.memoize to speed up communication with AWS [@cheald](https://github.com/cheald)
59 |
60 | ## 0.0.12
61 |
62 | * Use the instance's named state for searching for instances, rather than the code [@ronny](https://github.com/ronny)
63 |
64 | ## 0.0.11
65 |
66 | * Allow instances to have multiple projects deployed to them. [@rsslldnphy](https://github.com/rsslldnphy)
67 | * Fix the way instance tag matching works; the previous regex was not sufficient to ensure
68 | absolute matching of a given tag. [@rsslldnphy](https://github.com/rsslldnphy)
69 |
70 | ## 0.0.10
71 |
72 | * Allow configurable setting of the EC2 contact point [@christianblunden](https://github.com/christianblunden)
73 |
74 | ## 0.0.9
75 |
76 | * Handle no configured regions, (or specifically nil).
77 |
78 | ## 0.0.8
79 |
80 | * Made `config/ec2.yml` optional, set all options by Capistrano variable. [@rjocoleman](https://github.com/rjocoleman)
81 | * Remove requirement for default region to be set. [@rjocoleman](https://github.com/rjocoleman)
82 |
83 | ## 0.0.7
84 |
85 | * Removed monkey patching of `Capistrano::TaskEnhancements` [@rjocoleman](https://github.com/rjocoleman)
86 | * Instances don't always have a name tag, would cause `ec2:status` to blow up [@rjocoleman](https://github.com/rjocoleman)
87 |
88 | ## 0.0.6
89 |
90 | * Unbreak listing instances
91 |
92 | ## 0.0.5
93 |
94 | * Don't return terminated instances when looking up instances from EC2
95 | * Fix documentation to refer to correct tag for Stages [@shaneog](https://github.com/shaneog)
96 |
97 | ## 0.0.4
98 |
99 | * If you modified any of the tag names, the `ec2:status` table would blow up
100 | * Fixed a bug with stages
101 |
102 | ## 0.0.3
103 |
104 | * Rename the default tag name used for determining to 'Stages' from 'Stage'
105 |
106 | ## 0.0.2
107 |
108 | * Allow servers to be in multiple stages
109 |
110 | ## 0.0.1
111 |
112 | * Initial release
113 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # Specify your gem's dependencies in capify-v3-ec2.gemspec
4 | gemspec
5 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Andy Sykes
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.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # This repository is no longer maintained.
2 |
3 | Forward3D has not used cap-ec2 in production for several years, and thus we will no longer be maintaining this.
4 |
5 | # Cap-EC2
6 |
7 | [](http://badge.fury.io/rb/cap-ec2) [](https://codeclimate.com/github/forward3d/cap-ec2)
8 |
9 | Cap-EC2 is used to generate Capistrano namespaces and tasks from Amazon EC2 instance tags,
10 | dynamically building the list of servers to be deployed to.
11 |
12 | ## Notes
13 |
14 | Cap-EC2 is only compatible with Capistrano 3.x or later; if you want the Capistrano 2.x version,
15 | use [Capify-EC2](https://github.com/forward/capify-ec2). Note that the configuration file (`config/ec2.yml`)
16 | is not compatible between versions either.
17 |
18 | This documentation assumes familiarity with Capistrano 3.x.
19 |
20 | A number of features that are in Capify-EC2 are not yet available in Cap-EC2, due to the
21 | architectural changes in Capistrano 3.x. The following features are missing (this is not
22 | an exhaustive list!):
23 | * rolling deploy (this should be implemented via [SSHKit](https://github.com/capistrano/sshkit))
24 | * ELB registration/de-registration (not widely used)
25 | * Variables set by EC2 tags
26 | * Connecting to instances via SSH using a convenience task
27 |
28 | Pull requests for these would be welcomed, as would sending feedback via the Issues on this project about
29 | features you would like.
30 |
31 | ## Installation
32 |
33 | gem install cap-ec2
34 |
35 | or add the gem to your project's Gemfile.
36 |
37 | You also need to add the gem to your Capfile:
38 |
39 | ```ruby
40 | require "cap-ec2/capistrano"
41 | ```
42 |
43 |
44 | ## Configuration
45 |
46 | Configurable options, shown here with defaults:
47 |
48 | ```ruby
49 | set :ec2_config, 'config/ec2.yml'
50 |
51 | set :ec2_project_tag, 'Project'
52 | set :ec2_roles_tag, 'Roles'
53 | set :ec2_stages_tag, 'Stages'
54 | set :ec2_tag_delimiter, ","
55 |
56 | set :ec2_profile, 'myservice' # use ~/.aws/credentials with profile_name
57 | set :ec2_access_key_id, nil
58 | set :ec2_secret_access_key, nil
59 | set :ec2_region, %w{} # REQUIRED
60 | set :ec2_contact_point, nil
61 |
62 | set :ec2_filter_by_status_ok?, nil
63 | ```
64 |
65 | #### Order of inheritance
66 |
67 | `cap-ec2` supports multiple methods of configuration. The order of inheritance is:
68 | YAML File > ~/.aws/credentials > User Capistrano Config > Default Capistrano Config > ENV variables.
69 |
70 | #### Regions
71 |
72 | `:ec2_region` is an array of
73 | [AWS regions](http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region)
74 | and is required. Only list regions which you wish to query for
75 | instances; extra values simply slow down queries.
76 |
77 | If `:ec2_access_key_id` or `:ec2_secret_access_key` are not set in any
78 | configuration the environment variables `AWS_ACCESS_KEY_ID`,
79 | `AWS_SECRET_ACCESS_KEY` and `AWS_REGION` will be checked and the
80 | default credential load order (including instance profiles
81 | credentials) will be honored.
82 |
83 | #### Misc settings
84 |
85 | * project_tag
86 |
87 | Cap-EC2 will look for a tag with this name when searching for instances that belong to this project.
88 | Cap-EC2 will look for a value which matches the :application setting in your deploy.rb.
89 | The tag name defaults to "Project" and must be set on your instances.
90 |
91 | * stages_tag
92 |
93 | Cap-EC2 will look for a tag with this name to determine which instances belong to
94 | a given stage. The tag name defaults to "Stages".
95 |
96 | * roles_tag
97 |
98 | Cap-EC2 will look for a tag with this name to determine which instances belong to
99 | a given role. The tag name defaults to "Roles".
100 |
101 | * tag_delimiter
102 |
103 | When Cap-EC2 reads a tag value, this will be the default delimiter.
104 | For example, for a Roles tag with web,db and tag_delimiter set to ,(comma)
105 | the server will have the web and db roles.
106 |
107 | * filter_by_status_ok?
108 |
109 | If this is set to `true`, then Cap-EC2 will not return instances which do not have both EC2 status
110 | checks as `OK`. By default this is set to `nil`, so Cap-EC2 can return you instances which don't have
111 | `OK` status checks. Be warned that just-launched instances take a while to start returning `OK`.
112 |
113 | ### YAML Configuration
114 |
115 |
116 | If you'd prefer do your configuration via a YAML file `config/ec2.yml` can be used, (or an alternative name/location set via `set :ec2_config`):
117 |
118 | If so YAML file will look like this:
119 |
120 | ```ruby
121 | access_key_id: "YOUR ACCESS KEY"
122 | secret_access_key: "YOUR SECRET KEY"
123 | regions:
124 | - 'eu-west-1'
125 | project_tag: "Project"
126 | roles_tag: "Roles"
127 | stages_tag: "Stages"
128 | ```
129 |
130 |
131 | Your `config/ec2.yml` file can contain (`access_key_id`, `secret_access_key`, `regions`) - if a value is omitted then the order of inheritance is followed.
132 |
133 |
134 | ## Usage
135 |
136 | Imagine you have four servers on EC2 named and tagged as follows:
137 |
138 |
139 |
140 | 'Name' Tag |
141 | 'Roles' Tag |
142 | 'Stages' Tag |
143 |
144 |
145 | server-1 |
146 | web |
147 | production |
148 |
149 |
150 | server-2 |
151 | web,app |
152 | production |
153 |
154 |
155 | server-3 |
156 | app,db |
157 | production |
158 |
159 |
160 | server-4 |
161 | web,db,app |
162 | staging |
163 |
164 |
165 |
166 | Imagine also that we've called our app "testapp", as defined in `config/deploy.rb` like so:
167 |
168 | set :application, "testapp"
169 |
170 | ### Defining the roles in `config/deploy/[stage].rb`
171 |
172 | To define a role, edit `config/deploy/[stage].rb` and add the following:
173 |
174 | ec2_role :web
175 |
176 | Let's say we edited `config/deploy/production.rb`. Adding this configuration to the file would assign
177 | the role `:web` to any instance that has the following properties:
178 | * has a tag called "Roles" that contains the string "web"
179 | * has a tag called "Project" that contains the string "testapp"
180 | * has a tag called "Stages" that contains the current stage we're executing (in this case, "production")
181 |
182 | Looking at the above table, we can see we would match `server-1` and `server-2`. (You can have multiple
183 | roles in tag separated by commas.)
184 |
185 | Now we can define the other roles:
186 |
187 | ec2_role :app
188 | ec2_role :db
189 |
190 | In the "production" stage, the `:app` role would apply to `server-2` and `server-3`, and the `:db`
191 | role would apply to `server-3`.
192 |
193 | In the "staging" stage, all roles would apply *only* to `server-4`.
194 |
195 | ### Servers belonging to multiple projects
196 |
197 | If you require your servers to have multiple projects deployed to them, you can simply specify
198 | all the project names you want to the server to be part of in the 'Projects' tag, separated
199 | by commas. For example, you could place a server in the `testapp` and `myapp` projects by
200 | setting the 'Projects' tag to `testapp,myapp`.
201 |
202 | ### Servers in multiple stages
203 |
204 | If your use-case requires servers to be in multiple stages, simply specify all the stages you want
205 | the server to be in 'Stages' tag, separated by commas. For example, you could place a server in
206 | the `production` and `staging` stages by setting the 'Stages' tag to `production,staging`.
207 |
208 | ### Passing options to roles
209 |
210 | You can pass options when defining your roles. The options are *exactly* the same as the options that
211 | the Capistrano native `role` definition takes, since they are passed straight through to Capistrano.
212 | For example:
213 |
214 | ec2_role :app,
215 | user: 'user_name',
216 | ssh_options: {
217 | user: 'user_name', # overrides user setting above
218 | keys: %w(/home/user_name/.ssh/id_rsa),
219 | forward_agent: false,
220 | auth_methods: %w(publickey password)
221 | password: 'please use keys'
222 | }
223 |
224 | See the example config files Capistrano builds for you in `config/deploy` for more details.
225 |
226 | Note that at the moment there's no way to pass variables in from EC2 tags - but it would be
227 | trivial to add.
228 |
229 | ### Tasks and deployment
230 |
231 | You can now define your tasks for these roles in exactly the same way as you would if you weren't
232 | using this gem.
233 |
234 | ### Contacting instances
235 |
236 | By default, Cap-EC2 will attempt to communicate with the EC2 instance using the following instance
237 | interfaces in order:
238 |
239 | 1. Public DNS (`:public_dns`)
240 | 2. Public IP (`:public_ip`)
241 | 3. Private IP (`:private_ip`)
242 |
243 | This can be configured using the Capistrano variable `:ec2_contact_point`, and supplying one
244 | of the above symbols. For example:
245 |
246 | ```ruby
247 | set :ec2_contact_point, :private_ip
248 | ```
249 |
250 | This would cause Cap-EC2 to try communicating with the instance on its private IP address. If you leave
251 | this variable unset, the behaviour is as in previous Cap-EC2 instances (falling through the lookup list
252 | as specified above).
253 |
254 | ## Utility tasks
255 |
256 | Cap-EC2 adds a few utility tasks to Capistrano for displaying information about the instances that
257 | you will be deploying to. Note that unlike Capistrano 2.x, all tasks *require* a stage.
258 |
259 | ### View instances
260 |
261 | This command will show you information all the instances your configuration matches for a given stage.
262 |
263 | cap [stage] ec2:status
264 |
265 | Example:
266 |
267 | $ cap production ec2:status
268 |
269 | Num Name ID Type DNS Zone Roles Stage
270 | 00: server-1-20131030-1144-0 i-abcdefgh m1.small 192.168.202.248 us-west-2c banana,apple production
271 | 01: server-2-20131118-1839-0 i-hgfedcba m1.small 192.168.200.60 us-west-2a banana production
272 |
273 | ### View server names
274 |
275 | This command will show you the server names of the instances matching the given stage:
276 |
277 | cap [stage] ec2:server_names
278 |
279 | Example:
280 |
281 | $ cap production ec2:server_names
282 | server-1-20131030-1144-0
283 | server-2-20131118-1839-0
284 |
285 | ### View server instance IDs
286 |
287 | This command will show the instance IDs of the instances matching the given stage:
288 |
289 | cap [stage] ec2:instance_ids
290 |
291 | Example:
292 |
293 | $ cap production ec2:instance_ids
294 | i-abcdefgh
295 | i-hgfedcba
296 |
297 | ## Acknowledgements
298 |
299 | Thanks to [Rylon](https://github.com/Rylon) for maintaining Capify-EC2 and
300 | reviewing my thought processes for this project.
301 |
302 | ## Contributing
303 |
304 | 1. Fork it
305 | 2. Create your feature branch (`git checkout -b my-new-feature`)
306 | 3. Commit your changes (`git commit -am 'Add some feature'`)
307 | 4. Push to the branch (`git push origin my-new-feature`)
308 | 5. Create new Pull Request
309 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "bundler/gem_tasks"
2 |
--------------------------------------------------------------------------------
/cap-ec2.gemspec:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | lib = File.expand_path('../lib', __FILE__)
3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4 | require 'cap-ec2/version'
5 |
6 | Gem::Specification.new do |spec|
7 | spec.name = "cap-ec2"
8 | spec.version = CapEC2::VERSION
9 | spec.authors = ["Andy Sykes", "Robert Coleman", "Forward3D Developers"]
10 | spec.email = ["github@tinycat.co.uk", "github@robert.net.nz", "developers@forward3d.com"]
11 | spec.description = %q{Cap-EC2 is used to generate Capistrano namespaces and tasks from Amazon EC2 instance tags, dynamically building the list of servers to be deployed to.}
12 | spec.summary = %q{Cap-EC2 is used to generate Capistrano namespaces and tasks from Amazon EC2 instance tags, dynamically building the list of servers to be deployed to.}
13 | spec.homepage = "https://github.com/forward3d/cap-ec2"
14 | spec.license = "MIT"
15 |
16 | spec.files = `git ls-files`.split($/)
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_development_dependency "bundler", "~> 1.3"
22 | spec.add_development_dependency "rake"
23 |
24 | spec.add_dependency "aws-sdk", ">= 2.0"
25 | spec.add_dependency "capistrano", ">= 3.0"
26 | spec.add_dependency "terminal-table"
27 | spec.add_dependency "colorize"
28 | end
29 |
--------------------------------------------------------------------------------
/lib/cap-ec2/capistrano.rb:
--------------------------------------------------------------------------------
1 | require 'capistrano/configuration'
2 | require 'aws-sdk'
3 | require 'colorize'
4 | require 'terminal-table'
5 | require 'yaml'
6 | require_relative 'utils'
7 | require_relative 'ec2-handler'
8 | require_relative 'status-table'
9 |
10 | # Load extra tasks
11 | load File.expand_path("../tasks/ec2.rake", __FILE__)
12 |
13 | module Capistrano
14 | module DSL
15 | module Ec2
16 |
17 | def ec2_handler
18 | @ec2_handler ||= CapEC2::EC2Handler.new
19 | end
20 |
21 | def ec2_role(name, options={})
22 | ec2_handler.get_servers_for_role(name).each do |server|
23 | env.role(name, CapEC2::Utils.contact_point(server),
24 | options_with_instance_id(options, server))
25 | end
26 | end
27 |
28 | def env
29 | Configuration.env
30 | end
31 |
32 | private
33 |
34 | def options_with_instance_id(options, server)
35 | options.merge({aws_instance_id: server.instance_id})
36 | end
37 |
38 | end
39 | end
40 | end
41 |
42 | self.extend Capistrano::DSL::Ec2
43 |
44 | Capistrano::Configuration::Server.send(:include, CapEC2::Utils::Server)
45 |
--------------------------------------------------------------------------------
/lib/cap-ec2/ec2-handler.rb:
--------------------------------------------------------------------------------
1 | require 'aws-sdk'
2 |
3 | module CapEC2
4 | class EC2Handler
5 | include CapEC2::Utils
6 |
7 | def initialize
8 | load_config
9 | configured_regions = get_regions(fetch(:ec2_region))
10 | @ec2 = {}
11 | configured_regions.each do |region|
12 | @ec2[region] = ec2_connect(region)
13 | end
14 | end
15 |
16 | def ec2_connect(region=nil)
17 | Aws::EC2::Client.new(
18 | access_key_id: fetch(:ec2_access_key_id),
19 | secret_access_key: fetch(:ec2_secret_access_key),
20 | region: region
21 | )
22 | end
23 |
24 | def status_table
25 | CapEC2::StatusTable.new(
26 | defined_roles.map {|r| get_servers_for_role(r)}.flatten.uniq {|i| i.instance_id}
27 | )
28 | end
29 |
30 | def server_names
31 | puts defined_roles.map {|r| get_servers_for_role(r)}
32 | .flatten
33 | .uniq {|i| i.instance_id}
34 | .map {|i| tag_value(i, 'Name')}
35 | .join("\n")
36 | end
37 |
38 | def instance_ids
39 | puts defined_roles.map {|r| get_servers_for_role(r)}
40 | .flatten
41 | .uniq {|i| i.instance_id}
42 | .map {|i| i.instance_id}
43 | .join("\n")
44 | end
45 |
46 | def defined_roles
47 | roles(:all).flat_map(&:roles_array).uniq.sort
48 | end
49 |
50 | def stage
51 | Capistrano::Configuration.env.fetch(:stage).to_s
52 | end
53 |
54 | def application
55 | Capistrano::Configuration.env.fetch(:application).to_s
56 | end
57 |
58 | def tag(tag_name)
59 | "tag:#{tag_name}"
60 | end
61 |
62 | def get_servers_for_role(role)
63 | filters = [
64 | {name: 'tag-key', values: [stages_tag, project_tag]},
65 | {name: tag(project_tag), values: ["*#{application}*"]},
66 | {name: 'instance-state-name', values: %w(running)}
67 | ]
68 |
69 | servers = []
70 | @ec2.each do |_, ec2|
71 | ec2.describe_instances(filters: filters).reservations.each do |r|
72 | servers += r.instances.select do |i|
73 | instance_has_tag?(i, roles_tag, role) &&
74 | instance_has_tag?(i, stages_tag, stage) &&
75 | instance_has_tag?(i, project_tag, application) &&
76 | (fetch(:ec2_filter_by_status_ok?) ? instance_status_ok?(i) : true)
77 | end
78 | end
79 | end
80 |
81 | servers.sort_by { |s| tag_value(s, 'Name') || '' }
82 | end
83 |
84 | def get_server(instance_id)
85 | @ec2.reduce([]) do |acc, (_, ec2)|
86 | acc << ec2.instances[instance_id]
87 | end.flatten.first
88 | end
89 |
90 | private
91 |
92 | def instance_has_tag?(instance, key, value)
93 | (tag_value(instance, key) || '').split(tag_delimiter).map(&:strip).include?(value.to_s)
94 | end
95 |
96 | def instance_status_ok?(instance)
97 | @ec2.any? do |_, ec2|
98 | ec2.describe_instance_status(
99 | instance_ids: [instance.instance_id],
100 | filters: [{ name: 'instance-status.status', values: %w(ok) }]
101 | ).instance_statuses.length == 1
102 | end
103 | end
104 | end
105 | end
106 |
--------------------------------------------------------------------------------
/lib/cap-ec2/status-table.rb:
--------------------------------------------------------------------------------
1 | module CapEC2
2 | class StatusTable
3 | include CapEC2::Utils
4 |
5 | def initialize(instances)
6 | @instances = instances
7 | output
8 | end
9 |
10 | def header_row
11 | [
12 | bold("Num"),
13 | bold("Name"),
14 | bold("ID"),
15 | bold("Type"),
16 | bold("DNS"),
17 | bold("Zone"),
18 | bold("Roles"),
19 | bold("Stages")
20 | ]
21 | end
22 |
23 | def output
24 | table = Terminal::Table.new(
25 | :style => {
26 | :border_x => "",
27 | :border_i => "",
28 | :border_y => ""
29 | }
30 | )
31 | table.add_row header_row
32 | @instances.each_with_index do |instance,index|
33 | table.add_row instance_to_row(instance, index)
34 | end
35 | puts table.to_s
36 | end
37 |
38 | def instance_to_row(instance, index)
39 | [
40 | sprintf("%02d:", index),
41 | green(tag_value(instance, 'Name') || ''),
42 | red(instance.instance_id),
43 | cyan(instance.instance_type),
44 | bold(blue(CapEC2::Utils.contact_point(instance))),
45 | magenta(instance.placement.availability_zone),
46 | yellow(tag_value(instance, roles_tag)),
47 | yellow(tag_value(instance, stages_tag))
48 | ]
49 | end
50 |
51 | private
52 |
53 | (String.colors + String.modes).each do |format|
54 | define_method(format) do |string|
55 | if $stdout.tty?
56 | string.__send__(format)
57 | else
58 | string
59 | end
60 | end
61 | end
62 |
63 | end
64 | end
65 |
--------------------------------------------------------------------------------
/lib/cap-ec2/tasks/ec2.rake:
--------------------------------------------------------------------------------
1 | namespace :ec2 do
2 |
3 | desc "Show all information about EC2 instances that match this project"
4 | task :status do
5 | ec2_handler.status_table
6 | end
7 |
8 | desc "Show EC2 server names that match this project"
9 | task :server_names do
10 | ec2_handler.server_names
11 | end
12 |
13 | desc "Show EC2 instance IDs that match this project"
14 | task :instance_ids do
15 | ec2_handler.instance_ids
16 | end
17 |
18 | end
19 |
20 |
21 | namespace :load do
22 | task :defaults do
23 |
24 | set :ec2_config, 'config/ec2.yml'
25 |
26 | set :ec2_project_tag, 'Project'
27 | set :ec2_roles_tag, 'Roles'
28 | set :ec2_stages_tag, 'Stages'
29 | set :ec2_tag_delimiter, ","
30 |
31 | set :ec2_access_key_id, nil
32 | set :ec2_secret_access_key, nil
33 | set :ec2_region, %w{}
34 |
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/lib/cap-ec2/utils.rb:
--------------------------------------------------------------------------------
1 | require 'aws-sdk'
2 |
3 | module CapEC2
4 | module Utils
5 |
6 | module Server
7 | def ec2_tags
8 | id = self.properties.fetch(:aws_instance_id)
9 | ec2_handler.get_server(id).tags
10 | end
11 | end
12 |
13 | def project_tag
14 | fetch(:ec2_project_tag)
15 | end
16 |
17 | def roles_tag
18 | fetch(:ec2_roles_tag)
19 | end
20 |
21 | def stages_tag
22 | fetch(:ec2_stages_tag)
23 | end
24 |
25 | def tag_delimiter
26 | fetch(:ec2_tag_delimiter)
27 | end
28 |
29 | def tag_value(instance, key)
30 | instance.tags.find(-> { {} }) { |t| t[:key] == key.to_s }[:value]
31 | end
32 |
33 | def self.contact_point_mapping
34 | {
35 | :public_dns => :public_dns_name,
36 | :public_ip => :public_ip_address,
37 | :private_ip => :private_ip_address
38 | }
39 | end
40 |
41 | def self.contact_point(instance)
42 | ec2_interface = contact_point_mapping[fetch(:ec2_contact_point)]
43 | return instance.send(ec2_interface) if ec2_interface
44 |
45 | instance.public_dns_name || instance.public_ip_address || instance.private_ip_address
46 | end
47 |
48 | def load_config
49 | if fetch(:ec2_profile)
50 | credentials = Aws::SharedCredentials.new(profile_name: fetch(:ec2_profile)).credentials
51 | if credentials
52 | set :ec2_access_key_id, credentials.access_key_id
53 | set :ec2_secret_access_key, credentials.secret_access_key
54 | end
55 | end
56 |
57 | config_location = File.expand_path(fetch(:ec2_config), Dir.pwd) if fetch(:ec2_config)
58 | if config_location && File.exists?(config_location)
59 | config = YAML.load(ERB.new(File.read(fetch(:ec2_config))))
60 | if config
61 | set :ec2_project_tag, config['project_tag'] if config['project_tag']
62 | set :ec2_roles_tag, config['roles_tag'] if config['roles_tag']
63 | set :ec2_stages_tag, config['stages_tag'] if config['stages_tag']
64 | set :ec2_tag_delimiter, config['tag_delimiter'] if config['tag_delimiter']
65 |
66 | set :ec2_access_key_id, config['access_key_id'] if config['access_key_id']
67 | set :ec2_secret_access_key, config['secret_access_key'] if config['secret_access_key']
68 | set :ec2_region, config['regions'] if config['regions']
69 |
70 | set :ec2_filter_by_status_ok?, config['filter_by_status_ok?'] if config['filter_by_status_ok?']
71 | end
72 | end
73 | end
74 |
75 | def get_regions(regions_array=nil)
76 | unless regions_array.nil? || regions_array.empty?
77 | return regions_array
78 | else
79 | fail "You must specify at least one EC2 region."
80 | end
81 | end
82 |
83 | end
84 | end
85 |
--------------------------------------------------------------------------------
/lib/cap-ec2/version.rb:
--------------------------------------------------------------------------------
1 | module CapEC2
2 | VERSION = '1.1.2'
3 | end
4 |
--------------------------------------------------------------------------------