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,86 @@
1
+ require "cloudfront/CloudFront"
2
+ require "elb/ELB"
3
+ require "s3/S3"
4
+
5
+ module Cumulus
6
+ module Route53
7
+ # Public: A struct that matches the structure of the AWS alias target struct
8
+ AliasTarget = Struct.new(:name, :type, :local_zone_id) do
9
+ # Public: Produce the dns_name for this alias
10
+ #
11
+ # Returns the dns_name
12
+ def dns_name
13
+ if is_elb?
14
+ "dualstack.#{ELB::get_aws(name).dns_name}"
15
+ elsif is_s3?
16
+ "s3-website-#{S3::get_aws(name).location}.amazonaws.com"
17
+ elsif is_cloudfront?
18
+ CloudFront::get_aws(name).domain_name
19
+ else
20
+ name
21
+ end
22
+ end
23
+
24
+ # Public: Produce a hash representing this alias target
25
+ #
26
+ # Returns the hash
27
+ def to_hash
28
+ {
29
+ "name" => name,
30
+ "type" => type
31
+ }.reject { |k, v| v.nil? }
32
+ end
33
+
34
+ # Public: Produce the hosted_zone_id for this alias
35
+ #
36
+ # Returns the hosted_zone_id
37
+ def hosted_zone_id
38
+ if is_elb?
39
+ ELB::get_aws(name).canonical_hosted_zone_name_id
40
+ elsif is_record_set?
41
+ local_zone_id
42
+ elsif is_s3?
43
+ S3::zone_ids[S3::get_aws(name).location]
44
+ elsif is_cloudfront?
45
+ "Z2FDTNDATAQYW2" # AWS hard codes this
46
+ end
47
+ end
48
+
49
+ # Public: Determine whether to evaluate the health check on the target. Always
50
+ # false.
51
+ #
52
+ # Returns false
53
+ def evaluate_target_health
54
+ false
55
+ end
56
+
57
+ # Public: Determine if this alias is for an ELB
58
+ #
59
+ # Returns true if the alias is an ELB
60
+ def is_elb?
61
+ type.downcase == "elb"
62
+ end
63
+
64
+ # Public: Determine if this alias is for a record set
65
+ #
66
+ # Returns true if the alias is for a record set
67
+ def is_record_set?
68
+ type.downcase == "record"
69
+ end
70
+
71
+ # Public: Determine if this alias is for an s3 website
72
+ #
73
+ # Returns true if the alias is for an s3 website
74
+ def is_s3?
75
+ type.downcase == "s3"
76
+ end
77
+
78
+ # Public: Determine if this alias is for a Cloudfront distribution
79
+ #
80
+ # Returns true if the alias is for a Cloudfront distribution
81
+ def is_cloudfront?
82
+ type.downcase == "cloudfront"
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,178 @@
1
+ require "aws_extensions/route53/AliasTarget"
2
+ require "aws_extensions/s3/Bucket"
3
+ require "cloudfront/CloudFront"
4
+ require "elb/ELB"
5
+ require "route53/models/AliasTarget"
6
+ require "route53/models/RecordDiff"
7
+ require "s3/S3"
8
+
9
+ require "aws-sdk"
10
+
11
+ module Cumulus
12
+ module Route53
13
+ # Monkey patch AliasTarget so we can call a method that will compare ELB DNS names correctly
14
+ Aws::Route53::Types::AliasTarget.send(:include, AwsExtensions::Route53::AliasTarget)
15
+ # Monkey patch Bucket so we can get the location of the bucket
16
+ Aws::S3::Types::Bucket.send(:include, AwsExtensions::S3::Types::Bucket)
17
+
18
+ # Public: An object representing configurationf for a single record in a zone
19
+ class RecordConfig
20
+
21
+ attr_reader :alias_target
22
+ attr_reader :name
23
+ attr_reader :ttl
24
+ attr_reader :type
25
+ attr_reader :value
26
+
27
+ # Public: Constructor.
28
+ #
29
+ # json - a hash containing the JSON configuration for the record
30
+ # domain - the domain of the zone this record belongs to
31
+ # zone_id - the id of the zone this record belongs to
32
+ def initialize(json = nil, domain = nil, zone_id = nil)
33
+ if !json.nil?
34
+ @name = if json["name"] == "" then domain else "#{json["name"].chomp(".")}.#{domain}".chomp(".") end
35
+ @ttl = json["ttl"]
36
+ @type = json["type"]
37
+
38
+ if !json["value"].nil?
39
+ @value = json["value"]
40
+
41
+ # TXT and SPF records have each value wrapped in quotes
42
+ if @type == "TXT" or @type == "SPF"
43
+ @value = @value.map { |v| "\"#{v}\"" }
44
+ end
45
+ else
46
+ alias_name = if json["alias"]["name"].nil?
47
+ if json["alias"]["type"] == "s3" then @name else domain end
48
+ else
49
+ json["alias"]["name"].chomp(".")
50
+ end
51
+ @alias_target = AliasTarget.new(
52
+ alias_name,
53
+ json["alias"]["type"],
54
+ zone_id
55
+ )
56
+ end
57
+ end
58
+ end
59
+
60
+ # Public: Populate this RecordConfig from an AWS resource.
61
+ #
62
+ # aws - the aws resource
63
+ # domain - the domain of the parent hosted zone
64
+ def populate(aws, domain)
65
+ @name = aws.name.chomp(domain).chomp(".")
66
+ @ttl = aws.ttl
67
+ @type = aws.type
68
+ if !aws.resource_records.nil?
69
+ if @type == "TXT" or @type == "SPF"
70
+ @value = aws.resource_records.map { |r| r.value[1..-2] }
71
+ else
72
+ @value = aws.resource_records.map(&:value)
73
+ end
74
+ end
75
+
76
+ if !aws.alias_target.nil?
77
+ if aws.alias_target.dns_name.include? "elb"
78
+ @alias_target = AliasTarget.new(
79
+ Cumulus::ELB::get_aws_by_dns_name(aws.alias_target.elb_dns_name).load_balancer_name,
80
+ "elb",
81
+ nil
82
+ )
83
+ elsif aws.alias_target.dns_name.include? "s3"
84
+ @alias_target = AliasTarget.new(nil, "s3", nil)
85
+ elsif aws.alias_target.dns_name.include? "cloudfront"
86
+ @alias_target = AliasTarget.new(nil, "cloudfront", nil)
87
+ else
88
+ @alias_target = AliasTarget.new(aws.alias_target.dns_name.chomp("."), "record", nil)
89
+ end
90
+ end
91
+ end
92
+
93
+ # Public: Get the config as a hash
94
+ #
95
+ # Returns the hash
96
+ def to_hash
97
+ {
98
+ "name" => @name,
99
+ "type" => @type,
100
+ "ttl" => @ttl,
101
+ "value" => @value,
102
+ "alias" => if @alias_target.nil? then nil else @alias_target.to_hash end,
103
+ }.reject { |k, v| v.nil? }
104
+ end
105
+
106
+ # Public: Produce an array of differences between this local configuration and the
107
+ # configuration in AWS
108
+ #
109
+ # aws - the AWS resource
110
+ #
111
+ # Returns an array of the RecordDiffs that were found
112
+ def diff(aws)
113
+ diffs = []
114
+
115
+ if @ttl != aws.ttl
116
+ diffs << SingleRecordDiff.new(RecordChange::TTL, aws, self)
117
+ end
118
+ if !@value.nil? and @value.sort != aws.resource_records.map(&:value).sort
119
+ diffs << SingleRecordDiff.new(RecordChange::VALUE, aws, self)
120
+ end
121
+ if !@alias_target.nil?
122
+ if aws.alias_target.nil? or
123
+ (is_elb_alias? and aws.alias_target.elb_dns_name != ELB::get_aws(@alias_target.name).dns_name) or
124
+ (aws.alias_target.chomped_dns != @alias_target.dns_name)
125
+ diffs << SingleRecordDiff.new(RecordChange::ALIAS, aws, self)
126
+ end
127
+ end
128
+
129
+ diffs
130
+ end
131
+
132
+ # Public: Determine if the record is an alias for an ELB
133
+ #
134
+ # Returns whether this record is an alias for an ELB
135
+ def is_elb_alias?
136
+ !@alias_target.nil? and @alias_target.is_elb?
137
+ end
138
+
139
+ # Public: Determine if the recourd is an alias for another record
140
+ #
141
+ # Returns whether this record is an alias for another record
142
+ def is_record_set_alias?
143
+ !@alias_target.nil? and @alias_target.is_record_set?
144
+ end
145
+
146
+ # Public: Determine if the record is an alias for an S3 website
147
+ #
148
+ # Returns whether this record is an alias for an S3 website
149
+ def is_s3_alias?
150
+ !@alias_target.nil? and @alias_target.is_s3?
151
+ end
152
+
153
+ # Public: Determine if the record is an alias for a Cloudfront distribution
154
+ #
155
+ # Returns whether this record is an alias for a Cloudfront distribution
156
+ def is_cloudfront_alias?
157
+ !@alias_target.nil? and @alias_target.is_cloudfront?
158
+ end
159
+
160
+ # Public: Produce a `resource_records` array that is analogous to the one used in AWS from
161
+ # the values array used by Cumulus
162
+ #
163
+ # Returns the `resource_records`
164
+ def resource_records
165
+ if !@value.nil?
166
+ @value.map { |v| { value: v } }
167
+ end
168
+ end
169
+
170
+ # Public: Produce a useful human readable version of the name of this RecordConfig
171
+ #
172
+ # Returns the string name
173
+ def readable_name
174
+ "(#{@type}) #{@name}"
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,140 @@
1
+ require "common/models/Diff"
2
+ require "elb/ELB"
3
+ require "util/Colors"
4
+
5
+ module Cumulus
6
+ module Route53
7
+ # Public: The types of changes that can be made to records
8
+ module RecordChange
9
+ include Common::DiffChange
10
+
11
+ ALIAS = Common::DiffChange::next_change_id
12
+ CHANGED = Common::DiffChange::next_change_id
13
+ DEFAULT = Common::DiffChange::next_change_id
14
+ IGNORED = Common::DiffChange::next_change_id
15
+ TTL = Common::DiffChange::next_change_id
16
+ VALUE = Common::DiffChange::next_change_id
17
+ end
18
+
19
+ # Public: Represents differences between local configuration and AWS
20
+ # configuration for records.
21
+ class RecordDiff < Common::Diff
22
+ include RecordChange
23
+
24
+ attr_accessor :message
25
+ attr_accessor :changes
26
+
27
+ # Public: Static method that will create a diff that contains a message but is
28
+ # ignored when syncing because it is a default record.
29
+ #
30
+ # message - the message to display
31
+ # aws - the aws configuration for the record
32
+ #
33
+ # Returns the diff
34
+ def self.default(message, aws)
35
+ diff = RecordDiff.new(DEFAULT, aws)
36
+ diff.message = message
37
+ diff
38
+ end
39
+
40
+ # Public: Static method that will create a diff that contains a message but is
41
+ # ignored when syncing.
42
+ #
43
+ # message - the message to display
44
+ # aws - the aws configuration for the record
45
+ #
46
+ # Returns the diff
47
+ def self.ignored(message, aws)
48
+ diff = RecordDiff.new(IGNORED, aws)
49
+ diff.message = message
50
+ diff.info_only = true
51
+ diff
52
+ end
53
+
54
+ # Public: Static method that will create a diff that contains a bunch of
55
+ # singular changes.
56
+ #
57
+ # changes - the changes for the record
58
+ # local - the local configuration for the record
59
+ #
60
+ # Returns the diff
61
+ def self.changed(changes, local)
62
+ diff = RecordDiff.new(CHANGED, nil, local)
63
+ diff.changes = changes
64
+ diff
65
+ end
66
+
67
+ def asset_type
68
+ "Record"
69
+ end
70
+
71
+ def aws_name
72
+ "(#{@aws.type}) #{@aws.name}"
73
+ end
74
+
75
+ def local_name
76
+ @local.readable_name
77
+ end
78
+
79
+ def diff_string
80
+ case @type
81
+ when IGNORED
82
+ message
83
+ when DEFAULT
84
+ message
85
+ when CHANGED
86
+ [
87
+ "Record #{local_name}:",
88
+ changes.map { |c| "\t\t#{c}" }
89
+ ].flatten.join("\n")
90
+ end
91
+ end
92
+
93
+ end
94
+
95
+ # Public: Represents a single difference between local configuration and AWS
96
+ # configuration for a single record. This class allows all the changes for a
97
+ # record to be grouped together when printed.
98
+ class SingleRecordDiff < Common::Diff
99
+ include RecordChange
100
+
101
+ def diff_string
102
+ case @type
103
+ when ALIAS
104
+ if @local.is_elb_alias?
105
+ aws_name = ELB::get_aws_by_dns_name(@aws.alias_target.elb_dns_name).load_balancer_name
106
+ "Alias: AWS - #{Colors.aws_changes(aws_name)}, Local - #{Colors.local_changes(@local.alias_target.name)}"
107
+ else
108
+ "Alias: AWS - #{Colors.aws_changes(@aws.alias_target.chomped_dns)}, Local - #{Colors.local_changes(@local.alias_target.dns_name)}"
109
+ end
110
+ when TTL
111
+ "TTL: AWS - #{Colors.aws_changes(@aws.ttl)}, Local - #{Colors.local_changes(@local.ttl)}"
112
+ when VALUE
113
+ [
114
+ "Value:",
115
+ values_to_add.map { |v| Colors.added("\t\t\t#{v}") },
116
+ values_to_remove.map { |v| Colors.removed("\t\t\t#{v}") }
117
+ ].flatten.join("\n")
118
+ end
119
+ end
120
+
121
+ private
122
+
123
+ # Internal: Get the value parts that are in local configuration but not in AWS
124
+ #
125
+ # Returns the local value parts
126
+ def values_to_add
127
+ aws_value = @aws.resource_records.map(&:value)
128
+ @local.value.reject { |v| aws_value.include?(v) }
129
+ end
130
+
131
+ # Internal: Get the value parts that are in AWS but not local configuration
132
+ #
133
+ # Returns the AWS value parts
134
+ def values_to_remove
135
+ aws_value = @aws.resource_records.map(&:value)
136
+ aws_value.reject { |v| @local.value.include?(v) }
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,24 @@
1
+ module Cumulus
2
+ module Route53
3
+ Vpc = Struct.new(:id, :region) do
4
+ # Public: Implement <=> to allow sorting. Sorts by id and then region
5
+ def <=>(other)
6
+ if self.id == other.id
7
+ self.region <=> other.region
8
+ else
9
+ self.id <=> self.id
10
+ end
11
+ end
12
+
13
+ # Public: Produce a hash representing the VPC
14
+ #
15
+ # Returns the hash
16
+ def to_hash
17
+ {
18
+ "id" => id,
19
+ "region" => region
20
+ }
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,156 @@
1
+ require "conf/Configuration"
2
+ require "route53/loader/Loader"
3
+ require "route53/models/RecordConfig"
4
+ require "route53/models/RecordDiff"
5
+ require "route53/models/Vpc"
6
+ require "route53/models/ZoneDiff"
7
+
8
+ require "json"
9
+
10
+ module Cumulus
11
+ module Route53
12
+ # Public: An object representing configuration for a zone
13
+ class ZoneConfig
14
+ attr_reader :comment
15
+ attr_reader :domain
16
+ attr_reader :id
17
+ attr_reader :name
18
+ attr_reader :private
19
+ attr_reader :records
20
+ attr_reader :vpc
21
+
22
+ # Public: Constructor
23
+ #
24
+ # json - a hash containing the JSON configuration for the zone
25
+ def initialize(name, json = nil)
26
+ @name = name
27
+ if !json.nil?
28
+ @id = "/hostedzone/#{json["zone-id"]}"
29
+ @domain = json["domain"].chomp(".")
30
+ @private = json["private"]
31
+ @vpc = if @private then json["vpc"].map { |v| Vpc.new(v["id"], v["region"]) } else [] end
32
+ @comment = json["comment"]
33
+
34
+ includes = if json["records"]["includes"].nil? then [] else json["records"]["includes"] end
35
+ includes = includes.flat_map(&Loader.method(:includes_file))
36
+ @records = (json["records"]["inlines"] + includes).map do |j|
37
+ RecordConfig.new(j, @domain, json["zone-id"])
38
+ end
39
+ @ignored = if json["records"]["ignored"].nil? then [] else json["records"]["ignored"] end
40
+ end
41
+ end
42
+
43
+ # Public: Populate this ZoneConfig from an AWS resource
44
+ #
45
+ # aws - the aws resource
46
+ def populate(aws)
47
+ @id = aws.id.sub(/\/hostedzone\//, '')
48
+ @domain = aws.name
49
+ @private = aws.config.private_zone
50
+ @vpc = if @private then aws.vpc else nil end
51
+ @comment = aws.config.comment
52
+ @records = aws.records.map do |record|
53
+ r = RecordConfig.new()
54
+ r.populate(record, @domain)
55
+ r
56
+ end
57
+ end
58
+
59
+ # Public: Get the config as a prettified JSON string.
60
+ #
61
+ # Returns the JSON string
62
+ def pretty_json
63
+ JSON.pretty_generate({
64
+ "zone-id" => @id,
65
+ "domain" => @domain,
66
+ "private" => @private,
67
+ "vpc" => if @vpc.nil? then nil else @vpc.map(&:to_hash) end,
68
+ "comment" => @comment,
69
+ "records" => {
70
+ "ignored" => if @ignored.nil? then [] else @ignored end,
71
+ "includes" => [],
72
+ "inlines" => @records.map(&:to_hash),
73
+ },
74
+ }.reject { |k, v| v.nil? })
75
+ end
76
+
77
+ # Public: Produce an array of differences between this local configuration and the
78
+ # configuration in AWS
79
+ #
80
+ # aws - the AWS resource
81
+ #
82
+ # Returns an array of the ZoneDiffs that were found
83
+ def diff(aws)
84
+ diffs = []
85
+
86
+ if @comment != aws.config.comment
87
+ diffs << ZoneDiff.new(ZoneChange::COMMENT, aws, self)
88
+ end
89
+ if @domain != aws.name
90
+ diffs << ZoneDiff.new(ZoneChange::DOMAIN, aws, self)
91
+ end
92
+ if @private != aws.config.private_zone
93
+ diffs << ZoneDiff.new(ZoneChange::PRIVATE, aws, self)
94
+ end
95
+ if @private and @vpc.sort != aws.vpc.sort
96
+ diffs << ZoneDiff.new(ZoneChange::VPC, aws, self)
97
+ end
98
+
99
+ record_diffs = diff_records(aws.records)
100
+ if !record_diffs.empty?
101
+ diffs << ZoneDiff.records(record_diffs, self)
102
+ end
103
+
104
+ diffs
105
+ end
106
+
107
+ private
108
+
109
+ # The unique key on a record is a combination of name and type
110
+ RecordKey = Struct.new(:name, :type)
111
+
112
+ # Internal: Produce an array of differences between local record configuration and the
113
+ # configuration in AWS.
114
+ #
115
+ # aws - an array of records in aws
116
+ #
117
+ # Returns an array of the RecordDiffs that were found
118
+ def diff_records(aws)
119
+ diffs = []
120
+
121
+ # map the records to their keys
122
+ aws = Hash[aws.map { |r| [RecordKey.new(r.name.gsub(/\\100/, "@"), r.type), r] }]
123
+ local = Hash[@records.map { |r| [RecordKey.new(r.name, r.type), r] }]
124
+
125
+ # find records in aws that are not configured locally, ignoring the NS and SOA
126
+ # record for the domain
127
+ aws.each do |key, record|
128
+ if !local.include?(key)
129
+ if @domain == record.name and record.type == "NS"
130
+ diffs << RecordDiff.default("Default NS record is supplied in AWS, but not locally. It will be ignored when syncing.", record)
131
+ elsif @domain == record.name and record.type == "SOA"
132
+ diffs << RecordDiff.default("Default SOA record is supplied in AWS, but not locally. It will be ignored when syncing.", record)
133
+ elsif !@ignored.find_index { |i| !record.name.match(i).nil? }.nil?
134
+ diffs << RecordDiff.ignored("Record (#{record.type}) #{record.name} is ignored by your blacklist", record)
135
+ else
136
+ diffs << RecordDiff.unmanaged(record)
137
+ end
138
+ end
139
+ end
140
+
141
+ local.each do |key, record|
142
+ if !aws.include?(key)
143
+ diffs << RecordDiff.added(record)
144
+ else
145
+ d = record.diff(aws[key])
146
+ if !d.empty?
147
+ diffs << RecordDiff.changed(d, record)
148
+ end
149
+ end
150
+ end
151
+
152
+ diffs.flatten
153
+ end
154
+ end
155
+ end
156
+ end