mist_aws 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +62 -0
- data/Rakefile +15 -0
- data/lib/mist_aws.rb +6 -0
- data/lib/mist_aws/iam.rb +186 -0
- data/lib/mist_aws/version.rb +3 -0
- data/mist_aws.gemspec +29 -0
- data/spec/mist_aws_live_spec.rb +77 -0
- data/spec/mist_aws_spec.rb +231 -0
- data/spec/policy_document.txt +71 -0
- data/spec/spec_helper.rb +10 -0
- metadata +177 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ecbfb2c243c78f6e0b31be95db7ba6f10abe271f
|
4
|
+
data.tar.gz: 8f10d71ae5f7c1ae0a29aec510bbeea1d8506c65
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7137a1ecf189cecbf4ee81b43a263ecf97db5798e4e096f3b319b9706357bca2e417f40eb6259ceac0ddc2d33696e3405a745757d7a6c5765263b01404ad20dc
|
7
|
+
data.tar.gz: 40ac0d83153c646e93cce36f9a23c223eccf373fa65f6c3f27011fdfc997de034a801589c1408abad43a260948bf5c87aa3ae01089b61cb22921b4eddcdcec78
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Robert J. Berger
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# MistAws
|
2
|
+
|
3
|
+
Uses the [ruby aws-sdk v2](https://github.com/aws/aws-sdk-core-ruby) [Resource Interface](https://github.com/aws/aws-sdk-core-ruby#resource-interfaces) to create some use specific higher level functionality.
|
4
|
+
|
5
|
+
> NOTE: Currently the only object implemented is IAM for creating / deleting instance roles
|
6
|
+
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
### Install the Gem
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
gem 'mist_aws'
|
16
|
+
```
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
$ bundle
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
|
24
|
+
$ gem install mist_aws
|
25
|
+
|
26
|
+
### Generate Yard Docs
|
27
|
+
|
28
|
+
$ rake yard
|
29
|
+
$ open doc/index.html
|
30
|
+
|
31
|
+
## Usage
|
32
|
+
|
33
|
+
* Get the handle to the MistAws::Iam object
|
34
|
+
* Credentials will come from the `my_profile_name` profile in ~/.aws/credentials as per [Providing AWS Credentials in the AWS SDK for Ruby] (http://docs.aws.amazon.com/AWSSdkDocsRuby/latest/DeveloperGuide/prog-basics-creds.html#creds-explicit)
|
35
|
+
```
|
36
|
+
mist_aws = ::MistAws::Iam.new(profile_name: "my_profile_name", region: "us-east-1")
|
37
|
+
```
|
38
|
+
* Create an IAM Role
|
39
|
+
* role_policy_document is a JSON text file that is an [AWS IAM policy](http://docs.aws.amazon.com/IAM/latest/UserGuide/PoliciesOverview.html)
|
40
|
+
* This method will create:
|
41
|
+
* [Role](http://docs.aws.amazon.com/sdkforruby/api/Aws/IAM/Role.html)
|
42
|
+
* [Role Policy](http://docs.aws.amazon.com/sdkforruby/api/Aws/IAM/RolePolicy.html)
|
43
|
+
* [Instance profile](http://docs.aws.amazon.com/sdkforruby/api/Aws/IAM/InstanceProfile.html)
|
44
|
+
* Connect them all together
|
45
|
+
* Returns a [Role](http://docs.aws.amazon.com/sdkforruby/api/Aws/IAM/Role.html) Resource instance
|
46
|
+
```
|
47
|
+
role = mist_aws.create_iam_role(role_name, role_policy_name, role_policy_document, instance_profile_name)
|
48
|
+
```
|
49
|
+
|
50
|
+
## Contributing
|
51
|
+
|
52
|
+
I would be very interested in feedback on things that could be done
|
53
|
+
better. Also would be interested in how to better do the rspec
|
54
|
+
tests. I felt like there was WAY too much mocking. I did find it
|
55
|
+
useful for finding bugs and broken assumptions though.
|
56
|
+
|
57
|
+
|
58
|
+
1. Fork it ( https://github.com/[my-github-username]/mist_aws/fork )
|
59
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
60
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
61
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
62
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
5
|
+
|
6
|
+
task :default => :spec
|
7
|
+
|
8
|
+
require "yard"
|
9
|
+
|
10
|
+
current_dir = File.dirname(__FILE__)
|
11
|
+
|
12
|
+
YARD::Rake::YardocTask.new do |t|
|
13
|
+
t.files = ["#{current_dir}/**/*.rb"]
|
14
|
+
#t.options = ['--debug']
|
15
|
+
end
|
data/lib/mist_aws.rb
ADDED
data/lib/mist_aws/iam.rb
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'inifile'
|
3
|
+
require 'pp'
|
4
|
+
require 'aws-sdk'
|
5
|
+
require 'time'
|
6
|
+
require 'tempfile'
|
7
|
+
require 'pry'
|
8
|
+
require 'logger'
|
9
|
+
|
10
|
+
module MistAws
|
11
|
+
class Iam
|
12
|
+
|
13
|
+
# These are read-only accessor and are initializeds by initialize method
|
14
|
+
attr_reader :profile_name
|
15
|
+
attr_reader :credentials
|
16
|
+
attr_reader :region
|
17
|
+
attr_reader :logger
|
18
|
+
attr_reader :handle
|
19
|
+
attr_reader :iam_client
|
20
|
+
attr_reader :iam
|
21
|
+
|
22
|
+
|
23
|
+
# Initializes all you need to access aws resources in a region
|
24
|
+
# You can create multiple instances to allow access to multiple regions in one program/recipe
|
25
|
+
#
|
26
|
+
# @param opts [Hash] the key/value pairs for the initializer
|
27
|
+
# @option opts [String] :profile_name ('default' or ENV['AWS_PROFILE']) profile name to use to get creds from ~/.aws/credentials.
|
28
|
+
# @option opts [String] :region (us-east-1 or ENV['AWS_REGION']) AWS Region to use
|
29
|
+
# @option opts [Logger] :logger (STDERR) A logger instance to use for logging
|
30
|
+
# @return [MistAws::Iam]
|
31
|
+
#
|
32
|
+
def initialize(opts={})
|
33
|
+
# Ruby 1.9 backwards compatability
|
34
|
+
opts = {profile_name: nil, region: nil, logger: ::Logger.new(STDERR)}.merge(opts)
|
35
|
+
opts.each do |key, value|
|
36
|
+
instance_variable_set "@#{key}", value
|
37
|
+
end
|
38
|
+
|
39
|
+
# Set by rspec tests that are testing methods called by initialize
|
40
|
+
return if ENV['RSPEC_IGNORE_INITIALIZE']
|
41
|
+
|
42
|
+
@region ||= ENV['AWS_REGION'] ? ENV['AWS_REGION'] : 'us-east-1'
|
43
|
+
|
44
|
+
# Note get_creds also resolves and sets @profile_name as well as
|
45
|
+
# fetching the appropriate credentials based on the value of
|
46
|
+
# profile_name and various Environment Variables
|
47
|
+
@credentials = get_creds(profile_name)
|
48
|
+
|
49
|
+
@iam_client = Aws::IAM::Client.new(credentials: @credentials, region: @region)
|
50
|
+
@iam = Aws::IAM::Resource.new(client: @iam_client)
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
# Returns the proper Aws credential object.
|
55
|
+
#
|
56
|
+
# @param profile_name [String] (nil) Name of profile
|
57
|
+
# @return [Aws::SharedCredentials | Aws::Credentials]
|
58
|
+
#
|
59
|
+
def get_creds(profile_name=nil)
|
60
|
+
unless profile_name
|
61
|
+
if ENV['AWS_PROFILE']
|
62
|
+
@profile_name = ENV['AWS_PROFILE']
|
63
|
+
else
|
64
|
+
if ENV['AWS_ACCESS_KEY_ID'] && ENV['AWS_SECRET_ACCESS_KEY']
|
65
|
+
return Aws::Credentials.new(ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY'])
|
66
|
+
else
|
67
|
+
@profile_name = 'default'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
else
|
71
|
+
@profile_name = profile_name
|
72
|
+
end
|
73
|
+
|
74
|
+
begin
|
75
|
+
Aws::SharedCredentials.new(profile_name: @profile_name)
|
76
|
+
rescue StandardError => e
|
77
|
+
@logger.error e.inspect
|
78
|
+
raise e
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Check if a role exists
|
83
|
+
def role_exists?(role_name)
|
84
|
+
begin
|
85
|
+
iam_client.get_role role_name: role_name
|
86
|
+
rescue ::Aws::IAM::Errors::NoSuchEntity
|
87
|
+
return false
|
88
|
+
end
|
89
|
+
return true
|
90
|
+
end
|
91
|
+
|
92
|
+
# Check if an instance profile exists
|
93
|
+
def instance_profile_exists?(instance_profile_name)
|
94
|
+
instance_profile = iam.instance_profile(instance_profile_name)
|
95
|
+
case instance_profile.data_loaded?
|
96
|
+
when true
|
97
|
+
return instance_profile
|
98
|
+
else
|
99
|
+
return nil
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Check if there is a role_policy
|
104
|
+
def role_policy_exists?(role_name, policy_name)
|
105
|
+
begin
|
106
|
+
iam_client.get_role_policy(role_name: role_name, policy_name: policy_name)
|
107
|
+
exists = true
|
108
|
+
rescue Aws::IAM::Errors::NoSuchEntityException
|
109
|
+
logger.warn "No role_polcy: #{policy_name.inspect} for role: #{role_name.inspect}"
|
110
|
+
exists = false
|
111
|
+
end
|
112
|
+
exists
|
113
|
+
end
|
114
|
+
|
115
|
+
#
|
116
|
+
# Delete role, policy and instance_profile if they exist
|
117
|
+
#
|
118
|
+
def delete_iam_role(role_name, policy_name, instance_profile_name=nil)
|
119
|
+
unless role_exists? role_name
|
120
|
+
logger.warn "MistAws::Iam#delete_iam_role: No role: #{role_name.inspect}; Doing nothing"
|
121
|
+
return nil
|
122
|
+
end
|
123
|
+
|
124
|
+
role = iam.role role_name
|
125
|
+
|
126
|
+
# Removes the role from the instance_profiles
|
127
|
+
# Deletes the instance_profile
|
128
|
+
instance_profiles = role.instance_profiles
|
129
|
+
instance_profiles.each do | instance_profile |
|
130
|
+
instance_profile.remove_role role_name: role_name
|
131
|
+
instance_profile.delete
|
132
|
+
end
|
133
|
+
|
134
|
+
# Delete the role_policies associated with the role
|
135
|
+
role_policies = role.policies
|
136
|
+
role_policies.each do | role_policy |
|
137
|
+
role_policy.delete
|
138
|
+
end
|
139
|
+
|
140
|
+
# Delete the Role
|
141
|
+
role.delete
|
142
|
+
|
143
|
+
true
|
144
|
+
end
|
145
|
+
|
146
|
+
#
|
147
|
+
# Create an IAM role to be assigned to an ec2 instance
|
148
|
+
# Returns the Role object
|
149
|
+
#
|
150
|
+
# @param role_name [String] Name of the role to create
|
151
|
+
# @param policy_name [String]
|
152
|
+
def create_iam_role(role_name, policy_name, policy_document, instance_profile_name=nil)
|
153
|
+
result = role_exists? role_name
|
154
|
+
if result
|
155
|
+
logger.warn "MistAws::Iam#create_iam_role: Role: #{role_name.inspect} already exists; Doing nothing"
|
156
|
+
return nil
|
157
|
+
end
|
158
|
+
|
159
|
+
assume_role_policy = {
|
160
|
+
"Version" => "2012-10-17",
|
161
|
+
"Statement" => [
|
162
|
+
{"Effect" => "Allow",
|
163
|
+
"Principal" => {
|
164
|
+
"Service" => [
|
165
|
+
"ec2.amazonaws.com"
|
166
|
+
]
|
167
|
+
},
|
168
|
+
"Action" => ["sts:AssumeRole"]
|
169
|
+
}
|
170
|
+
]
|
171
|
+
}.to_json
|
172
|
+
|
173
|
+
# Create the role
|
174
|
+
role = iam.create_role(role_name: role_name, assume_role_policy_document: assume_role_policy)
|
175
|
+
|
176
|
+
# Create a policy object associated to the role and stuff the policy document into it
|
177
|
+
policy = Aws::IAM::RolePolicy.new(role_name: role_name, name: policy_name, client: iam_client)
|
178
|
+
policy.put(policy_document: policy_document)
|
179
|
+
|
180
|
+
# Create an instance_profile
|
181
|
+
instance_profile = iam.create_instance_profile(instance_profile_name: instance_profile_name)
|
182
|
+
instance_profile.add_role(role_name: role_name)
|
183
|
+
role
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
data/mist_aws.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'mist_aws/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "mist_aws"
|
8
|
+
spec.version = MistAws::VERSION
|
9
|
+
spec.authors = ["Robert J. Berger"]
|
10
|
+
spec.email = ["rberger@mistsys.com"]
|
11
|
+
spec.summary = %q{Wrapper around aws-sdk for higher level use}
|
12
|
+
spec.description = %q{Wrapper around aws-sdk for higher level use. So far only supports IAM Role create/delete}
|
13
|
+
spec.homepage = "https://github.com/mistsys/mist_aws"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "rspec", "~> 3.1.0"
|
24
|
+
spec.add_development_dependency "pry"
|
25
|
+
spec.add_development_dependency "yard"
|
26
|
+
spec.add_dependency "aws-sdk", "~>2.0.1.pre"
|
27
|
+
spec.add_dependency "json"
|
28
|
+
spec.add_dependency "inifile"
|
29
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# These tests actually hit AWS and are not mocked
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
include MistAws
|
7
|
+
|
8
|
+
describe "Live tests on AWS (not mocked)" do
|
9
|
+
describe Iam do
|
10
|
+
PRE = "trsh"
|
11
|
+
let(:profile_name) { 'mistsys' }
|
12
|
+
let(:region) { 'us-east-1' }
|
13
|
+
let(:role_name) { "#{PRE}_my_role" }
|
14
|
+
let(:role_policy_name) { "#{PRE}_my_role_policy_name" }
|
15
|
+
let(:instance_profile_name) { "#{PRE}_my_instance_profile" }
|
16
|
+
let(:test_dir) { File.dirname(__FILE__) }
|
17
|
+
let(:role_policy_document_filename) { "#{test_dir}/policy_document.txt" }
|
18
|
+
let(:role_policy_document) { File.readlines(role_policy_document_filename).join("\n") }
|
19
|
+
let(:assume_role_policy) {
|
20
|
+
{
|
21
|
+
"Version" => "2012-10-17",
|
22
|
+
"Statement" => [
|
23
|
+
{"Effect" => "Allow",
|
24
|
+
"Principal" => {
|
25
|
+
"Service" => [
|
26
|
+
"ec2.amazonaws.com"
|
27
|
+
]
|
28
|
+
},
|
29
|
+
"Action" => ["sts:AssumeRole"]
|
30
|
+
}
|
31
|
+
]
|
32
|
+
}.to_json
|
33
|
+
}
|
34
|
+
|
35
|
+
let(:test_credentials) { ::Aws::SharedCredentials.new(profile_name: profile_name) }
|
36
|
+
let(:test_iam_client) { ::Aws::IAM::Client.new(credentials: test_credentials, region: region) }
|
37
|
+
let(:test_iam) { ::Aws::IAM::Resource.new(client: test_iam_client) }
|
38
|
+
|
39
|
+
let(:mist_aws) { ::MistAws::Iam.new(profile_name: profile_name, region: region) }
|
40
|
+
|
41
|
+
describe '#create_iam_role' do
|
42
|
+
context 'when no role already exists' do
|
43
|
+
before(:each) do
|
44
|
+
allow(mist_aws.logger).to receive(:warn).with(/No role/)
|
45
|
+
mist_aws.delete_iam_role(role_name, role_policy_name, instance_profile_name)
|
46
|
+
end
|
47
|
+
|
48
|
+
after(:each) do
|
49
|
+
mist_aws.delete_iam_role(role_name, role_policy_name, instance_profile_name)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'creates a role' do
|
53
|
+
test_role = mist_aws.create_iam_role(role_name, role_policy_name, role_policy_document, instance_profile_name)
|
54
|
+
# test_role = test_iam.role role_name
|
55
|
+
expect(test_role.name).to eq role_name
|
56
|
+
expect(test_role.assume_role_policy_document).to include 'Statement'
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'when a role already exists' do
|
62
|
+
before(:each) do
|
63
|
+
allow(mist_aws.logger).to receive(:warn).with(/already exists/)
|
64
|
+
mist_aws.create_iam_role(role_name, role_policy_name, role_policy_document, instance_profile_name)
|
65
|
+
end
|
66
|
+
|
67
|
+
after(:each) do
|
68
|
+
mist_aws.delete_iam_role(role_name, role_policy_name, instance_profile_name)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'returns falsy' do
|
72
|
+
expect(mist_aws.role_exists? role_name).to be_truthy
|
73
|
+
expect(mist_aws.create_iam_role(role_name, role_policy_name, role_policy_document, instance_profile_name)).to be_falsy
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,231 @@
|
|
1
|
+
# These should be purly mocked and not hit AWS
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
include MistAws
|
7
|
+
|
8
|
+
describe "Fully Mocked Tests" do
|
9
|
+
describe Iam do
|
10
|
+
it 'has a version number' do
|
11
|
+
expect(MistAws::VERSION).not_to be nil
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
let(:my_profile_name) { 'arg_specified_profile_name' }
|
16
|
+
let(:env_profile_name) { 'env_profile_name' }
|
17
|
+
let(:access_key_value) { 'access_key_value' }
|
18
|
+
let(:default_region) { 'us-east-1' }
|
19
|
+
|
20
|
+
let(:mist_aws) { ::MistAws::Iam.new }
|
21
|
+
let(:role_name) { "my_role" }
|
22
|
+
let(:role_policy_name) { "my_role_policy_name" }
|
23
|
+
let(:role_policy_document) { "my_role_policy_document" }
|
24
|
+
let(:instance_profile_name) { "my_instance_profile" }
|
25
|
+
|
26
|
+
let (:shared_credentials) { instance_double("::Aws::SharedCredentials") }
|
27
|
+
#let(:credentials) {object_double(::Aws::SharedCredentials.new(profile_name: 'default')) }
|
28
|
+
let(:iam_client) { object_double(::Aws::IAM::Client.new(credentials: shared_credentials, region: 'us-east-1')) }
|
29
|
+
let(:iam) { object_double(::Aws::IAM::Resource.new(client: iam_client)) }
|
30
|
+
let(:context) { object_double(::Seahorse::Client::RequestContext) }
|
31
|
+
|
32
|
+
let(:role_policy) { instance_double("Aws::IAM::RolePolicy", role_name: role_name, name: role_policy_name) }
|
33
|
+
let(:instance_profile) { instance_double("Aws::IAM::InstanceProfile",name: instance_profile_name) }
|
34
|
+
|
35
|
+
let(:assume_role_policy) {
|
36
|
+
{
|
37
|
+
"Version" => "2012-10-17",
|
38
|
+
"Statement" => [
|
39
|
+
{"Effect" => "Allow",
|
40
|
+
"Principal" => {
|
41
|
+
"Service" => [
|
42
|
+
"ec2.amazonaws.com"
|
43
|
+
]
|
44
|
+
},
|
45
|
+
"Action" => ["sts:AssumeRole"]
|
46
|
+
}
|
47
|
+
]
|
48
|
+
}.to_json
|
49
|
+
}
|
50
|
+
|
51
|
+
let(:role) { object_double(::Aws::IAM::Role.new(name: role_name, client: iam_client)) }
|
52
|
+
|
53
|
+
before(:example, :run_global_before) do
|
54
|
+
ENV['AWS_PROFILE'] = ENV['AWS_REGION'] = ENV['AWS_ACCESS_KEY_ID'] = ENV['AWS_SECRET_ACCESS_KEY']= nil
|
55
|
+
allow_any_instance_of(::MistAws::Iam).to receive(:get_creds).and_return(shared_credentials)
|
56
|
+
allow(::Aws::IAM::Client).to receive(:new).with(credentials: shared_credentials, region: default_region).and_return(iam_client)
|
57
|
+
allow(::Aws::IAM::Resource).to receive(:new).with(client: iam_client).and_return(iam)
|
58
|
+
end
|
59
|
+
|
60
|
+
after(:example) do
|
61
|
+
ENV['RSPEC_IGNORE_INITIALIZE'] = nil
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#get_creds' do
|
65
|
+
before(:example) do
|
66
|
+
ENV['RSPEC_IGNORE_INITIALIZE'] = 'true'
|
67
|
+
ENV['AWS_PROFILE'] = ENV['AWS_REGION'] = ENV['AWS_ACCESS_KEY_ID'] = ENV['AWS_SECRET_ACCESS_KEY']= nil
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'when all inputs are nil' do
|
71
|
+
it 'will fetch the default shared credential' do
|
72
|
+
expect(::Aws::SharedCredentials).to receive(:new).with(profile_name: 'default')
|
73
|
+
my_mist_aws = ::MistAws::Iam.new
|
74
|
+
my_mist_aws.get_creds
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'profile is specified in arguments' do
|
79
|
+
|
80
|
+
it 'will fetch the profile specified by argument' do
|
81
|
+
expect(::Aws::SharedCredentials).to receive(:new).with(profile_name: my_profile_name)
|
82
|
+
mist_aws.get_creds(my_profile_name)
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
it 'will ignore ENV["AWS_PROFILE"]' do
|
87
|
+
ENV['AWS_PROFILE'] = 'invalid_profile_name'
|
88
|
+
expect(::Aws::SharedCredentials).to receive(:new).with(profile_name: my_profile_name)
|
89
|
+
mist_aws.get_creds(my_profile_name)
|
90
|
+
end
|
91
|
+
it 'will ignore ENV["AWS_ACCESS_KEY_ID"], ENV["AWS_SECRET_ACCESS_KEY"]' do
|
92
|
+
ENV['AWS_ACCESS_KEY_ID'] = ENV['AWS_SECRET_ACCESS_KEY'] = access_key_value
|
93
|
+
expect(::Aws::SharedCredentials).to receive(:new).with(profile_name: my_profile_name)
|
94
|
+
mist_aws.get_creds(my_profile_name)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'profile only specified in ENV["AWS_PROFILE"]' do
|
99
|
+
before(:example) do
|
100
|
+
ENV['AWS_PROFILE'] = env_profile_name
|
101
|
+
ENV['AWS_ACCESS_KEY_ID'] = ENV['AWS_SECRET_ACCESS_KEY'] = access_key_value
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'will fetch the profile specified by ENV["AWS_PROFILE\`]' do
|
105
|
+
expect(::Aws::SharedCredentials).to receive(:new).with(profile_name: env_profile_name)
|
106
|
+
mist_aws.get_creds
|
107
|
+
end
|
108
|
+
|
109
|
+
context "only ENV['AWS_ACCESS_KEY_ID'] and ENV['AWS_SECRET_ACCESS_KEY'] are set" do
|
110
|
+
before(:example) do
|
111
|
+
ENV['AWS_PROFILE'] = nil
|
112
|
+
end
|
113
|
+
it 'will use Aws::Credentials and not Aws::SharedCredentials' do
|
114
|
+
expect(::Aws::SharedCredentials).not_to receive(:new)
|
115
|
+
expect(::Aws::Credentials).to receive(:new).with(access_key_value, access_key_value)
|
116
|
+
|
117
|
+
mist_aws.get_creds
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe '.initialize', :run_global_before do
|
124
|
+
context 'when all inputs are nil' do
|
125
|
+
before(:example) do
|
126
|
+
expect_any_instance_of(Iam).to receive(:get_creds).with(nil).and_return(shared_credentials)
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'initializes an IAM Client' do
|
130
|
+
expect(Aws::IAM::Client).to receive(:new).with({credentials: shared_credentials, region: default_region})
|
131
|
+
Iam.new
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe '#role_exists?', :run_global_before do
|
137
|
+
it 'returns truthy when it exists' do
|
138
|
+
expect(iam_client).to receive(:get_role).with(role_name: role_name).and_return(role)
|
139
|
+
|
140
|
+
expect(mist_aws.role_exists?(role_name)).to be_truthy
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'returns falsy when it does not exist' do
|
144
|
+
expect(iam_client).to receive(:get_role).with(role_name: role_name).and_raise(Aws::IAM::Errors::NoSuchEntity.new(context, 'foo'))
|
145
|
+
|
146
|
+
expect(mist_aws.role_exists?(role_name)).to be_falsy
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe '#instance_profile_exists?', :run_global_before do
|
151
|
+
it 'returns truthy when it exists' do
|
152
|
+
expect(iam).to receive(:instance_profile).with(instance_profile_name).and_return(instance_profile)
|
153
|
+
expect(instance_profile).to receive(:data_loaded?).and_return(true)
|
154
|
+
|
155
|
+
expect(mist_aws.instance_profile_exists?(instance_profile_name)).to be_truthy
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'returns falsy when it does not exist' do
|
159
|
+
expect(iam).to receive(:instance_profile).with(instance_profile_name).and_return(instance_profile)
|
160
|
+
expect(instance_profile).to receive(:data_loaded?).and_return(false)
|
161
|
+
|
162
|
+
expect(mist_aws.instance_profile_exists?(instance_profile_name)).to be_falsy
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe '#role_policy_exists?', :run_global_before do
|
167
|
+
it 'returns truthy when it exists' do
|
168
|
+
expect(iam_client).to receive(:get_role_policy).with(role_name: role_name, policy_name: role_policy_name).and_return(role_policy)
|
169
|
+
|
170
|
+
expect(mist_aws.role_policy_exists?(role_name, role_policy_name)).to be_truthy
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'returns falsy when it does not exist' do
|
174
|
+
expect(mist_aws.logger).to receive(:warn).with(/No role/)
|
175
|
+
expect(iam_client).to receive(:get_role_policy).with(role_name: role_name, policy_name: role_policy_name).and_raise(Aws::IAM::Errors::NoSuchEntityException.new(context, 'foo'))
|
176
|
+
|
177
|
+
expect(mist_aws.role_policy_exists?(role_name, role_policy_name)).to be_falsy
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
describe '#create_iam_role', :run_global_before do
|
182
|
+
it 'creates an IAM role with a role policy and an instance_profile' do
|
183
|
+
expect_any_instance_of(::MistAws::Iam).to receive(:role_exists?).with(role_name).and_return(false)
|
184
|
+
expect(iam).to receive(:create_role).with(role_name: role_name, assume_role_policy_document: assume_role_policy).and_return(role)
|
185
|
+
|
186
|
+
expect(::Aws::IAM::RolePolicy).to receive(:new).with(role_name: role_name, name: role_policy_name, client: iam_client).and_return(role_policy)
|
187
|
+
expect(role_policy).to receive(:put).with(policy_document: role_policy_document)
|
188
|
+
|
189
|
+
expect(iam).to receive(:create_instance_profile).with(instance_profile_name: instance_profile_name).and_return(instance_profile)
|
190
|
+
expect(instance_profile).to receive(:add_role).with(role_name: role_name)
|
191
|
+
|
192
|
+
|
193
|
+
mist_aws.create_iam_role(role_name, role_policy_name, role_policy_document, instance_profile_name)
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
describe '#delete_iam_role', :run_global_before do
|
199
|
+
it 'Does nothing if there is no role' do
|
200
|
+
expect(mist_aws.logger).to receive(:warn).with(/No role/)
|
201
|
+
expect(iam_client).to receive(:get_role).with(role_name: role_name).and_raise(Aws::IAM::Errors::NoSuchEntity.new(context, 'foo'))
|
202
|
+
|
203
|
+
expect(mist_aws.delete_iam_role(role_name, role_policy_name, instance_profile_name)).to be_falsy
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'Existing role, policy and instance_profile' do
|
207
|
+
expect(iam_client).to receive(:get_role).with(role_name: role_name).and_return(role)
|
208
|
+
expect(iam).to receive(:role).with(role_name).and_return(role)
|
209
|
+
|
210
|
+
# Get all the instance_profiles associated with the role
|
211
|
+
expect(role).to receive(:instance_profiles).and_return([instance_profile])
|
212
|
+
|
213
|
+
# Removes the role from the instance_profile
|
214
|
+
expect(instance_profile).to receive(:remove_role).with(role_name: role_name)
|
215
|
+
# Delete the instance_profile
|
216
|
+
expect(instance_profile).to receive(:delete)
|
217
|
+
|
218
|
+
# Get all role_policies associated with the role
|
219
|
+
expect(role).to receive(:policies).and_return([role_policy])
|
220
|
+
|
221
|
+
# Deletes the role_policy
|
222
|
+
expect(role_policy).to receive(:delete)
|
223
|
+
|
224
|
+
# Deletes the role
|
225
|
+
expect(role).to receive(:delete)
|
226
|
+
|
227
|
+
expect(mist_aws.delete_iam_role(role_name, role_policy_name, instance_profile_name)).to be_truthy
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
{
|
2
|
+
"Version": "2012-10-17",
|
3
|
+
"Statement": [
|
4
|
+
{
|
5
|
+
"Action": [
|
6
|
+
"appstream:Get*",
|
7
|
+
"autoscaling:Describe*",
|
8
|
+
"cloudformation:DescribeStacks",
|
9
|
+
"cloudformation:DescribeStackEvents",
|
10
|
+
"cloudformation:DescribeStackResource",
|
11
|
+
"cloudformation:DescribeStackResources",
|
12
|
+
"cloudformation:GetTemplate",
|
13
|
+
"cloudformation:List*",
|
14
|
+
"cloudfront:Get*",
|
15
|
+
"cloudfront:List*",
|
16
|
+
"cloudtrail:DescribeTrails",
|
17
|
+
"cloudtrail:GetTrailStatus",
|
18
|
+
"cloudwatch:Describe*",
|
19
|
+
"cloudwatch:Get*",
|
20
|
+
"cloudwatch:List*",
|
21
|
+
"directconnect:Describe*",
|
22
|
+
"dynamodb:GetItem",
|
23
|
+
"dynamodb:BatchGetItem",
|
24
|
+
"dynamodb:Query",
|
25
|
+
"dynamodb:Scan",
|
26
|
+
"dynamodb:DescribeTable",
|
27
|
+
"dynamodb:ListTables",
|
28
|
+
"ec2:Describe*",
|
29
|
+
"elasticache:Describe*",
|
30
|
+
"elasticbeanstalk:Check*",
|
31
|
+
"elasticbeanstalk:Describe*",
|
32
|
+
"elasticbeanstalk:List*",
|
33
|
+
"elasticbeanstalk:RequestEnvironmentInfo",
|
34
|
+
"elasticbeanstalk:RetrieveEnvironmentInfo",
|
35
|
+
"elasticloadbalancing:Describe*",
|
36
|
+
"elastictranscoder:Read*",
|
37
|
+
"elastictranscoder:List*",
|
38
|
+
"iam:List*",
|
39
|
+
"iam:Get*",
|
40
|
+
"kinesis:Describe*",
|
41
|
+
"kinesis:Get*",
|
42
|
+
"kinesis:List*",
|
43
|
+
"opsworks:Describe*",
|
44
|
+
"opsworks:Get*",
|
45
|
+
"route53:Get*",
|
46
|
+
"route53:List*",
|
47
|
+
"redshift:Describe*",
|
48
|
+
"redshift:ViewQueriesInConsole",
|
49
|
+
"rds:Describe*",
|
50
|
+
"rds:ListTagsForResource",
|
51
|
+
"s3:Get*",
|
52
|
+
"s3:List*",
|
53
|
+
"sdb:GetAttributes",
|
54
|
+
"sdb:List*",
|
55
|
+
"sdb:Select*",
|
56
|
+
"ses:Get*",
|
57
|
+
"ses:List*",
|
58
|
+
"sns:Get*",
|
59
|
+
"sns:List*",
|
60
|
+
"sqs:GetQueueAttributes",
|
61
|
+
"sqs:ListQueues",
|
62
|
+
"sqs:ReceiveMessage",
|
63
|
+
"storagegateway:List*",
|
64
|
+
"storagegateway:Describe*",
|
65
|
+
"trustedadvisor:Describe*"
|
66
|
+
],
|
67
|
+
"Effect": "Allow",
|
68
|
+
"Resource": "*"
|
69
|
+
}
|
70
|
+
]
|
71
|
+
}
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mist_aws
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Robert J. Berger
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-10-14 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.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.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: 3.1.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.1.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
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: yard
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: aws-sdk
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 2.0.1.pre
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 2.0.1.pre
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: json
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: inifile
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: Wrapper around aws-sdk for higher level use. So far only supports IAM
|
126
|
+
Role create/delete
|
127
|
+
email:
|
128
|
+
- rberger@mistsys.com
|
129
|
+
executables: []
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- ".gitignore"
|
134
|
+
- ".rspec"
|
135
|
+
- ".travis.yml"
|
136
|
+
- Gemfile
|
137
|
+
- LICENSE.txt
|
138
|
+
- README.md
|
139
|
+
- Rakefile
|
140
|
+
- lib/mist_aws.rb
|
141
|
+
- lib/mist_aws/iam.rb
|
142
|
+
- lib/mist_aws/version.rb
|
143
|
+
- mist_aws.gemspec
|
144
|
+
- spec/mist_aws_live_spec.rb
|
145
|
+
- spec/mist_aws_spec.rb
|
146
|
+
- spec/policy_document.txt
|
147
|
+
- spec/spec_helper.rb
|
148
|
+
homepage: https://github.com/mistsys/mist_aws
|
149
|
+
licenses:
|
150
|
+
- MIT
|
151
|
+
metadata: {}
|
152
|
+
post_install_message:
|
153
|
+
rdoc_options: []
|
154
|
+
require_paths:
|
155
|
+
- lib
|
156
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - ">="
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '0'
|
161
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - ">="
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
requirements: []
|
167
|
+
rubyforge_project:
|
168
|
+
rubygems_version: 2.2.2
|
169
|
+
signing_key:
|
170
|
+
specification_version: 4
|
171
|
+
summary: Wrapper around aws-sdk for higher level use
|
172
|
+
test_files:
|
173
|
+
- spec/mist_aws_live_spec.rb
|
174
|
+
- spec/mist_aws_spec.rb
|
175
|
+
- spec/policy_document.txt
|
176
|
+
- spec/spec_helper.rb
|
177
|
+
has_rdoc:
|