cumulus-aws 0.11.1

Sign up to get free protection for your applications and to get access to all the features.
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