ufo 6.2.5 → 6.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 69d0e411c4b48b8ec8a0a2148caf5501b3c038cb991585c4741027579a102e0c
4
- data.tar.gz: f080bdf97e783b18bc54723ced3be52557b0749094451561de41766c052aba0e
3
+ metadata.gz: ee25a6ee92c06422088b489aa3f9d27d8fb0dfd229b7bb6e0b0eb2241a4d3902
4
+ data.tar.gz: f4a8043b8ab3bbb33794d3002d58890d8aa4753ffe3ac4b029432599195e5289
5
5
  SHA512:
6
- metadata.gz: 860f1d782b915338e903d2c2c0cad43fbe4a915261b46a2b615a7d24870247170a3041de32b6e9de8b11161da1c8447dc3d12683e9179c8c6cd0af7ef3dc4d77
7
- data.tar.gz: c02bc1f698f85055e2e1c99b7fbf6cf730044e23b7584509a5c2e66bd79259097b2c5ccf1706b186a64b329db31e5678fe6eacb3f120724dbfa896a0ab6fbb00
6
+ metadata.gz: 76beccace3016451330c40b589fa7c3f95680eff187a667f520eed80c7d4cd90b91667dd06d58288cabf4c5d5acb699cf53ecc6c296dcb48e66504a0c02147ad
7
+ data.tar.gz: 22700e11a6de03b489deacf6fbde1e7b5652b4048b078b1d3750dfc0f1b083e5e3bbc417057f04d9fe73e48ac609139aea92bc526ae4c33cfdee16253f341db8
data/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  This project *tries* to adhere to [Semantic Versioning](http://semver.org/), even before v1.0.
5
5
 
6
+ ## [6.3.2] - 2022-03-26
7
+ - [#164](https://github.com/tongueroo/ufo/pull/164) existing elb target group support
8
+ - [#165](https://github.com/tongueroo/ufo/pull/165) improve secrets support
9
+ - [#166](https://github.com/tongueroo/ufo/pull/166) infer elb dns name from target group when possible
10
+
11
+ ## [6.3.1] - 2022-03-25
12
+ - ufo init: improve vars base.rb
13
+
14
+ ## [6.3.0] - 2022-03-25
15
+ - [#162](https://github.com/tongueroo/ufo/pull/162) hooks support
16
+
6
17
  ## [6.2.5] - 2022-03-24
7
18
  - [#159](https://github.com/tongueroo/ufo/pull/159) improve ufo call line
8
19
  - [#160](https://github.com/tongueroo/ufo/pull/160) conventionally lookup up secrets and env file
@@ -0,0 +1,2 @@
1
+ # Docs: https://ufoships.com/docs/features/env_files/env/
2
+ KEY=value
@@ -0,0 +1,5 @@
1
+ # Docs: https://ufoships.com/docs/features/env_files/secrets/
2
+ DATABASE_URL # conventional
3
+ # explicit
4
+ USER=ssm:demo/<%= Ufo.env %>/USER
5
+ PASS=ssm:demo/<%= Ufo.env %>/PASS
@@ -0,0 +1,9 @@
1
+ # Docs: https://ufoships.com/docs/config/hooks/docker/
2
+
3
+ before("build",
4
+ execute: "echo 'docker build before hook'",
5
+ )
6
+
7
+ after("build",
8
+ execute: "echo 'docker build after hook'",
9
+ )
@@ -0,0 +1,9 @@
1
+ # Docs: https://ufoships.com/docs/config/hooks/ufo/
2
+
3
+ before("ship",
4
+ execute: "echo 'ufo before ship hook'",
5
+ )
6
+
7
+ after("ship",
8
+ execute: "echo 'ufo after ship hook'",
9
+ )
@@ -35,6 +35,5 @@ Ufo.configure do |config|
35
35
  # config.logs.filter_pattern = '- "HealthChecker"'
36
36
 
37
37
  # Docs: https://ufoships.com/docs/config/reference/
38
- # You may want to set to false if your docker build and push process takes a while
39
- # config.ship.docker.quiet = false # default is true.
38
+ # config.ship.docker.quiet = true # default is false
40
39
  end
@@ -7,8 +7,8 @@
7
7
  @name = role # IE: web worker clock
8
8
  @image = docker_image # includes the git sha org/repo:ufo-[sha].
9
9
  # Docs: https://ufoships.com/docs/helpers/builtin/secrets/
10
- # @environment = env_file(".env")
11
- # @secrets = secrets_file(".secrets")
10
+ @environment = env_file
11
+ @secrets = secrets_file
12
12
  @cpu = 256
13
13
  @memory = 256
14
14
  @memory_reservation = 256
@@ -4,7 +4,7 @@ module Ufo
4
4
  class Autoloader
5
5
  class Inflector < Zeitwerk::Inflector
6
6
  def camelize(basename, _abspath)
7
- map = { cli: "CLI", dsl: "DSL", version: "VERSION" }
7
+ map = { cli: "CLI", version: "VERSION" }
8
8
  map[basename.to_sym] || super
9
9
  end
10
10
  end
@@ -8,7 +8,7 @@ class Ufo::Cfn::Stack::Builder::Resources
8
8
  Comment: dns.comment,
9
9
  Type: dns.type, # CNAME
10
10
  TTL: dns.ttl, # 60 ttl has special casing
11
- ResourceRecords: [{"Fn::GetAtt": "Elb.DNSName"}]
11
+ ResourceRecords: [resource_record]
12
12
  }
13
13
  # HostedZoneName: yourdomain. # dont forget the trailing period
14
14
  props[:HostedZoneName] = hosted_zone_name if hosted_zone_name
@@ -21,6 +21,43 @@ class Ufo::Cfn::Stack::Builder::Resources
21
21
  end
22
22
 
23
23
  private
24
+ def resource_record
25
+ existing = Ufo.config.elb.existing
26
+ if existing.target_group
27
+ existing_dns_name
28
+ else
29
+ {"Fn::GetAtt": "Elb.DNSName"}
30
+ end
31
+ end
32
+
33
+ def existing_dns_name
34
+ existing = Ufo.config.elb.existing
35
+ resp = elb.describe_target_groups(target_group_arns: [existing.target_group])
36
+ target_group = resp.target_groups.first
37
+ load_balancer_arns = target_group.load_balancer_arns
38
+ if load_balancer_arns.size == 1
39
+ resp = elb.describe_load_balancers(load_balancer_arns: load_balancer_arns)
40
+ load_balancer = resp.load_balancers.first
41
+ load_balancer.dns_name
42
+ else
43
+ return existing.dns_name if existing.dns_name
44
+ logger.error "ERROR: config.existing.dns_name must to be set".color(:red)
45
+ logger.error <<~EOL
46
+ This target group is associated with multiple load balancers.
47
+ UFO cannot infer the dns name in this case. You must set:
48
+
49
+ config.existing.dns_name
50
+
51
+ Info:
52
+
53
+ target group: #{existing.target_group}
54
+ load balancers: #{load_balancer_arns}
55
+
56
+ EOL
57
+ exit 1
58
+ end
59
+ end
60
+
24
61
  def dns_name
25
62
  return unless dns.domain || dns.name
26
63
  name = dns.name # my.domain.com
@@ -1,7 +1,7 @@
1
1
  module Ufo::Cfn::Stack::Builder::Resources::IamRoles
2
2
  class Base < Ufo::Cfn::Stack::Builder::Base
3
3
  def build
4
- return unless self.class.build? # important because it runs DSL#evaluate
4
+ return unless self.class.build? # important because it runs Dsl#evaluate
5
5
  Ufo::IamRole::Builder.new(self.class.role_type).build
6
6
  end
7
7
 
@@ -13,7 +13,7 @@ module Ufo::Cfn::Stack::Builder::Resources::IamRoles
13
13
  def build?
14
14
  path = lookup_path
15
15
  return unless path.nil? || File.exist?(path)
16
- Ufo::IamRole::DSL.new(path).evaluate # runs the role.rb and registers items
16
+ Ufo::IamRole::Dsl.new(path).evaluate # runs the role.rb and registers items
17
17
  Ufo::IamRole::Builder.new(role_type).build?
18
18
  end
19
19
 
@@ -55,8 +55,8 @@ class Ufo::Cfn::Stack::Builder::Resources
55
55
  TargetGroupArn: {
56
56
  "Fn::If": [
57
57
  "ElbTargetGroupIsBlank",
58
- {Ref: "TargetGroup"},
59
- {Ref: "ElbTargetGroup"}
58
+ {Ref: "TargetGroup"}, # UFO managed
59
+ {Ref: "ElbTargetGroup"} # Managed by user outside of UFO
60
60
  ]
61
61
  }
62
62
  }
@@ -9,10 +9,11 @@ class Ufo::Cfn::Stack
9
9
  container: container,
10
10
  create_elb: create_elb?, # helps set Ecs DependsOn
11
11
  create_listener_ssl: create_listener_ssl?,
12
- create_route53: create_elb? && dns_configured?,
12
+ create_route53: create_route53?,
13
13
  default_listener_protocol: default_listener_protocol,
14
14
  default_listener_ssl_protocol: default_listener_ssl_protocol,
15
15
  default_target_group_protocol: default_target_group_protocol,
16
+ elb_target_group: elb_target_group,
16
17
  elb_type: elb_type,
17
18
  new_stack: new_stack,
18
19
  rollback_task_definition: rollback_task_definition,
@@ -63,15 +64,30 @@ class Ufo::Cfn::Stack
63
64
  elb.ssl.enabled && elb.ssl.certificates
64
65
  end
65
66
 
67
+ def create_route53?
68
+ return false unless dns_configured?
69
+ if create_elb?
70
+ true
71
+ else
72
+ Ufo.config.elb.existing.target_group
73
+ end
74
+ end
75
+
66
76
  def create_elb?
67
77
  elb = Ufo.config.elb
68
- if elb.enabled.to_s == "auto"
78
+ if elb.existing.target_group
79
+ false
80
+ elsif elb.enabled.to_s == "auto"
69
81
  container[:name] == "web" # convention
70
82
  else
71
83
  elb.enabled # true or false
72
84
  end
73
85
  end
74
86
 
87
+ def elb_target_group
88
+ Ufo.config.elb.existing.target_group
89
+ end
90
+
75
91
  def container
76
92
  task_definition = Builder::Resources::TaskDefinition::Reconstructor.new(@task_definition, @options[:rollback]).reconstruct
77
93
 
data/lib/ufo/cfn/stack.rb CHANGED
@@ -25,6 +25,7 @@ module Ufo::Cfn
25
25
  class Stack < Base
26
26
  extend Memoist
27
27
  include Ufo::TaskDefinition::Helpers::AwsHelper
28
+ include Ufo::Hooks::Concern
28
29
 
29
30
  def deploy
30
31
  build
@@ -39,15 +40,14 @@ module Ufo::Cfn
39
40
 
40
41
  exit_with_message(@stack) if @stack && !updatable?(@stack)
41
42
 
42
- @stack ? perform(:update) : perform(:create)
43
-
44
- stop_old_tasks if @options[:stop_old_task]
45
-
46
- return unless @options[:wait]
47
- status.wait
43
+ run_hooks(name: "ship", file: "ufo.rb") do
44
+ @stack ? perform(:update) : perform(:create)
45
+ stop_old_tasks if @options[:stop_old_task]
46
+ return unless @options[:wait]
47
+ status.wait
48
+ end
48
49
 
49
50
  logger.info status.rollback_error_message if status.update_rollback?
50
-
51
51
  status.success?
52
52
  end
53
53
 
@@ -78,13 +78,17 @@ module Ufo::Cfn
78
78
  end
79
79
  end
80
80
 
81
+ # Run hooks here so both ufo docker and ufo ship runs it
82
+ # ufo docker => CLI::Build#build => Cfn::Stack#build
81
83
  def build
82
- vars = Vars.new(@options).values
83
- options_with_vars = @options.dup.merge(vars: vars)
84
- params = Params.new(options_with_vars)
85
- @parameters = params.build
86
- template = Template.new(options_with_vars)
87
- @template_body = template.body
84
+ run_hooks(name: "build", file: "ufo.rb") do
85
+ vars = Vars.new(@options).values
86
+ options_with_vars = @options.dup.merge(vars: vars)
87
+ params = Params.new(options_with_vars)
88
+ @parameters = params.build
89
+ template = Template.new(options_with_vars)
90
+ @template_body = template.body
91
+ end
88
92
  end
89
93
 
90
94
  def scheduling_strategy
data/lib/ufo/cli/build.rb CHANGED
@@ -7,11 +7,6 @@ class Ufo::CLI
7
7
  end
8
8
  alias_method :all, :build
9
9
 
10
- def for_deploy
11
- docker
12
- task_definition
13
- end
14
-
15
10
  def task_definition
16
11
  Ufo::TaskDefinition::Builder.new(@options).build
17
12
  end
@@ -1,27 +1,30 @@
1
1
  class Ufo::CLI
2
2
  class Destroy < Base
3
+ include Ufo::Hooks::Concern
4
+
3
5
  def run
4
6
  are_you_sure?
5
7
 
6
8
  stack = find_stack(@stack_name)
7
9
  unless stack
8
- puts "Stack #{@stack_name.color(:green)} does not exist."
10
+ logger.info "Stack #{@stack_name.color(:green)} does not exist."
9
11
  exit 1
10
12
  end
11
13
 
12
14
  if stack.stack_status =~ /_IN_PROGRESS$/
13
- puts "Cannot destroy service #{@service.color(:green)}"
14
- puts "Cannot delete stack #{@stack_name.color(:green)} in this state: #{stack.stack_status.color(:green)}"
15
- puts "If the stack is taking a long time, you can cancel the current operation with:"
16
- puts " ufo cancel #{@service}"
15
+ logger.info "Cannot destroy service #{@service.color(:green)}"
16
+ logger.info "Cannot delete stack #{@stack_name.color(:green)} in this state: #{stack.stack_status.color(:green)}"
17
+ logger.info "If the stack is taking a long time, you can cancel the current operation with:"
18
+ logger.info " ufo cancel #{@service}"
17
19
  return
18
20
  end
19
21
 
20
- cfn.delete_stack(stack_name: @stack_name)
21
- puts "Deleting stack #{@stack_name.color(:green)}"
22
-
23
- return unless @options[:wait]
24
- status.wait
22
+ run_hooks(name: "destroy", file: "ufo.rb") do
23
+ cfn.delete_stack(stack_name: @stack_name)
24
+ logger.info "Deleting stack #{@stack_name.color(:green)}"
25
+ return unless @options[:wait]
26
+ status.wait
27
+ end
25
28
  end
26
29
 
27
30
  def are_you_sure?
@@ -0,0 +1,7 @@
1
+ ## Examples
2
+
3
+ $ ufo new hook docker
4
+ create .ufo/config/hooks/docker.rb
5
+ $ ufo new hook ufo
6
+ create .ufo/config/hooks/ufo.rb
7
+ $
@@ -0,0 +1,18 @@
1
+ class Ufo::CLI::New
2
+ class EnvFile < Sequence
3
+ argument :type, default: "env", description: "IE: env or secrets" # description doesnt really show up
4
+
5
+ def self.cli_options
6
+ [
7
+ [:force, aliases: ["y"], type: :boolean, desc: "Bypass overwrite are you sure prompt for existing files"],
8
+ ]
9
+ end
10
+ cli_options.each { |args| class_option(*args) }
11
+
12
+ public
13
+ def create_hook
14
+ set_template_source("env_file")
15
+ template "file.#{type}", ".ufo/config/env_files/#{Ufo.env}.#{type}"
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ class Ufo::CLI::New
2
+ class Hook < Sequence
3
+ argument :type, default: "ufo", description: "IE: docker, ufo" # description doesnt really show up
4
+
5
+ def self.cli_options
6
+ [
7
+ [:force, aliases: ["y"], type: :boolean, desc: "Bypass overwrite are you sure prompt for existing files"],
8
+ ]
9
+ end
10
+ cli_options.each { |args| class_option(*args) }
11
+
12
+ public
13
+ def create_hook
14
+ set_template_source("hook")
15
+ template "#{type}.rb", ".ufo/config/hooks/#{type}.rb"
16
+ end
17
+ end
18
+ end
data/lib/ufo/cli/new.rb CHANGED
@@ -1,17 +1,31 @@
1
1
  class Ufo::CLI
2
2
  class New < Ufo::Command
3
- desc "boot_hook", "Generate boot_hook file"
3
+ desc "boot_hook", "Generate boot_hook"
4
4
  long_desc Help.text("new/boot_hook")
5
5
  BootHook.cli_options.each do |args|
6
6
  option(*args)
7
7
  end
8
- register(BootHook, "boot_hook", "boot_hook", "Generate boot_hook file")
8
+ register(BootHook, "boot_hook", "boot_hook", "Generate boot_hook")
9
9
 
10
- desc "helper", "Generate helper file"
10
+ desc "env_file", "Generate env_file"
11
+ long_desc Help.text("new/env_file")
12
+ EnvFile.cli_options.each do |args|
13
+ option(*args)
14
+ end
15
+ register(EnvFile, "env_file", "env_file", "Generate env_file")
16
+
17
+ desc "helper", "Generate helper"
11
18
  long_desc Help.text("new/helper")
12
19
  Helper.cli_options.each do |args|
13
20
  option(*args)
14
21
  end
15
- register(Helper, "helper", "helper", "Generate helper file")
22
+ register(Helper, "helper", "helper", "Generate helper")
23
+
24
+ desc "hook", "Generate hook"
25
+ long_desc Help.text("new/hook")
26
+ Hook.cli_options.each do |args|
27
+ option(*args)
28
+ end
29
+ register(Hook, "hook", "hook", "Generate hook")
16
30
  end
17
31
  end
@@ -19,10 +19,12 @@ class Ufo::Config
19
19
  return false unless config_line # default is false
20
20
  config_value = config_line.gsub(/.*=/,'').strip.gsub(/["']/,'')
21
21
  case type
22
- when :boolean
23
- config_value != "false" && config_value != "nil"
24
22
  when :array
25
23
  eval(config_value) # IE: '["a"]' => ["a"]
24
+ when :boolean
25
+ config_value != "false" && config_value != "nil"
26
+ when :string
27
+ config_value.sub(/\s+#.*/,'') # remove trailing comment
26
28
  else
27
29
  raise "Type #{type.inspect} not supported"
28
30
  end
data/lib/ufo/config.rb CHANGED
@@ -63,6 +63,10 @@ module Ufo
63
63
  config.elb.default_actions = nil # full override
64
64
  config.elb.enabled = "auto" # "auto", true or false
65
65
 
66
+ config.elb.existing = ActiveSupport::OrderedOptions.new
67
+ config.elb.existing.target_group = nil
68
+ config.elb.existing.dns_name = nil # for managed route53 records
69
+
66
70
  config.elb.health_check_interval_seconds = 10 # keep at 10 in case of network ELB, which is min 10
67
71
  config.elb.health_check_path = nil # When nil its AWS default /
68
72
  config.elb.healthy_threshold_count = 3 # The AWS usual default is 5
@@ -85,6 +89,9 @@ module Ufo
85
89
  config.exec.command = "/bin/bash" # aws ecs execute-command cli
86
90
  config.exec.enabled = true # EcsService EnableExecuteCommand
87
91
 
92
+ config.hooks = ActiveSupport::OrderedOptions.new
93
+ config.hooks.show = true
94
+
88
95
  config.layering = ActiveSupport::OrderedOptions.new
89
96
  config.layering.show = parsed_layering_show
90
97
  config.layering.show_for_commands = parsed_layering_show_for
@@ -108,11 +115,9 @@ module Ufo
108
115
  config.ps.summary = true
109
116
 
110
117
  config.secrets = ActiveSupport::OrderedOptions.new
111
- config.secrets.pattern = ActiveSupport::OrderedOptions.new
112
- config.secrets.pattern.secretsmanager = ":APP-:ENV-:SECRET_NAME" # => demo-dev-DB_PASS
113
- config.secrets.pattern.ssm = ":APP/:ENV/:SECRET_NAME" # => demo/dev/DB_PASS
118
+ config.secrets.manager_pattern = ":APP/:ENV/:SECRET_NAME" # => demo/dev/DB_PASS
119
+ config.secrets.ssm_pattern = ":APP/:ENV/:SECRET_NAME" # => demo/dev/DB_PASS
114
120
  config.secrets.provider = "ssm" # default provider for conventional expansion IE: ssm or secretsmanager
115
- config.secrets.warning = true
116
121
 
117
122
  config.ship = ActiveSupport::OrderedOptions.new
118
123
  config.ship.docker = ActiveSupport::OrderedOptions.new
@@ -2,6 +2,7 @@ module Ufo::Docker
2
2
  class Builder
3
3
  extend Memoist
4
4
  include Concerns
5
+ include Ufo::Hooks::Concern
5
6
 
6
7
  delegate :push, to: :pusher
7
8
  def self.build(options={})
@@ -28,7 +29,10 @@ module Ufo::Docker
28
29
  update_auth_token
29
30
  command = "docker build #{build_options}-t #{docker_image} -f #{@dockerfile} ."
30
31
  log = ".ufo/log/docker.log" if @options[:quiet]
31
- success = execute(command, log: log)
32
+ success = nil
33
+ run_hooks(name: "build", file: "docker.rb") do
34
+ success = execute(command, log: log)
35
+ end
32
36
  unless success
33
37
  docker_version_success = system("docker version > /dev/null 2>&1")
34
38
  unless docker_version_success
@@ -1,6 +1,7 @@
1
1
  module Ufo::Docker
2
2
  class Pusher
3
3
  include Concerns
4
+ include Ufo::Hooks::Concern
4
5
 
5
6
  delegate :docker_image, to: :builder
6
7
  attr_reader :last_image_name
@@ -17,7 +18,10 @@ module Ufo::Docker
17
18
  logger.info "Pushing Docker Image"
18
19
  command = "docker push #{last_image_name}"
19
20
  log = ".ufo/log/docker.log" if @options[:quiet]
20
- success = execute(command, log: log)
21
+ success = nil
22
+ run_hooks(name: "push", file: "docker.rb") do
23
+ success = execute(command, log: log)
24
+ end
21
25
  unless success
22
26
  logger.info "ERROR: The docker image fail to push.".color(:red)
23
27
  exit 1
@@ -0,0 +1,51 @@
1
+ module Ufo::Hooks
2
+ class Builder
3
+ extend Memoist
4
+ include Dsl
5
+ include DslEvaluator
6
+ include Ufo::Utils::Logging
7
+
8
+ attr_accessor :name
9
+ def initialize(options={})
10
+ @options = options
11
+ @file = options[:file] # IE: docker.rb
12
+ @dsl_file = "#{Ufo.root}/.ufo/config/hooks/#{@file}"
13
+ @name = options[:name].to_s
14
+ @hooks = {before: {}, after: {}}
15
+ end
16
+
17
+ def build
18
+ evaluate_file(@dsl_file)
19
+ @hooks.deep_stringify_keys!
20
+ end
21
+ memoize :build
22
+
23
+ def run_hooks
24
+ build
25
+ run_each_hook("before")
26
+ out = yield if block_given?
27
+ run_each_hook("after")
28
+ out
29
+ end
30
+
31
+ def run_each_hook(type)
32
+ hooks = @hooks.dig(type, @name) || []
33
+ hooks.each do |hook|
34
+ run_hook(type, hook)
35
+ end
36
+ end
37
+
38
+ def run_hook(type, hook)
39
+ return unless run?(hook)
40
+
41
+ id = "#{type} #{@name}"
42
+ label = " label: #{hook["label"]}" if hook["label"]
43
+ logger.info "Hook: Running #{id} hook#{label}".color(:cyan) if Ufo.config.hooks.show
44
+ Runner.new(hook).run
45
+ end
46
+
47
+ def run?(hook)
48
+ !!hook["execute"]
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,10 @@
1
+ module Ufo::Hooks
2
+ module Concern
3
+ # options example: {name: "build", file: "docker.rb"}
4
+ def run_hooks(options={}, &block)
5
+ hooks = Ufo::Hooks::Builder.new(options)
6
+ hooks.build # build hooks
7
+ hooks.run_hooks(&block)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,20 @@
1
+ module Ufo::Hooks
2
+ module Dsl
3
+ def before(*commands, **props)
4
+ commands.each do |name|
5
+ each_hook(:before, name, props)
6
+ end
7
+ end
8
+
9
+ def after(*commands, **props)
10
+ commands.each do |name|
11
+ each_hook(:after, name, props)
12
+ end
13
+ end
14
+
15
+ def each_hook(type, name, props={})
16
+ @hooks[type][name] ||= []
17
+ @hooks[type][name] << props
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,37 @@
1
+ module Ufo::Hooks
2
+ class Runner
3
+ include Ufo::Utils::Logging
4
+ include Ufo::Utils::Execute
5
+
6
+ attr_reader :hook
7
+ def initialize(hook)
8
+ @hook = hook
9
+ @execute = @hook["execute"]
10
+ end
11
+
12
+ def run
13
+ case @execute
14
+ when String
15
+ execute(@execute, exit_on_fail: @hook["exit_on_fail"])
16
+ when -> (e) { e.respond_to?(:public_instance_methods) && e.public_instance_methods.include?(:call) }
17
+ executor = @execute.new
18
+ when -> (e) { e.respond_to?(:call) }
19
+ executor = @execute
20
+ else
21
+ logger.warn "WARN: execute option not set for hook: #{@hook.inspect}"
22
+ end
23
+
24
+ return unless executor
25
+
26
+ meth = executor.method(:call)
27
+ case meth.arity
28
+ when 0
29
+ executor.call # backwards compatibility
30
+ when 1
31
+ executor.call(self)
32
+ else
33
+ raise "The #{executor} call method definition has been more than 1 arguments and is not supported"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -1,5 +1,5 @@
1
1
  module Ufo::IamRole
2
- class DSL
2
+ class Dsl
3
3
  include DslEvaluator
4
4
  include Ufo::TaskDefinition::Helpers::AwsHelper
5
5
 
@@ -11,13 +11,13 @@ module Ufo::IamRole
11
11
  def register_policy(role_type, policy_name, *statements)
12
12
  statements.flatten!
13
13
  self.policies[role_type] ||= Set.new
14
- self.policies[role_type].add([policy_name, statements]) # using set so DSL can safely be evaluated multiple times
14
+ self.policies[role_type].add([policy_name, statements]) # using set so Dsl can safely be evaluated multiple times
15
15
  end
16
16
 
17
17
  def register_managed_policy(role_type, *policies)
18
18
  policies.flatten!
19
19
  self.managed_policies[role_type] ||= Set.new
20
- self.managed_policies[role_type].merge(policies) # using set so DSL can safely be evaluated multiple times
20
+ self.managed_policies[role_type].merge(policies) # using set so Dsl can safely be evaluated multiple times
21
21
  end
22
22
  end
23
23
  end
data/lib/ufo/param.rb CHANGED
@@ -18,8 +18,7 @@ module Ufo
18
18
  memoize :data
19
19
 
20
20
  def template_scope
21
- self # TODO: add access to helpers like network
22
- # @template_scope ||= Ufo::TemplateScope.new(Ufo::DSL::Helper.new, nil)
21
+ self
23
22
  end
24
23
  end
25
24
  end
@@ -37,8 +37,7 @@ module Ufo::TaskDefinition::Helpers::Vars
37
37
  ]
38
38
  layers.map! { |l| ".ufo/env_files/#{l}#{@ext}" }
39
39
  show_layers(layers)
40
- layers.select! { |l| File.exist?(l) }
41
- layers
40
+ layers.select { |l| File.exist?(l) }
42
41
  end
43
42
 
44
43
  def show_layers(paths)
@@ -63,7 +62,8 @@ module Ufo::TaskDefinition::Helpers::Vars
63
62
 
64
63
  def env(ext='.env')
65
64
  @ext = ext # assign instance variable so dont have to pass around
66
- lines = filtered_lines(content)
65
+ result = render_erb(content) # tricky: use result instead of content for variable assignment or content method is not called
66
+ lines = filtered_lines(result)
67
67
  lines.map do |line|
68
68
  line = line.sub('export ', '') # allow user to use export. ufo ignores it
69
69
  key,*value = line.strip.split("=").map do |x|
@@ -97,7 +97,7 @@ module Ufo::TaskDefinition::Helpers::Vars
97
97
  value.sub(/^ssm:/i, "arn:aws:ssm:#{region}:#{account}:parameter/")
98
98
  when /^secretsmanager:/i
99
99
  value.sub(/^secretsmanager:/i, "arn:aws:secretsmanager:#{region}:#{account}:secret:")
100
- when '' # blank string will mean use convention
100
+ when '', *available_providers # blank string will mean use convention
101
101
  conventional_pattern(name, value)
102
102
  else
103
103
  value # assume full arn has been passed
@@ -129,11 +129,11 @@ module Ufo::TaskDefinition::Helpers::Vars
129
129
  # DB_NAME=:APP/:ENV/:SECRET_NAME # expansion will use => demo/dev/DB_NAME
130
130
  #
131
131
  def conventional_pattern(name, value)
132
- secrets = Ufo.config.secrets
133
- provider = secrets.provider # ssm or secretsmanager
132
+ provider = get_provider(value)
134
133
  namespace = provider == "ssm" ? "parameter/" : "secret:"
135
134
 
136
- config_name = "secrets.pattern.#{provider}"
135
+ field = provider == "secretsmanager" ? "manager_pattern" : "ssm_pattern"
136
+ config_name = "secrets.#{field}"
137
137
  pattern = callable_option(
138
138
  config_name: config_name, # Ufo.config.names.stack => :APP-:ROLE-:ENV => demo-web-dev
139
139
  passed_args: [self],
@@ -143,6 +143,22 @@ module Ufo::TaskDefinition::Helpers::Vars
143
143
  "arn:aws:#{provider}:#{region}:#{account}:#{namespace}#{pattern}"
144
144
  end
145
145
 
146
+ # Allows user to override one-off value. IE: DB_PASS=secretsmanager
147
+ # Note there's no point in disabling this override ability since valueFrom examples a reference.
148
+ #
149
+ # {
150
+ # "name": "PASS",
151
+ # "valueFrom": "arn:aws:ssm:us-west-2:1111111111111:parameter/demo/dev/PASS"
152
+ # }
153
+ #
154
+ def get_provider(value)
155
+ available_providers.include?(value) ? value : Ufo.config.secrets.provider
156
+ end
157
+
158
+ def available_providers
159
+ %w[ssm secretsmanager]
160
+ end
161
+
146
162
  def remove_surrounding_quotes(s)
147
163
  if s =~ /^"/ && s =~ /"$/
148
164
  s.sub(/^["]/, '').gsub(/["]$/,'') # remove surrounding double quotes
@@ -162,5 +178,12 @@ module Ufo::TaskDefinition::Helpers::Vars
162
178
  # filter out empty lines
163
179
  lines = lines.reject { |l| l.strip.empty? }
164
180
  end
181
+
182
+ def render_erb(content)
183
+ path = ".ufo/output/params.erb"
184
+ FileUtils.mkdir_p(File.dirname(path))
185
+ IO.write(path, content)
186
+ RenderMePretty.result(path, context: self)
187
+ end
165
188
  end
166
189
  end
data/lib/ufo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Ufo
2
- VERSION = "6.2.5"
2
+ VERSION = "6.3.2"
3
3
  end
@@ -12,7 +12,7 @@ describe Ufo::IamRole::Builder do
12
12
  {:Action=>["cloudwatch:PutMetricData"], :Effect=>"Allow", :Resource=>"*"}
13
13
  )
14
14
  # Called twice on purpose to show that duplicated items in the set wont create doubles.
15
- # This allows the DSL evaluate to be ran multiple times.
15
+ # This allows the Dsl evaluate to be ran multiple times.
16
16
  Ufo::IamRole::Registry.register_policy("task_role",
17
17
  "CloudwatchWrite",
18
18
  {:Action=>["cloudwatch:PutMetricData"], :Effect=>"Allow", :Resource=>"*"}
@@ -1,9 +1,9 @@
1
- describe Ufo::IamRole::DSL do
1
+ describe Ufo::IamRole::Dsl do
2
2
  let(:dsl) { described_class.new(path) }
3
3
  let(:path) { "spec/fixtures/iam_roles/task_role.rb" }
4
4
 
5
5
  context "evaluate" do
6
- it "registers policies from role DSL" do
6
+ it "registers policies from role Dsl" do
7
7
  dsl.evaluate
8
8
  expect(Ufo::IamRole::Registry.policies).not_to be_empty
9
9
  expect(Ufo::IamRole::Registry.managed_policies).not_to be_empty
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ufo
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.2.5
4
+ version: 6.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tung Nguyen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-24 00:00:00.000000000 Z
11
+ date: 2022-03-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-logs
@@ -479,7 +479,11 @@ files:
479
479
  - exe/ufo
480
480
  - lib/templates/boot_hook/.ufo/config/boot.rb
481
481
  - lib/templates/docker/Dockerfile
482
+ - lib/templates/env_file/file.env
483
+ - lib/templates/env_file/file.secrets.tt
482
484
  - lib/templates/helper/%underscore_name%_helper.rb.tt
485
+ - lib/templates/hook/docker.rb
486
+ - lib/templates/hook/ufo.rb
483
487
  - lib/templates/init/.ufo/config.rb.tt
484
488
  - lib/templates/init/.ufo/config/web/base.rb
485
489
  - lib/templates/init/.ufo/config/web/dev.rb
@@ -559,6 +563,7 @@ files:
559
563
  - lib/ufo/cli/help/init.md
560
564
  - lib/ufo/cli/help/logs.md
561
565
  - lib/ufo/cli/help/new/boot_hook.md
566
+ - lib/ufo/cli/help/new/hook.md
562
567
  - lib/ufo/cli/help/ps.md
563
568
  - lib/ufo/cli/help/releases.md
564
569
  - lib/ufo/cli/help/rollback.md
@@ -569,7 +574,9 @@ files:
569
574
  - lib/ufo/cli/new.rb
570
575
  - lib/ufo/cli/new/boot_hook.rb
571
576
  - lib/ufo/cli/new/concerns.rb
577
+ - lib/ufo/cli/new/env_file.rb
572
578
  - lib/ufo/cli/new/helper.rb
579
+ - lib/ufo/cli/new/hook.rb
573
580
  - lib/ufo/cli/new/init.rb
574
581
  - lib/ufo/cli/new/sequence.rb
575
582
  - lib/ufo/cli/opts.rb
@@ -614,6 +621,10 @@ files:
614
621
  - lib/ufo/ext.rb
615
622
  - lib/ufo/ext/core/module.rb
616
623
  - lib/ufo/ext/core/nil_class.rb
624
+ - lib/ufo/hooks/builder.rb
625
+ - lib/ufo/hooks/concern.rb
626
+ - lib/ufo/hooks/dsl.rb
627
+ - lib/ufo/hooks/runner.rb
617
628
  - lib/ufo/iam_role/builder.rb
618
629
  - lib/ufo/iam_role/dsl.rb
619
630
  - lib/ufo/iam_role/registry.rb