ufo 4.6.3 → 5.0.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.
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