ufo 6.0.9 → 6.1.2

Sign up to get free protection for your applications and to get access to all the features.
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