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,148 @@
1
+ require "conf/Configuration"
2
+
3
+ require "aws-sdk"
4
+
5
+ module Cumulus
6
+ module ELB
7
+ class << self
8
+ @@client = Aws::ElasticLoadBalancing::Client.new(Configuration.instance.client)
9
+
10
+ # Public: Static method that will get an ELB from AWS by its name.
11
+ #
12
+ # name - the name of the ELB to get
13
+ #
14
+ # Returns the Aws::ElasticLoadBalancing::Types::LoadBalancerDescription by that name
15
+ def get_aws(name)
16
+ if elbs[name].nil?
17
+ puts "No ELB named #{name}"
18
+ exit
19
+ else
20
+ elbs[name]
21
+ end
22
+ end
23
+
24
+ # Public: Static method that will get an ELB from AWS by its dns name.
25
+ #
26
+ # dns_name - the dns name of the ELB to get
27
+ #
28
+ # Returns the Aws::ElasticLoadBalancing::Types::LoadBalancerDescription with that dns name
29
+ def get_aws_by_dns_name(dns_name)
30
+ elbs_to_dns_names[dns_name]
31
+ end
32
+
33
+ # Public: Provide a mapping of ELBs to their names. Lazily loads resources.
34
+ #
35
+ # Returns the ELBs mapped to their names
36
+ def elbs
37
+ @elbs ||= init_elbs
38
+ end
39
+
40
+ # Public: Provide the tags for an ELB by name
41
+ #
42
+ # Returns an array of Aws::ElasticLoadBalancing::Types::Tag
43
+ def elb_tags(elb_name)
44
+ @elb_tags ||= init_elb_tags
45
+
46
+ @elb_tags[elb_name]
47
+ end
48
+
49
+ # Public: Provide the attributes for an ELB by name, lazily loaded
50
+ #
51
+ # Returns an array of Aws::ElasticLoadBalancing::Types::LoadBalancerAttributes
52
+ def elb_attributes(elb_name)
53
+ @elb_attributes ||= {}
54
+ @elb_attributes[elb_name] ||= init_elb_attributes(elb_name)
55
+ end
56
+
57
+ # Public: Provide the default available policies
58
+ #
59
+ # Returns a Hash of Aws::ElasticLoadBalancing::Types::PolicyDescription to a policy's name
60
+ def default_policies
61
+ @default_policies ||= Hash[init_default_policies.map { |policy| [policy.policy_name, policy] }]
62
+ end
63
+
64
+ # Public: Provide the policies already created on a load balancer
65
+ #
66
+ # Returns a Hash of Aws::ElasticLoadBalancing::Types::PolicyDescription to a policy's name
67
+ def elb_policies(elb_name)
68
+ @elb_policies ||= {}
69
+ @elb_policies[elb_name] ||= Hash[init_elb_policies(elb_name).map { |policy| [policy.policy_name, policy] }]
70
+ end
71
+
72
+ private
73
+
74
+ # Internal: Provide a mapping of ELBs to their dns names. Lazily loads resources.
75
+ #
76
+ # Returns the ELBs mapped to their dns names
77
+ def elbs_to_dns_names
78
+ @elbs_to_dns_names ||= Hash[elbs.map { |_, elb| [elb.dns_name, elb] }]
79
+ end
80
+
81
+ # Internal: Load ELBs and map them to their names.
82
+ #
83
+ # Returns the ELBs mapped to their names
84
+ def init_elbs
85
+ elbs = []
86
+ all_records_retrieved = false
87
+ next_marker = nil
88
+
89
+ until all_records_retrieved
90
+ response = @@client.describe_load_balancers({
91
+ marker: next_marker
92
+ }.reject { |k, v| v.nil? })
93
+
94
+ elbs << response.load_balancer_descriptions
95
+ next_marker = response.next_marker
96
+
97
+ if next_marker == nil
98
+ all_records_retrieved = true
99
+ end
100
+ end
101
+
102
+ Hash[elbs.flatten.map { |elb| [elb.load_balancer_name, elb] }]
103
+ end
104
+
105
+ # Internal: Load ELB tags and map them to their names
106
+ #
107
+ # Returns the ELB tags mapped to ELB name
108
+ def init_elb_tags
109
+ tags = []
110
+ elbs.keys.each_slice(20) do |names|
111
+ tags << @@client.describe_tags({
112
+ load_balancer_names: names
113
+ }).tag_descriptions
114
+ end
115
+
116
+ Hash[tags.flatten.map { |td| [td.load_balancer_name, td.tags] }]
117
+ end
118
+
119
+ # Internal: Load ELB attributes for an ELB
120
+ #
121
+ # elb_name - the name of the ELB to get attributes for
122
+ #
123
+ # Returns the ELB attributes
124
+ def init_elb_attributes(elb_name)
125
+ @@client.describe_load_balancer_attributes({
126
+ load_balancer_name: elb_name
127
+ }).load_balancer_attributes
128
+ end
129
+
130
+ # Internal: Load the default ELB policies
131
+ #
132
+ # Returns an array of Aws::ElasticLoadBalancing::Types::PolicyDescription
133
+ def init_default_policies
134
+ @@client.describe_load_balancer_policies.policy_descriptions
135
+ end
136
+
137
+ # Internal: Load the policies for a load balancer
138
+ #
139
+ # Returns an array of Aws::ElasticLoadBalancing::Types::PolicyDescription
140
+ def init_elb_policies(elb_name)
141
+ @@client.describe_load_balancer_policies({
142
+ load_balancer_name: elb_name
143
+ }).policy_descriptions
144
+ end
145
+
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,65 @@
1
+ require "common/BaseLoader"
2
+ require "conf/Configuration"
3
+ require "elb/models/LoadBalancerConfig"
4
+ require "elb/models/ListenerConfig"
5
+
6
+ require "aws-sdk"
7
+
8
+ # Public: Load ELB assets
9
+ module Cumulus
10
+ module ELB
11
+ module Loader
12
+ include Common::BaseLoader
13
+
14
+ @@elbs_dir = Configuration.instance.elb.load_balancers_directory
15
+ @@listeners_dir = Configuration.instance.elb.listeners_directory
16
+ @@policies_dir = Configuration.instance.elb.policies_directory
17
+
18
+ # Public: Load all the load balancer configurations as LoadBalancerConfig objects
19
+ #
20
+ # Returns an array of LoadBalancerConfig
21
+ def self.elbs
22
+ Common::BaseLoader::resources(@@elbs_dir, &LoadBalancerConfig.method(:new))
23
+ end
24
+
25
+ # Public: Load a specified listener template by name, applying any variables
26
+ #
27
+ # name - the name of the listener template to load
28
+ # vars - the hash of vars to apply
29
+ #
30
+ # returns
31
+ def self.listener(name, vars)
32
+ Common::BaseLoader.template(
33
+ name,
34
+ @@listeners_dir,
35
+ vars,
36
+ &proc do |_, json|
37
+ ListenerConfig.new(json)
38
+ end
39
+ )
40
+ end
41
+
42
+ # Public: Load the specified user defined policy as an AWS policy
43
+ #
44
+ # Returns an Aws::ElasticLoadBalancing::Types::PolicyDescription
45
+ def self.policy(policy_name)
46
+ Common::BaseLoader::resource(policy_name, @@policies_dir) do |policy_name, policy|
47
+ Aws::ElasticLoadBalancing::Types::PolicyDescription.new({
48
+ policy_name: policy_name,
49
+ policy_type_name: policy.fetch("type"),
50
+ policy_attribute_descriptions: policy.fetch("attributes").map do |key, value|
51
+ Aws::ElasticLoadBalancing::Types::PolicyAttributeDescription.new({
52
+ attribute_name: key,
53
+ attribute_value: value
54
+ })
55
+ end
56
+ })
57
+ end
58
+ rescue KeyError
59
+ puts "policy configuration #{policy_name} does not contain all required keys `type` and `attributes`"
60
+ exit
61
+ end
62
+
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,581 @@
1
+ require "common/manager/Manager"
2
+ require "conf/Configuration"
3
+ require "elb/ELB"
4
+ require "elb/loader/Loader"
5
+ require "elb/models/LoadBalancerDiff"
6
+ require "security/SecurityGroups"
7
+ require "util/Colors"
8
+
9
+ require "aws-sdk"
10
+ require "json"
11
+
12
+ module Cumulus
13
+ module ELB
14
+ class Manager < Common::Manager
15
+
16
+ include LoadBalancerChange
17
+
18
+ require "aws_extensions/elb/PolicyDescription"
19
+ Aws::ElasticLoadBalancing::Types::PolicyDescription.send(:include, AwsExtensions::ELB::PolicyDescription)
20
+
21
+ def initialize
22
+ super()
23
+ @elb = Aws::ElasticLoadBalancing::Client.new(Configuration.instance.client)
24
+ end
25
+
26
+ def resource_name
27
+ "Elastic Load Balancer"
28
+ end
29
+
30
+ def local_resources
31
+ @local_resources ||= Hash[Loader.elbs.map { |local| [local.name, local] }]
32
+ end
33
+
34
+ def aws_resources
35
+ @aws_resources ||= ELB::elbs
36
+ end
37
+
38
+ def unmanaged_diff(aws)
39
+ LoadBalancerDiff.unmanaged(aws)
40
+ end
41
+
42
+ def added_diff(local)
43
+ LoadBalancerDiff.added(local)
44
+ end
45
+
46
+ def diff_resource(local, aws)
47
+ local.diff(aws)
48
+ end
49
+
50
+ # Migrates all of the default load balancer policies to Cumulus versions
51
+ def migrate_default_policies
52
+ policies_dir = "#{@migration_root}/elb-default-policies"
53
+
54
+ if !Dir.exists?(@migration_root)
55
+ Dir.mkdir(@migration_root)
56
+ end
57
+ if !Dir.exists?(policies_dir)
58
+ Dir.mkdir(policies_dir)
59
+ end
60
+
61
+ default_policies = ELB::default_policies
62
+ default_policies.map do |policy_name, policy|
63
+ puts "Processing #{policy_name}"
64
+
65
+ cumulus_name = "Cumulus-#{policy_name}"
66
+ json = JSON.pretty_generate(policy.to_cumulus_hash)
67
+
68
+ File.open("#{policies_dir}/#{cumulus_name}.json", "w") { |f| f.write(json) }
69
+ end
70
+ end
71
+
72
+ # Migrates all of the current AWS load balancers to Cumulus versions
73
+ def migrate_elbs
74
+ elbs_dir = "#{@migration_root}/elb-load-balancers"
75
+ policies_dir ="#{@migration_root}/elb-policies"
76
+
77
+ if !Dir.exists?(@migration_root)
78
+ Dir.mkdir(@migration_root)
79
+ end
80
+ if !Dir.exists?(elbs_dir)
81
+ Dir.mkdir(elbs_dir)
82
+ end
83
+ if !Dir.exists?(policies_dir)
84
+ Dir.mkdir(policies_dir)
85
+ end
86
+
87
+ ELB::elbs.map do |elb_name, elb|
88
+ puts "Migrating load balancer #{elb_name}"
89
+
90
+ local_elb = LoadBalancerConfig.new(elb_name)
91
+ tags = ELB::elb_tags(elb_name)
92
+ attributes = ELB::elb_attributes(elb_name)
93
+ local_elb.populate!(elb, tags, attributes)
94
+
95
+ # Migrate the backend and listener policies if they do not already
96
+ # exist locally and are not the default AWS policies
97
+ listener_policies = local_elb.listeners.flat_map { |l| l.policies }
98
+ backend_policies = local_elb.backend_policies.flat_map { |b| b.policy_names }
99
+
100
+ (listener_policies + backend_policies).uniq.map do |policy_name|
101
+ policy_file = "#{policies_dir}/#{policy_name}.json"
102
+ # If it is not already migrated and not a default policy, create it
103
+ if !File.file? policy_file
104
+ if !ELB::default_policies.has_key? policy_name
105
+ # Get the full policy description from the elb policies
106
+ full_policy = ELB::elb_policies(elb_name)[policy_name]
107
+
108
+ if full_policy.nil?
109
+ puts "Unable to migrate policy #{policy_name}"
110
+ else
111
+ puts "Migrating policy #{policy_name}"
112
+ json = JSON.pretty_generate(full_policy.to_cumulus_hash)
113
+ File.open(policy_file, "w") { |f| f.write(json) }
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ File.open("#{elbs_dir}/#{elb_name}.json", "w") { |f| f.write(local_elb.pretty_json) }
120
+ end
121
+
122
+ end
123
+
124
+ def update(local, diffs)
125
+ attributes_changed = false
126
+ diffs.each do |diff|
127
+ case diff.type
128
+ when LISTENERS
129
+ puts "Updating listeners..."
130
+ update_listeners(local.name, diff.local, diff.aws, diff.listeners)
131
+ when SUBNETS
132
+ puts "Updating subnets..."
133
+ update_subnets(local.name, diff.subnets)
134
+ when SECURITY
135
+ puts "Updating security groups..."
136
+ update_security_groups(local.name, local.vpc_id, local.security_groups)
137
+ when INTERNAL
138
+ puts "AWS does not allow changing internal after creation"
139
+ when TAGS
140
+ puts "Updating tags..."
141
+ update_tags(local.name, diff.tags)
142
+ when INSTANCES
143
+ if local.manage_instances != false
144
+ puts "Updating managed instances..."
145
+ update_instances(local.name, diff.instances.added, diff.instances.removed)
146
+ end
147
+ when HEALTH
148
+ puts "Updating health check..."
149
+ update_health_check(local.name, local.health_check)
150
+ when BACKEND
151
+ puts "Updating backend policies"
152
+ update_backend_policies(local.name, diff.backend_policies.added, diff.backend_policies.removed, diff.backend_policies.modified)
153
+ when CROSS
154
+ puts "Updating cross zone load balancing"
155
+ attributes_changed = true
156
+ when LOG
157
+ puts "Updating access log config"
158
+ attributes_changed = true
159
+ when DRAINING
160
+ puts "Updating connection draining"
161
+ attributes_changed = true
162
+ when IDLE
163
+ puts "Updating idle timeout"
164
+ attributes_changed = true
165
+ end
166
+ end
167
+
168
+ if attributes_changed
169
+ update_attributes(local)
170
+ end
171
+ end
172
+
173
+ def create(local)
174
+
175
+ # Create the load balancer with listeners, subnets, security groups, scheme, and tags
176
+ @elb.create_load_balancer({
177
+ load_balancer_name: local.name,
178
+ listeners: local.listeners.map(&:to_aws),
179
+ subnets: local.subnets.map { |subnet| subnet.subnet_id },
180
+ security_groups: local.security_groups.map do |sg_name|
181
+ SecurityGroups::vpc_security_groups[local.vpc_id][sg_name].group_id
182
+ end,
183
+ scheme: if local.internal then "internal" end,
184
+ tags: if !local.tags.empty?
185
+ local.tags.map do |tagKey, tagValue|
186
+ {
187
+ key: tagKey,
188
+ value: tagValue
189
+ }
190
+ end
191
+ end
192
+ })
193
+
194
+ # Set the policies for the attached listeners
195
+ local.listeners.each do |listener|
196
+ update_listener_policies(local.name, listener.load_balancer_port, listener.policies)
197
+ end
198
+
199
+ # Set the health check config
200
+ update_health_check(local.name, local.health_check)
201
+
202
+ # Set the attributes
203
+ update_attributes(local)
204
+
205
+ # Update the instances if they are managed
206
+ if local.manage_instances != false
207
+ update_instances(local.name, local.manage_instances)
208
+ end
209
+
210
+ # Set the backend policies
211
+ backend_added = local.backend_policies.map do |backend_policy|
212
+ LoadBalancerDiff::BackendChange.new(backend_policy.instance_port, nil, backend_policy.policy_names)
213
+ end
214
+ update_backend_policies(local.name, backend_added)
215
+
216
+ end
217
+
218
+ private
219
+
220
+ # Internal - a helper for attempting to update a resource that may require a rollback
221
+ #
222
+ # resource_name - the name of the resource to print in the error messages
223
+ # should_rollback - a boolean that indicates if a rollback should be attempted
224
+ # update - a Proc that will cause the update to happen
225
+ # rollback - a Proc that will undo whatever is necessary to return to a good state
226
+ def update_rollback(resource_name, should_rollback, update, rollback)
227
+ begin
228
+ update.call()
229
+ rescue => e
230
+ puts Colors.red("There was an error updating #{resource_name}: #{e}")
231
+
232
+ if should_rollback
233
+ puts Colors.red("Attempting rollback of #{resource_name}...")
234
+ begin
235
+ rollback.call()
236
+ puts Colors.orange("Rollback successful")
237
+ rescue => rollback_error
238
+ puts Colors.red("Unable to rollback #{resource_name}: #{rollback_error}")
239
+ raise rollback_error
240
+ end
241
+ end
242
+
243
+ raise e
244
+ end
245
+ end
246
+
247
+ # Internal - update the listeners for a load balancer
248
+ #
249
+ # elb_name - the name of the load balancer to update
250
+ # local_listeners - an array of the local ListenerConfig
251
+ # aws_listeners - an array of the aws Aws::ElasticLoadBalancing::Types::ListenerDescription
252
+ # in case we need to roll back changes
253
+ # listener_changes - a ListChange with details on what was changed
254
+ def update_listeners(elb_name, local_listeners, aws_listeners, listener_changes)
255
+ # First delete the removed listeners
256
+ deleted_ports = listener_changes.removed.map { |port, _| port }
257
+ deleted_listeners = aws_listeners.select { |l| deleted_ports.include? l.listener.load_balancer_port }
258
+
259
+ if !deleted_ports.empty?
260
+ @elb.delete_load_balancer_listeners({
261
+ load_balancer_name: elb_name,
262
+ load_balancer_ports: deleted_ports
263
+ })
264
+ end
265
+
266
+ # Add the added listeners. If anything goes wrong, attempt a rollback (if listeners were deleted)
267
+ added_ports = listener_changes.added.map { |port, _| port }
268
+ added_listeners = local_listeners.select { |l| added_ports.include? l.load_balancer_port}
269
+ if !added_listeners.empty?
270
+ # create the listeners
271
+ update_rollback("listeners", !deleted_ports.empty?,
272
+ # update
273
+ Proc.new {
274
+ @elb.create_load_balancer_listeners({
275
+ load_balancer_name: elb_name,
276
+ listeners: added_listeners.map { |l| l.to_aws }
277
+ })
278
+ },
279
+ # rollback
280
+ Proc.new {
281
+ @elb.create_load_balancer_listeners({
282
+ load_balancer_name: elb_name,
283
+ listeners: deleted_listeners.map { |l| l.listener }
284
+ })
285
+
286
+ deleted_listeners.each { |l| update_listener_policies(elb_name, l.listener.load_balancer_port, l.policy_names) }
287
+ }
288
+ )
289
+
290
+ # set the policies of each created listener
291
+ added_listeners.each { |listener| update_listener_policies(elb_name, listener.load_balancer_port, listener.policies) }
292
+ end
293
+
294
+
295
+ # For listeners where only the policy was modified, just set the listener policies
296
+ policy_only_listeners = listener_changes.modified.select do |port, diffs|
297
+ diffs.size == 1 && diffs.first.type == ListenerChange::POLICIES
298
+ end
299
+
300
+ policy_only_listeners.each do |port, diffs|
301
+ listener = local_listeners.select { |l| port == l.load_balancer_port }.first
302
+ update_listener_policies(elb_name, listener.load_balancer_port, listener.policies)
303
+ end
304
+
305
+ # For listeners with other changes, remove the old modified listeners and add the new ones, then update the listeners for each
306
+ modified_ports = (listener_changes.modified.reject { |port, _| policy_only_listeners.has_key? port }).map { |port, _| port }
307
+ modified_listeners = local_listeners.select { |l| modified_ports.include? l.load_balancer_port }
308
+
309
+ if !modified_listeners.empty?
310
+ @elb.delete_load_balancer_listeners({
311
+ load_balancer_name: elb_name,
312
+ load_balancer_ports: modified_ports
313
+ })
314
+
315
+ # recreate the modified listeners with the new attributes
316
+ update_rollback("listeners", true,
317
+ # update
318
+ Proc.new {
319
+ # create the listeners
320
+ @elb.create_load_balancer_listeners({
321
+ load_balancer_name: elb_name,
322
+ listeners: modified_listeners.map { |l| l.to_aws }
323
+ })
324
+ },
325
+ Proc.new {
326
+ # create the listeners using the old config
327
+ old_modified_listeners = aws_listeners.select { |l| modified_ports.include? l.listener.load_balancer_port }
328
+
329
+ @elb.create_load_balancer_listeners({
330
+ load_balancer_name: elb_name,
331
+ listeners: old_modified_listeners.map { |l| l.listener }
332
+ })
333
+
334
+ # set the old policies
335
+ old_modified_listeners.each { |l| update_listener_policies(elb_name, l.listener.load_balancer_port, l.policy_names) }
336
+ }
337
+ )
338
+
339
+ # set the policies
340
+ modified_listeners.each { |listener| update_listener_policies(elb_name, listener.load_balancer_port, listener.policies) }
341
+ end
342
+ end
343
+
344
+ # Internal: update the listener policies for a listener
345
+ #
346
+ # elb_name - the name of the load balancer to update
347
+ # port - the load balancer port for the listener to update policies for
348
+ # policy_names - the names of the policies to set
349
+ def update_listener_policies(elb_name, port, policy_names)
350
+ # Make sure a policy exists for each policy on the listener
351
+ policy_names.each do |policy_name|
352
+ ensure_policy_exists(elb_name, policy_name)
353
+ end
354
+
355
+ @elb.set_load_balancer_policies_of_listener({
356
+ load_balancer_name: elb_name,
357
+ load_balancer_port: port,
358
+ policy_names: policy_names
359
+ })
360
+ end
361
+
362
+ # Internal - update the subnets for the load balancer
363
+ #
364
+ # elb_name - the name of the load balancer to update
365
+ # subnet_changes - a ListChange with details on what was changed
366
+ def update_subnets(elb_name, subnet_changes)
367
+ # Since we cannot have multiple subnets in the same availability zone, we have to
368
+ # detach subnets before we add them.
369
+ detach_subnets = subnet_changes.removed.map { |subnet| subnet.subnet_id }
370
+ if !detach_subnets.empty?
371
+ @elb.detach_load_balancer_from_subnets({
372
+ load_balancer_name: elb_name,
373
+ subnets: detach_subnets
374
+ })
375
+ end
376
+
377
+ # Attach subnets. If something goes wrong, attempt a rollback (if any subnets were removed)
378
+ attach_subnets = subnet_changes.added.map { |subnet| subnet.subnet_id }
379
+ if !attach_subnets.empty?
380
+ update_rollback("subnets", !detach_subnets.empty?,
381
+ # update
382
+ Proc.new {
383
+ @elb.attach_load_balancer_to_subnets({
384
+ load_balancer_name: elb_name,
385
+ subnets: attach_subnets
386
+ })
387
+ },
388
+ # rollback
389
+ Proc.new {
390
+ @elb.attach_load_balancer_to_subnets({
391
+ load_balancer_name: elb_name,
392
+ subnets: detach_subnets
393
+ })
394
+ })
395
+ end
396
+ end
397
+
398
+ # Internal: update the security groups for the load balancer
399
+ #
400
+ # elb_name - the name of the load balancer to update
401
+ # vpc_id - the id of the vpc the elb is in
402
+ # security_groups - an array of security group ids tha will replace the existing config
403
+ def update_security_groups(elb_name, vpc_id, security_groups)
404
+ @elb.apply_security_groups_to_load_balancer({
405
+ load_balancer_name: elb_name,
406
+ security_groups: security_groups.map do |sg_name|
407
+ SecurityGroups::vpc_security_groups[vpc_id][sg_name].group_id
408
+ end
409
+ })
410
+ end
411
+
412
+ # Internal: update the tags for the load balancer
413
+ #
414
+ # elb_name - the name of the load balancer to update
415
+ # tag_changes - a ListChange describing what was modified
416
+ def update_tags(elb_name, tag_changes)
417
+
418
+ # First remove the tags that were deleted
419
+ if !tag_changes.removed.empty?
420
+ @elb.remove_tags({
421
+ load_balancer_names: [elb_name],
422
+ tags: tag_changes.removed.map { |t| { key: t.key } }
423
+ })
424
+ end
425
+
426
+ # Update and add the other tags
427
+ update_tags = (tag_changes.added + tag_changes.modified).map do |t|
428
+ {
429
+ key: t.key,
430
+ value: t.local
431
+ }
432
+ end
433
+ if !update_tags.empty?
434
+ update_rollback("tags", !tag_changes.removed.empty?,
435
+ # update
436
+ Proc.new {
437
+ @elb.add_tags({
438
+ load_balancer_names: [elb_name],
439
+ tags: update_tags
440
+ })
441
+ },
442
+ # rollback
443
+ Proc.new {
444
+ @elb.add_tags({
445
+ load_balancer_names: [elb_name],
446
+ tags: tag_changes.removed.map do |t|
447
+ {
448
+ key: t.key,
449
+ value: t.aws
450
+ }
451
+ end
452
+ })
453
+ })
454
+ end
455
+ end
456
+
457
+ # Internal: update the managed instances for a load balancer
458
+ #
459
+ # elb_name - then name of the load balancer to update
460
+ # instances_added - the array of instances that were added
461
+ # instances_removed - the array of instances that were removed
462
+ def update_instances(elb_name, instances_added, instances_removed = [])
463
+
464
+ # deregister instances that were removed
465
+ if !instances_removed.empty?
466
+ @elb.deregister_instances_from_load_balancer({
467
+ load_balancer_name: elb_name,
468
+ instances: instances_removed.map do |i|
469
+ {
470
+ instance_id: i
471
+ }
472
+ end
473
+ })
474
+ end
475
+
476
+ # register instances that were added
477
+ if !instances_added.empty?
478
+ @elb.register_instances_with_load_balancer({
479
+ load_balancer_name: elb_name,
480
+ instances: instances_added.map do |i|
481
+ {
482
+ instance_id: i
483
+ }
484
+ end
485
+ })
486
+ end
487
+ end
488
+
489
+ # Internal: update the health check config
490
+ #
491
+ # elb_name - the name of the load balancer to update
492
+ # health_check - the HealthCheckConfig to update with
493
+ def update_health_check(elb_name, health_check)
494
+ @elb.configure_health_check({
495
+ load_balancer_name: elb_name,
496
+ health_check: health_check.to_aws
497
+ })
498
+ end
499
+
500
+ # Internal: update the backend policies
501
+ #
502
+ # elb_name - the name of the load balancer to update
503
+ # backend_added - the backend policies to be added
504
+ # backend_removed - the backend policies to be removed
505
+ # backend_modified - the backend policies to be modified
506
+ def update_backend_policies(elb_name, backend_added, backend_removed = [], backend_modified = [])
507
+
508
+ # Update the created and modified policies
509
+ (backend_added + backend_modified).each do |backend|
510
+ # First make sure each policy exists
511
+ backend.local_policies.each do |policy_name|
512
+ ensure_policy_exists(elb_name, policy_name)
513
+ end
514
+
515
+ @elb.set_load_balancer_policies_for_backend_server({
516
+ load_balancer_name: elb_name,
517
+ instance_port: backend.port,
518
+ policy_names: backend.local_policies
519
+ })
520
+ end
521
+
522
+ # Update the deleted ones by setting policy names to []
523
+ backend_removed.each do |backend|
524
+ @elb.set_load_balancer_policies_for_backend_server({
525
+ load_balancer_name: elb_name,
526
+ instance_port: backend.port,
527
+ policy_names: []
528
+ })
529
+ end
530
+ end
531
+
532
+ # Internal: Updates all of the other attributes for an ELB:
533
+ # cross-zone load balancing, access log config, connection draining, idle timeout
534
+ #
535
+ # local_config - the full local config for the elb
536
+ def update_attributes(local_config)
537
+ @elb.modify_load_balancer_attributes({
538
+ load_balancer_name: local_config.name,
539
+ load_balancer_attributes: {
540
+ cross_zone_load_balancing: {
541
+ enabled: local_config.cross_zone
542
+ },
543
+ access_log: if local_config.access_log then local_config.access_log.to_aws end,
544
+ connection_draining: {
545
+ enabled: local_config.connection_draining != false,
546
+ timeout: if local_config.connection_draining != false then local_config.connection_draining end
547
+ },
548
+ connection_settings: {
549
+ idle_timeout: local_config.idle_timeout
550
+ }
551
+ }
552
+ })
553
+ end
554
+
555
+ # Internal: Makes sure the passed in policy exists on the load balancer
556
+ # so that it can be used in listeners and back ends. Does not update the policy
557
+ #
558
+ # elb_name - the name of the elb to create the policy for
559
+ # policy_name - The policy name. If the policy does not exist it will be loaded from local config
560
+ # and then created for this elb
561
+ def ensure_policy_exists(elb_name, policy_name)
562
+ existing_policies = ELB::elb_policies(elb_name)
563
+
564
+ if !existing_policies[policy_name]
565
+ local_policy = (Loader.policy(policy_name) rescue nil)
566
+ if local_policy.nil?
567
+ raise "#{policy_name} is not already defined on the load balancer and not defined locally"
568
+ end
569
+
570
+ @elb.create_load_balancer_policy({
571
+ load_balancer_name: elb_name,
572
+ policy_name: local_policy.policy_name,
573
+ policy_type_name: local_policy.policy_type_name,
574
+ policy_attributes: local_policy.policy_attribute_descriptions.map(&:to_h)
575
+ })
576
+ end
577
+ end
578
+
579
+ end
580
+ end
581
+ end