applb 0.1.0

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.
@@ -0,0 +1,22 @@
1
+ require 'erb'
2
+
3
+ module Applb
4
+ class Converter
5
+ def initialize(lbs_by_vpc_id, tags_by_arn)
6
+ @lbs_by_vpc_id = lbs_by_vpc_id
7
+ @tags_by_arn = tags_by_arn
8
+ end
9
+
10
+ def convert
11
+ @lbs_by_vpc_id.each do |vpc_id, lbs_by_name|
12
+ yield vpc_id, output_alb(vpc_id, @tags_by_arn, lbs_by_name)
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def output_alb(vpc_id, tags_by_arn, lbs_by_name)
19
+ ERB.new(File.read('lib/applb/output_alb.erb'), nil, '-').result(binding)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,56 @@
1
+ require 'hashie'
2
+ require 'applb/template_helper'
3
+ require 'applb/dsl/ec2'
4
+
5
+ module Applb
6
+ class DSL
7
+ include Applb::TemplateHelper
8
+
9
+ class << self
10
+ def define(source, filepath, options)
11
+ self.new(filepath, options) do
12
+ eval(source, binding, filepath)
13
+ end
14
+ end
15
+ end
16
+
17
+ attr_reader :result
18
+
19
+ def initialize(filepath, options,&block)
20
+ @filepath = filepath
21
+ @result = OpenStruct.new(ec2s: {})
22
+
23
+ @context = Hashie::Mash.new(
24
+ filepath: filepath,
25
+ templates: {},
26
+ options: options,
27
+ )
28
+
29
+ instance_eval(&block)
30
+ end
31
+
32
+ def require(file)
33
+ albfile = (file =~ %r|\A/|) ? file : File.expand_path(File.join(File.dirname(@path), file))
34
+
35
+ if File.exist?(albfile)
36
+ instance_eval(File.read(albfile), albfile)
37
+ elsif File.exist?("#{albfile}.rb")
38
+ instance_eval(File.read("#{albfile}.rb"), "#{albfile}.rb")
39
+ else
40
+ Kernel.require(file)
41
+ end
42
+ end
43
+
44
+ def template(name, &block)
45
+ @context.templates[name.to_s] = block
46
+ end
47
+
48
+ def ec2(vpc_id, &block)
49
+ if ec2_result = @result.ec2s[vpc_id]
50
+ @result.ec2s[vpc_id] = EC2.new(@context, vpc_id, ec2_result.load_balancers, &block).result
51
+ else
52
+ @result.ec2s[vpc_id] = EC2.new(@context, vpc_id, [], &block).result
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,49 @@
1
+ module Applb
2
+ class DSL
3
+ class EC2
4
+ class LoadBalancer
5
+ class Attributes
6
+ include Applb::DSL::Checker
7
+ include Applb::TemplateHelper
8
+
9
+ attr_reader :result
10
+
11
+ def initialize(context, lb_name, &block)
12
+ @context = context.dup
13
+ @lb_name = lb_name
14
+
15
+ @result = {
16
+ 'access_logs.s3.enabled' => false,
17
+ 'access_logs.s3.bucket' => '',
18
+ 'access_logs.s3.prefix' => '',
19
+ 'idle_timeout.timeout_seconds' => 60,
20
+ 'deletion_protection.enabled' => false,
21
+ }
22
+
23
+ instance_eval(&block)
24
+ end
25
+
26
+ def result
27
+ @result.map { |k, v| {key: k, value: v} }
28
+ end
29
+
30
+ private
31
+
32
+ def access_logs(args)
33
+ @result['access_logs.s3.enabled'] = args[:s3_enabled] if args[:s3_enabled]
34
+ @result['access_logs.s3.bucket'] = args[:s3_bucket] if args[:s3_bucket]
35
+ @result['access_logs.s3.prefix'] = args[:s3_prefix] if args[:s3_prefix]
36
+ end
37
+
38
+ def idle_timeout(timeout_seconds:)
39
+ @result['idle_timeout.timeout_seconds'] = timeout_seconds if timeout_seconds
40
+ end
41
+
42
+ def deletion_protection(enabled:)
43
+ @result['deletion_protection.enabled'] = enabled if enabled
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,27 @@
1
+ require 'applb/error'
2
+
3
+ module Applb
4
+ class DSL
5
+ module Checker
6
+ private
7
+
8
+ class ValidationError < Error
9
+ end
10
+
11
+ def required(name, value)
12
+ if value
13
+ case value
14
+ when String
15
+ invalid = value.strip.empty?
16
+ when Array, Hash
17
+ invalid = value.empty?
18
+ end
19
+ else
20
+ invalid = true
21
+ end
22
+
23
+ raise ValidationError.new("`#{name}' is required") if invalid
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,35 @@
1
+ require 'ostruct'
2
+ require 'applb/dsl/load_balancer'
3
+
4
+ module Applb
5
+ class DSL
6
+ class EC2
7
+ include Applb::TemplateHelper
8
+
9
+ attr_reader :result
10
+
11
+ def initialize(context, vpc_id, lbs, &block)
12
+ @context = context.merge(vpc_id: vpc_id)
13
+
14
+ @result = OpenStruct.new({
15
+ vpc_id: vpc_id,
16
+ load_balancers: lbs,
17
+ })
18
+
19
+ @names = lbs.map(&:name)
20
+ instance_eval(&block)
21
+ end
22
+
23
+ private
24
+
25
+ def elb_v2(name, &block)
26
+ if @names.include?(name)
27
+ raise "#{@result.vpc_id}: #{name} is already defined"
28
+ end
29
+
30
+ @result.load_balancers << LoadBalancer.new(@context, name, @result.vpc_id, &block).result
31
+ @names << name
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,140 @@
1
+ require 'applb/dsl/rules'
2
+ require 'applb/utils'
3
+ require 'applb/client_wrapper'
4
+
5
+ module Applb
6
+ class DSL
7
+ class EC2
8
+ class LoadBalancer
9
+ class Listeners
10
+ class Listener
11
+ include Checker
12
+ include Applb::TemplateHelper
13
+
14
+ class Result
15
+ ATTRIBUTES = %i/certificates ssl_policy port protocol default_actions rules load_balancer_arn/
16
+ attr_accessor *ATTRIBUTES
17
+
18
+ def initialize(context, lb_name)
19
+ @context = context
20
+ @options = context.options
21
+ @lb_name = lb_name
22
+ end
23
+
24
+ def to_h
25
+ Hash[ATTRIBUTES.sort.map { |name| [name, public_send(name)] }]
26
+ end
27
+
28
+ def aws(aws_listener)
29
+ @aws_listener = aws_listener
30
+ self
31
+ end
32
+
33
+ def create
34
+ Applb.logger.info("#{@lb_name} Create listener for port #{port}")
35
+ return if @options[:dry_run]
36
+ client.create_listener(create_option).listeners.first
37
+ end
38
+
39
+ def modify
40
+ dsl_hash = to_diff_h
41
+ aws_hash = to_diff_h_aws
42
+ return if dsl_hash == aws_hash
43
+
44
+ Applb.logger.info("#{@lb_name} Modify listener for port #{port}")
45
+ Applb.logger.info("<diff>\n#{Applb::Utils.diff(aws_hash, dsl_hash, color: @options[:color])}")
46
+ return if @options[:dry_run]
47
+
48
+ client.modify_listener(modify_option).listeners.first
49
+ end
50
+
51
+ private
52
+
53
+ def to_diff_h
54
+ options = Applb::Utils.normalize_hash(to_h)
55
+ target_group_name = options[:default_actions].first.delete(:target_group_name)
56
+ if options[:ssl_policy] && options[:ssl_policy].empty?
57
+ options.delete(:certificates)
58
+ options.delete(:ssl_policy)
59
+ end
60
+ options.reject! { |k, v| %i/listener_arn load_balancer_arn rules/.include?(k) }
61
+ end
62
+
63
+ def to_diff_h_aws
64
+ options = Applb::Utils.normalize_hash(@aws_listener.to_h)
65
+ if options[:ssl_policy] && options[:ssl_policy].empty?
66
+ options.delete(:certificates)
67
+ options.delete(:ssl_policy)
68
+ end
69
+ options.reject! { |k, v| %i/listener_arn load_balancer_arn rules/.include?(k) }
70
+ end
71
+
72
+ def create_option
73
+ options = to_h.reject { |k, _| %i/policy_name rules/.include?(k) }
74
+ options[:default_actions].first.delete(:target_group_name)
75
+ options
76
+ end
77
+
78
+ def modify_option
79
+ to_diff_h.merge(listener_arn: @aws_listener.listener_arn)
80
+ end
81
+
82
+ def client
83
+ @client ||= Applb::ClientWrapper.new(@options)
84
+ end
85
+ end
86
+
87
+ attr_reader :result
88
+
89
+ def initialize(context, lb_name, &block)
90
+ @context = context.dup
91
+ @lb_name = lb_name
92
+
93
+ @result = Result.new(@context, @lb_name)
94
+
95
+ instance_eval(&block)
96
+ end
97
+
98
+ private
99
+
100
+ def certificates(certificate_arn:)
101
+ @result.certificates ||= []
102
+ @result.certificates << {certificate_arn: certificate_arn}
103
+ end
104
+
105
+ def ssl_policy(ssl_policy)
106
+ @result.ssl_policy = ssl_policy if ssl_policy
107
+ end
108
+
109
+ def port(port)
110
+ @result.port = port
111
+ end
112
+
113
+ def protocol(protocol)
114
+ @result.protocol = protocol
115
+ end
116
+
117
+ def default_actions(target_group_name: nil, target_group_arn: nil, type:)
118
+ unless target_group_name || target_group_arn
119
+ raise "target_group_name or target_group_arn is required"
120
+ end
121
+ @result.default_actions ||= []
122
+ @result.default_actions << {
123
+ target_group_arn: target_group_arn,
124
+ target_group_name: target_group_name,
125
+ type: type,
126
+ }
127
+ end
128
+
129
+ def rules(&block)
130
+ rules = Rules.new(@context, self, &block).result
131
+ unless rules.empty?
132
+ @result.rules = rules
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,29 @@
1
+ require 'applb/dsl/listener'
2
+
3
+ module Applb
4
+ class DSL
5
+ class EC2
6
+ class LoadBalancer
7
+ class Listeners
8
+ include Applb::TemplateHelper
9
+
10
+ attr_reader :result
11
+
12
+ def initialize(context, lb_name, &block)
13
+ @context = context.dup
14
+ @lb_name = lb_name
15
+ @result = []
16
+
17
+ instance_eval(&block)
18
+ end
19
+
20
+ private
21
+
22
+ def listener(&block)
23
+ @result << Listener.new(@context, @lb_name, &block).result
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,209 @@
1
+ require 'applb/dsl/checker'
2
+ require 'applb/dsl/attributes'
3
+ require 'applb/dsl/listeners'
4
+ require 'applb/dsl/target_groups'
5
+
6
+ module Applb
7
+ class DSL
8
+ class EC2
9
+ class LoadBalancer
10
+ include Applb::DSL::Checker
11
+ include Applb::TemplateHelper
12
+
13
+ class Result
14
+ ATTRIBUTES = %i/name instances scheme subnets security_groups tags ip_address_type attributes target_groups listeners load_balancer_arn/
15
+ attr_accessor *ATTRIBUTES
16
+
17
+ def initialize(context)
18
+ @context = context
19
+ @options = context.options
20
+ end
21
+
22
+ def to_h
23
+ Hash[ATTRIBUTES.sort.map { |name| [name, public_send(name)] }]
24
+ end
25
+
26
+ CREATE_KEYS = %i/name subnets security_groups scheme tags ip_address_type/
27
+ def create_option
28
+ to_h.select { |k, _| CREATE_KEYS.include?(k) }
29
+ end
30
+
31
+ def create
32
+ Applb.logger.info "Create ELB v2 #{name}"
33
+ return if @options[:dry_run]
34
+
35
+ client.create_load_balancer(create_option).load_balancers.first
36
+ end
37
+
38
+ def aws(aws_lb)
39
+ @aws_lb = aws_lb
40
+ self
41
+ end
42
+
43
+ def subnets_updated?
44
+ subnets.sort != @aws_lb.availability_zones.map(&:subnet_id).sort
45
+ end
46
+
47
+ def security_groups_updated?
48
+ security_groups.sort != @aws_lb.security_groups.sort
49
+ end
50
+
51
+ def ip_address_type_updated?
52
+ ip_address_type != @aws_lb.ip_address_type
53
+ end
54
+
55
+ def modify_subnets
56
+ return unless subnets_updated?
57
+
58
+ Applb.logger.info("Modify #{name} subnets")
59
+ diff = Applb::Utils.diff(
60
+ @aws_lb.availability_zones.map(&:subnet_id).sort,
61
+ subnets.sort,
62
+ color: @options[:color],
63
+ )
64
+ Applb.logger.info("<diff>\n#{diff}")
65
+ return if @options[:dry_run]
66
+
67
+ client.set_subnets(
68
+ load_balancer_arn: @aws_lb.load_balancer_arn,
69
+ subnets: subnets,
70
+ ).availability_zones
71
+ end
72
+
73
+ def modify_security_groups
74
+ return unless security_groups_updated?
75
+
76
+ Applb.logger.info "Modify #{name} security_groups"
77
+ diff = Applb::Utils.diff(
78
+ @aws_lb.security_groups.sort,
79
+ security_groups.sort,
80
+ color: @options[:color],
81
+ )
82
+ Applb.logger.info("<diff>\n#{diff}")
83
+ return if @options[:dry_run]
84
+
85
+ client.set_security_groups(
86
+ load_balancer_arn: @aws_lb.load_balancer_arn,
87
+ security_groups: security_groups,
88
+ ).security_group_ids
89
+ end
90
+
91
+ def modify_ip_address_type
92
+ return unless ip_address_type_updated?
93
+
94
+ Applb.logger.info "Modify #{name} ip_address_type"
95
+ diff = Applb::Utils.diff(
96
+ @aws_lb.ip_address_type,
97
+ ip_address_type,
98
+ color: @options[:color],
99
+ )
100
+ Applb.logger.info("<diff>\n#{diff}")
101
+
102
+ return if @options[:dry_run]
103
+ client.set_ip_address_type(
104
+ load_balancer_arn: @aws_lb.load_balancer_arn,
105
+ ip_address_type: ip_address_type,
106
+ ).ip_address_type
107
+ end
108
+
109
+ def modify_load_balancer_attributes
110
+ attrs = attributes.map do |attr|
111
+ {key: attr[:key], value: attr[:value].to_s}
112
+ end
113
+ log_enabled = attrs.find { |attr| attr[:key] == 'access_logs.s3.enabled' }[:value]
114
+ if log_enabled.to_s == 'false'
115
+ attrs.reject! do |attr|
116
+ %w/access_logs.s3.bucket access_logs.s3.prefix/.include?(attr[:key])
117
+ end
118
+ end
119
+ dsl_hash = attrs.map { |a| a.to_h }.sort { |a, b| a[:key] <=> b[:key] }
120
+
121
+ aws_attributes = client.describe_load_balancer_attributes(
122
+ load_balancer_arn: @aws_lb.load_balancer_arn,
123
+ ).attributes
124
+ aws_hash = aws_attributes.map { |a| a.to_h }.sort { |a, b| a[:key] <=> b[:key] }
125
+ aws_log_enabled = aws_attributes.find { |attr| attr[:key] == 'access_logs.s3.enabled' }[:value]
126
+ if aws_log_enabled == 'false'
127
+ aws_hash.reject! do |attr|
128
+ %w/access_logs.s3.bucket access_logs.s3.prefix/.include?(attr[:key])
129
+ end
130
+ end
131
+
132
+ return if dsl_hash == aws_hash
133
+
134
+ Applb.logger.info "Modify #{name} load_balancer_attributes"
135
+ Applb.logger.info("<diff>\n#{Applb::Utils.diff(aws_hash, dsl_hash, color: @options[:color])}")
136
+ return if @options[:dry_run]
137
+
138
+ client.modify_load_balancer_attributes(
139
+ load_balancer_arn: @aws_lb.load_balancer_arn,
140
+ attributes: attrs,
141
+ ).attributes
142
+ end
143
+
144
+ private
145
+
146
+ def client
147
+ @client ||= Applb::ClientWrapper.new(@options)
148
+ end
149
+ end
150
+
151
+ def initialize(context, name, vpc_id, &block)
152
+ @name = name
153
+ @vpc_id = vpc_id
154
+ @context = context.merge(name: name)
155
+
156
+ @result = Result.new(@context)
157
+ @result.name = name
158
+ @result.attributes = Attributes.new(@context, @name) {}.result
159
+ @result.instances = []
160
+ @result.target_groups = []
161
+ @result.listeners = []
162
+
163
+ instance_eval(&block)
164
+ end
165
+
166
+ def result
167
+ required(:subnets, @result.subnets)
168
+ required(:security_groups, @result.security_groups)
169
+
170
+ @result
171
+ end
172
+
173
+ private
174
+
175
+ def subnets(*subnets)
176
+ @result.subnets = subnets
177
+ end
178
+
179
+ def security_groups(*security_groups)
180
+ @result.security_groups = security_groups
181
+ end
182
+
183
+ def scheme(scheme)
184
+ @result.scheme = scheme
185
+ end
186
+
187
+ def tags(tags)
188
+ @result.tags = tags.map { |k, v| { key: k, value: v } }
189
+ end
190
+
191
+ def ip_address_type(ip_address_type)
192
+ @result.ip_address_type = ip_address_type
193
+ end
194
+
195
+ def attributes(&block)
196
+ @result.attributes = Attributes.new(@context, @name, &block).result
197
+ end
198
+
199
+ def target_groups(&block)
200
+ @result.target_groups = TargetGroups.new(@context, @name, &block).result
201
+ end
202
+
203
+ def listeners(&block)
204
+ @result.listeners = Listeners.new(@context, @name, &block).result
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end