ufo 6.0.9 → 6.1.2

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/.cody/acceptance/bin/build.sh +8 -1
  3. data/CHANGELOG.md +25 -0
  4. data/lib/templates/boot_hook/.ufo/config/boot.rb +2 -0
  5. data/lib/templates/init/.ufo/vars/base.rb +1 -1
  6. data/lib/ufo/aws_services.rb +9 -1
  7. data/lib/ufo/cfn/stack/builder/resources/listener_ssl.rb +2 -2
  8. data/lib/ufo/cfn/stack/builder/resources/waf_association.rb +17 -0
  9. data/lib/ufo/cfn/stack/builder/resources.rb +2 -0
  10. data/lib/ufo/cfn/stack/vars.rb +2 -1
  11. data/lib/ufo/cfn/stack.rb +1 -6
  12. data/lib/ufo/cli/central/base.rb +13 -2
  13. data/lib/ufo/cli/central/clean.rb +1 -1
  14. data/lib/ufo/cli/central/update.rb +7 -8
  15. data/lib/ufo/cli/exec.rb +12 -2
  16. data/lib/ufo/cli/help/new/boot_hook.md +6 -0
  17. data/lib/ufo/cli/help.rb +0 -3
  18. data/lib/ufo/cli/new/boot_hook.rb +21 -0
  19. data/lib/ufo/cli/new.rb +7 -0
  20. data/lib/ufo/command.rb +25 -1
  21. data/lib/ufo/config/callable_option/concern.rb +11 -0
  22. data/lib/ufo/config/callable_option.rb +64 -0
  23. data/lib/ufo/config.rb +8 -7
  24. data/lib/ufo/core.rb +36 -4
  25. data/lib/ufo/iam_role/dsl.rb +1 -4
  26. data/lib/ufo/names.rb +26 -9
  27. data/lib/ufo/task_definition/helpers/acm.rb +12 -1
  28. data/lib/ufo/task_definition/helpers/{aws_data_helper.rb → aws_helper.rb} +4 -4
  29. data/lib/ufo/task_definition/helpers/ecr.rb +8 -1
  30. data/lib/ufo/task_definition/helpers/expansion.rb +11 -0
  31. data/lib/ufo/task_definition/helpers/vars.rb +8 -2
  32. data/lib/ufo/task_definition/helpers/waf.rb +28 -0
  33. data/lib/ufo/utils/call_line.rb +10 -0
  34. data/lib/ufo/utils/squeezer.rb +3 -1
  35. data/lib/ufo/version.rb +1 -1
  36. data/spec/ufo/utils/squeezer_spec.rb +21 -0
  37. data/ufo.gemspec +2 -1
  38. metadata +30 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1c4e6b0091848fd532fead2674d45a37691cd6e7305beee4bfcf9c2d3fad3ad7
4
- data.tar.gz: 0506141a6987010eac49ae0e940790de1790914f719e3459b87f45bba3117d56
3
+ metadata.gz: dbdb67f6df0ebbfb4054ad63a4fe615f1ecf52ef6141252bb4088f999b10a88c
4
+ data.tar.gz: 794d980bc87422f13acf68cbf9a323507230b9b7a479c1b58355e591122c5572
5
5
  SHA512:
6
- metadata.gz: bdd3f4a7ae8a6c258f21e88b791ad8c0afe23fa000c7c891441042dcad1a5d0f53c1e331c3090ace785513e91e7e1ddf323b4e49b65cc6f8598a54175c4d55be
7
- data.tar.gz: f8b7ed0ec7b0f2f85a7c47579b7800b406c9116e58fbb1dec603aec4d4bc0862de4d6e705433a41744b9d83918c19abc428bb15405559ab898110ea3273959cf
6
+ metadata.gz: 3cc7abf022ff951920d04a42e7eb5a5d0961f6da8a1e1f87036a790675b0cd8760df270663b0f1c8dbcd13dc31d621cead338834c458dab350805dd1b6bf32a8
7
+ data.tar.gz: dadacc6d60113cafa04841065373c1792a6129de9561408450f71d5125f98924d436e3ede2436dc40193f83db15f5c654a39c1d072507dc05e5902cf5444cc02
@@ -39,6 +39,8 @@ cat .ufo/resources/task_definitions/web.yml
39
39
  cat .ufo/vars/base.rb
40
40
  cat .ufo/vars/dev.rb
41
41
 
42
+ export UFO_ENV=qa
43
+
42
44
  # Deploy
43
45
  ufo ship -y
44
46
  # Check
@@ -57,6 +59,11 @@ Ufo.configure do |config|
57
59
  config.autoscaling.max_capacity = 3
58
60
  end
59
61
  EOF
62
+ cat << EOF > .ufo/config/web/qa.rb
63
+ Ufo.configure do |config|
64
+ config.autoscaling.max_capacity = 3
65
+ end
66
+ EOF
60
67
 
61
68
  # Update
62
69
  ufo clean -y # dont have to but good to test ufo clean
@@ -69,7 +76,7 @@ TASK=$(ufo ps --format json | jq -r '.[0].Task')
69
76
  echo "TASK $TASK"
70
77
 
71
78
  # TODO: create fargate spot cluster
72
- CLUSTER=dev
79
+ CLUSTER=qa
73
80
  # Just show for now. Might have to add wait logic to confirm new settings
74
81
  aws ecs describe-tasks --cluster $CLUSTER --tasks $TASK \
75
82
  | jq '.tasks[].containers[] | {cpu: .cpu, memory: .memory}'
data/CHANGELOG.md CHANGED
@@ -3,6 +3,31 @@
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.1.2] - 2022-03-14
7
+ - [#148](https://github.com/tongueroo/ufo/pull/148) Acm cert and user messaging improvements with config
8
+ - acm_cert helper improvements: warn when not found and only create ssl listener if cert is found
9
+ - improve squeezer: prevent infinite loop for edge case when data is [nil]
10
+ - messaging improvements: provide exact line of context of the config with the issue
11
+ - update dsl_evaluator dependency to at least 0.3.0
12
+
13
+ ## [6.1.1] - 2022-03-14
14
+ - [#146](https://github.com/tongueroo/ufo/pull/146) dont set default vpc explicitly, instead use implicit setting
15
+ - [#147](https://github.com/tongueroo/ufo/pull/147) explicitly set default vpc when not set cloudformation resources expect it
16
+ - clean up debugging puts in cli help
17
+
18
+ ## [6.1.0] - 2022-03-11
19
+ - [#136](https://github.com/tongueroo/ufo/pull/136) ufo central: dont load config
20
+ - [#137](https://github.com/tongueroo/ufo/pull/137) ufo new boot_hook generator
21
+ - [#138](https://github.com/tongueroo/ufo/pull/138) default config.autoscaling.predefined_metric_type = ECSServiceAverageMemoryUtilization
22
+ - [#139](https://github.com/tongueroo/ufo/pull/139) ELB WAF Support
23
+ - [#140](https://github.com/tongueroo/ufo/pull/140) warn user when ecr repo not found
24
+ - [#141](https://github.com/tongueroo/ufo/pull/141) check old version and show upgrade message to user
25
+ - [#142](https://github.com/tongueroo/ufo/pull/142) ufo exec: check stack exists
26
+ - [#143](https://github.com/tongueroo/ufo/pull/143) Aws helper
27
+ - [#144](https://github.com/tongueroo/ufo/pull/144) support :EXTRA in expansion and include as default for names
28
+ - [#145](https://github.com/tongueroo/ufo/pull/145) Callable options: ecs.cluster, names.stack, names.task_definition, secrets.pattern.ssm
29
+ - Ufo.app config loaded check to avoid accidental infinite loop
30
+
6
31
  ## [6.0.9] - 2022-03-10
7
32
  - fix config.autoscaling.manual_changes.warning cli help hint
8
33
 
@@ -0,0 +1,2 @@
1
+ # Docs: https://ufoships.com/docs/config/boot/
2
+ # Write your boot hook logic here
@@ -13,7 +13,7 @@
13
13
  @memory = 256
14
14
  @memory_reservation = 256
15
15
 
16
- @awslogs_group = ["ecs/#{Ufo.app}", Ufo.env, Ufo.extra].compact.join('-')
16
+ @awslogs_group = expansion("ecs/:APP-:ENV-:EXTRA")
17
17
  @awslogs_stream_prefix = role
18
18
  @awslogs_region = aws_region
19
19
 
@@ -7,6 +7,7 @@ require "aws-sdk-ecr"
7
7
  require "aws-sdk-ecs"
8
8
  require "aws-sdk-elasticloadbalancingv2"
9
9
  require "aws-sdk-ssm"
10
+ require "aws-sdk-wafv2"
10
11
 
11
12
  require "aws_mfa_secure/ext/aws" # add MFA support
12
13
  require "cfn_status"
@@ -55,11 +56,18 @@ module Ufo
55
56
  end
56
57
  memoize :elb
57
58
 
59
+ # ssm is a helper method
58
60
  def ssm_client
59
- Aws::SSM::Client.new
61
+ Aws::SSM::Client.new(aws_options)
60
62
  end
61
63
  memoize :ssm_client
62
64
 
65
+ # waf is a helper method
66
+ def waf_client
67
+ Aws::WAFV2::Client.new(aws_options)
68
+ end
69
+ memoize :waf_client
70
+
63
71
  # Override the AWS retry settings with AWS clients.
64
72
  #
65
73
  # The aws-sdk-core has exponential backup with this formula:
@@ -27,11 +27,11 @@ class Ufo::Cfn::Stack::Builder::Resources
27
27
  # nil on purpose
28
28
  def certificates
29
29
  ssl = Ufo.config.elb.ssl
30
- normalize(ssl.certificates)
30
+ normalize(ssl.certificates) if ssl.certificates
31
31
  end
32
32
 
33
33
  def normalize(*certs)
34
- certs = certs.flatten
34
+ certs = certs.flatten.compact
35
35
  certs.map do |cert|
36
36
  if cert.is_a?(String)
37
37
  {CertificateArn: cert}
@@ -0,0 +1,17 @@
1
+ # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-wafv2-webaclassociation.html
2
+ class Ufo::Cfn::Stack::Builder::Resources
3
+ class WafAssociation < Base
4
+ def build
5
+ web_acl_arn = Ufo.config.waf.web_acl_arn
6
+ return if web_acl_arn.blank?
7
+
8
+ {
9
+ Type: "AWS::WAFv2::WebACLAssociation",
10
+ Properties: {
11
+ ResourceArn: {Ref: "Elb"}, # String,
12
+ WebACLArn: web_acl_arn, # String
13
+ }
14
+ }
15
+ end
16
+ end
17
+ end
@@ -18,6 +18,8 @@ class Ufo::Cfn::Stack::Builder
18
18
  ScalingRole: Scaling::Role.build(@options),
19
19
  ScalingTarget: Scaling::Target.build(@options),
20
20
  ScalingPolicy: Scaling::Policy.build(@options),
21
+ # WAF Assocation
22
+ WafAssociation: WafAssociation.build(@options),
21
23
  }
22
24
  end
23
25
  end
@@ -59,7 +59,8 @@ class Ufo::Cfn::Stack
59
59
 
60
60
  # if the configuration is set to anything then enable it
61
61
  def create_listener_ssl?
62
- Ufo.config.elb.ssl.enabled
62
+ elb = Ufo.config.elb
63
+ elb.ssl.enabled && elb.ssl.certificates
63
64
  end
64
65
 
65
66
  def create_elb?
data/lib/ufo/cfn/stack.rb CHANGED
@@ -24,6 +24,7 @@
24
24
  module Ufo::Cfn
25
25
  class Stack < Base
26
26
  extend Memoist
27
+ include Ufo::TaskDefinition::Helpers::AwsHelper
27
28
 
28
29
  def deploy
29
30
  build
@@ -160,11 +161,5 @@ module Ufo::Cfn
160
161
  logger.info "The stack is not in a state to that is cancelable: #{stack.stack_status}"
161
162
  end
162
163
  end
163
-
164
- delegate :region, to: :aws
165
- def aws
166
- AwsData.new
167
- end
168
- memoize :aws
169
164
  end
170
165
  end
@@ -1,12 +1,23 @@
1
1
  class Ufo::CLI::Central
2
2
  class Base
3
- include Ufo::Utils::Execute
4
- include Ufo::Utils::Logging
5
3
  include Ufo::Utils::Pretty
6
4
  include Ufo::Utils::Sure
7
5
 
8
6
  def initialize(options={})
9
7
  @options = options
10
8
  end
9
+
10
+ # Do not use logger.info for ufo central commands as .ufo may not be yet setup
11
+ # We do not want any config calls to trigger a loading of the .ufo/config.rb etc
12
+ # Otherwise helper methods like ecr_repo may be called and not work yet
13
+ def log(msg)
14
+ puts msg
15
+ end
16
+
17
+ # Central has own version of execute because it doesnt have access to logger
18
+ def execute(command)
19
+ log "=> #{command}"
20
+ system command
21
+ end
11
22
  end
12
23
  end
@@ -4,7 +4,7 @@ class Ufo::CLI::Central
4
4
  path = "#{ENV['HOME']}/.ufo/central"
5
5
  sure?("Will remove folder with repo caches: #{pretty_home(path)}")
6
6
  FileUtils.rm_rf(path)
7
- logger.info "Removed: #{pretty_home(path)}"
7
+ log "Removed: #{pretty_home(path)}"
8
8
  end
9
9
  end
10
10
  end
@@ -4,7 +4,7 @@ class Ufo::CLI::Central
4
4
  validate!
5
5
  action = File.exist?(".ufo") ? "update" : "create"
6
6
  sure?("Will #{action} the .ufo symlink") # IE: Will create the .ufo symlink
7
- logger.info "Updating .ufo with #{central_repo}"
7
+ log "Updating .ufo with #{central_repo}"
8
8
  FileUtils.mkdir_p(tmp_area)
9
9
  pull
10
10
  symlink
@@ -12,7 +12,6 @@ class Ufo::CLI::Central
12
12
  end
13
13
 
14
14
  def pull
15
- logger.debug "Within #{tmp_area}"
16
15
  Dir.chdir(tmp_area) do
17
16
  if File.exist?(repo)
18
17
  execute "cd #{repo} && git pull"
@@ -36,8 +35,8 @@ class Ufo::CLI::Central
36
35
 
37
36
  report_broken_symlink
38
37
 
39
- logger.info "The .ufo symlink has been updated"
40
- logger.info "Symlink: .ufo -> #{pretty_home(src)}"
38
+ log "The .ufo symlink has been updated"
39
+ log "Symlink: .ufo -> #{pretty_home(src)}"
41
40
  end
42
41
 
43
42
  def report_broken_symlink
@@ -62,8 +61,8 @@ class Ufo::CLI::Central
62
61
 
63
62
  def validate!
64
63
  return if central_repo
65
- logger.info "ERROR: Please set the env var: UFO_CENTRAL_REPO".color(:red)
66
- logger.info "The ufo central update command requires it."
64
+ log "ERROR: Please set the env var: UFO_CENTRAL_REPO".color(:red)
65
+ log "The ufo central update command requires it."
67
66
  exit 1
68
67
  end
69
68
 
@@ -104,8 +103,8 @@ class Ufo::CLI::Central
104
103
  end
105
104
  end
106
105
  return if ok
107
- logger.info "No .ufo found in your .gitignore file".color(:yellow)
108
- logger.info <<~EOL
106
+ log "No .ufo found in your .gitignore file".color(:yellow)
107
+ log <<~EOL
109
108
  It's recommended to add .ufo to the .gitignore
110
109
  When using ufo in a central fashion
111
110
  EOL
data/lib/ufo/cli/exec.rb CHANGED
@@ -2,8 +2,18 @@ class Ufo::CLI
2
2
  class Exec < Base
3
3
  def run
4
4
  check_install!
5
+ stack = info.stack
6
+ unless stack
7
+ logger.error "Stack not found: #{@stack_name}".color(:red)
8
+ exit 1
9
+ end
10
+
5
11
  service = info.service
6
- return unless service # brand new deploy
12
+ unless service # brand new deploy
13
+ logger.error "ECS Service not yet available".color(:red)
14
+ logger.info "Try again in a little bit"
15
+ exit 1
16
+ end
7
17
 
8
18
  running = service_tasks.select do |task|
9
19
  task.last_status == "RUNNING"
@@ -18,7 +28,7 @@ class Ufo::CLI
18
28
 
19
29
  task_name = task.task_arn.split('/').last
20
30
  execute_command(
21
- cluster: "#{@cluster}",
31
+ cluster: @cluster,
22
32
  task: task_name,
23
33
  container: container(task), # only required if multiple containers in a task
24
34
  interactive: true,
@@ -0,0 +1,6 @@
1
+ ## Example
2
+
3
+ $ ufo new boot_hook
4
+ => Creating boot_hook
5
+ exist
6
+ create .ufo/config/boot.rb
data/lib/ufo/cli/help.rb CHANGED
@@ -18,13 +18,10 @@ class Ufo::CLI
18
18
  list.sort! { |a, b| a[0] <=> b[0] }
19
19
  filter = Proc.new do |command, desc|
20
20
  command = command.sub(/ --.*/,'')
21
- puts "command #{command.inspect}".color(:yellow)
22
21
  detected = main_commands.detect do |c|
23
22
  expr = "^ufo #{c}$"
24
- puts "expr #{expr}"
25
23
  command =~ Regexp.new(expr)
26
24
  end
27
- puts "detected #{detected.inspect}"
28
25
  detected
29
26
  end
30
27
  main = list.select(&filter)
@@ -0,0 +1,21 @@
1
+ class Ufo::CLI::New
2
+ class BootHook < Sequence
3
+ def self.cli_options
4
+ [
5
+ [:force, type: :boolean, desc: "Bypass overwrite are you sure prompt for existing files"],
6
+ ]
7
+ end
8
+ cli_options.each do |args|
9
+ class_option(*args)
10
+ end
11
+
12
+ def set_source
13
+ set_template_source "boot_hook"
14
+ end
15
+
16
+ def create_helper
17
+ logger.info "=> Creating boot_hook"
18
+ directory ".", "."
19
+ end
20
+ end
21
+ end
data/lib/ufo/cli/new.rb CHANGED
@@ -1,5 +1,12 @@
1
1
  class Ufo::CLI
2
2
  class New < Ufo::Command
3
+ desc "boot_hook", "Generate boot_hook file"
4
+ long_desc Help.text("new/boot_hook")
5
+ BootHook.cli_options.each do |args|
6
+ option(*args)
7
+ end
8
+ register(BootHook, "boot_hook", "boot_hook", "Generate boot_hook file")
9
+
3
10
  desc "helper", "Generate helper file"
4
11
  long_desc Help.text("new/helper")
5
12
  Helper.cli_options.each do |args|
data/lib/ufo/command.rb CHANGED
@@ -38,8 +38,16 @@ module Ufo
38
38
  # loads Ufo.config and Ufo::Config#load_project_config
39
39
  # This requires Ufo.role.
40
40
  # So we set Ufo.role before triggering Ufo.config loading
41
- configure_dsl_evaluator
42
41
  check_project!(args)
42
+ check_old_version_structure!(args)
43
+ # Special case for `ufo central` commands.
44
+ # Dont want to call configure_dsl_evaluator
45
+ # and trigger loading of config => .ufo/config.rb
46
+ # Also, using ARGV instead of args because args is called by thor in multiple passes
47
+ # For `ufo central update`:
48
+ # * 1st pass: "central"
49
+ # * 2nd pass: "update"
50
+ configure_dsl_evaluator unless ARGV[0] == "central"
43
51
 
44
52
  # Allow calling for help via:
45
53
  # ufo command help
@@ -98,6 +106,22 @@ module Ufo
98
106
  ENV['UFO_TEST'] ? raise : exit(1)
99
107
  end
100
108
 
109
+ def check_old_version_structure!(args)
110
+ return unless File.exist?('.ufo/settings.yml')
111
+ puts "ERROR: Old .ufo configurations detected".color(:red)
112
+ puts <<~EOL
113
+ It looks like this project .ufo files for an older ufo version.
114
+ The old .ufo structure does not work with this version of ufo.
115
+
116
+ Current Installed UFO Version: 6.0.9
117
+
118
+ Please upgrade.
119
+
120
+ See: https://ufoships.com/docs/upgrading/upgrade6/
121
+ EOL
122
+ exit 1
123
+ end
124
+
101
125
  # Override command_help to include the description at the top of the
102
126
  # long_description.
103
127
  def command_help(shell, command_name)
@@ -0,0 +1,11 @@
1
+ class Ufo::Config::CallableOption
2
+ module Concern
3
+ def callable_option(options={})
4
+ callable_option = Ufo::Config::CallableOption.new(
5
+ config_name: options[:config_name],
6
+ passed_args: options[:passed_args],
7
+ )
8
+ callable_option.object
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,64 @@
1
+ # Class represents a config option that is possibly callable. Examples:
2
+ #
3
+ # config.names.stack
4
+ # config.names.task_definition
5
+ #
6
+ # Abstraction is definitely obtuse. Using it to get rid of duplication.
7
+ #
8
+ class Ufo::Config
9
+ class CallableOption
10
+ include Ufo::Utils::Logging
11
+
12
+ def initialize(options={})
13
+ @options = options
14
+ # Example:
15
+ # config_name: names.stack
16
+ # config_value: Ufo.config.names.stack
17
+ # args: [self] # passed to object.call
18
+ @config_name = options[:config_name]
19
+ @config_value = options[:config_value] || inferred_config_value
20
+ @config_name = "config.#{@config_name}" unless @config_name.include?("config.")
21
+ @passed_args = options[:passed_args]
22
+ end
23
+
24
+ def inferred_config_value
25
+ args = @options[:config_name].split('.').map(&:to_sym) # @options before @config_name is adjust to have full config name
26
+ Ufo.config.dig(*args)
27
+ end
28
+
29
+ # Returns either an Array or nil
30
+ def object
31
+ case @config_value
32
+ when nil
33
+ return nil
34
+ when Array, String
35
+ return @config_value
36
+ when -> (c) { c.respond_to?(:public_instance_methods) && c.public_instance_methods.include?(:call) }
37
+ object= @config_value.new
38
+ when -> (c) { c.respond_to?(:call) }
39
+ object = @config_value
40
+ else
41
+ raise "Invalid option for #{@config_name}"
42
+ end
43
+
44
+ if object
45
+ result = @passed_args.empty? ? object.call : object.call(*@passed_args)
46
+ valid_classes = [Array, String, NilClass]
47
+ valid_classes_help = valid_classes
48
+ valid_classes_help[-1] = "or #{valid_classes_help[-1]}"
49
+ valid_classes_help = valid_classes.join(', ')
50
+ unless valid_classes.include?(result.class)
51
+ message = "ERROR: The #{@config_name} needs to return an #{valid_classes_help}"
52
+ logger.info message.color(:red)
53
+ logger.info <<~EOL
54
+ The #{@config_name} when assigned a class, object, or proc must implement
55
+ The call method and return an #{valid_classes_help}.
56
+ The current return value is a #{result.class}
57
+ EOL
58
+ exit 1
59
+ end
60
+ end
61
+ result
62
+ end
63
+ end
64
+ end
data/lib/ufo/config.rb CHANGED
@@ -26,7 +26,7 @@ module Ufo
26
26
  config.autoscaling.manual_changes.warning = true
27
27
  config.autoscaling.max_capacity = 5 # dont use max thats an OrderedOptions method
28
28
  config.autoscaling.min_capacity = 1 # dont use min thats an OrderedOptions method
29
- config.autoscaling.predefined_metric_type = "ECSServiceAverageCPUUtilization"
29
+ config.autoscaling.predefined_metric_type = "ECSServiceAverageMemoryUtilization"
30
30
  config.autoscaling.scale_in_cooldown = nil
31
31
  config.autoscaling.scale_out_cooldown = nil
32
32
  config.autoscaling.target_value = 75.0
@@ -95,8 +95,8 @@ module Ufo
95
95
  config.logs.filter_pattern = nil
96
96
 
97
97
  config.names = ActiveSupport::OrderedOptions.new
98
- config.names.stack = ":APP-:ROLE-:ENV" # => demo-web-dev
99
- config.names.task_definition = ":APP-:ROLE-:ENV" # => demo-web-dev
98
+ config.names.stack = ":APP-:ROLE-:ENV-:EXTRA" # => demo-web-dev
99
+ config.names.task_definition = ":APP-:ROLE-:ENV-:EXTRA" # => demo-web-dev
100
100
 
101
101
  config.ps = ActiveSupport::OrderedOptions.new
102
102
  config.ps.format = "auto" # CliFormat.default_format
@@ -116,6 +116,9 @@ module Ufo
116
116
  config.state = ActiveSupport::OrderedOptions.new
117
117
  config.state.reminder = true
118
118
 
119
+ config.waf = ActiveSupport::OrderedOptions.new
120
+ config.waf.web_acl_arn = nil
121
+
119
122
  # When not set, the default vpc is used
120
123
  config.vpc = ActiveSupport::OrderedOptions.new
121
124
  config.vpc.id = nil
@@ -184,14 +187,12 @@ module Ufo
184
187
  add_ext!(paths)
185
188
  end
186
189
 
187
- def add_ext!(paths)
188
- ext = "rb"
190
+ def add_ext!(paths)
189
191
  paths.map! do |path|
190
192
  path = path.sub(/\/$/,'') if path.ends_with?('/')
191
193
  "#{path}.rb"
192
194
  end
193
195
  paths
194
196
  end
195
-
196
- end
197
+ end
197
198
  end
data/lib/ufo/core.rb CHANGED
@@ -4,13 +4,31 @@ require 'yaml'
4
4
  module Ufo
5
5
  module Core
6
6
  extend Memoist
7
+ include Ufo::Utils::Pretty
7
8
 
8
9
  def role
9
10
  ENV['UFO_ROLE'] || 'web'
10
11
  end
11
12
 
12
13
  def app
13
- ENV['UFO_APP'] || config.app
14
+ return ENV['UFO_APP'] if ENV['UFO_APP']
15
+
16
+ if @@config_loaded
17
+ config.app
18
+ else
19
+ puts "ERROR: Using Ufo.app or :APP expansion very early in the UFO boot process".color(:red)
20
+ puts <<~EOL.color(:red)
21
+ The Ufo.app or :APP expansions are not yet available at this point
22
+ You can either:
23
+
24
+ 1. Use the UFO_APP env var to set it, which allows it to be used.
25
+ 2. Hard code your actual app name.
26
+
27
+ EOL
28
+ call_line = caller.find {|l| l.include?('.ufo') }
29
+ DslEvaluator.print_code(call_line)
30
+ exit 1
31
+ end
14
32
  end
15
33
 
16
34
  # v5: development is default
@@ -40,17 +58,31 @@ module Ufo
40
58
  Config.instance.configure(&block)
41
59
  end
42
60
 
43
- # Generally, use the Lono.config instead of Config.instance.config since it guarantees the load_project_config call
61
+ # Checking whether or not the config has been loaded and saving it to @@config_loaded
62
+ # because users can call helper methods in `.ufo/config.rb` files that rely on the config
63
+ # already being loaded. This would produce an infinite loop. The @@config_loaded allows
64
+ # methods to use this info to prevent an infinite loop.
65
+ # Notable methods that use this: Ufo.app and Ufo.logger
66
+ cattr_accessor :config_loaded
67
+ # In general, use the Ufo.config instead of Config.instance.config since it guarantees the load_project_config call
44
68
  def config
45
69
  Config.instance.load_project_config
70
+ @@config_loaded = true
46
71
  Config.instance.config
47
72
  end
48
73
  memoize :config
49
74
 
50
- # allow different logger when running up all or rspec-lono
75
+ # Allow different logger when running up all or rspec-lono
51
76
  cattr_writer :logger
52
77
  def logger
53
- @@logger ||= config.logger
78
+ if @@config_loaded
79
+ @@logger = config.logger
80
+ else
81
+ # When .ufo/config.rb is not yet loaded. IE: a helper method like waf
82
+ # gets called in the .ufo/config.rb itself and uses the logger.
83
+ # This avoids an infinite loop. Note: It does create a different Logger
84
+ @@logger ||= Logger.new(ENV['UFO_LOG_PATH'] || $stderr)
85
+ end
54
86
  end
55
87
  end
56
88
  end
@@ -1,6 +1,7 @@
1
1
  module Ufo::IamRole
2
2
  class DSL
3
3
  include DslEvaluator
4
+ include Ufo::TaskDefinition::Helpers::AwsHelper
4
5
 
5
6
  def initialize(path)
6
7
  @path = path # IE: .ufo/iam_roles/task_role.rb
@@ -19,9 +20,5 @@ module Ufo::IamRole
19
20
  role_type = File.basename(@path).sub('.rb','') # task_role or execution_role
20
21
  Registry.register_managed_policy(role_type, policies)
21
22
  end
22
-
23
- def aws
24
- AwsData.new
25
- end
26
23
  end
27
24
  end
data/lib/ufo/names.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  module Ufo
2
2
  class Names
3
3
  extend Memoist
4
+ include Ufo::TaskDefinition::Helpers::AwsHelper
5
+ include Ufo::Config::CallableOption::Concern
4
6
 
5
7
  attr_reader :role
6
8
  def initialize
@@ -8,18 +10,35 @@ module Ufo
8
10
  end
9
11
 
10
12
  def cluster
11
- expansion(Ufo.config.ecs.cluster) # IE: :ENV => dev
13
+ string = callable_option(
14
+ config_name: "ecs.cluster", # Ufo.ecs.cluster => :ENV => dev
15
+ passed_args: [self],
16
+ )
17
+ expansion(string) # IE: :ENV => dev
12
18
  end
13
19
  memoize :cluster
14
20
 
21
+ # Examples:
22
+ # When UFO_EXTRA not set: :APP-:ROLE-:ENV-:EXTRA => demo-web-dev
23
+ # When UFO_EXTRA=1: :APP-:ROLE-:ENV-:EXTRA => demo-web-dev-2
15
24
  def stack
16
- name = expansion(Ufo.config.names.stack) # IE: :APP-:ROLE-:ENV => demo-web-dev
17
- [name, Ufo.extra].compact.join('-')
25
+ string = callable_option(
26
+ config_name: "names.stack", # Ufo.config.names.stack => :APP-:ROLE-:ENV => demo-web-dev
27
+ passed_args: [self],
28
+ )
29
+ expansion(string) # IE: :APP-:ROLE-:ENV => demo-web-dev
18
30
  end
19
31
  memoize :stack
20
32
 
33
+ # Examples:
34
+ # When UFO_EXTRA not set: :APP-:ROLE-:ENV-:EXTRA => demo-web-dev
35
+ # When UFO_EXTRA=1: :APP-:ROLE-:ENV-:EXTRA => demo-web-dev-2
21
36
  def task_definition
22
- expansion(Ufo.config.names.task_definition) # IE: :APP-:ROLE-:ENV => demo-web-dev
37
+ string = callable_option(
38
+ config_name: "names.task_definition", # Ufo.config.names.task_definition => :APP-:ROLE-:ENV => demo-web-dev
39
+ passed_args: [self],
40
+ )
41
+ expansion(string) # IE: :APP-:ROLE-:ENV => demo-web-dev
23
42
  end
24
43
  memoize :task_definition
25
44
 
@@ -27,7 +46,7 @@ module Ufo
27
46
  return string unless string.is_a?(String) # in case of nil
28
47
 
29
48
  string = string.dup
30
- vars = string.scan(/:\w+/) # => [":APP", ":ROLE", :ENV"]
49
+ vars = string.scan(/:\w+/) # => [":APP", ":ROLE", :ENV", ":EXTRA"]
31
50
  vars.each do |var|
32
51
  string.gsub!(var, var_value(var))
33
52
  end
@@ -60,10 +79,8 @@ module Ufo
60
79
  end
61
80
  alias_method :ufo_env, :env
62
81
 
63
- delegate :region, to: :aws
64
- def aws
65
- AwsData.new
82
+ def extra
83
+ Ufo.extra
66
84
  end
67
- memoize :aws
68
85
  end
69
86
  end
@@ -1,12 +1,23 @@
1
1
  module Ufo::TaskDefinition::Helpers
2
2
  module Acm
3
+ include Ufo::Utils::CallLine
4
+ include Ufo::Utils::Pretty
5
+
3
6
  # returns cert arn
4
7
  def acm_cert(domain)
5
8
  certs = acm_certs
6
9
  cert = certs.find do |c|
7
10
  c.domain_name == domain
8
11
  end
9
- cert.certificate_arn if cert
12
+ if cert
13
+ cert.certificate_arn
14
+ else
15
+ # Logger causes infinite loop when waf helper used in .ufo/
16
+ logger.warn "WARN: ACM cert not found: #{domain}".color(:yellow)
17
+ call_line = ufo_config_call_line
18
+ DslEvaluator.print_code(call_line)
19
+ nil
20
+ end
10
21
  end
11
22
 
12
23
  # TODO: handle when there are lots of certs by paging
@@ -1,18 +1,18 @@
1
1
  module Ufo::TaskDefinition::Helpers
2
- module AwsDataHelper
2
+ module AwsHelper
3
3
  extend Memoist
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- delegate :account, :region, to: :aws_data
7
+ delegate :account, :region, to: :aws
8
8
  alias_method :aws_region, :region
9
9
  alias_method :current_region, :region
10
10
  end
11
11
 
12
12
  # Duplicated in vars.rb
13
- def aws_data
13
+ def aws
14
14
  AwsData.new
15
15
  end
16
- memoize :aws_data
16
+ memoize :aws
17
17
  end
18
18
  end
@@ -1,13 +1,20 @@
1
1
  module Ufo::TaskDefinition::Helpers
2
2
  module Ecr
3
+ include Ufo::Utils::CallLine
4
+
3
5
  def ecr_repo(name)
4
6
  repository = ecr_repository(name)
5
- repository.repository_uri
7
+ repository.repository_uri if repository
6
8
  end
7
9
 
8
10
  def ecr_repository(name)
9
11
  resp = ecr.describe_repositories(repository_names: [name])
10
12
  resp.repositories.first
13
+ rescue Aws::ECR::Errors::RepositoryNotFoundException => e
14
+ logger.warn "WARN: #{e.class} #{e.message}".color(:yellow)
15
+ call_line = ufo_config_call_line
16
+ DslEvaluator.print_code(call_line)
17
+ nil
11
18
  end
12
19
  end
13
20
  end
@@ -0,0 +1,11 @@
1
+ module Ufo::TaskDefinition::Helpers
2
+ module Expansion
3
+ include Ufo::Concerns::Names
4
+
5
+ # Note: vars expansion is different than the TaskDefinition expansion helper
6
+ # See: Ufo::TaskDefinition::Helpers::Vars#expansion
7
+ def expansion(string)
8
+ names.expansion(string) # dasherize: false. dont turn SECRET_NAME => SECRET-NAME
9
+ end
10
+ end
11
+ end
@@ -3,9 +3,10 @@ require "aws_data"
3
3
  module Ufo::TaskDefinition::Helpers
4
4
  class Vars
5
5
  extend Memoist
6
- include AwsDataHelper
6
+ include AwsHelper
7
7
  include Ufo::Concerns::Names
8
8
  include Ufo::Utils::Pretty
9
+ include Ufo::Config::CallableOption::Concern
9
10
 
10
11
  def initialize(options={})
11
12
  # use either file or text. text takes higher precedence
@@ -89,7 +90,12 @@ module Ufo::TaskDefinition::Helpers
89
90
  secrets = Ufo.config.secrets
90
91
  provider = secrets.provider # ssm or secretsmanager
91
92
  namespace = provider == "ssm" ? "parameter/" : "secret:"
92
- pattern = secrets.pattern[provider] # IE: Ufo.config.secrets.pattern.ssm => :APP/:ENV/:SECRET_NAME
93
+
94
+ config_name = "secrets.pattern.#{provider}"
95
+ pattern = callable_option(
96
+ config_name: config_name, # Ufo.config.names.stack => :APP-:ROLE-:ENV => demo-web-dev
97
+ passed_args: [self],
98
+ )
93
99
  # replace :SECRET_NAME since names expand doesnt know how to nor do we want to add logic there
94
100
  pattern = pattern.sub(':SECRET_NAME', name)
95
101
  "arn:aws:#{provider}:#{region}:#{account}:#{namespace}#{pattern}"
@@ -0,0 +1,28 @@
1
+ module Ufo::TaskDefinition::Helpers
2
+ module Waf
3
+ include Ufo::Utils::CallLine
4
+ include Ufo::Utils::Pretty
5
+
6
+ # Waf names are uniq within their scope. Tested with AWS console
7
+ # Only use regional since this is for ELB support
8
+ # Returns waf arn
9
+ def waf(name, options={})
10
+ resp = waf_client.list_web_acls(
11
+ scope: "REGIONAL", # required, accepts CLOUDFRONT, REGIONAL
12
+ # next_marker: "NextMarker",
13
+ # limit: 1,
14
+ )
15
+ web_acl = resp.web_acls.find do |acl|
16
+ acl.name == name
17
+ end
18
+ if web_acl
19
+ web_acl.arn
20
+ else
21
+ # Logger causes infinite loop when waf helper used in .ufo/
22
+ logger.warn "WARN: Web ACL not found: #{name}".color(:yellow)
23
+ call_line = ufo_config_call_line
24
+ DslEvaluator.print_code(call_line)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,10 @@
1
+ module Ufo::Utils
2
+ module CallLine
3
+ include Pretty
4
+
5
+ def ufo_config_call_line
6
+ caller.find { |l| l.include?('.ufo/') }
7
+ end
8
+ end
9
+ end
10
+
@@ -9,7 +9,9 @@ module Ufo::Utils
9
9
 
10
10
  case data
11
11
  when Array
12
- data.map! { |v| squeeze(v) }
12
+ # .compact prevents infinite loop when data = [nil] on accident
13
+ # IE: config.elb.ssl.certificates = acm_cert("domain.not.found")
14
+ data.compact.map! { |v| squeeze(v) }
13
15
  when Hash
14
16
  data.each_with_object({}) do |(k,v), squeezed|
15
17
  # only remove nil and empty Array values within Hash structures
data/lib/ufo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Ufo
2
- VERSION = "6.0.9"
2
+ VERSION = "6.1.2"
3
3
  end
@@ -0,0 +1,21 @@
1
+ describe Ufo::Utils::Squeezer do
2
+ subject { Ufo::Utils::Squeezer.new(data) }
3
+
4
+ context("Array with nil") do
5
+ let(:data) { [nil] }
6
+ # Prevents infinite loop
7
+ it "remove nil" do
8
+ squeezed = subject.squeeze
9
+ expect(squeezed).to eq []
10
+ end
11
+ end
12
+
13
+ context("Hash with nil value") do
14
+ let(:data) { {a: 1, b: nil } }
15
+ # Prevents infinite loop
16
+ it "remove nil" do
17
+ squeezed = subject.squeeze
18
+ expect(squeezed).to eq(a: 1)
19
+ end
20
+ end
21
+ end
data/ufo.gemspec CHANGED
@@ -29,11 +29,12 @@ Gem::Specification.new do |spec|
29
29
  spec.add_dependency "aws-sdk-ecs"
30
30
  spec.add_dependency "aws-sdk-elasticloadbalancingv2"
31
31
  spec.add_dependency "aws-sdk-ssm"
32
+ spec.add_dependency "aws-sdk-wafv2"
32
33
  spec.add_dependency "aws_data"
33
34
  spec.add_dependency "cfn-status"
34
35
  spec.add_dependency "cli-format"
35
36
  spec.add_dependency "deep_merge"
36
- spec.add_dependency "dsl_evaluator", ">= 0.2.5" # for DslEvaluator.print_code
37
+ spec.add_dependency "dsl_evaluator", ">= 0.3.0" # for DslEvaluator.print_code
37
38
  spec.add_dependency "memoist"
38
39
  spec.add_dependency "plissken"
39
40
  spec.add_dependency "rainbow"
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.0.9
4
+ version: 6.1.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-10 00:00:00.000000000 Z
11
+ date: 2022-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-logs
@@ -164,6 +164,20 @@ dependencies:
164
164
  - - ">="
165
165
  - !ruby/object:Gem::Version
166
166
  version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: aws-sdk-wafv2
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
167
181
  - !ruby/object:Gem::Dependency
168
182
  name: aws_data
169
183
  requirement: !ruby/object:Gem::Requirement
@@ -226,14 +240,14 @@ dependencies:
226
240
  requirements:
227
241
  - - ">="
228
242
  - !ruby/object:Gem::Version
229
- version: 0.2.5
243
+ version: 0.3.0
230
244
  type: :runtime
231
245
  prerelease: false
232
246
  version_requirements: !ruby/object:Gem::Requirement
233
247
  requirements:
234
248
  - - ">="
235
249
  - !ruby/object:Gem::Version
236
- version: 0.2.5
250
+ version: 0.3.0
237
251
  - !ruby/object:Gem::Dependency
238
252
  name: memoist
239
253
  requirement: !ruby/object:Gem::Requirement
@@ -442,6 +456,7 @@ files:
442
456
  - README.md
443
457
  - Rakefile
444
458
  - exe/ufo
459
+ - lib/templates/boot_hook/.ufo/config/boot.rb
445
460
  - lib/templates/docker/Dockerfile
446
461
  - lib/templates/helper/%underscore_name%_helper.rb.tt
447
462
  - lib/templates/init/.ufo/config.rb.tt
@@ -487,6 +502,7 @@ files:
487
502
  - lib/ufo/cfn/stack/builder/resources/target_group.rb
488
503
  - lib/ufo/cfn/stack/builder/resources/task_definition.rb
489
504
  - lib/ufo/cfn/stack/builder/resources/task_definition/reconstructor.rb
505
+ - lib/ufo/cfn/stack/builder/resources/waf_association.rb
490
506
  - lib/ufo/cfn/stack/custom_properties.rb
491
507
  - lib/ufo/cfn/stack/params.rb
492
508
  - lib/ufo/cfn/stack/status.rb
@@ -520,6 +536,7 @@ files:
520
536
  - lib/ufo/cli/help/help.md
521
537
  - lib/ufo/cli/help/init.md
522
538
  - lib/ufo/cli/help/logs.md
539
+ - lib/ufo/cli/help/new/boot_hook.md
523
540
  - lib/ufo/cli/help/ps.md
524
541
  - lib/ufo/cli/help/releases.md
525
542
  - lib/ufo/cli/help/rollback.md
@@ -528,6 +545,7 @@ files:
528
545
  - lib/ufo/cli/help/stop.md
529
546
  - lib/ufo/cli/logs.rb
530
547
  - lib/ufo/cli/new.rb
548
+ - lib/ufo/cli/new/boot_hook.rb
531
549
  - lib/ufo/cli/new/concerns.rb
532
550
  - lib/ufo/cli/new/helper.rb
533
551
  - lib/ufo/cli/new/init.rb
@@ -552,6 +570,8 @@ files:
552
570
  - lib/ufo/concerns/autoscaling.rb
553
571
  - lib/ufo/concerns/names.rb
554
572
  - lib/ufo/config.rb
573
+ - lib/ufo/config/callable_option.rb
574
+ - lib/ufo/config/callable_option/concern.rb
555
575
  - lib/ufo/config/inits.rb
556
576
  - lib/ufo/core.rb
557
577
  - lib/ufo/docker/builder.rb
@@ -587,19 +607,22 @@ files:
587
607
  - lib/ufo/task_definition/erb/yaml.rb
588
608
  - lib/ufo/task_definition/helpers.rb
589
609
  - lib/ufo/task_definition/helpers/acm.rb
590
- - lib/ufo/task_definition/helpers/aws_data_helper.rb
610
+ - lib/ufo/task_definition/helpers/aws_helper.rb
591
611
  - lib/ufo/task_definition/helpers/core.rb
592
612
  - lib/ufo/task_definition/helpers/ecr.rb
613
+ - lib/ufo/task_definition/helpers/expansion.rb
593
614
  - lib/ufo/task_definition/helpers/ssm.rb
594
615
  - lib/ufo/task_definition/helpers/ssm/fetcher.rb
595
616
  - lib/ufo/task_definition/helpers/stack_output.rb
596
617
  - lib/ufo/task_definition/helpers/vars.rb
597
618
  - lib/ufo/task_definition/helpers/vpc.rb
619
+ - lib/ufo/task_definition/helpers/waf.rb
598
620
  - lib/ufo/upgrade/params.yml
599
621
  - lib/ufo/upgrade/upgrade3.rb
600
622
  - lib/ufo/upgrade/upgrade33to34.rb
601
623
  - lib/ufo/upgrade/upgrade4.rb
602
624
  - lib/ufo/upgrade/upgrade43to45.rb
625
+ - lib/ufo/utils/call_line.rb
603
626
  - lib/ufo/utils/execute.rb
604
627
  - lib/ufo/utils/logging.rb
605
628
  - lib/ufo/utils/pretty.rb
@@ -630,6 +653,7 @@ files:
630
653
  - spec/ufo/iam_role/builder_spec.rb
631
654
  - spec/ufo/iam_role/dsl_spec.rb
632
655
  - spec/ufo/logs_spec.rb
656
+ - spec/ufo/utils/squeezer_spec.rb
633
657
  - ufo.gemspec
634
658
  homepage: http://ufoships.com
635
659
  licenses:
@@ -676,3 +700,4 @@ test_files:
676
700
  - spec/ufo/iam_role/builder_spec.rb
677
701
  - spec/ufo/iam_role/dsl_spec.rb
678
702
  - spec/ufo/logs_spec.rb
703
+ - spec/ufo/utils/squeezer_spec.rb