cloudutil 0.0.1
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 +41 -0
- data/.rubocop.yml +24 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +38 -0
- data/Rakefile +29 -0
- data/cloudutil.gemspec +27 -0
- data/lib/cloudutil/aws/core.rb +92 -0
- data/lib/cloudutil/aws/ec2.rb +203 -0
- data/lib/cloudutil/aws.rb +1 -0
- data/lib/cloudutil/version.rb +4 -0
- data/lib/cloudutil.rb +2 -0
- data/spec/aws_core_spec.rb +107 -0
- data/spec/aws_ec2_spec.rb +170 -0
- metadata +169 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1409a2b9cdb13ae5236b4d4f70fb4eec0b16f47d
|
4
|
+
data.tar.gz: 6ae2a2a2554cc5af1f2ea34e394a89deea8112f3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f087f0aa8a8ff7202e2bbf8016d6f653e21b617210b72abdfeb8bc2fdbc3f10346f4a2393136c1ee9a091317904e0f1236c8bbab117cc570f0e1b29c245262ad
|
7
|
+
data.tar.gz: 5f83cf1c8de01886765863a7ca87693ccd644d0c4ed9962a03093a85f066fc149386795994ba156a4cb8d7bd8b27bf96c7edcf94b1e0775e39b2ff893dfb974a
|
data/.gitignore
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/test/tmp/
|
9
|
+
/test/version_tmp/
|
10
|
+
/tmp/
|
11
|
+
|
12
|
+
## Specific to RubyMotion:
|
13
|
+
.dat*
|
14
|
+
.repl_history
|
15
|
+
build/
|
16
|
+
|
17
|
+
## Documentation cache and generated files:
|
18
|
+
/.yardoc/
|
19
|
+
/_yardoc/
|
20
|
+
/doc/
|
21
|
+
/rdoc/
|
22
|
+
|
23
|
+
## Environment normalisation:
|
24
|
+
/.bundle/
|
25
|
+
/lib/bundler/man/
|
26
|
+
|
27
|
+
# for a library or gem, you might want to ignore these files since the code is
|
28
|
+
# intended to run in multiple environments; otherwise, check them in:
|
29
|
+
# Gemfile.lock
|
30
|
+
# .ruby-version
|
31
|
+
# .ruby-gemset
|
32
|
+
|
33
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
34
|
+
.rvmrc
|
35
|
+
|
36
|
+
Gemfile.lock
|
37
|
+
*.bundle
|
38
|
+
*.so
|
39
|
+
*.o
|
40
|
+
*.a
|
41
|
+
mkmf.log
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Encoding:
|
2
|
+
Enabled: false
|
3
|
+
|
4
|
+
NumericLiterals:
|
5
|
+
Enabled: false
|
6
|
+
|
7
|
+
ClassLength:
|
8
|
+
Max: 200
|
9
|
+
|
10
|
+
MethodLength:
|
11
|
+
Max: 25
|
12
|
+
|
13
|
+
LineLength:
|
14
|
+
Max: 128
|
15
|
+
|
16
|
+
HashSyntax:
|
17
|
+
Exclude:
|
18
|
+
- Rakefile
|
19
|
+
|
20
|
+
CyclomaticComplexity:
|
21
|
+
Max: 10
|
22
|
+
|
23
|
+
PerceivedComplexity:
|
24
|
+
Max: 10
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Mike Morris
|
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
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# Cloudutil
|
2
|
+
|
3
|
+
A utility library for performing helpful tasks with various cloud platform providers
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'cloudutil'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle install
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install cloudutil
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
For AWS helpers:
|
22
|
+
|
23
|
+
require 'cloudutil/aws'
|
24
|
+
|
25
|
+
aws = Cloudutil::AWS(config: existing_AWS::Config_object) -or-
|
26
|
+
aws = Cloudutil::AWS(access_key: your_AWS_access_key, secret_key: your_AWS_secret_key)
|
27
|
+
|
28
|
+
subnet_id = aws.resolve_subnet_id('my_subnet_name_or_tag')
|
29
|
+
sec_grp_id = aws.resolve_subnet_id('my_security_group_name_or_tag')
|
30
|
+
ami_id = aws.resolve_subnet_id('my_ami_name_or_tag')
|
31
|
+
|
32
|
+
## Contributing
|
33
|
+
|
34
|
+
1. Fork it ( https://github.com/mmmorris1975/cloudutil/fork )
|
35
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
36
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
37
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
38
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
|
4
|
+
gem_dir = ::File.dirname(__FILE__)
|
5
|
+
|
6
|
+
desc 'Rubocop'
|
7
|
+
task :rc => [:rubocop]
|
8
|
+
task :rubocop do
|
9
|
+
sh "bundle exec rubocop -D #{gem_dir}"
|
10
|
+
end
|
11
|
+
|
12
|
+
task default: [:rc] do
|
13
|
+
Rake::Task[:spec].invoke('progress', %w(~slow ~account_specific))
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Run rspec'
|
17
|
+
RSpec::Core::RakeTask.new(:spec, [:format, :tags]) do |t, args|
|
18
|
+
format = args[:format] || 'documentation'
|
19
|
+
tags = args[:tags] || []
|
20
|
+
t.verbose = false
|
21
|
+
t.fail_on_error = true
|
22
|
+
t.rspec_opts = "--format=#{format}"
|
23
|
+
|
24
|
+
tags.flatten.compact.each do |tag|
|
25
|
+
t.rspec_opts += " --tag #{tag}"
|
26
|
+
end
|
27
|
+
|
28
|
+
t.ruby_opts = '-W0'
|
29
|
+
end
|
data/cloudutil.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cloudutil/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'cloudutil'
|
8
|
+
spec.version = Cloudutil::VERSION
|
9
|
+
spec.authors = ['Mike Morris']
|
10
|
+
spec.email = ['michael.m.morris@pearson.com']
|
11
|
+
spec.summary = 'A utility library for performing helpful tasks with various cloud platform providers'
|
12
|
+
spec.description = IO.read(File.join(File.dirname(__FILE__), 'README.md'))
|
13
|
+
spec.homepage = ''
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(/^(test|spec|features)\//)
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
spec.add_development_dependency 'rspec'
|
24
|
+
spec.add_development_dependency 'rubocop'
|
25
|
+
|
26
|
+
spec.add_dependency 'aws-sdk', '~> 1.44'
|
27
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
|
3
|
+
module Cloudutil
|
4
|
+
module AWS
|
5
|
+
# Class containing common methods and attributes for interfacing with AWS
|
6
|
+
class Core
|
7
|
+
DEFAULT_REGION = 'us-east-1'
|
8
|
+
|
9
|
+
attr_writer :region
|
10
|
+
attr_writer :access_key
|
11
|
+
attr_writer :secret_key
|
12
|
+
|
13
|
+
# Initialize an instance of this class. Will accept options as a hash, or an array,
|
14
|
+
# with hash form being preferred. There are no required parameters for this method.
|
15
|
+
#
|
16
|
+
# Array args must be in the order of: AWS region, AWS access key, AWS secret key.
|
17
|
+
#
|
18
|
+
# Supported hash option keys are:
|
19
|
+
# - config (an pre-built AWS::config object to use for API calls)
|
20
|
+
# - region (the AWS region to make API calls to)
|
21
|
+
# - access_key (the AWS access key to use for API authentication)
|
22
|
+
# - secret_key (the AWS secret key to use for API authentication)
|
23
|
+
#
|
24
|
+
# If no AWS::config object was passed, this class's config method will automatically
|
25
|
+
# be called to create one
|
26
|
+
#
|
27
|
+
def initialize(*args)
|
28
|
+
if args.respond_to? :to_h
|
29
|
+
_process_hash_init(args.to_h)
|
30
|
+
elsif args[0].respond_to? :to_h
|
31
|
+
_process_hash_init(args[0].to_h)
|
32
|
+
else
|
33
|
+
_process_array_init(args.flatten)
|
34
|
+
end
|
35
|
+
|
36
|
+
config(true) if @config.nil?
|
37
|
+
end
|
38
|
+
|
39
|
+
# Getter for the 'region' attribute, uses DEFAULT_REGION
|
40
|
+
# if not explicitly set
|
41
|
+
def region
|
42
|
+
@region = DEFAULT_REGION if !@region || @region.length < 1
|
43
|
+
@region
|
44
|
+
end
|
45
|
+
|
46
|
+
# Getter for the 'access_key' attribute, will use values of the
|
47
|
+
# AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY env vars, if not explicitly set
|
48
|
+
def access_key
|
49
|
+
if !@access_key || @access_key.length < 1
|
50
|
+
@access_key = ENV['AWS_ACCESS_KEY_ID'] || ENV['AWS_ACCESS_KEY'] || nil
|
51
|
+
end
|
52
|
+
|
53
|
+
@access_key
|
54
|
+
end
|
55
|
+
|
56
|
+
# Getter for the 'secret_key' attribute, will use values of the
|
57
|
+
# AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY env vars, if not explicitly set
|
58
|
+
def secret_key
|
59
|
+
if !@secret_key || @secret_key.length < 1
|
60
|
+
@secret_key = ENV['AWS_SECRET_ACCESS_KEY'] || ENV['AWS_SECRET_KEY'] || nil
|
61
|
+
end
|
62
|
+
|
63
|
+
@secret_key
|
64
|
+
end
|
65
|
+
|
66
|
+
# Create (if not already existing) and return an AWS::config object.
|
67
|
+
# If creating a new AWS::config object, will make calls to this class's
|
68
|
+
# region, access_key, and secret_key methods to populate the config data
|
69
|
+
def config(reconfig = false)
|
70
|
+
if !@config || reconfig
|
71
|
+
@config = ::AWS.config(region: region, access_key_id: access_key, secret_access_key: secret_key)
|
72
|
+
end
|
73
|
+
|
74
|
+
@config
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def _process_hash_init(args)
|
80
|
+
return unless args
|
81
|
+
@config = args[:config]
|
82
|
+
@region = args[:region]
|
83
|
+
@access_key = args[:access_key]
|
84
|
+
@secret_key = args[:secret_key]
|
85
|
+
end
|
86
|
+
|
87
|
+
def _process_array_init(args)
|
88
|
+
_process_hash_init(region: args[0], access_key: args[1], secret_key: args[2]) if args
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
require_relative './core'
|
2
|
+
|
3
|
+
module Cloudutil
|
4
|
+
module AWS
|
5
|
+
# A class of utility methods for the AWS EC2 service
|
6
|
+
class EC2 < Cloudutil::AWS::Core
|
7
|
+
# Resolve the provided value to an AWS subnet id. By default, the search is not case-sensitive,
|
8
|
+
# but that can be overridden by supplying 'false' as a second argument to this method.
|
9
|
+
# Will return the 1st value in a set, if multiple values returned.
|
10
|
+
def resolve_subnet_id(subnet, ignore_case = true)
|
11
|
+
obj = lookup_subnet(subnet, ignore_case).first
|
12
|
+
return obj.subnet_id unless obj.nil?
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
|
16
|
+
# Resolve the provided value to an array of AWS subnet objects. By default, the search is not
|
17
|
+
# case-sensitive, but that can be overridden by supplying 'false' as a second argument to this method.
|
18
|
+
def lookup_subnet(subnet, ignore_case = true, nap = 2, retries = 5)
|
19
|
+
ec2 = ::AWS::EC2.new
|
20
|
+
subnets = []
|
21
|
+
|
22
|
+
begin
|
23
|
+
if aws_subnet_id? subnet
|
24
|
+
subnets << ec2.subnets[subnet]
|
25
|
+
else
|
26
|
+
subnets = do_subnet_lookup(ec2.subnets.filter('state', 'available'), subnet, ignore_case)
|
27
|
+
end
|
28
|
+
rescue ::AWS::EC2::Errors::RequestLimitExceeded, ::AWS::Errors::ServerError
|
29
|
+
raise if retries < 1
|
30
|
+
sleep nap
|
31
|
+
lookup_subnet(subnet, ignore_case, (nap * 2), (retries - 1))
|
32
|
+
end
|
33
|
+
|
34
|
+
subnets
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns true if the supplied value looks like an AWS subnet id
|
38
|
+
def aws_subnet_id?(subnet)
|
39
|
+
return true if subnet.match(/^subnet-\h{8,}$/)
|
40
|
+
false
|
41
|
+
end
|
42
|
+
|
43
|
+
# Resolve the provided value to an AWS sec group id. By default, the search is not case-sensitive,
|
44
|
+
# but that can be overridden by supplying 'false' as a second argument to this method.
|
45
|
+
# Will return the 1st value in a set, if multiple values returned.
|
46
|
+
def resolve_security_group_id(group, ignore_case = true)
|
47
|
+
obj = lookup_security_group(group, ignore_case).first
|
48
|
+
return obj.security_group_id unless obj.nil?
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
# Resolve the provided value to an array of AWS sec group objects. By default, the search is not
|
53
|
+
# case-sensitive, but that can be overridden by supplying 'false' as a second argument to this method.
|
54
|
+
def lookup_security_group(group, ignore_case = true, nap = 2, retries = 5)
|
55
|
+
ec2 = ::AWS::EC2.new
|
56
|
+
groups = []
|
57
|
+
|
58
|
+
begin
|
59
|
+
if aws_security_group_id? group
|
60
|
+
groups << ec2.security_groups[group]
|
61
|
+
else
|
62
|
+
groups = do_security_group_lookup_by_name(ec2, group, ignore_case)
|
63
|
+
groups = do_security_group_lookup_by_name_tag(ec2, group, ignore_case) if groups.nil? || groups.empty?
|
64
|
+
end
|
65
|
+
rescue ::AWS::EC2::Errors::RequestLimitExceeded, ::AWS::Errors::ServerError
|
66
|
+
raise if retries < 1
|
67
|
+
sleep nap
|
68
|
+
lookup_security_group(group, ignore_case, (nap * 2), (retries - 1))
|
69
|
+
end
|
70
|
+
|
71
|
+
groups
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns true if the supplied value looks like an AWS security group id
|
75
|
+
def aws_security_group_id?(group)
|
76
|
+
return true if group.match(/^sg-\h{8,}$/)
|
77
|
+
false
|
78
|
+
end
|
79
|
+
|
80
|
+
# Resolve the provided value to an AWS AMI id. By default, the search is not case-sensitive,
|
81
|
+
# but that can be overridden by supplying 'false' as a second argument to this method.
|
82
|
+
# Will return the 1st value in a set, if multiple values returned.
|
83
|
+
def resolve_ami_id(ami, ignore_case = true)
|
84
|
+
obj = lookup_ami(ami, ignore_case).first
|
85
|
+
return obj.image_id unless obj.nil?
|
86
|
+
nil
|
87
|
+
end
|
88
|
+
|
89
|
+
# Resolve the provided value to an array of AWS AMI objects. By default, the search is not
|
90
|
+
# case-sensitive, but that can be overridden by supplying 'false' as a second argument to this method.
|
91
|
+
def lookup_ami(ami, ignore_case = true, nap = 2, retries = 5)
|
92
|
+
ec2 = ::AWS::EC2.new
|
93
|
+
amis = []
|
94
|
+
|
95
|
+
begin
|
96
|
+
if aws_ami_id? ami
|
97
|
+
amis << ec2.images[ami]
|
98
|
+
else
|
99
|
+
# Don't bother memoizing, this is fast
|
100
|
+
imgs = ec2.images.filter('image-type', 'machine').filter('state', 'available')
|
101
|
+
|
102
|
+
amis = do_ami_lookup_by_name_tag(imgs, ami, ignore_case)
|
103
|
+
amis = do_private_ami_lookup_by_name(imgs, ami, ignore_case) if amis.nil? || amis.empty?
|
104
|
+
amis = do_ami_lookup_by_name(imgs, ami, ignore_case) if amis.nil? || amis.empty?
|
105
|
+
end
|
106
|
+
rescue ::AWS::EC2::Errors::RequestLimitExceeded, ::AWS::Errors::ServerError
|
107
|
+
raise if retries < 1
|
108
|
+
sleep nap
|
109
|
+
lookup_ami(ami, ignore_case, (nap * 2), (retries - 1))
|
110
|
+
end
|
111
|
+
|
112
|
+
amis
|
113
|
+
end
|
114
|
+
|
115
|
+
# Returns true if the supplied value looks like an AWS AMI id
|
116
|
+
def aws_ami_id?(ami)
|
117
|
+
return true if ami.match(/^ami-\h{8,}$/)
|
118
|
+
false
|
119
|
+
end
|
120
|
+
|
121
|
+
protected
|
122
|
+
|
123
|
+
# Find an AMI based on the value of the 'Name' tag
|
124
|
+
def do_ami_lookup_by_name_tag(filter, ami, ignore_case)
|
125
|
+
# Using memoization, this is fast, relativly speaking (can be < 1 sec, 15-20 sec w/o memoization)
|
126
|
+
::AWS.memoize do
|
127
|
+
if ignore_case
|
128
|
+
filter.tagged('Name')
|
129
|
+
.select { |obj| !obj.tags.Name.nil? && obj.tags.Name.casecmp(ami) == 0 }
|
130
|
+
else
|
131
|
+
filter.tagged('Name')
|
132
|
+
.select { |obj| !obj.tags.Name.nil? && obj.tags.Name.eql?(ami) }
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Find a non-public AMI based on the value of the AMI's name property
|
138
|
+
def do_private_ami_lookup_by_name(filter, ami, ignore_case)
|
139
|
+
# Do lookup of non-public AMIs we have access to. This should be a really short list,
|
140
|
+
# so it should come back pretty fast
|
141
|
+
::AWS.memoize do
|
142
|
+
if ignore_case
|
143
|
+
filter.filter('is-public', 'false')
|
144
|
+
.select { |obj| !obj.name.nil? && obj.name.casecmp(ami) == 0 }
|
145
|
+
else
|
146
|
+
filter.filter('is-public', 'false')
|
147
|
+
.select { |obj| !obj.name.nil? && obj.name.eql?(ami) }
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Find an AMI based on the value of the AMI's name property
|
153
|
+
def do_ami_lookup_by_name(filter, ami, ignore_case)
|
154
|
+
# This is an absolute pig of a lookup (30+ sec to return), that's why we do it after the faster tag
|
155
|
+
# and private AMI lookups. Otherwise this code ends up inspecting all 31k+ public AMIs on AWS
|
156
|
+
::AWS.memoize do
|
157
|
+
if ignore_case
|
158
|
+
filter.select { |obj| !obj.name.nil? && obj.name.casecmp(ami) == 0 }
|
159
|
+
else
|
160
|
+
filter.select { |obj| !obj.name.nil? && obj.name.eql?(ami) }
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# Find a VPC subnet based on the 'Name' tag (subnets don't have a name attribute)
|
166
|
+
def do_subnet_lookup(filter, subnet, ignore_case)
|
167
|
+
::AWS.memoize do
|
168
|
+
if ignore_case
|
169
|
+
filter.tagged('Name')
|
170
|
+
.select { |obj| !obj.tags.Name.nil? && obj.tags.Name.casecmp(subnet) == 0 }
|
171
|
+
else
|
172
|
+
filter.tagged('Name')
|
173
|
+
.select { |obj| !obj.tags.Name.nil? && obj.tags.Name.eql?(subnet) }
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Find a VPC security group based on the name attribute
|
179
|
+
def do_security_group_lookup_by_name(ec2, group, ignore_case)
|
180
|
+
::AWS.memoize do
|
181
|
+
if ignore_case
|
182
|
+
ec2.security_groups.select { |obj| !obj.name.nil? && obj.name.casecmp(group) == 0 }
|
183
|
+
else
|
184
|
+
ec2.security_groups.select { |obj| !obj.name.nil? && obj.name.eql?(group) }
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Find a VPC security group based on the 'Name' tag
|
190
|
+
def do_security_group_lookup_by_name_tag(ec2, group, ignore_case)
|
191
|
+
::AWS.memoize do
|
192
|
+
if ignore_case
|
193
|
+
ec2.security_groups.tagged('Name')
|
194
|
+
.select { |obj| !obj.tags.Name.nil? && obj.tags.Name.casecmp(group) == 0 }
|
195
|
+
else
|
196
|
+
ec2.security_groups.tagged('Name')
|
197
|
+
.select { |obj| !obj.tags.Name.nil? && obj.tags.Name.eql?(group) }
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative './aws/ec2'
|
data/lib/cloudutil.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
require_relative '../lib/cloudutil/aws.rb'
|
2
|
+
|
3
|
+
describe Cloudutil::AWS::Core do
|
4
|
+
context 'object initialization' do
|
5
|
+
my_env = ENV.to_h
|
6
|
+
|
7
|
+
it 'creates an object using primary env vars' do
|
8
|
+
ENV.clear
|
9
|
+
ENV['AWS_ACCESS_KEY_ID'] = 'my_primary_access_key'
|
10
|
+
ENV['AWS_SECRET_ACCESS_KEY'] = 'my_primary_secret_key'
|
11
|
+
|
12
|
+
aws = Cloudutil::AWS::Core.new
|
13
|
+
|
14
|
+
expect(aws.region).to eq 'us-east-1'
|
15
|
+
expect(aws.access_key).to eq 'my_primary_access_key'
|
16
|
+
expect(aws.secret_key).to eq 'my_primary_secret_key'
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'creates an object using secondary env vars' do
|
20
|
+
ENV.clear
|
21
|
+
ENV['AWS_ACCESS_KEY'] = 'my_secondary_access_key'
|
22
|
+
ENV['AWS_SECRET_KEY'] = 'my_secondary_secret_key'
|
23
|
+
|
24
|
+
aws = Cloudutil::AWS::Core.new
|
25
|
+
|
26
|
+
expect(aws.region).to eq 'us-east-1'
|
27
|
+
expect(aws.access_key).to eq 'my_secondary_access_key'
|
28
|
+
expect(aws.secret_key).to eq 'my_secondary_secret_key'
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'creates an object without env vars' do
|
32
|
+
ENV.clear
|
33
|
+
|
34
|
+
aws = Cloudutil::AWS::Core.new
|
35
|
+
|
36
|
+
expect(aws.region).to eq 'us-east-1'
|
37
|
+
expect(aws.access_key).to be_nil
|
38
|
+
expect(aws.secret_key).to be_nil
|
39
|
+
end
|
40
|
+
|
41
|
+
ENV.update(my_env)
|
42
|
+
|
43
|
+
it 'creates an object using implicit hash args' do
|
44
|
+
reg = 'us-west-1'
|
45
|
+
ak = 'my_access_key1'
|
46
|
+
sk = 'my_secret_key1'
|
47
|
+
|
48
|
+
aws = Cloudutil::AWS::Core.new(region: reg, access_key: ak, secret_key: sk)
|
49
|
+
|
50
|
+
expect(aws.region).to eq reg
|
51
|
+
expect(aws.access_key).to eq ak
|
52
|
+
expect(aws.secret_key).to eq sk
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'creates an object using explicit hash args' do
|
56
|
+
reg = 'us-west-2'
|
57
|
+
ak = 'my_access_key2'
|
58
|
+
sk = 'my_secret_key2'
|
59
|
+
|
60
|
+
aws = Cloudutil::AWS::Core.new(region: reg, access_key: ak, secret_key: sk)
|
61
|
+
|
62
|
+
expect(aws.region).to eq reg
|
63
|
+
expect(aws.access_key).to eq ak
|
64
|
+
expect(aws.secret_key).to eq sk
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'creates an object using sparse hash args' do
|
68
|
+
aws = Cloudutil::AWS::Core.new(access_key: 'test_key')
|
69
|
+
|
70
|
+
expect(aws.region).to eq 'us-east-1'
|
71
|
+
expect(aws.access_key).to eq 'test_key'
|
72
|
+
expect(aws.secret_key).to_not be_nil if ENV['AWS_SECRET_ACCESS_KEY']
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'creates an object using implicit array args' do
|
76
|
+
reg = 'us-west-1'
|
77
|
+
ak = 'my_access_key3'
|
78
|
+
sk = 'my_secret_key3'
|
79
|
+
|
80
|
+
aws = Cloudutil::AWS::Core.new(region: reg, access_key: ak, secret_key: sk)
|
81
|
+
|
82
|
+
expect(aws.region).to eq reg
|
83
|
+
expect(aws.access_key).to eq ak
|
84
|
+
expect(aws.secret_key).to eq sk
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'creates an object using explicit array args' do
|
88
|
+
reg = 'us-west-2'
|
89
|
+
ak = 'my_access_key4'
|
90
|
+
sk = 'my_secret_key4'
|
91
|
+
|
92
|
+
aws = Cloudutil::AWS::Core.new(region: reg, access_key: ak, secret_key: sk)
|
93
|
+
|
94
|
+
expect(aws.region).to eq reg
|
95
|
+
expect(aws.access_key).to eq ak
|
96
|
+
expect(aws.secret_key).to eq sk
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'creates an object using sparse array args' do
|
100
|
+
aws = Cloudutil::AWS::Core.new('us-west-2')
|
101
|
+
|
102
|
+
expect(aws.region).to eq 'us-west-2'
|
103
|
+
expect(aws.access_key).to_not be_nil if ENV['AWS_ACCESS_KEY_ID']
|
104
|
+
expect(aws.secret_key).to_not be_nil if ENV['AWS_SECRET_ACCESS_KEY']
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
require_relative '../lib/cloudutil/aws.rb'
|
2
|
+
|
3
|
+
describe Cloudutil::AWS::EC2 do
|
4
|
+
context 'object initialization' do
|
5
|
+
it 'creates an object using primary env vars' do
|
6
|
+
ENV.clear
|
7
|
+
ENV['AWS_ACCESS_KEY_ID'] = 'my_primary_access_key'
|
8
|
+
ENV['AWS_SECRET_ACCESS_KEY'] = 'my_primary_secret_key'
|
9
|
+
|
10
|
+
aws = Cloudutil::AWS::EC2.new
|
11
|
+
|
12
|
+
expect(aws.region).to eq 'us-east-1'
|
13
|
+
expect(aws.access_key).to eq 'my_primary_access_key'
|
14
|
+
expect(aws.secret_key).to eq 'my_primary_secret_key'
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'creates an object using implicit hash args' do
|
18
|
+
reg = 'us-west-1'
|
19
|
+
ak = 'my_access_key1'
|
20
|
+
sk = 'my_secret_key1'
|
21
|
+
|
22
|
+
aws = Cloudutil::AWS::EC2.new(region: reg, access_key: ak, secret_key: sk)
|
23
|
+
|
24
|
+
expect(aws.region).to eq reg
|
25
|
+
expect(aws.access_key).to eq ak
|
26
|
+
expect(aws.secret_key).to eq sk
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'resolving VPC subnets' do
|
31
|
+
# Need to do this to read AWS creds from env
|
32
|
+
my_env = ENV.to_h
|
33
|
+
before(:each) do
|
34
|
+
@aws = Cloudutil::AWS::EC2.new(access_key: my_env['AWS_ACCESS_KEY_ID'], secret_key: my_env['AWS_SECRET_ACCESS_KEY'])
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'returns what we passed in, if it looks like an AWS subnet id' do
|
38
|
+
id = @aws.resolve_subnet_id('subnet-deadbeef')
|
39
|
+
expect(id).to eq 'subnet-deadbeef'
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'returns a known VPC subnet object, case-sensitive', account_specific: true do
|
43
|
+
id = @aws.resolve_subnet_id('vpc-private-azA', false)
|
44
|
+
|
45
|
+
expect(id).to_not be_nil
|
46
|
+
expect(id).to match(/^subnet-\h{8,}$/)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'returns a known VPC subnet object, case-insensitive', account_specific: true do
|
50
|
+
id = @aws.resolve_subnet_id('VPC-Private-AZa')
|
51
|
+
|
52
|
+
expect(id).to_not be_nil
|
53
|
+
expect(id).to match(/^subnet-\h{8,}$/)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'returns nil for no case-sensitive VPC subnet match', account_specific: true do
|
57
|
+
id = @aws.resolve_subnet_id('VPC-private-aza', false)
|
58
|
+
expect(id).to be_nil
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'returns nil if given a completely bogus name' do
|
62
|
+
id = @aws.resolve_subnet_id('my_totally_bogus_subnet_name')
|
63
|
+
expect(id).to be_nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'resolving security groups' do
|
68
|
+
my_env = ENV.to_h
|
69
|
+
before(:each) do
|
70
|
+
@aws = Cloudutil::AWS::EC2.new(access_key: my_env['AWS_ACCESS_KEY_ID'], secret_key: my_env['AWS_SECRET_ACCESS_KEY'])
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'returns what we pass in, if it looks like an AWS security group id' do
|
74
|
+
id = @aws.resolve_security_group_id('sg-deadbeef')
|
75
|
+
expect(id).to eq 'sg-deadbeef'
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'returns a known security group by name, case-sensitive', account_specific: true do
|
79
|
+
id = @aws.resolve_security_group_id('VPC_Private', false)
|
80
|
+
|
81
|
+
expect(id).to_not be_nil
|
82
|
+
expect(id).to match(/^sg-\h{8,}$/)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'returns a known security group by name, case-insensitive', account_specific: true do
|
86
|
+
id = @aws.resolve_security_group_id('vpc_PRIVATE')
|
87
|
+
|
88
|
+
expect(id).to_not be_nil
|
89
|
+
expect(id).to match(/^sg-\h{8,}$/)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'returns a known security group by tag, case-sensitive', account_specific: true do
|
93
|
+
id = @aws.resolve_security_group_id('vpc-private', false)
|
94
|
+
|
95
|
+
expect(id).to_not be_nil
|
96
|
+
expect(id).to match(/^sg-\h{8,}$/)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'returns a known security group by tag, case-insensitive', account_specific: true do
|
100
|
+
id = @aws.resolve_security_group_id('VPC-PrIvAtE')
|
101
|
+
|
102
|
+
expect(id).to_not be_nil
|
103
|
+
expect(id).to match(/^sg-\h{8,}$/)
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'returns nil for no case-sensitive security group name or tag match', account_specific: true do
|
107
|
+
id = @aws.resolve_security_group_id('vpc_PrivatE', false)
|
108
|
+
expect(id).to be_nil
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'returns nil if given a completely bogus name/tag value' do
|
112
|
+
id = @aws.resolve_subnet_id('my_totally_bogus_subnet_name')
|
113
|
+
expect(id).to be_nil
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'resolving AMIs' do
|
118
|
+
my_env = ENV.to_h
|
119
|
+
before(:each) do
|
120
|
+
@aws = Cloudutil::AWS::EC2.new(access_key: my_env['AWS_ACCESS_KEY_ID'], secret_key: my_env['AWS_SECRET_ACCESS_KEY'])
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'returns what we pass in, if it looks like an AWS ami id' do
|
124
|
+
id = @aws.resolve_ami_id('ami-deadbeef')
|
125
|
+
expect(id).to eq 'ami-deadbeef'
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'returns a known AMI by name, case-sensitive', account_specific: true do
|
129
|
+
id = @aws.resolve_ami_id('AMIBakery-MRE-28', false)
|
130
|
+
|
131
|
+
expect(id).to_not be_nil
|
132
|
+
expect(id).to match(/^ami-\h{8,}$/)
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'returns a known AMI by name, case-insensitive', account_specific: true do
|
136
|
+
id = @aws.resolve_ami_id('amiBAKERY-mre-28')
|
137
|
+
|
138
|
+
expect(id).to_not be_nil
|
139
|
+
expect(id).to match(/^ami-\h{8,}$/)
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'returns a known AMI by tag, case-sensitive', account_specific: true do
|
143
|
+
id = @aws.resolve_ami_id('pearson_jenkins_ubuntu', false)
|
144
|
+
|
145
|
+
expect(id).to_not be_nil
|
146
|
+
expect(id).to match(/^ami-\h{8,}$/)
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'returns a known AMI by tag, case-insensitive', account_specific: true do
|
150
|
+
id = @aws.resolve_ami_id('Pearson_JENKINS_uBuntu')
|
151
|
+
|
152
|
+
expect(id).to_not be_nil
|
153
|
+
expect(id).to match(/^ami-\h{8,}$/)
|
154
|
+
end
|
155
|
+
|
156
|
+
# WARNING: This test can take over a minute to run, since it has to inspect the
|
157
|
+
# properties of all 31k public AMIs + any private AMIs my account has access to.
|
158
|
+
it 'returns nil for no case-sensitive AMI name or tag match', slow: true, account_specific: true do
|
159
|
+
id = @aws.resolve_ami_id('amiBakery-mRe-28', false)
|
160
|
+
expect(id).to be_nil
|
161
|
+
end
|
162
|
+
|
163
|
+
# WARNING: This test can take over a minute to run, since it has to inspect the
|
164
|
+
# properties of all 31k public AMIs + any private AMIs my account has access to.
|
165
|
+
it 'returns nil if given a completly bogus name/tag value', slow: true do
|
166
|
+
id = @aws.resolve_ami_id('my_totally_bogus_ami_name')
|
167
|
+
expect(id).to be_nil
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
metadata
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cloudutil
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mike Morris
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
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: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
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: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
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: aws-sdk
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.44'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.44'
|
83
|
+
description: |
|
84
|
+
# Cloudutil
|
85
|
+
|
86
|
+
A utility library for performing helpful tasks with various cloud platform providers
|
87
|
+
|
88
|
+
## Installation
|
89
|
+
|
90
|
+
Add this line to your application's Gemfile:
|
91
|
+
|
92
|
+
gem 'cloudutil'
|
93
|
+
|
94
|
+
And then execute:
|
95
|
+
|
96
|
+
$ bundle install
|
97
|
+
|
98
|
+
Or install it yourself as:
|
99
|
+
|
100
|
+
$ gem install cloudutil
|
101
|
+
|
102
|
+
## Usage
|
103
|
+
|
104
|
+
For AWS helpers:
|
105
|
+
|
106
|
+
require 'cloudutil/aws'
|
107
|
+
|
108
|
+
aws = Cloudutil::AWS(config: existing_AWS::Config_object) -or-
|
109
|
+
aws = Cloudutil::AWS(access_key: your_AWS_access_key, secret_key: your_AWS_secret_key)
|
110
|
+
|
111
|
+
subnet_id = aws.resolve_subnet_id('my_subnet_name_or_tag')
|
112
|
+
sec_grp_id = aws.resolve_subnet_id('my_security_group_name_or_tag')
|
113
|
+
ami_id = aws.resolve_subnet_id('my_ami_name_or_tag')
|
114
|
+
|
115
|
+
## Contributing
|
116
|
+
|
117
|
+
1. Fork it ( https://github.com/mmmorris1975/cloudutil/fork )
|
118
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
119
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
120
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
121
|
+
5. Create a new Pull Request
|
122
|
+
email:
|
123
|
+
- michael.m.morris@pearson.com
|
124
|
+
executables: []
|
125
|
+
extensions: []
|
126
|
+
extra_rdoc_files: []
|
127
|
+
files:
|
128
|
+
- .gitignore
|
129
|
+
- .rubocop.yml
|
130
|
+
- Gemfile
|
131
|
+
- LICENSE
|
132
|
+
- README.md
|
133
|
+
- Rakefile
|
134
|
+
- cloudutil.gemspec
|
135
|
+
- lib/cloudutil.rb
|
136
|
+
- lib/cloudutil/aws.rb
|
137
|
+
- lib/cloudutil/aws/core.rb
|
138
|
+
- lib/cloudutil/aws/ec2.rb
|
139
|
+
- lib/cloudutil/version.rb
|
140
|
+
- spec/aws_core_spec.rb
|
141
|
+
- spec/aws_ec2_spec.rb
|
142
|
+
homepage: ''
|
143
|
+
licenses:
|
144
|
+
- MIT
|
145
|
+
metadata: {}
|
146
|
+
post_install_message:
|
147
|
+
rdoc_options: []
|
148
|
+
require_paths:
|
149
|
+
- lib
|
150
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
151
|
+
requirements:
|
152
|
+
- - '>='
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: '0'
|
155
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - '>='
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
requirements: []
|
161
|
+
rubyforge_project:
|
162
|
+
rubygems_version: 2.2.2
|
163
|
+
signing_key:
|
164
|
+
specification_version: 4
|
165
|
+
summary: A utility library for performing helpful tasks with various cloud platform
|
166
|
+
providers
|
167
|
+
test_files:
|
168
|
+
- spec/aws_core_spec.rb
|
169
|
+
- spec/aws_ec2_spec.rb
|