cumulus-aws 0.11.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.
Files changed (173) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +3 -0
  3. data/.travis.yml +12 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +29 -0
  6. data/LICENSE +202 -0
  7. data/README.md +41 -0
  8. data/autocomplete +137 -0
  9. data/bin/cumulus +658 -0
  10. data/cumulus +2 -0
  11. data/cumulus-aws.gemspec +20 -0
  12. data/lib/autoscaling/AutoScaling.rb +40 -0
  13. data/lib/autoscaling/loader/Loader.rb +56 -0
  14. data/lib/autoscaling/manager/Manager.rb +360 -0
  15. data/lib/autoscaling/models/AlarmConfig.rb +165 -0
  16. data/lib/autoscaling/models/AlarmDiff.rb +172 -0
  17. data/lib/autoscaling/models/AutoScalingDiff.rb +178 -0
  18. data/lib/autoscaling/models/GroupConfig.rb +330 -0
  19. data/lib/autoscaling/models/PolicyConfig.rb +135 -0
  20. data/lib/autoscaling/models/PolicyDiff.rb +73 -0
  21. data/lib/autoscaling/models/ScheduledActionDiff.rb +53 -0
  22. data/lib/autoscaling/models/ScheduledConfig.rb +96 -0
  23. data/lib/aws_extensions/ec2/DhcpOptions.rb +41 -0
  24. data/lib/aws_extensions/ec2/Instance.rb +29 -0
  25. data/lib/aws_extensions/ec2/NetworkAcl.rb +25 -0
  26. data/lib/aws_extensions/ec2/NetworkInterface.rb +14 -0
  27. data/lib/aws_extensions/ec2/RouteTable.rb +26 -0
  28. data/lib/aws_extensions/ec2/SecurityGroup.rb +16 -0
  29. data/lib/aws_extensions/ec2/Subnet.rb +28 -0
  30. data/lib/aws_extensions/ec2/Volume.rb +24 -0
  31. data/lib/aws_extensions/ec2/Vpc.rb +14 -0
  32. data/lib/aws_extensions/ec2/VpcEndpoint.rb +11 -0
  33. data/lib/aws_extensions/elb/BackendServerDescription.rb +12 -0
  34. data/lib/aws_extensions/elb/PolicyDescription.rb +14 -0
  35. data/lib/aws_extensions/kinesis/StreamDescription.rb +12 -0
  36. data/lib/aws_extensions/route53/AliasTarget.rb +21 -0
  37. data/lib/aws_extensions/s3/Bucket.rb +33 -0
  38. data/lib/aws_extensions/s3/BucketAcl.rb +28 -0
  39. data/lib/aws_extensions/s3/BucketCors.rb +17 -0
  40. data/lib/aws_extensions/s3/BucketLifecycle.rb +21 -0
  41. data/lib/aws_extensions/s3/BucketLogging.rb +18 -0
  42. data/lib/aws_extensions/s3/BucketNotification.rb +23 -0
  43. data/lib/aws_extensions/s3/BucketPolicy.rb +18 -0
  44. data/lib/aws_extensions/s3/BucketTagging.rb +15 -0
  45. data/lib/aws_extensions/s3/BucketVersioning.rb +14 -0
  46. data/lib/aws_extensions/s3/BucketWebsite.rb +49 -0
  47. data/lib/aws_extensions/s3/CORSRule.rb +27 -0
  48. data/lib/aws_extensions/s3/ReplicationConfiguration.rb +22 -0
  49. data/lib/cloudfront/CloudFront.rb +83 -0
  50. data/lib/cloudfront/loader/Loader.rb +31 -0
  51. data/lib/cloudfront/manager/Manager.rb +183 -0
  52. data/lib/cloudfront/models/CacheBehaviorConfig.rb +237 -0
  53. data/lib/cloudfront/models/CacheBehaviorDiff.rb +211 -0
  54. data/lib/cloudfront/models/CustomOriginConfig.rb +51 -0
  55. data/lib/cloudfront/models/CustomOriginDiff.rb +74 -0
  56. data/lib/cloudfront/models/DistributionConfig.rb +183 -0
  57. data/lib/cloudfront/models/DistributionDiff.rb +131 -0
  58. data/lib/cloudfront/models/InvalidationConfig.rb +37 -0
  59. data/lib/cloudfront/models/OriginConfig.rb +144 -0
  60. data/lib/cloudfront/models/OriginDiff.rb +86 -0
  61. data/lib/cloudfront/models/OriginSslProtocols.rb +28 -0
  62. data/lib/cloudfront/models/OriginSslProtocolsDiff.rb +39 -0
  63. data/lib/common/BaseLoader.rb +80 -0
  64. data/lib/common/manager/Manager.rb +148 -0
  65. data/lib/common/models/Diff.rb +114 -0
  66. data/lib/common/models/ListChange.rb +21 -0
  67. data/lib/common/models/TagsDiff.rb +55 -0
  68. data/lib/common/models/UTCTimeSource.rb +17 -0
  69. data/lib/conf/Configuration.rb +365 -0
  70. data/lib/ec2/EC2.rb +503 -0
  71. data/lib/ec2/IPProtocolMapping.rb +165 -0
  72. data/lib/ec2/loaders/EbsLoader.rb +19 -0
  73. data/lib/ec2/loaders/InstanceLoader.rb +32 -0
  74. data/lib/ec2/managers/EbsManager.rb +176 -0
  75. data/lib/ec2/managers/InstanceManager.rb +509 -0
  76. data/lib/ec2/models/EbsGroupConfig.rb +133 -0
  77. data/lib/ec2/models/EbsGroupDiff.rb +48 -0
  78. data/lib/ec2/models/InstanceConfig.rb +202 -0
  79. data/lib/ec2/models/InstanceDiff.rb +95 -0
  80. data/lib/elb/ELB.rb +148 -0
  81. data/lib/elb/loader/Loader.rb +65 -0
  82. data/lib/elb/manager/Manager.rb +581 -0
  83. data/lib/elb/models/AccessLogConfig.rb +82 -0
  84. data/lib/elb/models/AccessLogDiff.rb +47 -0
  85. data/lib/elb/models/HealthCheckConfig.rb +91 -0
  86. data/lib/elb/models/HealthCheckDiff.rb +50 -0
  87. data/lib/elb/models/ListenerConfig.rb +99 -0
  88. data/lib/elb/models/ListenerDiff.rb +91 -0
  89. data/lib/elb/models/LoadBalancerConfig.rb +239 -0
  90. data/lib/elb/models/LoadBalancerDiff.rb +265 -0
  91. data/lib/iam/IAM.rb +36 -0
  92. data/lib/iam/loader/Loader.rb +117 -0
  93. data/lib/iam/manager/IamGroups.rb +98 -0
  94. data/lib/iam/manager/IamResource.rb +288 -0
  95. data/lib/iam/manager/IamRoles.rb +112 -0
  96. data/lib/iam/manager/IamUsers.rb +54 -0
  97. data/lib/iam/manager/Manager.rb +29 -0
  98. data/lib/iam/migration/AssumeRoleUnifier.rb +34 -0
  99. data/lib/iam/migration/PolicyUnifier.rb +90 -0
  100. data/lib/iam/models/GroupConfig.rb +40 -0
  101. data/lib/iam/models/IamDiff.rb +132 -0
  102. data/lib/iam/models/PolicyConfig.rb +67 -0
  103. data/lib/iam/models/ResourceWithPolicy.rb +208 -0
  104. data/lib/iam/models/RoleConfig.rb +53 -0
  105. data/lib/iam/models/StatementConfig.rb +35 -0
  106. data/lib/iam/models/UserConfig.rb +21 -0
  107. data/lib/kinesis/Kinesis.rb +94 -0
  108. data/lib/kinesis/loader/Loader.rb +19 -0
  109. data/lib/kinesis/manager/Manager.rb +206 -0
  110. data/lib/kinesis/models/StreamConfig.rb +75 -0
  111. data/lib/kinesis/models/StreamDiff.rb +58 -0
  112. data/lib/lambda/Lambda.rb +41 -0
  113. data/lib/route53/loader/Loader.rb +32 -0
  114. data/lib/route53/manager/Manager.rb +241 -0
  115. data/lib/route53/models/AliasTarget.rb +86 -0
  116. data/lib/route53/models/RecordConfig.rb +178 -0
  117. data/lib/route53/models/RecordDiff.rb +140 -0
  118. data/lib/route53/models/Vpc.rb +24 -0
  119. data/lib/route53/models/ZoneConfig.rb +156 -0
  120. data/lib/route53/models/ZoneDiff.rb +118 -0
  121. data/lib/s3/S3.rb +89 -0
  122. data/lib/s3/loader/Loader.rb +66 -0
  123. data/lib/s3/manager/Manager.rb +296 -0
  124. data/lib/s3/models/BucketConfig.rb +321 -0
  125. data/lib/s3/models/BucketDiff.rb +167 -0
  126. data/lib/s3/models/GrantConfig.rb +189 -0
  127. data/lib/s3/models/GrantDiff.rb +50 -0
  128. data/lib/s3/models/LifecycleConfig.rb +142 -0
  129. data/lib/s3/models/LifecycleDiff.rb +46 -0
  130. data/lib/s3/models/LoggingConfig.rb +81 -0
  131. data/lib/s3/models/NotificationConfig.rb +157 -0
  132. data/lib/s3/models/NotificationDiff.rb +62 -0
  133. data/lib/s3/models/ReplicationConfig.rb +133 -0
  134. data/lib/s3/models/ReplicationDiff.rb +60 -0
  135. data/lib/s3/models/WebsiteConfig.rb +107 -0
  136. data/lib/security/SecurityGroups.rb +39 -0
  137. data/lib/security/loader/Loader.rb +94 -0
  138. data/lib/security/manager/Manager.rb +246 -0
  139. data/lib/security/models/RuleConfig.rb +161 -0
  140. data/lib/security/models/RuleDiff.rb +72 -0
  141. data/lib/security/models/RuleMigration.rb +127 -0
  142. data/lib/security/models/SecurityGroupConfig.rb +172 -0
  143. data/lib/security/models/SecurityGroupDiff.rb +112 -0
  144. data/lib/sns/SNS.rb +40 -0
  145. data/lib/sqs/SQS.rb +62 -0
  146. data/lib/sqs/loader/Loader.rb +34 -0
  147. data/lib/sqs/manager/Manager.rb +128 -0
  148. data/lib/sqs/models/DeadLetterConfig.rb +70 -0
  149. data/lib/sqs/models/DeadLetterDiff.rb +35 -0
  150. data/lib/sqs/models/QueueConfig.rb +115 -0
  151. data/lib/sqs/models/QueueDiff.rb +89 -0
  152. data/lib/util/Colors.rb +111 -0
  153. data/lib/util/StatusCodes.rb +51 -0
  154. data/lib/vpc/loader/Loader.rb +73 -0
  155. data/lib/vpc/manager/Manager.rb +954 -0
  156. data/lib/vpc/models/AclEntryConfig.rb +150 -0
  157. data/lib/vpc/models/AclEntryDiff.rb +54 -0
  158. data/lib/vpc/models/DhcpConfig.rb +100 -0
  159. data/lib/vpc/models/DhcpDiff.rb +90 -0
  160. data/lib/vpc/models/EndpointConfig.rb +76 -0
  161. data/lib/vpc/models/EndpointDiff.rb +69 -0
  162. data/lib/vpc/models/NetworkAclConfig.rb +87 -0
  163. data/lib/vpc/models/NetworkAclDiff.rb +116 -0
  164. data/lib/vpc/models/RouteConfig.rb +82 -0
  165. data/lib/vpc/models/RouteDiff.rb +50 -0
  166. data/lib/vpc/models/RouteTableConfig.rb +92 -0
  167. data/lib/vpc/models/RouteTableDiff.rb +101 -0
  168. data/lib/vpc/models/SubnetConfig.rb +113 -0
  169. data/lib/vpc/models/SubnetDiff.rb +78 -0
  170. data/lib/vpc/models/VpcConfig.rb +173 -0
  171. data/lib/vpc/models/VpcDiff.rb +315 -0
  172. data/rakefile.rb +8 -0
  173. metadata +245 -0
@@ -0,0 +1,19 @@
1
+ require "common/BaseLoader"
2
+ require "conf/Configuration"
3
+ require "kinesis/models/StreamConfig"
4
+
5
+ module Cumulus
6
+ module Kinesis
7
+ module Loader
8
+
9
+ include Common::BaseLoader
10
+
11
+ @@streams_dir = Configuration.instance.kinesis.directory
12
+
13
+ def self.streams
14
+ Common::BaseLoader::resources(@@streams_dir, &StreamConfig.method(:new))
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,206 @@
1
+ require "common/manager/Manager"
2
+ require "conf/Configuration"
3
+ require "kinesis/Kinesis"
4
+ require "kinesis/loader/Loader"
5
+ require "kinesis/models/StreamConfig"
6
+ require "kinesis/models/StreamDiff"
7
+
8
+ require "aws-sdk"
9
+
10
+ module Cumulus
11
+ module Kinesis
12
+ class Manager < Common::Manager
13
+
14
+ def initialize
15
+ super()
16
+ @create_asset = true
17
+ @client = Aws::Kinesis::Client.new(Configuration.instance.client)
18
+ end
19
+
20
+ def resource_name
21
+ "Kinesis Stream"
22
+ end
23
+
24
+ def local_resources
25
+ @local_resources ||= Hash[Loader.streams.map { |local| [local.name, local] }]
26
+ end
27
+
28
+ def aws_resources
29
+ @aws_resources ||= Kinesis::named_streams
30
+ end
31
+
32
+ def unmanaged_diff(aws)
33
+ StreamDiff.unmanaged(aws)
34
+ end
35
+
36
+ def added_diff(local)
37
+ StreamDiff.added(local)
38
+ end
39
+
40
+ def diff_resource(local, aws)
41
+ local.diff(aws)
42
+ end
43
+
44
+ def migrate
45
+ puts Colors.blue("Migrating Kinesis Streams...")
46
+
47
+ # Create the directories
48
+ streams_dir = "#{@migration_root}/kinesis"
49
+
50
+ if !Dir.exists?(@migration_root)
51
+ Dir.mkdir(@migration_root)
52
+ end
53
+ if !Dir.exists?(streams_dir)
54
+ Dir.mkdir(streams_dir)
55
+ end
56
+
57
+ Kinesis::named_streams.each do |name, stream|
58
+ puts "Migrating stream #{name}"
59
+
60
+ cumulus_stream = StreamConfig.new(name).populate!(stream)
61
+ json = JSON.pretty_generate(cumulus_stream.to_hash)
62
+ File.open("#{streams_dir}/#{name}.json", "w") { |f| f.write(json) }
63
+ end
64
+
65
+ end
66
+
67
+ def create(local)
68
+ @client.create_stream({
69
+ stream_name: local.name,
70
+ shard_count: local.shards
71
+ })
72
+
73
+ @client.wait_until(:stream_exists, {
74
+ stream_name: local.name
75
+ })
76
+
77
+ # Describe the newly created stream
78
+ created_stream = Kinesis::describe_stream(local.name)
79
+
80
+ # If the stream retention period is different, then update it
81
+ if created_stream.retention_period_hours > local.retention_period
82
+ @client.decrease_stream_retention_period({
83
+ stream_name: local.name,
84
+ retention_period_hours: local.retention_period
85
+ })
86
+ elsif created_stream.retention_period_hours < local.retention_period
87
+ @client.increase_stream_retention_period({
88
+ stream_name: local.name,
89
+ retention_period_hours: local.retention_period
90
+ })
91
+ end
92
+
93
+ # If the created stream has tags, add them
94
+ if !local.tags.empty?
95
+ @client.add_tags_to_stream({
96
+ stream_name: local.name,
97
+ tags: local.tags
98
+ })
99
+ end
100
+
101
+ end
102
+
103
+ def update(local, diffs)
104
+ diffs.each do |diff|
105
+ case diff.type
106
+ when StreamChange::SHARDS
107
+
108
+ # See if we are splitting or merging and make sure it is a multiple of 2
109
+ if diff.aws < diff.local
110
+ if diff.aws != diff.local / 2.0
111
+ puts Colors.red("Can only increase the number of shards by a factor of 2")
112
+ else
113
+ aws_stream = Kinesis::named_streams[local.name]
114
+
115
+ # Split the shards 1 at a time
116
+ aws_stream.sorted_shards.each do |shard|
117
+ puts Colors.blue("Splitting shard #{shard.shard_id}")
118
+
119
+ # The splitting point is halfway between the hash start and end
120
+ hash_start = shard.hash_key_range.starting_hash_key.to_i
121
+ hash_end = shard.hash_key_range.ending_hash_key.to_i
122
+ hash_split = hash_start + ((hash_end - hash_start) / 2)
123
+
124
+ @client.split_shard({
125
+ stream_name: local.name,
126
+ shard_to_split: shard.shard_id,
127
+ new_starting_hash_key: hash_split.to_s
128
+ })
129
+
130
+ # After every split we have to wait until the stream is ready
131
+ @client.wait_until(:stream_exists, {
132
+ stream_name: local.name
133
+ })
134
+ end
135
+
136
+ end
137
+ elsif diff.aws > diff.local
138
+ aws_stream = Kinesis::named_streams[local.name]
139
+
140
+ if aws_stream.sorted_shards.length != local.shards * 2.0
141
+ puts Colors.red("Can only decrease the number of shards by a factor of 2")
142
+ else
143
+ # Merge the sorted shards in groups of 2
144
+ aws_stream.sorted_shards.each_slice(2) do |slice|
145
+ puts Colors.blue("Merging shards #{slice[0].shard_id} and #{slice[1].shard_id}")
146
+
147
+ @client.merge_shards({
148
+ stream_name: local.name,
149
+ shard_to_merge: slice[0].shard_id,
150
+ adjacent_shard_to_merge: slice[1].shard_id
151
+ })
152
+
153
+ # After every merge we have to wait until the stream is ready
154
+ @client.wait_until(:stream_exists, {
155
+ stream_name: local.name
156
+ })
157
+ end
158
+
159
+ end
160
+ end
161
+
162
+ when StreamChange::RETENTION
163
+ puts Colors.blue("Updating retention period...")
164
+
165
+
166
+ if diff.aws > local.retention_period
167
+ @client.decrease_stream_retention_period({
168
+ stream_name: local.name,
169
+ retention_period_hours: local.retention_period
170
+ })
171
+ elsif diff.aws < local.retention_period
172
+ @client.increase_stream_retention_period({
173
+ stream_name: local.name,
174
+ retention_period_hours: local.retention_period
175
+ })
176
+ end
177
+
178
+ # Wait for the stream to be in an active state or shard updates will fail
179
+ @client.wait_until(:stream_exists, {
180
+ stream_name: local.name
181
+ })
182
+ when StreamChange::TAGS
183
+ puts Colors.blue("Updating tags...")
184
+
185
+ if !diff.tags_to_remove.empty?
186
+ @client.remove_tags_from_stream({
187
+ stream_name: local.name,
188
+ tag_keys: diff.tags_to_remove.keys
189
+ })
190
+ end
191
+
192
+ if !diff.tags_to_add.empty?
193
+ @client.add_tags_to_stream({
194
+ stream_name: local.name,
195
+ tags: diff.tags_to_add
196
+ })
197
+ end
198
+
199
+ end
200
+ end
201
+
202
+ end
203
+
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,75 @@
1
+ require "conf/Configuration"
2
+ require "kinesis/loader/Loader"
3
+ require "kinesis/models/StreamDiff"
4
+
5
+ require "json"
6
+
7
+ module Cumulus
8
+ module Kinesis
9
+
10
+ # Public: An object representing configuration for a Kiensis stream
11
+ class StreamConfig
12
+ attr_reader :name
13
+ attr_reader :retention_period
14
+ attr_reader :shards
15
+ attr_reader :tags
16
+
17
+ # Public: Constructor
18
+ #
19
+ # json - a hash containing the JSON configuration for the stream
20
+ def initialize(name, json = nil)
21
+ @name = name
22
+ if !json.nil?
23
+ @shards = json["shards"]
24
+ @retention_period = json["retention-period"] || 24
25
+ @tags = json["tags"] || {}
26
+ end
27
+ end
28
+
29
+ def to_hash
30
+ {
31
+ "retention-period" => @retention_period,
32
+ "shards" => @shards,
33
+ "tags" => @tags
34
+ }
35
+ end
36
+
37
+ # Public: Populate a config object with AWS configuration
38
+ #
39
+ # aws - the AWS configuration for the strean
40
+ def populate!(aws)
41
+ @retention_period = aws.retention_period_hours
42
+ @shards = aws.sorted_shards.length
43
+ @tags = Kinesis::stream_tags[aws.stream_name] || {}
44
+
45
+ self
46
+ end
47
+
48
+ # Public: Produce an array of differences between this local configuration and the
49
+ # configuration in AWS
50
+ #
51
+ # aws - the AWS resource
52
+ #
53
+ # Returns an array of the StreamDiffs that were found
54
+ def diff(aws)
55
+ diffs = []
56
+
57
+ if @retention_period != aws.retention_period_hours
58
+ diffs << StreamDiff.new(StreamChange::RETENTION, aws.retention_period_hours, @retention_period)
59
+ end
60
+
61
+ if @shards != aws.sorted_shards.length
62
+ diffs << StreamDiff.new(StreamChange::SHARDS, aws.sorted_shards.length, @shards)
63
+ end
64
+
65
+ aws_tags = Kinesis::stream_tags[aws.stream_name]
66
+ if @tags != aws_tags
67
+ diffs << StreamDiff.new(StreamChange::TAGS, aws_tags, @tags)
68
+ end
69
+
70
+ diffs
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,58 @@
1
+ require "common/models/Diff"
2
+ require "common/models/ListChange"
3
+ require "common/models/TagsDiff"
4
+ require "util/Colors"
5
+
6
+ module Cumulus
7
+ module Kinesis
8
+ # Public: The types of changes that can be made to a stream
9
+ module StreamChange
10
+ include Common::DiffChange
11
+
12
+ SHARDS = Common::DiffChange.next_change_id
13
+ RETENTION = Common::DiffChange.next_change_id
14
+ TAGS = Common::DiffChange.next_change_id
15
+ end
16
+
17
+ # Public: Represents a single difference between local configuration and AWS configuration
18
+ class StreamDiff < Common::Diff
19
+ include StreamChange
20
+ include Common::TagsDiff
21
+
22
+ def local_tags
23
+ @local
24
+ end
25
+
26
+ def aws_tags
27
+ @aws
28
+ end
29
+
30
+ def asset_type
31
+ "Stream"
32
+ end
33
+
34
+ def aws_name
35
+ @aws.stream_name
36
+ end
37
+
38
+ def diff_string
39
+ case @type
40
+ when SHARDS
41
+ [
42
+ "Shards:",
43
+ Colors.aws_changes("\tAWS - #{aws}"),
44
+ Colors.local_changes("\tLocal - #{local}"),
45
+ ].join("\n")
46
+ when RETENTION
47
+ [
48
+ "Retention:",
49
+ Colors.aws_changes("\tAWS - #{aws} hours"),
50
+ Colors.local_changes("\tLocal - #{local} hours"),
51
+ ].join("\n")
52
+ when TAGS
53
+ tags_diff_string
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,41 @@
1
+ require "conf/Configuration"
2
+
3
+ require "aws-sdk"
4
+
5
+ module Cumulus
6
+ module Lambda
7
+ class << self
8
+ @@client = Aws::Lambda::Client.new(Configuration.instance.client)
9
+
10
+ # Public: Static method that will get a Lambda function from AWS by its
11
+ # name
12
+ #
13
+ # name - the name of the function to get
14
+ #
15
+ # Returns the function
16
+ def get_aws(name)
17
+ functions.fetch(name)
18
+ rescue KeyError
19
+ puts "No Lambda function named #{name}"
20
+ exit
21
+ end
22
+
23
+ # Public: Provide a mapping of functions to their names. Lazily loads
24
+ # resources.
25
+ #
26
+ # Returns the functions mapped to their names
27
+ def functions
28
+ @functions ||= init_functions
29
+ end
30
+
31
+ private
32
+
33
+ # Internal: Load the functions and map them to their names
34
+ #
35
+ # Returns the functions mapped to their names
36
+ def init_functions
37
+ Hash[@@client.list_functions.functions.map { |f| [f.function_name, f] }]
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,32 @@
1
+ require "common/BaseLoader"
2
+ require "conf/Configuration"
3
+ require "route53/models/ZoneConfig"
4
+
5
+ # Public: Load Route53 assets
6
+ module Cumulus
7
+ module Route53
8
+ module Loader
9
+ include Common::BaseLoader
10
+
11
+ @@zones_dir = Configuration.instance.route53.zones_directory
12
+ @@includes_dir = Configuration.instance.route53.includes_directory
13
+
14
+ # Public: Load all the zone configurations as ZoneConfig objects
15
+ #
16
+ # Returns an array of ZoneConfig
17
+ def self.zones
18
+ Common::BaseLoader::resources(@@zones_dir, &ZoneConfig.method(:new))
19
+ end
20
+
21
+ # Public: Load a single "includes file" as parsed JSON
22
+ #
23
+ # name - the name of the file to include
24
+ #
25
+ # Returns an array of parsed JSON
26
+ def self.includes_file(name)
27
+ Common::BaseLoader::resource(name, @@includes_dir, &Proc.new { |n, json| json })
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,241 @@
1
+ require "common/manager/Manager"
2
+ require "conf/Configuration"
3
+ require "route53/loader/Loader"
4
+ require "route53/models/RecordDiff"
5
+ require "route53/models/Vpc"
6
+ require "route53/models/ZoneDiff"
7
+ require "util/Colors"
8
+
9
+ require "aws-sdk"
10
+
11
+ module Cumulus
12
+ module Route53
13
+ class Manager < Common::Manager
14
+ def initialize
15
+ super()
16
+ @create_asset = false
17
+ @route53 = Aws::Route53::Client.new(Configuration.instance.client)
18
+ end
19
+
20
+ # Public: Migrate AWS Route53 configuration to Cumulus configuration.
21
+ def migrate
22
+ zones_dir = "#{@migration_root}/zones"
23
+
24
+ if !Dir.exists?(@migration_root)
25
+ Dir.mkdir(@migration_root)
26
+ end
27
+ if !Dir.exists?(zones_dir)
28
+ Dir.mkdir(zones_dir)
29
+ end
30
+
31
+ aws_resources.each_value do |resource|
32
+ puts "Processing #{resource.name}..."
33
+ config = ZoneConfig.new(resource.name)
34
+ config.populate(resource)
35
+
36
+ puts "Writing #{resource.name} configuration to file"
37
+ filename = if config.private then "#{config.name}-private" else config.name end
38
+ File.open("#{zones_dir}/#{filename.sub(".", "-")}.json", "w") { |f| f.write(config.pretty_json) }
39
+ end
40
+ end
41
+
42
+ def resource_name
43
+ "Zone"
44
+ end
45
+
46
+ def local_resources
47
+ @local_resources ||= Hash[Loader.zones.map { |local| [local.id, local] }]
48
+ end
49
+
50
+ def aws_resources
51
+ @aws_resources ||= init_aws_resources
52
+ end
53
+
54
+ def unmanaged_diff(aws)
55
+ ZoneDiff.unmanaged(aws)
56
+ end
57
+
58
+ def added_diff(local)
59
+ ZoneDiff.added(local)
60
+ end
61
+
62
+ def diff_resource(local, aws)
63
+ local.diff(aws)
64
+ end
65
+
66
+ def update(local, diffs)
67
+ diffs.each do |diff|
68
+ case diff.type
69
+ when ZoneChange::COMMENT
70
+ puts Colors.blue("\tupdating comment...")
71
+ update_comment(local.id, local.comment)
72
+ when ZoneChange::DOMAIN
73
+ puts "\tAWS doesn't allow you to change the domain for a zone."
74
+ when ZoneChange::PRIVATE
75
+ puts "\tAWS doesn't allow you to change whether a zone is private."
76
+ when ZoneChange::VPC
77
+ update_vpc(local.id, diff.added_vpc_ids, diff.removed_vpc_ids)
78
+ when ZoneChange::RECORD
79
+ update_records(
80
+ local.id,
81
+ diff.changed_records.reject do |r|
82
+ r.type == RecordChange::IGNORED or r.type == RecordChange::DEFAULT
83
+ end
84
+ )
85
+
86
+ ignored = diff.changed_records.select { |r| r.type == RecordChange::IGNORED }
87
+ if Configuration.instance.route53.print_all_ignored
88
+ ignored.each do |record_diff|
89
+ puts "\tIgnoring record #{record_diff.aws_name}"
90
+ end
91
+ else
92
+ if ignored.size > 0
93
+ puts "\tYour blacklist ignored #{ignored.size} records."
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ private
101
+
102
+ # Internal: Update the comment associated with a zone.
103
+ #
104
+ # id - the id of the zone to update
105
+ # comment - the new comment
106
+ def update_comment(id, comment)
107
+ @route53.update_hosted_zone_comment({
108
+ id: id,
109
+ comment: comment
110
+ })
111
+ end
112
+
113
+ # Internal: Update the VPCs associated with a zone.
114
+ #
115
+ # id - the id of the zone to update
116
+ # associate - the vpc ids to associate with the zone
117
+ # dissociate - the vpc ids to dissociate from the zone
118
+ def update_vpc(id, associate, dissociate)
119
+ if !associate.empty?
120
+ puts Colors.blue("\tassociating VPCs...")
121
+ associate.each do |vpc|
122
+ @route53.associate_vpc_with_hosted_zone({
123
+ hosted_zone_id: id,
124
+ vpc: { vpc_id: vpc.id, vpc_region: vpc.region }
125
+ })
126
+ end
127
+ end
128
+ if !dissociate.empty?
129
+ puts Colors.blue("\tdissociating VPCs...")
130
+ dissociate.each do |vpc|
131
+ @route53.disassociate_vpc_from_hosted_zone({
132
+ hosted_zone_id: id,
133
+ vpc: { vpc_id: vpc.id, vpc_region: vpc.region }
134
+ })
135
+ end
136
+ end
137
+ end
138
+
139
+ # Internal: Update the records associated with a zone.
140
+ #
141
+ # id - the id of the zone to update
142
+ # records - RecordDiff objects representing the changes
143
+ def update_records(id, records)
144
+ puts Colors.blue("\tupdating records...")
145
+ if !records.empty?
146
+ changes = records.map do |record|
147
+ action = nil
148
+ resource = nil
149
+
150
+ case record.type
151
+ when RecordChange::CHANGED
152
+ action = "UPSERT"
153
+ resource = record.local
154
+ when RecordChange::ADD
155
+ action = "CREATE"
156
+ resource = record.local
157
+ when RecordChange::UNMANAGED
158
+ action = "DELETE"
159
+ resource = record.aws
160
+ end
161
+
162
+ {
163
+ action: action,
164
+ resource_record_set: {
165
+ name: resource.name.gsub("@", "\\\\100"),
166
+ type: resource.type,
167
+ ttl: resource.ttl,
168
+ resource_records: resource.resource_records,
169
+ alias_target: if resource.alias_target.nil? then nil else {
170
+ hosted_zone_id: resource.alias_target.hosted_zone_id,
171
+ dns_name: resource.alias_target.dns_name,
172
+ evaluate_target_health: resource.alias_target.evaluate_target_health
173
+ } end
174
+ }
175
+ }
176
+ end
177
+
178
+ @route53.change_resource_record_sets({
179
+ hosted_zone_id: id,
180
+ change_batch: {
181
+ changes: changes
182
+ }
183
+ })
184
+ end
185
+ end
186
+
187
+ # A struct that combines all the data about a hosted zone in AWS
188
+ AwsZone = Struct.new(:id, :name, :config, :vpc, :route53) do
189
+ def records
190
+ @records ||= get_zone_records
191
+ end
192
+
193
+ private
194
+
195
+ # Internal: Get the records for this hosted zone.
196
+ #
197
+ # Returns an array of records belonging to the zone
198
+ def get_zone_records()
199
+ records = []
200
+ all_records_retrieved = false
201
+ next_record_name = nil
202
+ next_record_type = nil
203
+ next_record_identifier = nil
204
+
205
+ until all_records_retrieved
206
+ response = route53.list_resource_record_sets({
207
+ hosted_zone_id: id,
208
+ start_record_name: next_record_name,
209
+ start_record_type: next_record_type,
210
+ start_record_identitifier: next_record_identifier
211
+ }.reject { |k, v| v.nil? })
212
+ records << response.resource_record_sets
213
+ next_record_name = response.next_record_name
214
+ next_record_type = response.next_record_type
215
+ next_record_identifier = response.next_record_identifier
216
+
217
+ if !response.is_truncated
218
+ all_records_retrieved = true
219
+ end
220
+ end
221
+
222
+ records.flatten.map { |r| r.name = r.name.chomp(".").sub(/\\052/, "*"); r }
223
+ end
224
+ end
225
+
226
+ def init_aws_resources
227
+ aws = @route53.list_hosted_zones.hosted_zones.map do |zone|
228
+ vpc = if zone.config.private_zone
229
+ details = @route53.get_hosted_zone(id: zone.id)
230
+ details.vp_cs.map { |v| Vpc.new(v.vpc_id, v.vpc_region) }
231
+ else
232
+ nil
233
+ end
234
+ AwsZone.new(zone.id, zone.name.chomp("."), zone.config, vpc, @route53)
235
+ end
236
+ Hash[aws.map { |z| [z.id, z] }]
237
+ end
238
+
239
+ end
240
+ end
241
+ end