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,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