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 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
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cloudutil.gemspec
4
+ gemspec
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'
@@ -0,0 +1,4 @@
1
+ # Module to hold the version number for this library
2
+ module Cloudutil
3
+ VERSION = '0.0.1'
4
+ end
data/lib/cloudutil.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'cloudutil/version'
2
+ require 'cloudutil/aws'
@@ -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