cumulus-aws 0.11.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +3 -0
- data/.travis.yml +12 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +29 -0
- data/LICENSE +202 -0
- data/README.md +41 -0
- data/autocomplete +137 -0
- data/bin/cumulus +658 -0
- data/cumulus +2 -0
- data/cumulus-aws.gemspec +20 -0
- data/lib/autoscaling/AutoScaling.rb +40 -0
- data/lib/autoscaling/loader/Loader.rb +56 -0
- data/lib/autoscaling/manager/Manager.rb +360 -0
- data/lib/autoscaling/models/AlarmConfig.rb +165 -0
- data/lib/autoscaling/models/AlarmDiff.rb +172 -0
- data/lib/autoscaling/models/AutoScalingDiff.rb +178 -0
- data/lib/autoscaling/models/GroupConfig.rb +330 -0
- data/lib/autoscaling/models/PolicyConfig.rb +135 -0
- data/lib/autoscaling/models/PolicyDiff.rb +73 -0
- data/lib/autoscaling/models/ScheduledActionDiff.rb +53 -0
- data/lib/autoscaling/models/ScheduledConfig.rb +96 -0
- data/lib/aws_extensions/ec2/DhcpOptions.rb +41 -0
- data/lib/aws_extensions/ec2/Instance.rb +29 -0
- data/lib/aws_extensions/ec2/NetworkAcl.rb +25 -0
- data/lib/aws_extensions/ec2/NetworkInterface.rb +14 -0
- data/lib/aws_extensions/ec2/RouteTable.rb +26 -0
- data/lib/aws_extensions/ec2/SecurityGroup.rb +16 -0
- data/lib/aws_extensions/ec2/Subnet.rb +28 -0
- data/lib/aws_extensions/ec2/Volume.rb +24 -0
- data/lib/aws_extensions/ec2/Vpc.rb +14 -0
- data/lib/aws_extensions/ec2/VpcEndpoint.rb +11 -0
- data/lib/aws_extensions/elb/BackendServerDescription.rb +12 -0
- data/lib/aws_extensions/elb/PolicyDescription.rb +14 -0
- data/lib/aws_extensions/kinesis/StreamDescription.rb +12 -0
- data/lib/aws_extensions/route53/AliasTarget.rb +21 -0
- data/lib/aws_extensions/s3/Bucket.rb +33 -0
- data/lib/aws_extensions/s3/BucketAcl.rb +28 -0
- data/lib/aws_extensions/s3/BucketCors.rb +17 -0
- data/lib/aws_extensions/s3/BucketLifecycle.rb +21 -0
- data/lib/aws_extensions/s3/BucketLogging.rb +18 -0
- data/lib/aws_extensions/s3/BucketNotification.rb +23 -0
- data/lib/aws_extensions/s3/BucketPolicy.rb +18 -0
- data/lib/aws_extensions/s3/BucketTagging.rb +15 -0
- data/lib/aws_extensions/s3/BucketVersioning.rb +14 -0
- data/lib/aws_extensions/s3/BucketWebsite.rb +49 -0
- data/lib/aws_extensions/s3/CORSRule.rb +27 -0
- data/lib/aws_extensions/s3/ReplicationConfiguration.rb +22 -0
- data/lib/cloudfront/CloudFront.rb +83 -0
- data/lib/cloudfront/loader/Loader.rb +31 -0
- data/lib/cloudfront/manager/Manager.rb +183 -0
- data/lib/cloudfront/models/CacheBehaviorConfig.rb +237 -0
- data/lib/cloudfront/models/CacheBehaviorDiff.rb +211 -0
- data/lib/cloudfront/models/CustomOriginConfig.rb +51 -0
- data/lib/cloudfront/models/CustomOriginDiff.rb +74 -0
- data/lib/cloudfront/models/DistributionConfig.rb +183 -0
- data/lib/cloudfront/models/DistributionDiff.rb +131 -0
- data/lib/cloudfront/models/InvalidationConfig.rb +37 -0
- data/lib/cloudfront/models/OriginConfig.rb +144 -0
- data/lib/cloudfront/models/OriginDiff.rb +86 -0
- data/lib/cloudfront/models/OriginSslProtocols.rb +28 -0
- data/lib/cloudfront/models/OriginSslProtocolsDiff.rb +39 -0
- data/lib/common/BaseLoader.rb +80 -0
- data/lib/common/manager/Manager.rb +148 -0
- data/lib/common/models/Diff.rb +114 -0
- data/lib/common/models/ListChange.rb +21 -0
- data/lib/common/models/TagsDiff.rb +55 -0
- data/lib/common/models/UTCTimeSource.rb +17 -0
- data/lib/conf/Configuration.rb +365 -0
- data/lib/ec2/EC2.rb +503 -0
- data/lib/ec2/IPProtocolMapping.rb +165 -0
- data/lib/ec2/loaders/EbsLoader.rb +19 -0
- data/lib/ec2/loaders/InstanceLoader.rb +32 -0
- data/lib/ec2/managers/EbsManager.rb +176 -0
- data/lib/ec2/managers/InstanceManager.rb +509 -0
- data/lib/ec2/models/EbsGroupConfig.rb +133 -0
- data/lib/ec2/models/EbsGroupDiff.rb +48 -0
- data/lib/ec2/models/InstanceConfig.rb +202 -0
- data/lib/ec2/models/InstanceDiff.rb +95 -0
- data/lib/elb/ELB.rb +148 -0
- data/lib/elb/loader/Loader.rb +65 -0
- data/lib/elb/manager/Manager.rb +581 -0
- data/lib/elb/models/AccessLogConfig.rb +82 -0
- data/lib/elb/models/AccessLogDiff.rb +47 -0
- data/lib/elb/models/HealthCheckConfig.rb +91 -0
- data/lib/elb/models/HealthCheckDiff.rb +50 -0
- data/lib/elb/models/ListenerConfig.rb +99 -0
- data/lib/elb/models/ListenerDiff.rb +91 -0
- data/lib/elb/models/LoadBalancerConfig.rb +239 -0
- data/lib/elb/models/LoadBalancerDiff.rb +265 -0
- data/lib/iam/IAM.rb +36 -0
- data/lib/iam/loader/Loader.rb +117 -0
- data/lib/iam/manager/IamGroups.rb +98 -0
- data/lib/iam/manager/IamResource.rb +288 -0
- data/lib/iam/manager/IamRoles.rb +112 -0
- data/lib/iam/manager/IamUsers.rb +54 -0
- data/lib/iam/manager/Manager.rb +29 -0
- data/lib/iam/migration/AssumeRoleUnifier.rb +34 -0
- data/lib/iam/migration/PolicyUnifier.rb +90 -0
- data/lib/iam/models/GroupConfig.rb +40 -0
- data/lib/iam/models/IamDiff.rb +132 -0
- data/lib/iam/models/PolicyConfig.rb +67 -0
- data/lib/iam/models/ResourceWithPolicy.rb +208 -0
- data/lib/iam/models/RoleConfig.rb +53 -0
- data/lib/iam/models/StatementConfig.rb +35 -0
- data/lib/iam/models/UserConfig.rb +21 -0
- data/lib/kinesis/Kinesis.rb +94 -0
- data/lib/kinesis/loader/Loader.rb +19 -0
- data/lib/kinesis/manager/Manager.rb +206 -0
- data/lib/kinesis/models/StreamConfig.rb +75 -0
- data/lib/kinesis/models/StreamDiff.rb +58 -0
- data/lib/lambda/Lambda.rb +41 -0
- data/lib/route53/loader/Loader.rb +32 -0
- data/lib/route53/manager/Manager.rb +241 -0
- data/lib/route53/models/AliasTarget.rb +86 -0
- data/lib/route53/models/RecordConfig.rb +178 -0
- data/lib/route53/models/RecordDiff.rb +140 -0
- data/lib/route53/models/Vpc.rb +24 -0
- data/lib/route53/models/ZoneConfig.rb +156 -0
- data/lib/route53/models/ZoneDiff.rb +118 -0
- data/lib/s3/S3.rb +89 -0
- data/lib/s3/loader/Loader.rb +66 -0
- data/lib/s3/manager/Manager.rb +296 -0
- data/lib/s3/models/BucketConfig.rb +321 -0
- data/lib/s3/models/BucketDiff.rb +167 -0
- data/lib/s3/models/GrantConfig.rb +189 -0
- data/lib/s3/models/GrantDiff.rb +50 -0
- data/lib/s3/models/LifecycleConfig.rb +142 -0
- data/lib/s3/models/LifecycleDiff.rb +46 -0
- data/lib/s3/models/LoggingConfig.rb +81 -0
- data/lib/s3/models/NotificationConfig.rb +157 -0
- data/lib/s3/models/NotificationDiff.rb +62 -0
- data/lib/s3/models/ReplicationConfig.rb +133 -0
- data/lib/s3/models/ReplicationDiff.rb +60 -0
- data/lib/s3/models/WebsiteConfig.rb +107 -0
- data/lib/security/SecurityGroups.rb +39 -0
- data/lib/security/loader/Loader.rb +94 -0
- data/lib/security/manager/Manager.rb +246 -0
- data/lib/security/models/RuleConfig.rb +161 -0
- data/lib/security/models/RuleDiff.rb +72 -0
- data/lib/security/models/RuleMigration.rb +127 -0
- data/lib/security/models/SecurityGroupConfig.rb +172 -0
- data/lib/security/models/SecurityGroupDiff.rb +112 -0
- data/lib/sns/SNS.rb +40 -0
- data/lib/sqs/SQS.rb +62 -0
- data/lib/sqs/loader/Loader.rb +34 -0
- data/lib/sqs/manager/Manager.rb +128 -0
- data/lib/sqs/models/DeadLetterConfig.rb +70 -0
- data/lib/sqs/models/DeadLetterDiff.rb +35 -0
- data/lib/sqs/models/QueueConfig.rb +115 -0
- data/lib/sqs/models/QueueDiff.rb +89 -0
- data/lib/util/Colors.rb +111 -0
- data/lib/util/StatusCodes.rb +51 -0
- data/lib/vpc/loader/Loader.rb +73 -0
- data/lib/vpc/manager/Manager.rb +954 -0
- data/lib/vpc/models/AclEntryConfig.rb +150 -0
- data/lib/vpc/models/AclEntryDiff.rb +54 -0
- data/lib/vpc/models/DhcpConfig.rb +100 -0
- data/lib/vpc/models/DhcpDiff.rb +90 -0
- data/lib/vpc/models/EndpointConfig.rb +76 -0
- data/lib/vpc/models/EndpointDiff.rb +69 -0
- data/lib/vpc/models/NetworkAclConfig.rb +87 -0
- data/lib/vpc/models/NetworkAclDiff.rb +116 -0
- data/lib/vpc/models/RouteConfig.rb +82 -0
- data/lib/vpc/models/RouteDiff.rb +50 -0
- data/lib/vpc/models/RouteTableConfig.rb +92 -0
- data/lib/vpc/models/RouteTableDiff.rb +101 -0
- data/lib/vpc/models/SubnetConfig.rb +113 -0
- data/lib/vpc/models/SubnetDiff.rb +78 -0
- data/lib/vpc/models/VpcConfig.rb +173 -0
- data/lib/vpc/models/VpcDiff.rb +315 -0
- data/rakefile.rb +8 -0
- metadata +245 -0
@@ -0,0 +1,80 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
module Cumulus
|
4
|
+
module Common
|
5
|
+
# Public: A module that handles loading all the 4 configuration files and
|
6
|
+
# creating objects from them.
|
7
|
+
module BaseLoader
|
8
|
+
# Internal: Load the resources in a directory, handling each file with the
|
9
|
+
# function passed in.
|
10
|
+
#
|
11
|
+
# dir - the directory to load resources from
|
12
|
+
# json - indicates if the resources are in json format
|
13
|
+
# individual_loader - the function that loads a resource from each file name
|
14
|
+
#
|
15
|
+
# Returns an array of resources
|
16
|
+
def self.resources(dir, json = true, &individual_loader)
|
17
|
+
Dir.entries(dir)
|
18
|
+
.reject { |f| f == "." or f == ".." or File.directory?(File.join(dir, f)) }
|
19
|
+
.map { |f| resource(f, dir, json, &individual_loader) }.reject(&:nil?)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Internal: Load the resource, passing the parsed JSON to the function passed
|
23
|
+
# in
|
24
|
+
#
|
25
|
+
# file - the name of the file to load
|
26
|
+
# dir - the directory the file is located in
|
27
|
+
# json - indicates if the resources are in json format
|
28
|
+
# loader - the function that will handle the read json
|
29
|
+
def self.resource(file, dir, json = true, &loader)
|
30
|
+
name = file.end_with?(".json") ? file[0...-5] : file
|
31
|
+
|
32
|
+
begin
|
33
|
+
contents = load_file(file, dir)
|
34
|
+
loader.call(
|
35
|
+
name,
|
36
|
+
if json then JSON.parse(contents) else contents end
|
37
|
+
)
|
38
|
+
rescue => e
|
39
|
+
puts "Unable to load resource #{file}: #{e}"
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Internal: Load the template, apply variables, and pass the parsed JSON to
|
45
|
+
# the function passed in
|
46
|
+
#
|
47
|
+
# file - the name of the file to load
|
48
|
+
# dir - the directory the file is located in
|
49
|
+
# vars - the variables to apply to the template
|
50
|
+
# loader - the function that will handle the read json
|
51
|
+
def self.template(file, dir, vars, &loader)
|
52
|
+
template = load_file(file, dir)
|
53
|
+
vars.each do |key, value|
|
54
|
+
template.gsub!("{{#{key}}}", "#{value}")
|
55
|
+
end
|
56
|
+
json = JSON.parse(template)
|
57
|
+
|
58
|
+
loader.call(nil, json)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Internal: Load a file. Will check if a file exists by the name passed in, or
|
62
|
+
# with a .json extension.
|
63
|
+
#
|
64
|
+
# file - the name of the file to load
|
65
|
+
# dir - the directory the file is located in
|
66
|
+
#
|
67
|
+
# Returns the contents of the file
|
68
|
+
def self.load_file(file, dir)
|
69
|
+
path = File.join(dir, file)
|
70
|
+
if File.exist?(path)
|
71
|
+
File.read(path)
|
72
|
+
elsif File.exist?("#{path}.json")
|
73
|
+
File.read("#{path}.json")
|
74
|
+
else
|
75
|
+
throw "File does not exist: #{path}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require "common/models/Diff"
|
2
|
+
require "util/Colors"
|
3
|
+
require "util/StatusCodes"
|
4
|
+
|
5
|
+
module Cumulus
|
6
|
+
module Common
|
7
|
+
# Public: Base class for AWS resource manager classes.
|
8
|
+
#
|
9
|
+
# Classes that extend this class should provide the following methods:
|
10
|
+
#
|
11
|
+
# resource_name - return the resource name type (ie "Autoscaling Group", "Security Group", etc)
|
12
|
+
# local_resources - return a Hash of local resource name to local resource config object
|
13
|
+
# aws_resources - return a Hash of aws resource name to aws resource object
|
14
|
+
# diff_resource - a function that will produce an array of differences between the local resource
|
15
|
+
# passed in and the aws resource passed in
|
16
|
+
# unmanaged_diff - return the correct type of diff from an AWS resource
|
17
|
+
# added_diff - return the correct type of diff from a local configuration object
|
18
|
+
# create - given a local configuration, create the AWS resource
|
19
|
+
# update - given a local configuration and an array of diffs, update the AWS resource
|
20
|
+
#
|
21
|
+
# Additionally, the following instance variables can be set to change the behavior of the manager:
|
22
|
+
#
|
23
|
+
# create_asset - if true, the asset will be created, if false, a warning will be printed about
|
24
|
+
# the asset not being created
|
25
|
+
class Manager
|
26
|
+
def initialize
|
27
|
+
@migration_root = "generated"
|
28
|
+
@create_asset = true
|
29
|
+
end
|
30
|
+
|
31
|
+
# Public: Print a diff between local configuration and configuration in AWS
|
32
|
+
def diff
|
33
|
+
each_difference(local_resources, true) { |key, diffs| print_difference(key, diffs) }
|
34
|
+
end
|
35
|
+
|
36
|
+
# Public: Print the diff between local configuration and AWS for a single resource
|
37
|
+
#
|
38
|
+
# name - the name of the resource to diff
|
39
|
+
def diff_one(name)
|
40
|
+
each_difference(filter_local(name), false) { |key, diffs| print_difference(key, diffs) }
|
41
|
+
end
|
42
|
+
|
43
|
+
# Public: Print out the names of all resources managed by Cumulus
|
44
|
+
def list
|
45
|
+
puts local_resources.map { |key, l| l.name }.join(" ")
|
46
|
+
end
|
47
|
+
|
48
|
+
# Public: Sync local configuration to AWS
|
49
|
+
def sync
|
50
|
+
each_difference(local_resources, true) { |key, diffs| sync_difference(key, diffs) }
|
51
|
+
end
|
52
|
+
|
53
|
+
# Public: Sync local configuration to AWS for a single resource
|
54
|
+
#
|
55
|
+
# name - the name of the resource to sync
|
56
|
+
def sync_one(name)
|
57
|
+
each_difference(filter_local(name), false) { |key, diffs| sync_difference(key, diffs) }
|
58
|
+
end
|
59
|
+
|
60
|
+
# Public: Select local resources based on name
|
61
|
+
def filter_local(name)
|
62
|
+
local_resources.reject { |key, l| l.name != name }
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# Internal: Loop through the differences between local configuration and AWS
|
68
|
+
#
|
69
|
+
# locals - the local configurations to compare against
|
70
|
+
# include_unmanaged - whether to include unmanaged resources in the list of changes
|
71
|
+
# f - a function that will be passed the name of the resource and an array of
|
72
|
+
# diffs
|
73
|
+
def each_difference(locals, include_unmanaged, &f)
|
74
|
+
|
75
|
+
unmanaged = if include_unmanaged
|
76
|
+
Hash[aws_resources.map do |key, resource|
|
77
|
+
[key, [unmanaged_diff(resource)]] if !locals.include?(key)
|
78
|
+
end.compact]
|
79
|
+
else
|
80
|
+
{}
|
81
|
+
end
|
82
|
+
|
83
|
+
managed = Hash[locals.map do |key, resource|
|
84
|
+
if !aws_resources.include?(key)
|
85
|
+
[key, [added_diff(resource)]]
|
86
|
+
else
|
87
|
+
[key, diff_resource(resource, aws_resources[key])]
|
88
|
+
end
|
89
|
+
end]
|
90
|
+
|
91
|
+
combined = unmanaged.merge(managed)
|
92
|
+
sorted_keys = combined.keys.sort
|
93
|
+
sorted_keys.each do |key|
|
94
|
+
f.call(key, combined[key])
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Internal: Print differences.
|
99
|
+
#
|
100
|
+
# key - the name of the resource to print
|
101
|
+
# diffs - the differences between local configuration and AWS
|
102
|
+
def print_difference(key, diffs)
|
103
|
+
if diffs.size > 0
|
104
|
+
|
105
|
+
if diffs.reject(&:info_only).size > 0
|
106
|
+
StatusCodes::set_status(StatusCodes::DIFFS)
|
107
|
+
end
|
108
|
+
|
109
|
+
if diffs.size == 1 and (diffs[0].type == DiffChange::ADD or
|
110
|
+
diffs[0].type == DiffChange::UNMANAGED)
|
111
|
+
puts diffs[0]
|
112
|
+
else
|
113
|
+
puts "#{resource_name} #{local_resources[key].name} has the following changes:"
|
114
|
+
diffs.each do |diff|
|
115
|
+
diff_string = diff.to_s.lines.map { |s| "\t#{s}" }.join
|
116
|
+
puts diff_string
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Internal: Sync differences.
|
123
|
+
#
|
124
|
+
# key - the name of the resource to sync
|
125
|
+
# diffs - the differences between local configuration and AWS
|
126
|
+
def sync_difference(key, diffs)
|
127
|
+
if diffs.size > 0
|
128
|
+
|
129
|
+
StatusCodes::set_status(StatusCodes::SYNC_DIFFS)
|
130
|
+
|
131
|
+
if diffs[0].type == DiffChange::UNMANAGED
|
132
|
+
puts diffs[0]
|
133
|
+
elsif diffs[0].type == DiffChange::ADD
|
134
|
+
if @create_asset
|
135
|
+
puts Colors.added("creating #{local_resources[key].name}...")
|
136
|
+
create(local_resources[key])
|
137
|
+
else
|
138
|
+
puts "not creating #{local_resources[key].name}..."
|
139
|
+
end
|
140
|
+
else
|
141
|
+
puts Colors.blue("updating #{local_resources[key].name}...")
|
142
|
+
update(local_resources[key], diffs)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require "util/Colors"
|
2
|
+
|
3
|
+
module Cumulus
|
4
|
+
module Common
|
5
|
+
# Public: The types of changes common to all Diffs
|
6
|
+
module DiffChange
|
7
|
+
@@current = 0
|
8
|
+
|
9
|
+
# Public: Produce the next id for a change type. Use this to avoid id
|
10
|
+
# collisions.
|
11
|
+
#
|
12
|
+
# Returns the new id
|
13
|
+
def self.next_change_id
|
14
|
+
@@current += 1
|
15
|
+
@@current
|
16
|
+
end
|
17
|
+
|
18
|
+
ADD = next_change_id
|
19
|
+
UNMANAGED = next_change_id
|
20
|
+
MODIFIED = next_change_id
|
21
|
+
end
|
22
|
+
|
23
|
+
# Public: The base class for all Diff classes.
|
24
|
+
#
|
25
|
+
# To extend this class, do the following:
|
26
|
+
#
|
27
|
+
# 1. Provide a `diff_string` method. This method will be called if the default
|
28
|
+
# to_s method cannot produce a result.
|
29
|
+
# 2. Provide a `asset_type` method. This method should return the string type of
|
30
|
+
# asset for which this is a diff.
|
31
|
+
# 3. Provide an `aws_name` method. This method should give back the string name
|
32
|
+
# of the aws asset.
|
33
|
+
# 4. (Optional) Replace the existing `local_name` method. This method produces the string name
|
34
|
+
# of the local asset. Defaults to `name` on the local asset.
|
35
|
+
class Diff
|
36
|
+
include DiffChange
|
37
|
+
|
38
|
+
attr_reader :aws, :local, :type
|
39
|
+
attr_accessor :changes, :info_only
|
40
|
+
|
41
|
+
# Public: Static method that will produce an "unmanaged" diff
|
42
|
+
#
|
43
|
+
# aws - the aws resource that is unmanaged
|
44
|
+
#
|
45
|
+
# Returns the diff
|
46
|
+
def self.unmanaged(aws)
|
47
|
+
self.new(UNMANAGED, aws)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Public: Static method that will produce an "added" diff
|
51
|
+
#
|
52
|
+
# local - the local configuration that is added
|
53
|
+
#
|
54
|
+
# Returns the diff
|
55
|
+
def self.added(local)
|
56
|
+
self.new(ADD, nil, local)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Public: Static method that will produce a "modified" diff
|
60
|
+
#
|
61
|
+
# local - the local configuration
|
62
|
+
# aws - the aws resource
|
63
|
+
# changes - an object describing what was modified
|
64
|
+
def self.modified(aws, local, changes)
|
65
|
+
self.new(MODIFIED, aws, local, changes)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Public: Constructor
|
69
|
+
#
|
70
|
+
# type - the type of the difference
|
71
|
+
# aws - the aws resource that's different (defaults to nil)
|
72
|
+
# local - the local resource that's difference (defaults to nil)
|
73
|
+
# changes - an object to describe what changed in a MODIFIED diff (defaults to nil)
|
74
|
+
def initialize(type, aws = nil, local = nil, changes = nil)
|
75
|
+
@aws = aws
|
76
|
+
@local = local
|
77
|
+
@type = type
|
78
|
+
@changes = changes
|
79
|
+
@info_only = false
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_s
|
83
|
+
case @type
|
84
|
+
when ADD
|
85
|
+
Colors.added("#{asset_type} #{local_name} #{add_string}")
|
86
|
+
when UNMANAGED
|
87
|
+
Colors.unmanaged("#{asset_type} #{aws_name} #{unmanaged_string}")
|
88
|
+
else
|
89
|
+
diff_string
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Public: A method that produces the string that describes what will be done with new assets.
|
94
|
+
# This can be overridden for the case that the ADD case doesn't create the asset.
|
95
|
+
#
|
96
|
+
# Returns the string describing the action that will be taken.
|
97
|
+
def add_string
|
98
|
+
"will be created."
|
99
|
+
end
|
100
|
+
|
101
|
+
# Public: A method that produces the string that describes what will be done with unmanaged
|
102
|
+
# assets. This can be overriden for the case that the UNMANAGED case does not ignore the asset.
|
103
|
+
#
|
104
|
+
# Returns the string describing the action that will be taken
|
105
|
+
def unmanaged_string
|
106
|
+
"is not managed by Cumulus."
|
107
|
+
end
|
108
|
+
|
109
|
+
def local_name
|
110
|
+
@local.name
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Cumulus
|
2
|
+
module Common
|
3
|
+
ListChange = Struct.new(:added, :removed, :modified) do
|
4
|
+
# Public: Creates a ListChange from aws and local arrays with simple types
|
5
|
+
# where the ListChange only has added and removed
|
6
|
+
def self.simple_list_diff(aws, local)
|
7
|
+
added = local - aws
|
8
|
+
removed = aws - local
|
9
|
+
|
10
|
+
if !added.empty? or !removed.empty?
|
11
|
+
ListChange.new(added, removed, nil)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Public: Returns true if all of added, removed, and modified are either nil or empty
|
16
|
+
def empty?
|
17
|
+
(self.added.nil? or self.added.empty?) and (self.removed.nil? or self.removed.empty?) and (self.modified.nil? or self.modified.empty?)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Cumulus
|
2
|
+
module Common
|
3
|
+
# Public: A module to be mixed in to Diff classes that have tags
|
4
|
+
module TagsDiff
|
5
|
+
# Public: Get the diff string for tag differences
|
6
|
+
#
|
7
|
+
# Returns the string to display
|
8
|
+
def tags_diff_string
|
9
|
+
lines = ["Tags:"]
|
10
|
+
lines << tags_to_remove.map { |k, v| "\t#{Colors.removed("#{k} => #{v}")}" }
|
11
|
+
lines << tags_to_add.map { |k, v| "\t#{Colors.added("#{k} => #{v}")}" }
|
12
|
+
lines.flatten.join("\n")
|
13
|
+
end
|
14
|
+
|
15
|
+
# Public: Get the tags that are in AWS that are not in local configuration
|
16
|
+
#
|
17
|
+
# Returns a hash of tags
|
18
|
+
def tags_to_remove
|
19
|
+
aws_tags.reject { |t, v| local_tags.include?(t) and local_tags[t] == v }
|
20
|
+
end
|
21
|
+
|
22
|
+
# Public: Get the tags that are in local configuration but not in AWS
|
23
|
+
#
|
24
|
+
# Returns a hash of tags
|
25
|
+
def tags_to_add
|
26
|
+
local_tags.reject { |t, v| aws_tags.include?(t) and aws_tags[t] == v }
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# Internal: Override this method if the tags are not found on the tags attribute
|
32
|
+
# of the local object
|
33
|
+
#
|
34
|
+
# Returns the tags
|
35
|
+
def local_tags
|
36
|
+
@local.tags
|
37
|
+
end
|
38
|
+
|
39
|
+
# Internal: Get the tags in AWS as a hash of key to value
|
40
|
+
#
|
41
|
+
# Returns a hash of tags
|
42
|
+
def aws_tags
|
43
|
+
@aws_tags ||= Hash[aws_tags_list.map { |tag| [tag.key, tag.value] }]
|
44
|
+
end
|
45
|
+
|
46
|
+
# Internal: Override this method if tags are not found on the tags attribute of
|
47
|
+
# the aws object.
|
48
|
+
#
|
49
|
+
# Returns the tags
|
50
|
+
def aws_tags_list
|
51
|
+
@aws.tags
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,365 @@
|
|
1
|
+
require "json"
|
2
|
+
require "aws-sdk"
|
3
|
+
|
4
|
+
module Cumulus
|
5
|
+
# Public: A module that contains helper methods for the configuration classes.
|
6
|
+
#
|
7
|
+
# When mixing in this module, make sure your class has a @node instance variable
|
8
|
+
# for what node in the json it expect to get config from, ie. "s3" or "iam"
|
9
|
+
module Config
|
10
|
+
@@json = nil
|
11
|
+
@@conf_dir = nil
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def json
|
15
|
+
@@json
|
16
|
+
end
|
17
|
+
|
18
|
+
def json=(value)
|
19
|
+
@@json = value
|
20
|
+
end
|
21
|
+
|
22
|
+
def conf_dir
|
23
|
+
@@conf_dir
|
24
|
+
end
|
25
|
+
|
26
|
+
def conf_dir=(value)
|
27
|
+
@@conf_dir = value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Internal: Take a path relative to the project root and turn it into an
|
34
|
+
# absolute path
|
35
|
+
#
|
36
|
+
# relative_path - The String path from `conf_dir` to the desired file
|
37
|
+
#
|
38
|
+
# Returns the absolute path as a String
|
39
|
+
def absolute_path(relative_path)
|
40
|
+
if relative_path.start_with?("/")
|
41
|
+
relative_path
|
42
|
+
else
|
43
|
+
File.join(@@conf_dir, relative_path)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Internal: Handle any KeyErrors that occur while getting a configuration value
|
48
|
+
# by printing out a message describing the missing key and exiting.
|
49
|
+
#
|
50
|
+
# key - the full key to get ex. `s3.buckets.directory`
|
51
|
+
# allow_missing - if true we will return nil for missing values instead of exiting
|
52
|
+
# handler - a block that will do additional processing on the key. If nil,
|
53
|
+
# the value is returned as is.
|
54
|
+
#
|
55
|
+
# Returns the configuration value if successful
|
56
|
+
def conf(key, allow_missing = false, &handler)
|
57
|
+
value = nil
|
58
|
+
key.split(".").each do |part|
|
59
|
+
if value
|
60
|
+
value = value.fetch(part)
|
61
|
+
else
|
62
|
+
value = @@json.fetch(part)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
if handler
|
67
|
+
handler.call(value)
|
68
|
+
else
|
69
|
+
value
|
70
|
+
end
|
71
|
+
rescue KeyError => e
|
72
|
+
puts "Your configuration file is missing $.#{key}."
|
73
|
+
if allow_missing
|
74
|
+
nil
|
75
|
+
else
|
76
|
+
exit
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
# Public: Contains the configuration values set in the configuration.json file.
|
83
|
+
# Provides a Singleton that can be accessed throughout the application.
|
84
|
+
class Configuration
|
85
|
+
include Config
|
86
|
+
|
87
|
+
attr_reader :colors_enabled
|
88
|
+
attr_reader :iam, :autoscaling, :route53, :s3, :security, :cloudfront, :elb, :vpc, :kinesis, :sqs, :ec2
|
89
|
+
attr_reader :client
|
90
|
+
|
91
|
+
# Internal: Constructor. Sets up the `instance` variable, which is the access
|
92
|
+
# point for the Singleton.
|
93
|
+
#
|
94
|
+
# conf_dir - The String path to the directory the configuration can be found in
|
95
|
+
# profile - The String profile name that will be used to make AWS API calls
|
96
|
+
# assume_role - The ARN of the role to assume when making AWS API calls
|
97
|
+
# autoscaling_force_size
|
98
|
+
# - Determines whether autoscaling should use configured values for
|
99
|
+
# min/max/desired group size
|
100
|
+
def initialize(conf_dir, profile, assume_role, autoscaling_force_size)
|
101
|
+
Config.conf_dir = conf_dir;
|
102
|
+
Config.json = JSON.parse(File.read(absolute_path("configuration.json")))
|
103
|
+
@colors_enabled = conf "colors-enabled"
|
104
|
+
@iam = IamConfig.new
|
105
|
+
@autoscaling = AutoScalingConfig.new(autoscaling_force_size)
|
106
|
+
@route53 = Route53Config.new
|
107
|
+
@security = SecurityConfig.new
|
108
|
+
@cloudfront = CloudFrontConfig.new
|
109
|
+
@s3 = S3Config.new
|
110
|
+
@elb = ELBConfig.new
|
111
|
+
@vpc = VpcConfig.new
|
112
|
+
@kinesis = KinesisConfig.new
|
113
|
+
@sqs = SQSConfig.new
|
114
|
+
@ec2 = EC2Config.new
|
115
|
+
|
116
|
+
region = conf "region"
|
117
|
+
credentials = if assume_role
|
118
|
+
Aws::AssumeRoleCredentials.new(
|
119
|
+
client: Aws::STS::Client.new(profile: profile, region: region),
|
120
|
+
role_arn: assume_role,
|
121
|
+
role_session_name: "#{region}-#{@profile}"
|
122
|
+
)
|
123
|
+
end
|
124
|
+
|
125
|
+
@client = {
|
126
|
+
:region => region,
|
127
|
+
:profile => profile,
|
128
|
+
:credentials => credentials,
|
129
|
+
}.reject { |_, v| v.nil? }
|
130
|
+
end
|
131
|
+
|
132
|
+
class << self
|
133
|
+
# Public: Initialize the Configuration Singleton. Must be called before any
|
134
|
+
# access to `Configuration.instance` is used.
|
135
|
+
#
|
136
|
+
# conf_dir - The String path to the directory the configuration can be found in
|
137
|
+
# profile - The String profile name that will be used to make AWS API calls
|
138
|
+
# assume_role - The ARN of the role to assume when making AWS API calls
|
139
|
+
# autoscaling_force_size
|
140
|
+
# - Determines whether autoscaling should use configured values for
|
141
|
+
# min/max/desired group size
|
142
|
+
def init(conf_dir, profile, assume_role, autoscaling_force_size)
|
143
|
+
instance = new(conf_dir, profile, assume_role, autoscaling_force_size)
|
144
|
+
@@instance = instance
|
145
|
+
end
|
146
|
+
|
147
|
+
# Public: The Singleton instance of Configuration.
|
148
|
+
#
|
149
|
+
# Returns the Configuration instance.
|
150
|
+
def instance
|
151
|
+
@@instance
|
152
|
+
end
|
153
|
+
|
154
|
+
private :new
|
155
|
+
end
|
156
|
+
|
157
|
+
# Public: Inner class that contains IAM configuration options
|
158
|
+
class IamConfig
|
159
|
+
include Config
|
160
|
+
|
161
|
+
attr_reader :groups_directory
|
162
|
+
attr_reader :policy_document_directory
|
163
|
+
attr_reader :policy_prefix
|
164
|
+
attr_reader :policy_suffix
|
165
|
+
attr_reader :policy_version
|
166
|
+
attr_reader :roles_directory
|
167
|
+
attr_reader :static_policy_directory
|
168
|
+
attr_reader :template_policy_directory
|
169
|
+
attr_reader :users_directory
|
170
|
+
|
171
|
+
# Public: Constructor.
|
172
|
+
def initialize
|
173
|
+
@groups_directory = absolute_path "iam/groups"
|
174
|
+
@policy_document_directory = absolute_path "iam/roles/policy-documents"
|
175
|
+
@policy_prefix = conf "iam.policies.prefix"
|
176
|
+
@policy_suffix = conf "iam.policies.suffix"
|
177
|
+
@policy_version = conf "iam.policies.version"
|
178
|
+
@roles_directory = absolute_path "iam/roles"
|
179
|
+
@static_policy_directory = absolute_path "iam/policies/static"
|
180
|
+
@template_policy_directory = absolute_path "iam/policies/template"
|
181
|
+
@users_directory = absolute_path "iam/users"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Public: Inner class that contains AutoScaling configuration options
|
186
|
+
class AutoScalingConfig
|
187
|
+
include Config
|
188
|
+
|
189
|
+
attr_reader :groups_directory
|
190
|
+
attr_reader :override_launch_config_on_sync
|
191
|
+
attr_reader :static_policy_directory
|
192
|
+
attr_reader :template_policy_directory
|
193
|
+
attr_reader :force_size
|
194
|
+
|
195
|
+
# Public: Constructor.
|
196
|
+
def initialize(force_size = false)
|
197
|
+
@groups_directory = absolute_path "autoscaling/groups"
|
198
|
+
@override_launch_config_on_sync = conf "autoscaling.groups.override-launch-config-on-sync"
|
199
|
+
@static_policy_directory = absolute_path "autoscaling/policies/static"
|
200
|
+
@template_policy_directory = absolute_path "autoscaling/policies/templates"
|
201
|
+
@force_size = force_size
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
205
|
+
|
206
|
+
# Public: Inner class that contains Route53 configuration options
|
207
|
+
class Route53Config
|
208
|
+
include Config
|
209
|
+
|
210
|
+
attr_reader :includes_directory
|
211
|
+
attr_reader :print_all_ignored
|
212
|
+
attr_reader :zones_directory
|
213
|
+
|
214
|
+
# Public: Constructor
|
215
|
+
def initialize
|
216
|
+
@includes_directory = absolute_path "route53/includes"
|
217
|
+
@print_all_ignored = conf "route53.print-all-ignored"
|
218
|
+
@zones_directory = absolute_path "route53/zones"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
# Public: Inner class that contains S3 configuration options
|
223
|
+
class S3Config
|
224
|
+
include Config
|
225
|
+
|
226
|
+
attr_reader :buckets_directory
|
227
|
+
attr_reader :cors_directory
|
228
|
+
attr_reader :policies_directory
|
229
|
+
attr_reader :print_progress
|
230
|
+
|
231
|
+
# Public: Constructor
|
232
|
+
def initialize
|
233
|
+
@node = "s3"
|
234
|
+
@buckets_directory = absolute_path "s3/buckets"
|
235
|
+
@cors_directory = absolute_path "s3/cors"
|
236
|
+
@policies_directory = absolute_path "s3/policies"
|
237
|
+
@print_progress = conf "s3.print-progress"
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
# Public: Inner class that contains Security Group configuration options
|
242
|
+
class SecurityConfig
|
243
|
+
include Config
|
244
|
+
|
245
|
+
attr_reader :groups_directory
|
246
|
+
attr_reader :rules_directory
|
247
|
+
attr_reader :outbound_default_all_allowed
|
248
|
+
attr_reader :subnet_files
|
249
|
+
|
250
|
+
# Public: Constructor.
|
251
|
+
def initialize
|
252
|
+
@groups_directory = absolute_path "security-groups/groups"
|
253
|
+
@rules_directory = absolute_path "security-groups/rules"
|
254
|
+
@outbound_default_all_allowed = conf "security.outbound-default-all-allowed"
|
255
|
+
@subnet_files = conf("security.subnet-files", true) { |paths| paths.map{ |p| absolute_path(p) } }
|
256
|
+
|
257
|
+
if !@subnet_files
|
258
|
+
default_file = absolute_path("security-groups/subnets.json")
|
259
|
+
@subnet_files = [default_file]
|
260
|
+
puts "Using default subnets file at #{default_file}"
|
261
|
+
end
|
262
|
+
|
263
|
+
end
|
264
|
+
|
265
|
+
end
|
266
|
+
|
267
|
+
# Public: Inner class that contains cloudfront configuration options
|
268
|
+
class CloudFrontConfig
|
269
|
+
include Config
|
270
|
+
|
271
|
+
attr_reader :distributions_directory
|
272
|
+
attr_reader :invalidations_directory
|
273
|
+
|
274
|
+
def initialize
|
275
|
+
@distributions_directory = absolute_path "cloudfront/distributions"
|
276
|
+
@invalidations_directory = absolute_path "cloudfront/invalidations"
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
# Public: Inner class that contains elb configuration options
|
281
|
+
class ELBConfig
|
282
|
+
include Config
|
283
|
+
|
284
|
+
attr_reader :load_balancers_directory
|
285
|
+
attr_reader :listeners_directory
|
286
|
+
attr_reader :policies_directory
|
287
|
+
|
288
|
+
def initialize
|
289
|
+
@load_balancers_directory = absolute_path "elb/load-balancers"
|
290
|
+
@listeners_directory = absolute_path "elb/listeners"
|
291
|
+
@policies_directory = absolute_path "elb/policies"
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
# Public: Inner class that contains vpc configuration options
|
296
|
+
class VpcConfig
|
297
|
+
include Config
|
298
|
+
|
299
|
+
attr_reader :vpcs_directory
|
300
|
+
attr_reader :subnets_directory
|
301
|
+
attr_reader :route_tables_directory
|
302
|
+
attr_reader :policies_directory
|
303
|
+
attr_reader :network_acls_directory
|
304
|
+
|
305
|
+
def initialize
|
306
|
+
@vpcs_directory = absolute_path "vpc/vpcs"
|
307
|
+
@subnets_directory = absolute_path "vpc/subnets"
|
308
|
+
@route_tables_directory = absolute_path "vpc/route-tables"
|
309
|
+
@policies_directory = absolute_path "vpc/policies"
|
310
|
+
@network_acls_directory = absolute_path "vpc/network-acls"
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
# Public: Inner class that contains kinesis configuration options
|
315
|
+
class KinesisConfig
|
316
|
+
include Config
|
317
|
+
|
318
|
+
attr_reader :directory
|
319
|
+
|
320
|
+
def initialize
|
321
|
+
@directory = absolute_path "kinesis"
|
322
|
+
end
|
323
|
+
|
324
|
+
end
|
325
|
+
|
326
|
+
# Public: Inner class that contains SQS configuration options
|
327
|
+
class SQSConfig
|
328
|
+
include Config
|
329
|
+
|
330
|
+
attr_reader :queues_directory
|
331
|
+
attr_reader :policies_directory
|
332
|
+
|
333
|
+
def initialize
|
334
|
+
@queues_directory = absolute_path "sqs/queues"
|
335
|
+
@policies_directory = absolute_path "sqs/policies"
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
# Public: Inner class that contains EC2 configuration options
|
340
|
+
class EC2Config
|
341
|
+
include Config
|
342
|
+
|
343
|
+
attr_reader :ebs_directory
|
344
|
+
attr_reader :instances_directory
|
345
|
+
attr_reader :ignore_unmanaged_instances
|
346
|
+
attr_reader :user_data_directory
|
347
|
+
attr_reader :default_image_id
|
348
|
+
attr_reader :volume_mount_base
|
349
|
+
attr_reader :volume_mount_start
|
350
|
+
attr_reader :volume_mount_end
|
351
|
+
|
352
|
+
def initialize
|
353
|
+
@ebs_directory = absolute_path "ec2/ebs"
|
354
|
+
@instances_directory = absolute_path "ec2/instances"
|
355
|
+
@user_data_directory = absolute_path "ec2/user-data-scripts"
|
356
|
+
@ignore_unmanaged_instances = conf "ec2.instances.ignore-unmanaged"
|
357
|
+
@default_image_id = conf "ec2.instances.default-image-id"
|
358
|
+
@volume_mount_base = conf "ec2.instances.volume-mounting.base"
|
359
|
+
@volume_mount_start = conf "ec2.instances.volume-mounting.start"
|
360
|
+
@volume_mount_end = conf "ec2.instances.volume-mounting.end"
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
end
|
365
|
+
end
|