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,89 @@
1
+ require "common/models/Diff"
2
+ require "util/Colors"
3
+
4
+ module Cumulus
5
+ module SQS
6
+ # Public: The types of changes that can be made to a dead letter config
7
+ module QueueChange
8
+ include Common::DiffChange
9
+
10
+ DELAY = Common::DiffChange.next_change_id
11
+ MESSAGE_SIZE = Common::DiffChange.next_change_id
12
+ MESSAGE_RETENTION = Common::DiffChange.next_change_id
13
+ RECEIVE_WAIT = Common::DiffChange.next_change_id
14
+ VISIBILITY = Common::DiffChange.next_change_id
15
+ DEAD = Common::DiffChange.next_change_id
16
+ POLICY = Common::DiffChange.next_change_id
17
+ end
18
+
19
+ # Public: Represents a single difference between local configuration and AWS configuration
20
+ class QueueDiff < Common::Diff
21
+ include QueueChange
22
+
23
+ def asset_type
24
+ "Queue"
25
+ end
26
+
27
+ def aws_name
28
+ @aws.name
29
+ end
30
+
31
+ def diff_string
32
+ case @type
33
+ when DELAY
34
+ [
35
+ "Delay",
36
+ Colors.aws_changes("\tAWS - #{aws} seconds"),
37
+ Colors.local_changes("\tLocal - #{local} seconds")
38
+ ].join("\n")
39
+ when MESSAGE_SIZE
40
+ [
41
+ "Max Message Size",
42
+ Colors.aws_changes("\tAWS - #{aws} bytes"),
43
+ Colors.local_changes("\tLocal - #{local} bytes")
44
+ ].join("\n")
45
+ when MESSAGE_RETENTION
46
+ [
47
+ "Message Retention Period",
48
+ Colors.aws_changes("\tAWS - #{aws} seconds"),
49
+ Colors.local_changes("\tLocal - #{local} seconds")
50
+ ].join("\n")
51
+ when RECEIVE_WAIT
52
+ [
53
+ "Receive Wait Time",
54
+ Colors.aws_changes("\tAWS - #{aws} seconds"),
55
+ Colors.local_changes("\tLocal - #{local} seconds")
56
+ ].join("\n")
57
+ when VISIBILITY
58
+ [
59
+ "Message Visibility",
60
+ Colors.aws_changes("\tAWS - #{aws} seconds"),
61
+ Colors.local_changes("\tLocal - #{local} seconds")
62
+ ].join("\n")
63
+ when DEAD
64
+ [
65
+ "Dead Letter Queue",
66
+ @changes.join("\n").lines.map { |l| "\t#{l}".chomp("\n") }
67
+ ].flatten.join("\n")
68
+ when POLICY
69
+ [
70
+ "Policy:",
71
+ if aws
72
+ Colors.unmanaged([
73
+ "\tRemoving:",
74
+ JSON.pretty_generate(aws).lines.map { |l| "\t\t#{l}".chomp("\n") }
75
+ ].join("\n"))
76
+ end,
77
+ if local
78
+ Colors.added([
79
+ "\tAdding:",
80
+ JSON.pretty_generate(local).lines.map { |l| "\t\t#{l}".chomp("\n") }
81
+ ].join("\n"))
82
+ end
83
+ ].reject(&:nil?).join("\n")
84
+ end
85
+ end
86
+
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,111 @@
1
+ require "conf/Configuration"
2
+
3
+ module Cumulus
4
+ # Public: Provides methods for creating strings with different colors in the
5
+ # console.
6
+ class Colors
7
+ @@colors_enabled = Configuration.instance.colors_enabled
8
+ @@color_prefix = "\033["
9
+ @@no_color = "#{@@color_prefix}0m"
10
+ @@red = "#{@@color_prefix}0;31m"
11
+ @@green = "#{@@color_prefix}0;32m"
12
+ @@orange = "#{@@color_prefix}1;33m"
13
+ @@blue = "#{@@color_prefix}1;34m"
14
+
15
+ # Public: color format a string that describes an added resource
16
+ #
17
+ # s - the string to format
18
+ #
19
+ # Returns the formatted string
20
+ def self.added(s)
21
+ self.green(s)
22
+ end
23
+
24
+ # Public: color format a string that describes an unmanaged resource
25
+ #
26
+ # s - the string to format
27
+ #
28
+ # Returns the formatted string
29
+ def self.unmanaged(s)
30
+ self.red(s)
31
+ end
32
+
33
+ # Public: color format a string the describes a removed resource
34
+ #
35
+ # s - the string to format
36
+ #
37
+ # Returns the formatted string
38
+ def self.removed(s)
39
+ self.red(s)
40
+ end
41
+
42
+ # Public: color format a string that describes the changes in AWS
43
+ #
44
+ # s - the string to format
45
+ #
46
+ # Returns the formatted string
47
+ def self.aws_changes(s)
48
+ self.blue(s)
49
+ end
50
+
51
+ # Public: color format a string that describes the local changes
52
+ #
53
+ # s - the string to format
54
+ #
55
+ # Returns the formatted string
56
+ def self.local_changes(s)
57
+ self.orange(s)
58
+ end
59
+
60
+ # Public: create a string that has a specific color. Will not output color
61
+ # if `colors_enabled` is set to false. This can be set in "configuration.json"
62
+ #
63
+ # s - the string to format
64
+ # color - the color to use
65
+ #
66
+ # Returns the formatted string
67
+ def self.colorize(s, color)
68
+ if @@colors_enabled
69
+ "#{color}#{s}#{@@no_color}"
70
+ else
71
+ s
72
+ end
73
+ end
74
+
75
+ # Public: Create a string that is red.
76
+ #
77
+ # s - the string to format
78
+ #
79
+ # Returns the red string
80
+ def self.red(s)
81
+ Colors.colorize(s, @@red)
82
+ end
83
+
84
+ # Public: Create a string that is green.
85
+ #
86
+ # s - the string to format
87
+ #
88
+ # Returns the green string
89
+ def self.green(s)
90
+ Colors.colorize(s, @@green)
91
+ end
92
+
93
+ # Public: Create a string that is blue.
94
+ #
95
+ # s - the string to format
96
+ #
97
+ # Returns the blue string
98
+ def self.blue(s)
99
+ Colors.colorize(s, @@blue)
100
+ end
101
+
102
+ # Public: Create a string that is orange.
103
+ #
104
+ # s - the string to format
105
+ #
106
+ # Returns the orange string
107
+ def self.orange(s)
108
+ Colors.colorize(s, @@orange)
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,51 @@
1
+ module Cumulus
2
+
3
+ # Public: Provide methods for setting the status code that
4
+ # Cumulus should exit with
5
+ module StatusCodes
6
+
7
+ # Indicates that we are exiting normally
8
+ OK = 0
9
+
10
+ # Indicates there was an exception during execution
11
+ EXCEPTION = 1
12
+
13
+ # Indicates that there were diffs
14
+ DIFFS = 2
15
+
16
+ # Indicates that there were diffs and they were synced
17
+ SYNC_DIFFS = 3
18
+
19
+ # Static attributes and methods for keeping track of status codes
20
+ class << self
21
+ # Holds the value for the current status
22
+ @@CURRENT_STATUS = OK
23
+
24
+ # Public: Sets the status code if it is more severe than the current status code
25
+ def set_status(status)
26
+
27
+ # Only set the status if we are not already in exception state
28
+ if @@CURRENT_STATUS != EXCEPTION
29
+
30
+ # Only set the status if it is more severe (higher) than the current status
31
+ if status > @@CURRENT_STATUS
32
+ @@CURRENT_STATUS = status
33
+ end
34
+
35
+ end
36
+ end
37
+
38
+ # On exit, if the exit was successful, exit with the
39
+ # status codes that describes what happened while running
40
+ at_exit do
41
+
42
+ if $!.nil? || ($!.is_a?(SystemExit) && $!.success?)
43
+ exit @@CURRENT_STATUS
44
+ end
45
+
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,73 @@
1
+ require "common/BaseLoader"
2
+ require "conf/Configuration"
3
+ require "vpc/models/RouteTableConfig"
4
+ require "vpc/models/SubnetConfig"
5
+ require "vpc/models/VpcConfig"
6
+
7
+ require "aws-sdk"
8
+
9
+ # Public: Load VPC assets
10
+ module Cumulus
11
+ module VPC
12
+ module Loader
13
+ include Common::BaseLoader
14
+
15
+ @@vpcs_dir = Configuration.instance.vpc.vpcs_directory
16
+ @@subnets_dir = Configuration.instance.vpc.subnets_directory
17
+ @@route_tables_dir = Configuration.instance.vpc.route_tables_directory
18
+ @@policies_dir = Configuration.instance.vpc.policies_directory
19
+ @@network_acls_dir = Configuration.instance.vpc.network_acls_directory
20
+
21
+ # Public: Load all the VPC configurations as VpcConfig objects
22
+ #
23
+ # Returns an array of VpcConfig
24
+ def self.vpcs
25
+ Common::BaseLoader::resources(@@vpcs_dir, &VpcConfig.method(:new))
26
+ end
27
+
28
+ # Public: Load a single VPC configuration
29
+ #
30
+ # Returns a VpcConfig
31
+ def self.vpc(vpc_name)
32
+ Common::BaseLoader::resource(vpc_name, @@vpcs_dir, &VpcConfig.method(:new))
33
+ end
34
+
35
+ # Public: Load the specified policy as a JSON object
36
+ #
37
+ # Returns the JSON object for the policy
38
+ def self.policy(policy_name)
39
+ Common::BaseLoader::resource(policy_name, @@policies_dir) do |policy_name, policy|
40
+ policy
41
+ end
42
+ end
43
+
44
+ # Public: Load all subnets as SubnetConfig objects
45
+ #
46
+ # Returns an array of SubnetConfig
47
+ def self.subnets
48
+ Common::BaseLoader::resources(@@subnets_dir, &SubnetConfig.method(:new))
49
+ end
50
+
51
+ # Public: Load a subnet as a SubnetConfig
52
+ #
53
+ # Returns the SubnetConfig
54
+ def self.subnet(subnet_name)
55
+ Common::BaseLoader::resource(subnet_name, @@subnets_dir, &SubnetConfig.method(:new))
56
+ end
57
+
58
+ # Public: Load a route table as a RouteTableConfig
59
+ #
60
+ # Returns the RouteTableConfig
61
+ def self.route_table(rt_name)
62
+ Common::BaseLoader::resource(rt_name, @@route_tables_dir, &RouteTableConfig.method(:new))
63
+ end
64
+
65
+ # Public: Load a network acl as a NetworkAclConfig
66
+ #
67
+ # Returns the NetworkAclConfig
68
+ def self.network_acl(acl_name)
69
+ Common::BaseLoader::resource(acl_name, @@network_acls_dir, &NetworkAclConfig.method(:new))
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,954 @@
1
+ require "common/manager/Manager"
2
+ require "conf/Configuration"
3
+ require "util/Colors"
4
+ require "vpc/loader/Loader"
5
+ require "vpc/models/VpcDiff"
6
+ require "vpc/models/VpcConfig"
7
+ require "vpc/models/RouteTableDiff"
8
+ require "vpc/models/RouteTableConfig"
9
+ require "vpc/models/EndpointDiff"
10
+ require "vpc/models/NetworkAclDiff"
11
+ require "vpc/models/SubnetDiff"
12
+ require "vpc/models/SubnetConfig"
13
+ require "ec2/EC2"
14
+ require "ec2/IPProtocolMapping"
15
+
16
+ require "aws-sdk"
17
+ require "json"
18
+
19
+ module Cumulus
20
+ module VPC
21
+ class Manager < Common::Manager
22
+
23
+ include VpcChange
24
+
25
+ def initialize
26
+ super()
27
+ @create_asset = false
28
+ @client = Aws::EC2::Client.new(Configuration.instance.client)
29
+ end
30
+
31
+ def resource_name
32
+ "Virtual Private Cloud"
33
+ end
34
+
35
+ def local_resources
36
+ @local_resources ||= Hash[Loader.vpcs.map { |local| [local.name, local] }]
37
+ end
38
+
39
+ def aws_resources
40
+ @aws_resources ||= EC2::named_vpcs
41
+ end
42
+
43
+ def unmanaged_diff(aws)
44
+ VpcDiff.unmanaged(aws)
45
+ end
46
+
47
+ def added_diff(local)
48
+ VpcDiff.added(local)
49
+ end
50
+
51
+ def diff_resource(local, aws)
52
+ local.diff(aws)
53
+ end
54
+
55
+ def update(local, diffs)
56
+ aws_vpc = EC2::named_vpcs[local.name]
57
+
58
+ diffs.each do |diff|
59
+ case diff.type
60
+ when CIDR
61
+ puts Colors.blue("CIDR Block cannot be updated. You must create a new VPC.")
62
+ when TENANCY
63
+ puts Colors.blue("Tenancy cannot be updated. You must create a new VPC.")
64
+ when DHCP
65
+ puts Colors.blue("Updating DHCP Options...")
66
+ update_dhcp_options(aws_vpc, local.dhcp)
67
+ when ROUTE_TABLES
68
+ puts Colors.blue("Updating Route Tables...")
69
+ update_route_tables(aws_vpc, diff.changes)
70
+ when ENDPOINTS
71
+ puts Colors.blue("Updating Endpoints...")
72
+ update_endpoints(aws_vpc, diff.changes)
73
+ when ADDRESSES
74
+ puts Colors.blue("Updating Address Associations...")
75
+ update_address_associations(aws_vpc, diff.changes)
76
+ when NETWORK_ACLS
77
+ puts Colors.blue("Updating Network ACLs")
78
+ update_network_acls(aws_vpc, diff.changes)
79
+ when SUBNETS
80
+ puts Colors.blue("Updating Subnets...")
81
+ update_subnets(aws_vpc, diff.changes)
82
+ when TAGS
83
+ puts Colors.blue("Updating Tags...")
84
+
85
+ if !diff.tags_to_remove.empty?
86
+ delete_tags(aws_vpc.vpc_id, diff.tags_to_remove)
87
+ end
88
+
89
+ if !diff.tags_to_add.empty?
90
+ create_tags(aws_vpc.vpc_id, diff.tags_to_add)
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ # Public: Renames an asset and all references to it and syncs the name tag change to AWS.
97
+ def rename(asset_type, old_name, new_name)
98
+
99
+ # Strip .json from the old_name and new_name
100
+ old_name = old_name.end_with?(".json") ? old_name[0...-5] : old_name
101
+ new_name = new_name.end_with?(".json") ? new_name[0...-5] : new_name
102
+
103
+ vpcs_dir = Configuration.instance.vpc.vpcs_directory
104
+ subnets_dir = Configuration.instance.vpc.subnets_directory
105
+ route_tables_dir = Configuration.instance.vpc.route_tables_directory
106
+ policies_dir = Configuration.instance.vpc.policies_directory
107
+ network_acls_dir = Configuration.instance.vpc.network_acls_directory
108
+
109
+ case asset_type
110
+ when "network-acl"
111
+
112
+ puts Colors.blue("Renaming Network ACL #{old_name} to #{new_name}")
113
+
114
+ # Update the Name tag and resave the file
115
+ acl_local = Loader.network_acl(old_name)
116
+ acl_local.tags["Name"] = new_name
117
+ json = JSON.pretty_generate(acl_local.to_hash)
118
+ File.open("#{network_acls_dir}/#{new_name}.json", "w") { |f| f.write(json) }
119
+
120
+ # Update the tags in AWS
121
+ acl_aws = EC2::named_network_acls[old_name]
122
+ create_tags(acl_aws.network_acl_id, { "Name" => new_name})
123
+
124
+ # Update the vpc references to it
125
+ Loader.vpcs.each do |vpc|
126
+ vpc.network_acls.collect! { |acl_name| if acl_name == old_name then new_name else acl_name end }
127
+
128
+ json = JSON.pretty_generate(vpc.to_hash)
129
+ File.open("#{vpcs_dir}/#{vpc.name}.json", "w") { |f| f.write(json) }
130
+ end
131
+
132
+ # Update the subnet references to it
133
+ Loader.subnets.each do |subnet|
134
+ if subnet.network_acl == old_name
135
+ subnet.network_acl = new_name
136
+
137
+ json = JSON.pretty_generate(subnet.to_hash)
138
+ File.open("#{subnets_dir}/#{subnet.name}.json", "w") { |f| f.write(json) }
139
+ end
140
+ end
141
+
142
+ # Delete the old file
143
+ File.delete("#{network_acls_dir}/#{old_name}.json")
144
+
145
+ when "policy"
146
+
147
+ puts Colors.blue("Renaming policy #{old_name} to #{new_name}")
148
+
149
+ # Rename the file
150
+ File.rename("#{policies_dir}/#{old_name}.json", "#{policies_dir}/#{new_name}.json")
151
+
152
+ # Update the references to it in vpc endpoint
153
+ Loader.vpcs.each do |vpc|
154
+
155
+ endpoints_updated = false
156
+ vpc.endpoints.each do |endpoint|
157
+ if endpoint.policy == old_name
158
+ endpoint.policy = new_name
159
+ endpoints_updated = true
160
+ end
161
+ end
162
+
163
+ if endpoints_updated
164
+ json = JSON.pretty_generate(vpc.to_hash)
165
+ File.open("#{vpcs_dir}/#{vpc.name}.json", "w") { |f| f.write(json) }
166
+ end
167
+ end
168
+
169
+ when "route-table"
170
+ puts Colors.blue("Renaming route table #{old_name} to #{new_name}")
171
+
172
+ # Update the Name tag and resave the file
173
+ rt_local = Loader.route_table(old_name)
174
+ rt_local.tags["Name"] = new_name
175
+ json = JSON.pretty_generate(rt_local.to_hash)
176
+ File.open("#{route_tables_dir}/#{new_name}.json", "w") { |f| f.write(json) }
177
+
178
+ # Update the tags in AWS
179
+ rt_aws = EC2::named_route_tables[old_name]
180
+ create_tags(rt_aws.route_table_id, { "Name" => new_name})
181
+
182
+ # Update the vpc references to it
183
+ Loader.vpcs.each do |vpc|
184
+ vpc.route_tables.collect! { |rt_name| if rt_name == old_name then new_name else rt_name end }
185
+
186
+ json = JSON.pretty_generate(vpc.to_hash)
187
+ File.open("#{vpcs_dir}/#{vpc.name}.json", "w") { |f| f.write(json) }
188
+ end
189
+
190
+ # Update the subnet references to it
191
+ Loader.subnets.each do |subnet|
192
+ if subnet.route_table == old_name
193
+ subnet.route_table = new_name
194
+ json = JSON.pretty_generate(subnet.to_hash)
195
+ File.open("#{subnets_dir}/#{subnet.name}.json", "w") { |f| f.write(json) }
196
+ end
197
+ end
198
+
199
+ # Delete the old file
200
+ File.delete("#{route_tables_dir}/#{old_name}.json")
201
+ when "subnet"
202
+ puts Colors.blue("Renaming subnet #{old_name} to #{new_name}")
203
+
204
+ # Update the Name tag and resave the file
205
+ subnet_local = Loader.subnet(old_name)
206
+ subnet_local.tags["Name"] = new_name
207
+ json = JSON.pretty_generate(subnet_local.to_hash)
208
+ File.open("#{subnets_dir}/#{new_name}.json", "w") { |f| f.write(json) }
209
+
210
+ # Update the tags in AWS
211
+ subnet_aws = EC2::named_subnets[old_name]
212
+ create_tags(subnet_aws.subnet_id, { "Name" => new_name})
213
+
214
+ # Update the vpc references to it
215
+ Loader.vpcs.each do |vpc|
216
+ vpc.subnets.collect! { |subnet_name| if subnet_name == old_name then new_name else subnet_name end }
217
+
218
+ json = JSON.pretty_generate(vpc.to_hash)
219
+ File.open("#{vpcs_dir}/#{vpc.name}.json", "w") { |f| f.write(json) }
220
+ end
221
+
222
+ # Delete the old file
223
+ File.delete("#{subnets_dir}/#{old_name}.json")
224
+ when "vpc"
225
+ puts Colors.blue("Renaming vpc #{old_name} to #{new_name}")
226
+
227
+ # Update the Name tag and resave the file
228
+ vpc_local = Loader.vpc(old_name)
229
+ vpc_local.tags["Name"] = new_name
230
+ json = JSON.pretty_generate(vpc_local.to_hash)
231
+ File.open("#{vpcs_dir}/#{new_name}.json", "w") { |f| f.write(json) }
232
+
233
+ # Update the tags in AWS
234
+ vpc_aws = EC2::named_vpcs[old_name]
235
+ create_tags(vpc_aws.vpc_id, { "Name" => new_name})
236
+
237
+ # Delete the old file
238
+ File.delete("#{vpcs_dir}/#{old_name}.json")
239
+ end
240
+ end
241
+
242
+ # Public: Migrates the existing AWS config to Cumulus
243
+ def migrate
244
+ # Create the directories
245
+ vpc_dir = "#{@migration_root}/vpc"
246
+ policies_dir = "#{vpc_dir}/policies"
247
+ route_tables_dir = "#{vpc_dir}/route-tables"
248
+ network_acls_dir = "#{vpc_dir}/network-acls"
249
+ subnets_dir = "#{vpc_dir}/subnets"
250
+ vpcs_dir = "#{vpc_dir}/vpcs"
251
+
252
+ if !Dir.exists?(@migration_root)
253
+ Dir.mkdir(@migration_root)
254
+ end
255
+ if !Dir.exists?(vpc_dir)
256
+ Dir.mkdir(vpc_dir)
257
+ end
258
+ if !Dir.exists?(policies_dir)
259
+ Dir.mkdir(policies_dir)
260
+ end
261
+ if !Dir.exists?(route_tables_dir)
262
+ Dir.mkdir(route_tables_dir)
263
+ end
264
+ if !Dir.exists?(network_acls_dir)
265
+ Dir.mkdir(network_acls_dir)
266
+ end
267
+ if !Dir.exists?(subnets_dir)
268
+ Dir.mkdir(subnets_dir)
269
+ end
270
+ if !Dir.exists?(vpcs_dir)
271
+ Dir.mkdir(vpcs_dir)
272
+ end
273
+
274
+ # Migrate the different assets
275
+ migrate_policies(policies_dir)
276
+ route_table_names = migrate_route_tables(route_tables_dir)
277
+ network_acl_names = migrate_network_acls(network_acls_dir)
278
+ subnet_names = migrate_subnets(subnets_dir, route_table_names, network_acl_names)
279
+ migrate_vpcs(vpcs_dir, route_table_names, subnet_names, network_acl_names)
280
+ end
281
+
282
+ # Public: Migrates the endpoint policies. Uses Version to name the file
283
+ #
284
+ # dir - the directory to store policies in
285
+ def migrate_policies(dir)
286
+ puts Colors.blue("Migrating policies to #{dir}")
287
+ # Get the policies for each endpoint
288
+ policies = EC2::endpoints.map { |endpoint| endpoint.parsed_policy }
289
+ policies.each do |policy|
290
+ name = policy["Version"]
291
+ puts "Migrating policy #{name}"
292
+ json = JSON.pretty_generate(policy)
293
+ File.open("#{dir}/#{name}.json", "w") { |f| f.write(json) }
294
+ end
295
+ end
296
+
297
+ # Public: Migrates route tables. Uses name if available or id to name file.
298
+ #
299
+ # dir - the directory to store route table configurations in
300
+ #
301
+ # Returns a Hash of route table id to file name
302
+ def migrate_route_tables(dir)
303
+ puts Colors.blue("Migrating route tables to #{dir}")
304
+ Hash[EC2::named_route_tables.map do |name, route_table|
305
+ puts "Migrating route table #{name}"
306
+
307
+ cumulus_rt = RouteTableConfig.new(name).populate!(route_table)
308
+
309
+ json = JSON.pretty_generate(cumulus_rt.to_hash)
310
+ File.open("#{dir}/#{name}.json", "w") { |f| f.write(json) }
311
+
312
+ [route_table.route_table_id, name]
313
+ end]
314
+ end
315
+
316
+ # Public: Migrate Network ACLs. Uses name if available or id to name file.
317
+ #
318
+ # dir - the directory to store network acl configurations in
319
+ #
320
+ # Returns a Hash of network acl id to file name
321
+ def migrate_network_acls(dir)
322
+ puts Colors.blue("Migrating network acls to #{dir}")
323
+ Hash[EC2::named_network_acls.map do |name, network_acl|
324
+ puts "Migrating network acl #{name}"
325
+
326
+ cumulus_acl = NetworkAclConfig.new(name).populate!(network_acl)
327
+
328
+ json = JSON.pretty_generate(cumulus_acl.to_hash)
329
+ File.open("#{dir}/#{name}.json", "w") { |f| f.write(json) }
330
+
331
+ [network_acl.network_acl_id, name]
332
+ end]
333
+ end
334
+
335
+ # Public: Migrates subnets. Uses name if available or id to name file.
336
+ #
337
+ # dir - the directory to store subnet configurations in
338
+ # rt_map - a map of route table ids to names to substitute for in the subnet config
339
+ # acl_map - a map of network acl ids to names to substitute for in the subnet config
340
+ #
341
+ # Returns a hash of subnet id to file name
342
+ def migrate_subnets(dir, rt_map, acl_map)
343
+ puts Colors.blue("Migrating subnets to #{dir}")
344
+ Hash[EC2::named_subnets.map do |name, subnet|
345
+ puts "Migrating subnet #{name}"
346
+
347
+ cumulus_subnet = SubnetConfig.new(name).populate!(subnet, rt_map, acl_map)
348
+
349
+ json = JSON.pretty_generate(cumulus_subnet.to_hash)
350
+ File.open("#{dir}/#{name}.json", "w") { |f| f.write(json) }
351
+
352
+ [subnet.subnet_id, name]
353
+ end]
354
+ end
355
+
356
+ # Public: Migrates vpcs. Uses name if available or id to name file.
357
+ #
358
+ # dir - the directory to store vpc configurations in
359
+ # rt_map - a map of route table ids to names to substitute for in the vpc config
360
+ # subnet_map - a map of subnet ids to names to substitute for in the vpc config
361
+ # acl_map - a map of network acl ids to names to substitute fo rin the vpc config
362
+ def migrate_vpcs(dir, rt_map, subnet_map, acl_map)
363
+ puts Colors.blue("Migrating vpcs to #{dir}")
364
+ Hash[EC2::named_vpcs.map do |name, vpc|
365
+ puts "Migrating vpc #{name}"
366
+
367
+ cumulus_vpc = (VpcConfig.new(name)).populate!(vpc, rt_map, subnet_map, acl_map)
368
+
369
+ json = JSON.pretty_generate(cumulus_vpc.to_hash)
370
+ File.open("#{dir}/#{name}.json", "w") { |f| f.write(json) }
371
+ end]
372
+ end
373
+
374
+ private
375
+
376
+ def create_tags(resource_id, tags)
377
+ @client.create_tags({
378
+ resources: [resource_id],
379
+ tags: tags.map do |key, value|
380
+ {
381
+ key: key,
382
+ value: value
383
+ }
384
+ end
385
+ })
386
+ end
387
+
388
+ def delete_tags(resource_id, tags)
389
+ @client.delete_tags({
390
+ resources: [resource_id],
391
+ tags: tags.map do |key, value|
392
+ {
393
+ key: key,
394
+ value: value
395
+ }
396
+ end
397
+ })
398
+ end
399
+
400
+ def update_dhcp_options(aws_vpc, dhcp_config)
401
+ old_options_id = aws_vpc.dhcp_options_id
402
+
403
+ options_id = if !dhcp_config or dhcp_config.to_aws.empty?
404
+ "default"
405
+ else
406
+ @client.create_dhcp_options({
407
+ dhcp_configurations: dhcp_config.to_aws
408
+ }).dhcp_options.dhcp_options_id
409
+ end
410
+
411
+ @client.associate_dhcp_options({
412
+ vpc_id: aws_vpc.vpc_id,
413
+ dhcp_options_id: options_id
414
+ })
415
+
416
+ EC2::refresh_vpcs!
417
+
418
+ # Delete the old options if no other vpc depends on it
419
+ if old_options_id != "default" and !EC2::vpcs.any? { |vpc| vpc.dhcp_options_id == old_options_id }
420
+ @client.delete_dhcp_options({
421
+ dhcp_options_id: old_options_id
422
+ })
423
+ end
424
+ end
425
+
426
+ def update_route_tables(aws_vpc, rt_changes)
427
+ # Added route tables
428
+ rt_changes.added.each do |rt_name, added_diff|
429
+ added_rt = added_diff.local
430
+
431
+ puts "Creating route table #{rt_name}"
432
+ create_route_table(aws_vpc.vpc_id, added_rt)
433
+ end
434
+
435
+ # Removed route tables
436
+ rt_changes.removed.each do |rt_name, removed_diff|
437
+ removed_rt = removed_diff.aws
438
+
439
+ # Make sure it is not the main route table
440
+ if removed_rt.associations.any? { |assoc| assoc.main }
441
+ puts Colors.red("Cannot delete main route table #{rt_name}")
442
+ else
443
+ puts "Deleting route table #{rt_name}"
444
+ delete_route_table(removed_rt)
445
+ end
446
+ end
447
+
448
+ # Modified route tables
449
+ rt_changes.modified.each do |rt_name, modified_diff|
450
+ puts "Updating #{rt_name}"
451
+ aws_rt = EC2::named_route_tables[rt_name]
452
+ aws_rt_id = aws_rt.route_table_id
453
+
454
+ modified_diff.changes.each do |diff|
455
+ case diff.type
456
+ when RouteTableChange::ROUTES
457
+ # Added routes
458
+ diff.changes.added.each do |cidr, route_diff|
459
+ create_route(aws_rt_id, route_diff.local)
460
+ end
461
+
462
+ # Removed routes
463
+ diff.changes.removed.each do |cidr, route_diff|
464
+ delete_route(aws_rt_id, route_diff.aws.destination_cidr_block)
465
+ end
466
+
467
+ # Modified routes
468
+ diff.changes.modified.each do |cidr, route_diff|
469
+ replace_route(aws_rt_id, route_diff.local)
470
+ end
471
+ when RouteTableChange::VGWS
472
+ # Added vgws
473
+ diff.changes.added.each do |vgw|
474
+ enable_vgw(aws_rt_id, vgw)
475
+ end
476
+
477
+ # Removed vgws
478
+ diff.changes.removed.each do |vgw|
479
+ disable_vgw(aws_rt_id, vgw)
480
+ end
481
+ when RouteTableChange::TAGS
482
+ if !diff.tags_to_remove.empty?
483
+ delete_tags(aws_rt_id, diff.tags_to_remove)
484
+ end
485
+
486
+ if !diff.tags_to_add.empty?
487
+ create_tags(aws_rt_id, diff.tags_to_add)
488
+ end
489
+ end
490
+ end
491
+ end
492
+ end
493
+
494
+ def create_route_table(vpc_id, rt)
495
+ rt_id = @client.create_route_table({
496
+ vpc_id: vpc_id
497
+ }).route_table.route_table_id
498
+
499
+ if !rt.tags.empty?
500
+ create_tags(rt_id, rt.tags)
501
+ end
502
+
503
+ rt.propagate_vgws.each do |vgw|
504
+ enable_vgw(rt_id, vgw)
505
+ end
506
+
507
+ rt.routes.each do |route|
508
+ create_route(rt_id, route)
509
+ end
510
+ end
511
+
512
+ def delete_route_table(aws_rt)
513
+ # First have to disassociate all of the subnets from the route table
514
+ aws_rt.associations.each do |assoc|
515
+ @client.disassociate_route_table({
516
+ association_id: assoc.route_table_association_id
517
+ })
518
+ end
519
+
520
+ # Delete the route table
521
+ @client.delete_route_table({
522
+ route_table_id: aws_rt.route_table_id
523
+ })
524
+ end
525
+
526
+ def create_route(rt_id, route)
527
+ puts "Creating route #{route.dest_cidr}"
528
+ @client.create_route({
529
+ route_table_id: rt_id,
530
+ destination_cidr_block: route.dest_cidr,
531
+ gateway_id: if route.gateway_id then route.gateway_id end,
532
+ network_interface_id: if route.network_interface_id then route.network_interface_id end,
533
+ vpc_peering_connection_id: if route.vpc_peering_connection_id then route.vpc_peering_connection_id end
534
+ })
535
+ end
536
+
537
+ def delete_route(rt_id, dest_cidr)
538
+ puts "Deleting route #{dest_cidr}"
539
+ @client.delete_route({
540
+ route_table_id: rt_id,
541
+ destination_cidr_block: dest_cidr
542
+ })
543
+ end
544
+
545
+ def replace_route(rt_id, route)
546
+ puts "Replacing route #{route.dest_cidr}"
547
+ @client.replace_route({
548
+ route_table_id: rt_id,
549
+ destination_cidr_block: route.dest_cidr,
550
+ gateway_id: if route.gateway_id then route.gateway_id end,
551
+ network_interface_id: if route.network_interface_id then route.network_interface_id end,
552
+ vpc_peering_connection_id: if route.vpc_peering_connection_id then route.vpc_peering_connection_id end
553
+ })
554
+ end
555
+
556
+ def enable_vgw(rt_id, vgw)
557
+ @client.enable_vgw_route_propagation({
558
+ route_table_id: rt_id,
559
+ gateway_id: vgw
560
+ })
561
+ end
562
+
563
+ def disable_vgw(rt_id, vgw)
564
+ @client.disable_vgw_route_propagation({
565
+ route_table_id: rt_id,
566
+ gateway_id: vgw
567
+ })
568
+ end
569
+
570
+ def update_endpoints(aws_vpc, endpoint_changes)
571
+
572
+ def rt_ids(rt_names)
573
+ rt_names.map do |rt_name|
574
+ aws_rt = EC2::named_route_tables[rt_name]
575
+ if aws_rt.nil?
576
+ puts Colors.red("Error updating endpoints. No route table found for #{rt_name}.")
577
+ exit 1
578
+ else
579
+ aws_rt.route_table_id
580
+ end
581
+ end
582
+ end
583
+
584
+ # Added endpoints
585
+ endpoint_changes.added.each do |endpoint_name, added_diff|
586
+ puts "Adding endpoint #{endpoint_name}"
587
+ endpoint = added_diff.local
588
+
589
+ @client.create_vpc_endpoint({
590
+ vpc_id: aws_vpc.vpc_id,
591
+ service_name: endpoint.service_name,
592
+ policy_document: if endpoint.policy then JSON.generate(Loader.policy(endpoint.policy)) end,
593
+ route_table_ids: rt_ids(endpoint.route_tables)
594
+ })
595
+ end
596
+
597
+ # Deleted endpoints
598
+ endpoint_changes.removed.each do |endpoint_name, removed_diff|
599
+ puts "Deleting endpoint #{endpoint_name}"
600
+ @client.delete_vpc_endpoints({
601
+ vpc_endpoint_ids: [removed_diff.aws.vpc_endpoint_id]
602
+ })
603
+ end
604
+
605
+ # Modified endpoints
606
+ endpoint_changes.modified.each do |endpoint_name, modified_diff|
607
+ puts "Updating endpoint #{endpoint_name}"
608
+
609
+ aws_endpoint = modified_diff.aws
610
+ local_endpoint = modified_diff.local
611
+
612
+ add_rt_ids = nil
613
+ remove_rt_ids = nil
614
+
615
+ modified_diff.changes.select { |d| d.type == EndpointChange::ROUTE_TABLES}.map do |endpoint_diff|
616
+ if !endpoint_diff.changes.added.empty?
617
+ add_rt_ids = rt_ids(endpoint_diff.changes.added)
618
+ end
619
+
620
+ if !endpoint_diff.changes.removed.empty?
621
+ remove_rt_ids = rt_ids(endpoint_diff.changes.removed)
622
+ end
623
+ end
624
+
625
+ @client.modify_vpc_endpoint({
626
+ vpc_endpoint_id: aws_endpoint.vpc_endpoint_id,
627
+ reset_policy: false,
628
+ policy_document: if local_endpoint.policy then JSON.generate(Loader.policy(local_endpoint.policy)) end,
629
+ add_route_table_ids: add_rt_ids,
630
+ remove_route_table_ids: remove_rt_ids
631
+ })
632
+ end
633
+ end
634
+
635
+ def update_address_associations(aws_vpc, address_changes)
636
+ # Added associations
637
+ address_changes.added.each do |ip, addr_change|
638
+ if addr_change.local == "any"
639
+ puts "#{ip} must be associated manually to any interface"
640
+ else
641
+ puts "Associating #{ip} to #{addr_change.local_name}"
642
+ aws_address = EC2::public_addresses[ip]
643
+ @client.associate_address({
644
+ allow_reassociation: false, # This makes the operation fail if it was already associated
645
+ allocation_id: aws_address.allocation_id,
646
+ network_interface_id: addr_change.local.network_interface_id
647
+ })
648
+ end
649
+ end
650
+
651
+ # Removed associations
652
+ address_changes.removed.each do |ip, addr_change|
653
+ puts "Disassociating #{ip} from #{addr_change.aws_name}"
654
+ aws_address = EC2::public_addresses[ip]
655
+ @client.disassociate_address({
656
+ association_id: aws_address.association_id
657
+ })
658
+ end
659
+
660
+ # Modified associations
661
+ address_changes.modified.each do |ip, addr_change|
662
+ puts "Changing association for #{ip} from #{addr_change.aws_name} to #{addr_change.local_name}"
663
+ aws_address = EC2::public_addresses[ip]
664
+ @client.associate_address({
665
+ allow_reassociation: true, # We know it was associated to something else so allow reassociation
666
+ allocation_id: aws_address.allocation_id,
667
+ network_interface_id: addr_change.local.network_interface_id
668
+ })
669
+ end
670
+ end
671
+
672
+ def update_network_acls(aws_vpc, network_changes)
673
+ # Added network acls
674
+ network_changes.added.each do |acl_name, added_diff|
675
+ puts "Creating Network ACL #{acl_name}"
676
+ created_id = @client.create_network_acl({
677
+ vpc_id: aws_vpc.vpc_id
678
+ }).network_acl.network_acl_id
679
+
680
+ acl_config = added_diff.local
681
+
682
+ # Associate tags
683
+ create_tags(created_id, acl_config.tags)
684
+
685
+ # Create outbound entries
686
+ acl_config.outbound.each do |entry|
687
+ puts "Creating outbound entry with rule #{entry.rule}"
688
+ create_network_acl_entry(created_id, entry, true)
689
+ end
690
+
691
+ # Create inbound entries
692
+ acl_config.inbound.each do |entry|
693
+ puts "Creating inbound entry with rule #{entry.rule}"
694
+ create_network_acl_entry(created_id, entry, false)
695
+ end
696
+ end
697
+
698
+ # Deleted network acls
699
+ network_changes.removed.each do |acl_name, removed_diff|
700
+ aws_acl = removed_diff.aws
701
+
702
+ # Make sure the user isn't trying to delete the default acl
703
+ if aws_acl.is_default
704
+ puts Colors.red("Cannot delete the default Network ACL #{acl_name}")
705
+ # Make sure there are no subnets associated with the acl
706
+ elsif !aws_acl.associations.empty?
707
+ puts Colors.red("Cannot delete a Network ACL with subnets associated to it")
708
+ else
709
+ puts "Deleting Network ACL #{acl_name}"
710
+ @client.delete_network_acl({
711
+ network_acl_id: aws_acl.network_acl_id
712
+ })
713
+ end
714
+
715
+ end
716
+
717
+ # Modified network acls
718
+ network_changes.modified.each do |acl_name, modified_diff|
719
+ acl_id = modified_diff.aws.network_acl_id
720
+
721
+ modified_diff.changes.each do |net_acl_diff|
722
+ case net_acl_diff.type
723
+ when NetworkAclChange::OUTBOUND
724
+ # Added outbound entries
725
+ net_acl_diff.changes.added.each do |rule, added_entry_diff|
726
+ puts "Creating outbound entry with rule #{rule}"
727
+ create_network_acl_entry(acl_id, added_entry_diff.local, true)
728
+ end
729
+
730
+ # Removed outbound entries
731
+ net_acl_diff.changes.removed.each do |rule, removed_entry_diff|
732
+ puts "Removing outbound entry with rule #{rule}"
733
+ delete_network_acl_entry(acl_id, rule, true)
734
+ end
735
+
736
+ # Modified outbound entries
737
+ net_acl_diff.changes.modified.each do |rule, modified_entry_diff|
738
+ puts "Updating outbound entry with rule #{rule}"
739
+ replace_network_acl_entry(acl_id, modified_entry_diff.local, true)
740
+ end
741
+ when NetworkAclChange::INBOUND
742
+ # Added inbound entries
743
+ net_acl_diff.changes.added.each do |rule, added_entry_diff|
744
+ puts "Creating inbound entry with rule #{rule}"
745
+ create_network_acl_entry(acl_id, added_entry_diff.local, false)
746
+ end
747
+
748
+ # Removed outbound entries
749
+ net_acl_diff.changes.removed.each do |rule, removed_entry_diff|
750
+ puts "Removing outbound entry with rule #{rule}"
751
+ delete_network_acl_entry(acl_id, rule, false)
752
+ end
753
+
754
+ # Modified outbound entries
755
+ net_acl_diff.changes.modified.each do |rule, modified_entry_diff|
756
+ puts "Updating outbound entry with rule #{rule}"
757
+ replace_network_acl_entry(acl_id, modified_entry_diff.local, false)
758
+ end
759
+ when NetworkAclChange::TAGS
760
+ if !net_acl_diff.tags_to_remove.empty?
761
+ delete_tags(acl_id, net_acl_diff.tags_to_remove)
762
+ end
763
+
764
+ if !net_acl_diff.tags_to_add.empty?
765
+ create_tags(acl_id, net_acl_diff.tags_to_add)
766
+ end
767
+ end
768
+ end
769
+ end
770
+ end
771
+
772
+ def create_network_acl_entry(network_acl_id, entry, egress)
773
+ @client.create_network_acl_entry({
774
+ network_acl_id: network_acl_id,
775
+ rule_number: entry.rule,
776
+ protocol: EC2::IPProtocolMapping.protocol(entry.protocol),
777
+ rule_action: entry.action,
778
+ egress: egress,
779
+ cidr_block: entry.cidr_block,
780
+ icmp_type_code:
781
+ if entry.icmp_type || entry.icmp_code
782
+ {
783
+ type: entry.icmp_type,
784
+ code: entry.icmp_code
785
+ }
786
+ end,
787
+ port_range:
788
+ if entry.ports
789
+ from_port, to_port = entry.expand_ports
790
+ {
791
+ from: from_port,
792
+ to: to_port
793
+ }
794
+ end
795
+ })
796
+ end
797
+
798
+ def delete_network_acl_entry(network_acl_id, rule, egress)
799
+ @client.delete_network_acl_entry({
800
+ network_acl_id: network_acl_id,
801
+ rule_number: rule,
802
+ egress: egress
803
+ })
804
+ end
805
+
806
+ def replace_network_acl_entry(network_acl_id, entry, egress)
807
+ @client.replace_network_acl_entry({
808
+ network_acl_id: network_acl_id,
809
+ rule_number: entry.rule,
810
+ protocol: EC2::IPProtocolMapping.protocol(entry.protocol),
811
+ rule_action: entry.action,
812
+ egress: egress,
813
+ cidr_block: entry.cidr_block,
814
+ icmp_type_code:
815
+ if entry.icmp_type || entry.icmp_code
816
+ {
817
+ type: entry.icmp_type,
818
+ code: entry.icmp_code
819
+ }
820
+ end,
821
+ port_range:
822
+ if entry.ports
823
+ from_port, to_port = entry.expand_ports
824
+ {
825
+ from: from_port,
826
+ to: to_port
827
+ }
828
+ end
829
+ })
830
+ end
831
+
832
+ def update_subnets(aws_vpc, subnet_changes)
833
+ # Refresh route tables so that the updated ones
834
+ # (if any) can be associated with subnets
835
+ EC2::refresh_route_tables!
836
+
837
+ # Created subnets
838
+ subnet_changes.added.each do |subnet_name, added_diff|
839
+ puts "Creating subnet #{subnet_name}"
840
+
841
+ subnet = added_diff.local
842
+
843
+ # Create the subnet
844
+ created_subnet = @client.create_subnet({
845
+ vpc_id: aws_vpc.vpc_id,
846
+ cidr_block: subnet.cidr_block,
847
+ availability_zone: subnet.availability_zone
848
+ }).subnet
849
+
850
+ # Add tags
851
+ if !subnet.tags.empty?
852
+ create_tags(created_subnet.subnet_id, subnet.tags)
853
+ end
854
+
855
+ # Map public ip if needed
856
+ if created_subnet.map_public_ip_on_launch != subnet.map_public_ip
857
+ @client.modify_subnet_attribute({
858
+ subnet_id: created_subnet.subnet_id,
859
+ map_public_ip_on_launch: {
860
+ value: subnet.map_public_ip
861
+ }
862
+ })
863
+ end
864
+
865
+ set_subnet_route_table(created_subnet.subnet_id, subnet.route_table)
866
+
867
+ # Refresh network acls since a subnet is automatically associatd with the deafult one
868
+ # and that association is needed to update it
869
+ EC2::refresh_network_acls!
870
+ set_subnet_network_acl(created_subnet.subnet_id, subnet.network_acl)
871
+ end
872
+
873
+ # Modified subnets
874
+ subnet_changes.modified.each do |subnet_name, modified_diff|
875
+ puts "Updating Subnet #{subnet_name}"
876
+ local_subnet = modified_diff.local
877
+ subnet_id = modified_diff.aws.subnet_id
878
+
879
+ modified_diff.changes.each do |subnet_diff|
880
+ case subnet_diff.type
881
+ when SubnetChange::CIDR
882
+ puts "Cannot update CIDR Block"
883
+ when SubnetChange::AZ
884
+ puts "Cannot update Availability Zone"
885
+ when SubnetChange::ROUTE_TABLE
886
+ puts "Updating Route Table"
887
+ set_subnet_route_table(subnet_id, local_subnet.route_table)
888
+ when SubnetChange::NETWORK_ACL
889
+ puts "Updating Network ACL"
890
+ set_subnet_network_acl(subnet_id, local_subnet.network_acl)
891
+ when SubnetChange::TAGS
892
+ puts "Updating Tags"
893
+ if !subnet_diff.tags_to_remove.empty?
894
+ delete_tags(subnet_id, subnet_diff.tags_to_remove)
895
+ end
896
+
897
+ if !subnet_diff.tags_to_add.empty?
898
+ create_tags(subnet_id, subnet_diff.tags_to_add)
899
+ end
900
+ end
901
+ end
902
+ end
903
+ end
904
+
905
+ def set_subnet_route_table(subnet_id, rt_name)
906
+ # Get the association for the subnet
907
+ aws_rt = EC2::subnet_route_tables[subnet_id]
908
+ association_id = if aws_rt and !aws_rt.associations.empty?
909
+ subnet_assoc = aws_rt.associations.select { |assoc| assoc.subnet_id = subnet_id }.first
910
+ if subnet_assoc
911
+ subnet_assoc.route_table_association_id
912
+ end
913
+ end
914
+
915
+ rt_id = if rt_name then EC2::named_route_tables[rt_name].route_table_id end
916
+
917
+ if association_id
918
+ # If there was an association then replace or disassociate
919
+ if rt_id
920
+ # Replace
921
+ @client.replace_route_table_association({
922
+ association_id: association_id,
923
+ route_table_id: rt_id
924
+ })
925
+ else
926
+ # Disassociate
927
+ @client.disassociate_route_table({
928
+ association_id: association_id
929
+ })
930
+ end
931
+ elsif rt_id
932
+ # Create a new association
933
+ @client.associate_route_table({
934
+ subnet_id: subnet_id,
935
+ route_table_id: rt_id
936
+ })
937
+ end
938
+
939
+ end
940
+
941
+ def set_subnet_network_acl(subnet_id, acl_name)
942
+ # Get the association for the subnet
943
+ association_id = EC2::subnet_network_acls[subnet_id].associations.select { |assoc| assoc.subnet_id = subnet_id }.first.network_acl_association_id
944
+ acl_id = EC2::named_network_acls[acl_name].network_acl_id
945
+
946
+ @client.replace_network_acl_association({
947
+ association_id: association_id,
948
+ network_acl_id: acl_id
949
+ })
950
+ end
951
+
952
+ end
953
+ end
954
+ end