ufo 4.6.3 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/docs/_docs/extras/notification-arns.md +21 -0
  4. data/docs/_docs/helpers.md +6 -4
  5. data/docs/_docs/iam-roles.md +111 -0
  6. data/docs/_docs/secrets.md +112 -0
  7. data/docs/_docs/settings/cluster.md +7 -13
  8. data/docs/_includes/subnav.html +3 -0
  9. data/docs/_reference/ufo-deploy.md +1 -2
  10. data/docs/_reference/ufo-logs.md +1 -1
  11. data/docs/_reference/ufo-rollback.md +2 -0
  12. data/docs/_reference/ufo-ship.md +1 -2
  13. data/docs/_reference/ufo-ships.md +1 -2
  14. data/docs/_reference/ufo-tasks-build.md +1 -2
  15. data/lib/template/.secrets +3 -0
  16. data/lib/template/.ufo/settings.yml.tt +1 -0
  17. data/lib/template/.ufo/settings/cfn/default.yml.tt +27 -27
  18. data/lib/template/.ufo/settings/network/default.yml.tt +9 -0
  19. data/lib/template/.ufo/templates/fargate.json.erb +3 -0
  20. data/lib/template/.ufo/templates/main.json.erb +3 -0
  21. data/lib/template/.ufo/variables/base.rb.tt +1 -0
  22. data/lib/ufo.rb +2 -1
  23. data/lib/ufo/autoloader.rb +9 -0
  24. data/lib/ufo/cli.rb +3 -2
  25. data/lib/ufo/core.rb +1 -9
  26. data/lib/ufo/docker/cleaner.rb +1 -1
  27. data/lib/ufo/dsl.rb +6 -1
  28. data/lib/ufo/dsl/helper.rb +19 -37
  29. data/lib/ufo/dsl/helper/vars.rb +98 -0
  30. data/lib/ufo/dsl/outputter.rb +12 -9
  31. data/lib/ufo/log_group.rb +1 -0
  32. data/lib/ufo/role/builder.rb +66 -0
  33. data/lib/ufo/role/dsl.rb +21 -0
  34. data/lib/ufo/role/registry.rb +24 -0
  35. data/lib/ufo/rollback.rb +2 -1
  36. data/lib/ufo/setting/profile.rb +11 -7
  37. data/lib/ufo/setting/security_groups.rb +22 -0
  38. data/lib/ufo/settings.rb +20 -0
  39. data/lib/ufo/stack.rb +24 -24
  40. data/lib/ufo/stack/builder.rb +26 -0
  41. data/lib/ufo/stack/builder/base.rb +54 -0
  42. data/lib/ufo/stack/builder/conditions.rb +23 -0
  43. data/lib/ufo/stack/builder/outputs.rb +24 -0
  44. data/lib/ufo/stack/builder/parameters.rb +45 -0
  45. data/lib/ufo/stack/builder/resources.rb +20 -0
  46. data/lib/ufo/stack/builder/resources/base.rb +4 -0
  47. data/lib/ufo/stack/builder/resources/dns.rb +17 -0
  48. data/lib/ufo/stack/builder/resources/ecs.rb +63 -0
  49. data/lib/ufo/stack/builder/resources/elb.rb +45 -0
  50. data/lib/ufo/stack/builder/resources/listener.rb +42 -0
  51. data/lib/ufo/stack/builder/resources/listener_ssl.rb +16 -0
  52. data/lib/ufo/stack/builder/resources/roles/base.rb +22 -0
  53. data/lib/ufo/stack/builder/resources/roles/execution_role.rb +4 -0
  54. data/lib/ufo/stack/builder/resources/roles/task_role.rb +4 -0
  55. data/lib/ufo/stack/builder/resources/security_group/base.rb +4 -0
  56. data/lib/ufo/stack/builder/resources/security_group/ecs.rb +44 -0
  57. data/lib/ufo/stack/builder/resources/security_group/ecs_rule.rb +25 -0
  58. data/lib/ufo/stack/builder/resources/security_group/elb.rb +57 -0
  59. data/lib/ufo/stack/builder/resources/target_group.rb +39 -0
  60. data/lib/ufo/stack/builder/resources/task_definition.rb +24 -0
  61. data/lib/ufo/stack/builder/resources/task_definition/reconstructor.rb +49 -0
  62. data/lib/ufo/stack/context.rb +41 -48
  63. data/lib/ufo/stack/custom_properties.rb +59 -0
  64. data/lib/ufo/stack/helper.rb +2 -5
  65. data/lib/ufo/stack/template_body.rb +13 -0
  66. data/lib/ufo/task.rb +2 -7
  67. data/lib/ufo/tasks.rb +1 -1
  68. data/lib/ufo/tasks/builder.rb +0 -1
  69. data/lib/ufo/template_scope.rb +1 -66
  70. data/lib/ufo/utils/squeezer.rb +24 -0
  71. data/lib/ufo/version.rb +1 -1
  72. data/spec/fixtures/iam_roles/task_role.rb +17 -0
  73. data/spec/lib/role/builder_spec.rb +67 -0
  74. data/spec/lib/role/dsl_spec.rb +12 -0
  75. data/ufo.gemspec +1 -0
  76. metadata +57 -3
  77. data/lib/cfn/stack.yml +0 -283
@@ -0,0 +1,4 @@
1
+ class Ufo::Stack::Builder::Resources
2
+ class Base < Ufo::Stack::Builder::Base
3
+ end
4
+ end
@@ -0,0 +1,17 @@
1
+ class Ufo::Stack::Builder::Resources
2
+ class Dns < Base
3
+ def build
4
+ return unless @create_route53
5
+
6
+ {
7
+ Type: "AWS::Route53::RecordSet",
8
+ Properties: {
9
+ Comment: "cname to load balancer",
10
+ Type: "CNAME",
11
+ TTL: 60, # ttl has special casing
12
+ ResourceRecords: [{"Fn::GetAtt": "Elb.DNSName"}]
13
+ }
14
+ }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,63 @@
1
+ class Ufo::Stack::Builder::Resources
2
+ class Ecs < Base
3
+ def build
4
+ attrs = {
5
+ Type: "AWS::ECS::Service",
6
+ Properties: properties
7
+ }
8
+
9
+ attrs[:DependsOn] = "Listener" if @create_elb
10
+
11
+ attrs
12
+ end
13
+
14
+ def properties
15
+ props = {
16
+ Cluster: @cluster,
17
+ DesiredCount: {
18
+ "Fn::If": [
19
+ "EcsDesiredCountIsBlank",
20
+ {Ref: "AWS::NoValue"},
21
+ {Ref: "EcsDesiredCount"}
22
+ ]
23
+ },
24
+ NetworkConfiguration: {
25
+ AwsvpcConfiguration: {
26
+ Subnets: {Ref: "EcsSubnets"},
27
+ SecurityGroups: security_groups(:ecs)
28
+ }
29
+ },
30
+ LoadBalancers: {
31
+ "Fn::If": [
32
+ "CreateTargetGroupIsTrue",
33
+ [
34
+ {
35
+ ContainerName: "web",
36
+ ContainerPort: @container[:port],
37
+ TargetGroupArn: {Ref: "TargetGroup"}
38
+ }
39
+ ],
40
+ {
41
+ "Fn::If": [
42
+ "ElbTargetGroupIsBlank",
43
+ [],
44
+ [
45
+ {
46
+ ContainerName: "web",
47
+ ContainerPort: @container[:port],
48
+ TargetGroupArn: {Ref: "ElbTargetGroup"}
49
+ }
50
+ ]
51
+ ]
52
+ }
53
+ ]
54
+ },
55
+ SchedulingStrategy: {Ref: "EcsSchedulingStrategy"}
56
+ }
57
+
58
+ props[:TaskDefinition] = @rollback_definition_arn ? @rollback_definition_arn : {Ref: "TaskDefinition"}
59
+
60
+ props
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,45 @@
1
+ class Ufo::Stack::Builder::Resources
2
+ class Elb < Base
3
+ def build
4
+ {
5
+ Type: "AWS::ElasticLoadBalancingV2::LoadBalancer",
6
+ Condition: "CreateElbIsTrue",
7
+ Properties: properties,
8
+ }
9
+ end
10
+
11
+ def properties
12
+ props = {
13
+ Type: @elb_type,
14
+ Tags: [
15
+ {Key: "Name", Value: @stack_name}
16
+ ],
17
+ Subnets: {Ref: "ElbSubnets"},
18
+ Scheme: "internet-facing"
19
+ }
20
+
21
+ props[:SecurityGroups] = security_groups(:elb) if @elb_type == "application"
22
+ subnets(props)
23
+
24
+ props
25
+ end
26
+
27
+ def subnets(props)
28
+ mappings = @elb_type == "network" && @subnet_mappings && !@subnet_mappings.empty?
29
+ if mappings
30
+ props[:SubnetMappings] = subnet_mappings
31
+ else
32
+ props[:Subnets] = {Ref: "ElbSubnets"}
33
+ end
34
+ end
35
+
36
+ def subnet_mappings
37
+ @subnet_mappings.map do |allocation_id, subnet_id|
38
+ {
39
+ AllocationId: allocation_id,
40
+ SubnetId: subnet_id,
41
+ }
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,42 @@
1
+ class Ufo::Stack::Builder::Resources
2
+ class Listener < Base
3
+ def build
4
+ {
5
+ Type: "AWS::ElasticLoadBalancingV2::Listener",
6
+ Condition: "CreateElbIsTrue",
7
+ Properties: properties,
8
+ }
9
+ end
10
+
11
+ def properties
12
+ props = {
13
+ DefaultActions: [
14
+ {
15
+ Type: "forward",
16
+ TargetGroupArn: {
17
+ "Fn::If": [
18
+ "ElbTargetGroupIsBlank",
19
+ {Ref: "TargetGroup"},
20
+ {Ref: "ElbTargetGroup"}
21
+ ]
22
+ }
23
+ }
24
+ ],
25
+ LoadBalancerArn: {Ref: "Elb"},
26
+ Protocol: protocol,
27
+ }
28
+
29
+ props[:Port] = port if port
30
+
31
+ props
32
+ end
33
+
34
+ def protocol
35
+ @default_listener_protocol
36
+ end
37
+
38
+ def port
39
+ 80
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,16 @@
1
+ class Ufo::Stack::Builder::Resources
2
+ class ListenerSsl < Listener
3
+ def build
4
+ return unless @create_listener_ssl
5
+ super
6
+ end
7
+
8
+ def protocol
9
+ @default_listener_ssl_protocol
10
+ end
11
+
12
+ # nil on purpose
13
+ def port
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ module Ufo::Stack::Builder::Resources::Roles
2
+ class Base < Ufo::Stack::Builder::Base
3
+ def build
4
+ return unless self.class.build? # important because it runs DSL#evaluate
5
+ Ufo::Role::Builder.new(self.class.role_type).build
6
+ end
7
+
8
+
9
+ class << self
10
+ def role_type
11
+ self.name.to_s.split("::").last.underscore
12
+ end
13
+
14
+ def build?
15
+ path = "#{Ufo.root}/.ufo/iam_roles/#{role_type}.rb"
16
+ return unless File.exist?(path)
17
+ Ufo::Role::DSL.new(path).evaluate # runs the role.rb and registers items
18
+ Ufo::Role::Builder.new(role_type).build?
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,4 @@
1
+ module Ufo::Stack::Builder::Resources::Roles
2
+ class ExecutionRole < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Ufo::Stack::Builder::Resources::Roles
2
+ class TaskRole < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Ufo::Stack::Builder::Resources::SecurityGroup
2
+ class Base < Ufo::Stack::Builder::Base
3
+ end
4
+ end
@@ -0,0 +1,44 @@
1
+ module Ufo::Stack::Builder::Resources::SecurityGroup
2
+ class Ecs < Base
3
+ def build
4
+ return unless managed_security_groups_enabled?
5
+
6
+ {
7
+ Type: "AWS::EC2::SecurityGroup",
8
+ Properties: properties
9
+ }
10
+ end
11
+
12
+ def properties
13
+ props = {
14
+ GroupDescription: "Allow http to client host",
15
+ VpcId: {Ref: "Vpc"},
16
+ SecurityGroupEgress: [
17
+ {
18
+ IpProtocol: "-1",
19
+ CidrIp: "0.0.0.0/0",
20
+ Description: "outbound traffic"
21
+ }
22
+ ],
23
+ Tags: [
24
+ {
25
+ Key: "Name",
26
+ Value: @stack_name,
27
+ }
28
+ ]
29
+ }
30
+
31
+ if @elb_type == "network"
32
+ props[:SecurityGroupIngress] = {
33
+ IpProtocol: "tcp",
34
+ FromPort: @container[:port],
35
+ ToPort: @container[:port],
36
+ CidrIp: "0.0.0.0/0",
37
+ Description: "docker ephemeral port range for network elb",
38
+ }
39
+ end
40
+
41
+ props
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,25 @@
1
+ module Ufo::Stack::Builder::Resources::SecurityGroup
2
+ class EcsRule < Base
3
+ def build
4
+ return unless managed_security_groups_enabled?
5
+ return unless @elb_type == "application"
6
+
7
+ {
8
+ Type: "AWS::EC2::SecurityGroupIngress",
9
+ Condition: "CreateElbIsTrue",
10
+ Properties: {
11
+ IpProtocol: "tcp",
12
+ FromPort: "0",
13
+ ToPort: "65535",
14
+ SourceSecurityGroupId: {
15
+ "Fn::GetAtt": "ElbSecurityGroup.GroupId"
16
+ },
17
+ GroupId: {
18
+ "Fn::GetAtt": "EcsSecurityGroup.GroupId"
19
+ },
20
+ Description: "application elb access to ecs"
21
+ }
22
+ }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,57 @@
1
+ module Ufo::Stack::Builder::Resources::SecurityGroup
2
+ class Elb < Base
3
+ def build
4
+ return unless managed_security_groups_enabled?
5
+ return unless @elb_type == "application"
6
+
7
+ {
8
+ Type: "AWS::EC2::SecurityGroup",
9
+ Condition: "CreateElbIsTrue",
10
+ Properties: properties
11
+ }
12
+ end
13
+
14
+ def properties
15
+ port = cfn.dig(:Listener, :Port) || cfn.dig(:listener, :port) # backwards compatiblity
16
+
17
+ props = {
18
+ GroupDescription: "Allow http to client host",
19
+ VpcId: {Ref: "Vpc"},
20
+ SecurityGroupIngress: [
21
+ {
22
+ IpProtocol: "tcp",
23
+ FromPort: port,
24
+ ToPort: port,
25
+ CidrIp: "0.0.0.0/0"
26
+ }
27
+ ],
28
+ SecurityGroupEgress: [
29
+ {
30
+ IpProtocol: "tcp",
31
+ FromPort: "0",
32
+ ToPort: "65535",
33
+ CidrIp: "0.0.0.0/0"
34
+ }
35
+ ],
36
+ Tags: [
37
+ {
38
+ Key: "Name",
39
+ Value: "#{@stack_name}-elb"
40
+ }
41
+ ]
42
+ }
43
+
44
+ if @create_listener_ssl
45
+ ssl_port = cfn.dig(:ListenerSsl, :Port) || cfn.dig(:listener_ssl, :port) # backwards compatiblity
46
+ props[:SecurityGroupIngress] << {
47
+ IpProtocol: "tcp",
48
+ FromPort: ssl_port,
49
+ ToPort: ssl_port,
50
+ CidrIp: "0.0.0.0/0"
51
+ }
52
+ end
53
+
54
+ props
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,39 @@
1
+ class Ufo::Stack::Builder::Resources
2
+ class TargetGroup < Base
3
+ def build
4
+ {
5
+ Type: "AWS::ElasticLoadBalancingV2::TargetGroup",
6
+ Condition: "CreateTargetGroupIsTrue",
7
+ Properties: properties,
8
+ }
9
+ end
10
+
11
+ def properties
12
+ props = {
13
+ VpcId: {Ref: "Vpc"},
14
+ Tags: [
15
+ {
16
+ Key: "Name",
17
+ Value: @stack_name,
18
+ }
19
+ ],
20
+ Protocol: @default_target_group_protocol,
21
+ Port: 80,
22
+ HealthCheckIntervalSeconds: 10,
23
+ HealthyThresholdCount: 2,
24
+ UnhealthyThresholdCount: 2,
25
+ TargetGroupAttributes: [
26
+ {
27
+ Key: "deregistration_delay.timeout_seconds",
28
+ Value: 10
29
+ }
30
+ ]
31
+ }
32
+
33
+ props[:TargetType] = "ip" if @container[:network_mode] == "awsvpc"
34
+ props[:HealthCheckPort] = @container[:port] if @elb_type == "network" && @network_mode == "awsvpc"
35
+
36
+ props
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,24 @@
1
+ class Ufo::Stack::Builder::Resources
2
+ class TaskDefinition < Base
3
+ def build
4
+ return if @rollback_definition_arn
5
+
6
+ {
7
+ Type: "AWS::ECS::TaskDefinition",
8
+ Properties: properties,
9
+ DeletionPolicy: "Retain",
10
+ UpdateReplacePolicy: "Retain",
11
+ }
12
+ end
13
+
14
+ def properties
15
+ props = Reconstructor.new(@task_definition).reconstruct
16
+
17
+ # Decorate with iam roles if needed
18
+ props[:TaskRoleArn] = {"Fn::GetAtt": "TaskRole.Arn"} if Roles::TaskRole.build?
19
+ props[:ExecutionRoleArn] = {"Fn::GetAtt": "ExecutionRole.Arn"} if Roles::ExecutionRole.build?
20
+
21
+ props
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,49 @@
1
+ class Ufo::Stack::Builder::Resources::TaskDefinition
2
+ class Reconstructor
3
+ include Ufo::AwsService
4
+
5
+ def initialize(task_definition, rollback=false)
6
+ @task_definition, @rollback = task_definition, rollback
7
+ end
8
+
9
+ def reconstruct
10
+ camelize(data)
11
+ end
12
+
13
+ def data
14
+ if @rollback
15
+ resp = ecs.describe_task_definition(task_definition: @task_definition)
16
+ resp.task_definition.to_h
17
+ else
18
+ path = "#{Ufo.root}/.ufo/output/#{@task_definition}.json"
19
+ JSON.load(IO.read(path))
20
+ end
21
+ end
22
+
23
+ # non-destructive
24
+ def camelize(value, parent_keys=[])
25
+ case value
26
+ when Array
27
+ value.map { |v| camelize(v, parent_keys) }
28
+ when Hash
29
+ initializer = value.map do |k, v|
30
+ new_key = camelize_key(k, parent_keys)
31
+ [new_key, camelize(v, parent_keys+[new_key])]
32
+ end
33
+ Hash[initializer]
34
+ else
35
+ value # do not camelize values
36
+ end
37
+ end
38
+
39
+ def camelize_key(k, parent_keys=[])
40
+ k = k.to_s
41
+ special = %w[Options] & parent_keys.map(&:to_s)
42
+ if special.empty?
43
+ k.camelize
44
+ else
45
+ k # pass through untouch
46
+ end
47
+ end
48
+ end
49
+ end