ufo 4.6.2 → 5.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/docs/_docs/conventions.md +1 -1
  4. data/docs/_docs/extras/codebuild-iam-role.md +1 -1
  5. data/docs/_docs/extras/dockerfile-erb.md +1 -1
  6. data/docs/_docs/extras/ecs-network-mode.md +1 -1
  7. data/docs/_docs/extras/load-balancer.md +1 -1
  8. data/docs/_docs/extras/minimal-deploy-iam.md +1 -1
  9. data/docs/_docs/extras/notification-arns.md +21 -0
  10. data/docs/_docs/extras/redirection-support.md +9 -9
  11. data/docs/_docs/extras/route53-support.md +4 -4
  12. data/docs/_docs/extras/security-groups.md +1 -1
  13. data/docs/_docs/extras/ssl-support.md +5 -5
  14. data/docs/_docs/faq.md +1 -1
  15. data/docs/_docs/helpers.md +7 -5
  16. data/docs/_docs/iam-roles.md +112 -0
  17. data/docs/_docs/install.md +0 -10
  18. data/docs/_docs/more/auto-completion.md +1 -1
  19. data/docs/_docs/more/automated-cleanup.md +1 -1
  20. data/docs/_docs/more/customize-cloudformation.md +1 -1
  21. data/docs/_docs/more/migrations.md +1 -1
  22. data/docs/_docs/more/run-in-pieces.md +1 -1
  23. data/docs/_docs/more/single-task.md +1 -1
  24. data/docs/_docs/more/stuck-cloudformation.md +1 -1
  25. data/docs/_docs/more/why-cloudformation.md +1 -1
  26. data/docs/_docs/next-steps.md +1 -1
  27. data/docs/_docs/quick-start-ec2.md +1 -0
  28. data/docs/_docs/secrets.md +135 -0
  29. data/docs/_docs/settings.md +10 -9
  30. data/docs/_docs/settings/cluster.md +7 -13
  31. data/docs/_docs/settings/manage-security-groups.md +24 -0
  32. data/docs/_docs/settings/network.md +11 -1
  33. data/docs/_docs/structure.md +10 -9
  34. data/docs/_docs/tutorial-ufo-init.md +1 -7
  35. data/docs/_docs/ufo-current.md +1 -1
  36. data/docs/_docs/ufo-env-extra.md +1 -1
  37. data/docs/_docs/ufo-env.md +3 -5
  38. data/docs/_docs/ufo-logs.md +1 -2
  39. data/docs/_docs/ufo-task-params.md +1 -1
  40. data/docs/_docs/upgrading.md +1 -1
  41. data/docs/_docs/upgrading/upgrade4.5.md +2 -2
  42. data/docs/_docs/upgrading/upgrade4.md +2 -2
  43. data/docs/_docs/upgrading/upgrade5.md +19 -0
  44. data/docs/_docs/variables.md +1 -1
  45. data/docs/_includes/cfn-customize.md +4 -4
  46. data/docs/_includes/footer.html +6 -5
  47. data/docs/_includes/subnav.html +3 -0
  48. data/docs/_reference/ufo-deploy.md +1 -2
  49. data/docs/_reference/ufo-init.md +15 -16
  50. data/docs/_reference/ufo-logs.md +1 -1
  51. data/docs/_reference/ufo-rollback.md +2 -0
  52. data/docs/_reference/ufo-ship.md +1 -2
  53. data/docs/_reference/ufo-ships.md +1 -2
  54. data/docs/_reference/ufo-tasks-build.md +1 -2
  55. data/docs/articles.md +1 -1
  56. data/docs/quick-start.md +1 -0
  57. data/lib/template/.secrets +5 -0
  58. data/lib/template/.ufo/iam_roles/execution_role.rb +7 -0
  59. data/lib/template/.ufo/iam_roles/task_role.rb +21 -0
  60. data/lib/template/.ufo/settings.yml.tt +1 -0
  61. data/lib/template/.ufo/settings/cfn/default.yml.tt +27 -27
  62. data/lib/template/.ufo/settings/network/default.yml.tt +9 -0
  63. data/lib/template/.ufo/templates/fargate.json.erb +3 -1
  64. data/lib/template/.ufo/templates/main.json.erb +3 -0
  65. data/lib/template/.ufo/variables/base.rb.tt +1 -0
  66. data/lib/ufo.rb +2 -1
  67. data/lib/ufo/autoloader.rb +9 -0
  68. data/lib/ufo/cli.rb +3 -2
  69. data/lib/ufo/core.rb +1 -9
  70. data/lib/ufo/docker/cleaner.rb +1 -1
  71. data/lib/ufo/dsl.rb +6 -1
  72. data/lib/ufo/dsl/helper.rb +19 -37
  73. data/lib/ufo/dsl/helper/vars.rb +97 -0
  74. data/lib/ufo/dsl/outputter.rb +12 -9
  75. data/lib/ufo/ecr/auth.rb +10 -21
  76. data/lib/ufo/help/init.md +1 -1
  77. data/lib/ufo/init.rb +0 -2
  78. data/lib/ufo/log_group.rb +1 -0
  79. data/lib/ufo/role/builder.rb +66 -0
  80. data/lib/ufo/role/dsl.rb +21 -0
  81. data/lib/ufo/role/registry.rb +24 -0
  82. data/lib/ufo/rollback.rb +2 -1
  83. data/lib/ufo/sequence.rb +0 -16
  84. data/lib/ufo/setting/profile.rb +11 -7
  85. data/lib/ufo/setting/security_groups.rb +22 -0
  86. data/lib/ufo/settings.rb +20 -0
  87. data/lib/ufo/stack.rb +24 -24
  88. data/lib/ufo/stack/builder.rb +26 -0
  89. data/lib/ufo/stack/builder/base.rb +54 -0
  90. data/lib/ufo/stack/builder/conditions.rb +23 -0
  91. data/lib/ufo/stack/builder/outputs.rb +24 -0
  92. data/lib/ufo/stack/builder/parameters.rb +45 -0
  93. data/lib/ufo/stack/builder/resources.rb +20 -0
  94. data/lib/ufo/stack/builder/resources/base.rb +4 -0
  95. data/lib/ufo/stack/builder/resources/dns.rb +17 -0
  96. data/lib/ufo/stack/builder/resources/ecs.rb +71 -0
  97. data/lib/ufo/stack/builder/resources/elb.rb +45 -0
  98. data/lib/ufo/stack/builder/resources/listener.rb +42 -0
  99. data/lib/ufo/stack/builder/resources/listener_ssl.rb +16 -0
  100. data/lib/ufo/stack/builder/resources/roles/base.rb +22 -0
  101. data/lib/ufo/stack/builder/resources/roles/execution_role.rb +4 -0
  102. data/lib/ufo/stack/builder/resources/roles/task_role.rb +4 -0
  103. data/lib/ufo/stack/builder/resources/security_group/base.rb +4 -0
  104. data/lib/ufo/stack/builder/resources/security_group/ecs.rb +44 -0
  105. data/lib/ufo/stack/builder/resources/security_group/ecs_rule.rb +25 -0
  106. data/lib/ufo/stack/builder/resources/security_group/elb.rb +57 -0
  107. data/lib/ufo/stack/builder/resources/target_group.rb +39 -0
  108. data/lib/ufo/stack/builder/resources/task_definition.rb +24 -0
  109. data/lib/ufo/stack/builder/resources/task_definition/reconstructor.rb +49 -0
  110. data/lib/ufo/stack/context.rb +41 -48
  111. data/lib/ufo/stack/custom_properties.rb +59 -0
  112. data/lib/ufo/stack/helper.rb +2 -5
  113. data/lib/ufo/stack/template_body.rb +13 -0
  114. data/lib/ufo/task.rb +2 -7
  115. data/lib/ufo/tasks.rb +1 -1
  116. data/lib/ufo/tasks/builder.rb +0 -1
  117. data/lib/ufo/template_scope.rb +1 -66
  118. data/lib/ufo/utils/squeezer.rb +24 -0
  119. data/lib/ufo/version.rb +1 -1
  120. data/spec/fixtures/iam_roles/task_role.rb +17 -0
  121. data/spec/lib/ecr_auth_spec.rb +32 -20
  122. data/spec/lib/role/builder_spec.rb +67 -0
  123. data/spec/lib/role/dsl_spec.rb +12 -0
  124. data/ufo.gemspec +2 -1
  125. metadata +66 -8
  126. data/lib/cfn/stack.yml +0 -283
@@ -0,0 +1,71 @@
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
+ LoadBalancers: {
25
+ "Fn::If": [
26
+ "CreateTargetGroupIsTrue",
27
+ [
28
+ {
29
+ ContainerName: @container[:name],
30
+ ContainerPort: @container[:port],
31
+ TargetGroupArn: {Ref: "TargetGroup"}
32
+ }
33
+ ],
34
+ {
35
+ "Fn::If": [
36
+ "ElbTargetGroupIsBlank",
37
+ [],
38
+ [
39
+ {
40
+ ContainerName: @container[:name],
41
+ ContainerPort: @container[:port],
42
+ TargetGroupArn: {Ref: "ElbTargetGroup"}
43
+ }
44
+ ]
45
+ ]
46
+ }
47
+ ]
48
+ },
49
+ SchedulingStrategy: {Ref: "EcsSchedulingStrategy"}
50
+ }
51
+
52
+ props[:TaskDefinition] = @rollback_definition_arn ? @rollback_definition_arn : {Ref: "TaskDefinition"}
53
+
54
+ if @container[:network_mode].to_s == 'awsvpc'
55
+ props[:NetworkConfiguration] = {
56
+ AwsvpcConfiguration: {
57
+ Subnets: {Ref: "EcsSubnets"},
58
+ SecurityGroups: security_groups(:ecs)
59
+ }
60
+ }
61
+
62
+ if @container[:fargate]
63
+ props[:LaunchType] = "FARGATE"
64
+ props[:NetworkConfiguration][:AwsvpcConfiguration][:AssignPublicIp] = "ENABLED" # Works with fargate but doesnt seem to work with non-fargate
65
+ end
66
+ end
67
+
68
+ props
69
+ end
70
+ end
71
+ 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?
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?
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?
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