ufo 3.3.2 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/Gemfile.lock +9 -7
  4. data/docs/README.md +1 -2
  5. data/docs/_docs/params.md +72 -0
  6. data/docs/_docs/settings.md +1 -6
  7. data/docs/_docs/tutorial-ufo-init.md +0 -5
  8. data/docs/_docs/ufo-env.md +1 -1
  9. data/docs/_includes/subnav.html +1 -0
  10. data/docs/_reference/ufo-deploy.md +9 -1
  11. data/docs/_reference/ufo-init.md +30 -9
  12. data/docs/_reference/ufo-task.md +11 -12
  13. data/docs/_reference/ufo-upgrade3_3_to_3_4.md +23 -0
  14. data/docs/reference.md +1 -0
  15. data/lib/template/.ufo/params.yml.tt +65 -0
  16. data/lib/template/.ufo/settings.yml.tt +0 -4
  17. data/lib/template/.ufo/templates/fargate.json.erb +37 -0
  18. data/lib/template/.ufo/variables/base.rb.tt +15 -0
  19. data/lib/template/.ufo/variables/development.rb +1 -1
  20. data/lib/ufo.rb +3 -1
  21. data/lib/ufo/cli.rb +7 -3
  22. data/lib/ufo/default/settings.yml +0 -4
  23. data/lib/ufo/destroy.rb +1 -1
  24. data/lib/ufo/docker/builder.rb +1 -5
  25. data/lib/ufo/docker/cleaner.rb +1 -2
  26. data/lib/ufo/docker/pusher.rb +1 -5
  27. data/lib/ufo/dsl.rb +1 -1
  28. data/lib/ufo/dsl/helper.rb +3 -8
  29. data/lib/ufo/dsl/task_definition.rb +7 -45
  30. data/lib/ufo/ecr/cleaner.rb +2 -2
  31. data/lib/ufo/help/deploy.md +9 -1
  32. data/lib/ufo/help/init.md +18 -0
  33. data/lib/ufo/help/task.md +6 -6
  34. data/lib/ufo/init.rb +9 -2
  35. data/lib/ufo/param.rb +24 -0
  36. data/lib/ufo/scale.rb +1 -1
  37. data/lib/ufo/sequence.rb +14 -0
  38. data/lib/ufo/ship.rb +6 -6
  39. data/lib/ufo/task.rb +9 -1
  40. data/lib/ufo/tasks/register.rb +21 -1
  41. data/lib/ufo/template_scope.rb +45 -0
  42. data/lib/ufo/upgrade/params.yml +47 -0
  43. data/lib/ufo/upgrade33_to_34.rb +32 -0
  44. data/lib/ufo/util.rb +25 -0
  45. data/lib/ufo/version.rb +1 -1
  46. data/spec/fixtures/settings.yml +0 -4
  47. data/spec/lib/setting_spec.rb +3 -3
  48. data/spec/spec_helper.rb +1 -1
  49. data/ufo.gemspec +1 -0
  50. metadata +25 -4
  51. data/lib/template/.ufo/variables/base.rb +0 -6
  52. data/lib/ufo/default.rb +0 -40
@@ -0,0 +1,15 @@
1
+ # Example ufo/variables/base.rb
2
+ # More info on how variables work: http://ufoships.com/docs/variables/
3
+ @image = helper.full_image_name # includes the git sha tongueroo/hi:ufo-[sha].
4
+ @environment = helper.env_file(".env")
5
+ <% if @options[:launch_type] == "fargate" -%>
6
+ # Ensure that the cpu and memory values are a supported combination by Fargate.
7
+ # More info: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-cpu-memory-error.html"
8
+ <% end -%>
9
+ @cpu = 256
10
+ @memory = 512
11
+ @memory_reservation = 256
12
+
13
+ <% if @execution_role_arn_input -%>
14
+ @execution_role_arn = "<%= @execution_role_arn_input %>"
15
+ <% end -%>
@@ -1,6 +1,6 @@
1
1
  # Example ufo/variables/development.rb
2
2
  # More info on how variables work: http://ufoships.com/docs/variables/
3
- @cpu = 192
3
+ @cpu = 256
4
4
  @environment = helper.env_vars(%Q[
5
5
  RAILS_ENV=development
6
6
  SECRET_KEY_BASE=secret
data/lib/ufo.rb CHANGED
@@ -7,7 +7,6 @@ require 'render_me_pretty'
7
7
 
8
8
  module Ufo
9
9
  autoload :Core, 'ufo/core'
10
- autoload :Default, 'ufo/default'
11
10
  autoload :AwsService, 'ufo/aws_service'
12
11
  autoload :Command, 'ufo/command'
13
12
  autoload :Setting, 'ufo/setting'
@@ -23,6 +22,8 @@ module Ufo
23
22
  autoload :Scale, 'ufo/scale'
24
23
  autoload :LogGroup, 'ufo/log_group'
25
24
  autoload :ECS, 'ufo/ecs'
25
+ autoload :Param, 'ufo/param'
26
+ autoload :TemplateScope, 'ufo/template_scope'
26
27
 
27
28
  autoload :Docker, 'ufo/docker'
28
29
  autoload :Ecr, 'ufo/ecr'
@@ -30,6 +31,7 @@ module Ufo
30
31
  autoload :Completion, "ufo/completion"
31
32
  autoload :Completer, "ufo/completer"
32
33
  autoload :Upgrade3, "ufo/upgrade3"
34
+ autoload :Upgrade33_to_34, "ufo/upgrade33_to_34"
33
35
 
34
36
  extend Core
35
37
  end
@@ -77,9 +77,9 @@ module Ufo
77
77
  desc "task TASK_DEFINITION", "Run a one-time task."
78
78
  long_desc Help.text(:task)
79
79
  option :docker, type: :boolean, desc: "Enable docker build and push", default: true
80
- option :command, type: :array, desc: "Override the command used for the container"
80
+ option :command, type: :array, aliases: 'c', desc: "Override the command used for the container"
81
81
  def task(task_definition)
82
- Docker::Builder.build(options)
82
+ Docker::Builder.build(options) if @options[:docker]
83
83
  Tasks::Builder.ship(task_definition, options)
84
84
  Task.new(task_definition, options).run
85
85
  end
@@ -111,11 +111,15 @@ module Ufo
111
111
  end
112
112
 
113
113
  desc "upgrade3", "Upgrade from version 2 to 3."
114
- long_desc Help.text("upgrade3")
115
114
  def upgrade3
116
115
  Upgrade3.new(options).run
117
116
  end
118
117
 
118
+ desc "upgrade3_3_to_3_4", "Upgrade from version 3.3 to 3.4"
119
+ def upgrade3_3_to_3_4
120
+ Upgrade33_to_34.new(options).run
121
+ end
122
+
119
123
  desc "version", "Prints version number of installed ufo."
120
124
  def version
121
125
  puts VERSION
@@ -7,10 +7,6 @@ base:
7
7
  # clean_keep: 30
8
8
  # ecr_keep: 30
9
9
  # defaults when an new ECS service is created by ufo ship
10
- new_service:
11
- maximum_percent: 200
12
- minimum_healthy_percent: 100
13
- desired_count: 1
14
10
 
15
11
  development:
16
12
  # cluster: dev
@@ -1,6 +1,6 @@
1
1
  module Ufo
2
2
  class Destroy
3
- include Default
3
+ include Util
4
4
  include AwsService
5
5
 
6
6
  def initialize(service, options={})
@@ -51,7 +51,7 @@ class Ufo::Docker
51
51
 
52
52
  # full_image - does not include the tag
53
53
  def image_name
54
- setting.data["image"]
54
+ settings["image"]
55
55
  end
56
56
 
57
57
  # full_image - includes the tag
@@ -97,10 +97,6 @@ class Ufo::Docker
97
97
  @git_sha.strip!
98
98
  end
99
99
 
100
- def setting
101
- @setting ||= Ufo::Setting.new(Ufo.root)
102
- end
103
-
104
100
  def update_dockerfile
105
101
  dockerfile = Dockerfile.new(full_image_name, @options)
106
102
  dockerfile.update
@@ -1,14 +1,13 @@
1
1
  module Ufo
2
2
  class Docker::Cleaner
3
3
  include Util
4
- include Default
5
4
 
6
5
  def initialize(docker_image_name, options)
7
6
  # docker_image_name does not containg the tag
8
7
  # Example: 123456789.dkr.ecr.us-east-1.amazonaws.com/image
9
8
  @docker_image_name = docker_image_name
10
9
  @options = options
11
- @keep = options[:keep] || setting.data["clean_keep"] || 3
10
+ @keep = options[:keep] || settings["clean_keep"] || 3
12
11
  @tag_prefix = options[:tag_prefix] || "ufo"
13
12
  end
14
13
 
@@ -43,11 +43,7 @@ class Ufo::Docker
43
43
 
44
44
  # full_image - does not include the tag
45
45
  def image_name
46
- setting.data["image"]
47
- end
48
-
49
- def setting
50
- @setting ||= Ufo::Setting.new(Ufo.root)
46
+ settings["image"]
51
47
  end
52
48
  end
53
49
  end
@@ -89,7 +89,7 @@ module Ufo
89
89
  end
90
90
 
91
91
  def helper
92
- Helper.new(@options)
92
+ Helper.new
93
93
  end
94
94
  end
95
95
  end
@@ -8,9 +8,7 @@ module Ufo
8
8
  class DSL
9
9
  # provides some helperally context variables
10
10
  class Helper
11
- def initialize(options={})
12
- @options = options
13
- end
11
+ include Ufo::Util
14
12
 
15
13
  ##############
16
14
  # helper variables
@@ -22,7 +20,8 @@ module Ufo
22
20
  end
23
21
 
24
22
  def full_image_name
25
- Docker::Builder.new(@options).full_image_name
23
+ # Dont need to use @options here. Helps simplify the Helper initialization.
24
+ Docker::Builder.new({}).full_image_name
26
25
  end
27
26
 
28
27
  #############
@@ -64,10 +63,6 @@ module Ufo
64
63
  @current_region ||= `aws configure get region`.strip rescue 'us-east-1'
65
64
  end
66
65
 
67
- def setting
68
- @setting ||= Setting.new(Ufo.root)
69
- end
70
-
71
66
  def parse_for_dockerfile_port(dockerfile_path)
72
67
  lines = IO.read(dockerfile_path).split("\n")
73
68
  expose_line = lines.find { |l| l =~ /^EXPOSE / }
@@ -17,52 +17,14 @@ module Ufo
17
17
  @dsl.helper
18
18
  end
19
19
 
20
- def build
21
- load_variables
22
- instance_eval(&@block)
23
-
24
- hash = assign_instance_variables
25
- RenderMePretty.result(source_path, hash)
26
- end
27
-
28
- def assign_instance_variables
29
- # copy over the instance variables from TaskDefinition scope to RenderMePretty's scope
30
- hash = {}
31
- instance_variables.each do |var|
32
- key = var.to_s.sub('@','') # rid of the leading @
33
- hash[key.to_sym] = instance_variable_get(var)
34
- end
35
- hash
36
- end
37
-
38
- def load_variables
39
- load_variables_file("base")
40
- load_variables_file(Ufo.env)
20
+ def template_scope
21
+ @template_scope ||= Ufo::TemplateScope.new(helper)
41
22
  end
42
23
 
43
- # Load the variables defined in ufo/variables/* to make available in the
44
- # template blocks in ufo/templates/*.
45
- #
46
- # Example:
47
- #
48
- # `ufo/variables/base.rb`:
49
- # @name = "docker-process-name"
50
- # @image = "docker-image-name"
51
- #
52
- # `ufo/templates/main.json.erb`:
53
- # {
54
- # "containerDefinitions": [
55
- # {
56
- # "name": "<%= @name %>",
57
- # "image": "<%= @image %>",
58
- # ....
59
- # }
60
- #
61
- # NOTE: Only able to make instance variables avaialble with instance_eval
62
- # Wasnt able to make local variables available.
63
- def load_variables_file(filename)
64
- path = "#{Ufo.root}/.ufo/variables/#{filename}.rb"
65
- instance_eval(IO.read(path)) if File.exist?(path)
24
+ def build
25
+ instance_eval(&@block)
26
+ vars = template_scope.assign_instance_variables
27
+ RenderMePretty.result(source_path, vars)
66
28
  end
67
29
 
68
30
  # at this point instance_eval has been called and source has possibly been called
@@ -75,7 +37,7 @@ module Ufo
75
37
  if instance_variable_defined?("@#{var}")
76
38
  puts "WARNING: The instance variable @#{var} is already used internally with ufo. Please name you variable another name!"
77
39
  end
78
- instance_variable_set("@#{var}", value)
40
+ template_scope.instance_variable_set("@#{var}", value)
79
41
  end
80
42
  end
81
43
 
@@ -5,15 +5,15 @@ require "json"
5
5
  # ufo ship app-web --cluster prod --noop
6
6
  module Ufo
7
7
  class Ecr::Cleaner
8
+ include Util
8
9
  include AwsService
9
- include Default
10
10
 
11
11
  def initialize(docker_image_name, options={})
12
12
  # docker_image_name does not containg the tag
13
13
  # Example: 123456789.dkr.ecr.us-east-1.amazonaws.com/image
14
14
  @docker_image_name = docker_image_name
15
15
  @options = options
16
- @keep = options[:ecr_keep] || setting.data["ecr_keep"]
16
+ @keep = options[:ecr_keep] || settings["ecr_keep"]
17
17
  @tag_prefix = options[:tag_prefix] || "ufo"
18
18
  end
19
19
 
@@ -7,7 +7,15 @@ The above command does the following:
7
7
  1. register the `.ufo/output/hi-web.json` task definition to ECS untouched.
8
8
  2. deploys it to ECS by updating the service
9
9
 
10
- The `ufo deploy` command does less than the `ufo ship` command. Typically, people use `ufo ship` over the `ufo deploy` command do everything in one step:
10
+ ### ufo tasks build
11
+
12
+ To regenerate a `.ufo/output/hi-web.json` definition:
13
+
14
+ ufo tasks build
15
+
16
+ ### ufo ship
17
+
18
+ The `ufo deploy` command does less than the `ufo ship` command. Normally, it is recommended to use `ufo ship` over the `ufo deploy` command to do everything in one step:
11
19
 
12
20
  1. build the Docker image
13
21
  2. register the ECS task definition
@@ -19,6 +19,12 @@ For this example we will use [tongueroo/hi](https://github.com/tongueroo/hi) whi
19
19
  append .gitignore
20
20
  Starter ufo files created.
21
21
 
22
+ ## More Short Examples
23
+
24
+ ufo init --image httpd --app demo
25
+ ufo init --image 123456789012.dkr.ecr.us-west-2.amazonaws.com/myimage --app demo
26
+ ufo init --image tongueroo/hi --app hi --launch-type fargate --execution-role-arn arn:aws:iam::536766270177:role/ecsTaskExecutionRole
27
+
22
28
  ## Options: app and image
23
29
 
24
30
  The `app` is that application name that you want to show up on the ECS dashboard. It is encouraged to have the app name be a single word.
@@ -46,6 +52,18 @@ The standard directory structure of the `.ufo` folder that was created looks lik
46
52
 
47
53
  For a explanation of the folders and files refer to [Structure]({% link _docs/structure.md %}).
48
54
 
55
+ ## Fargate Support
56
+
57
+ For ECS Fargate, the ECS task definition structure is a bit different. To initialize a project to support Fargate use the `--launch-type fargate` option. You'll be prompted for a execution role arn. This value gets added to the generated `.ufo/variables/base.rb` and used in the `.ufo/templates/main.json.erb`.
58
+
59
+ ufo init --image tongueroo/hi --app hi --force --launch-type fargate
60
+
61
+ You can also generate the init ufo files and bypass the prompt by providing the `----execution-role-arn` option upfront.
62
+
63
+ ufo init --image tongueroo/hi --app hi --force --launch-type fargate --execution-role-arn arn:aws:iam::536766270177:role/ecsTaskExecutionRole
64
+
65
+ Important: You will need to adjust adjust the generated `.ufo/params.yml` and set the subnet and security_group values which are required for Fargate.
66
+
49
67
  ## Custom Templates
50
68
 
51
69
  If you would like the `ufo init` command to use your own custom templates, you can achieve this with the `--template` and `--template-mode` options. Example:
@@ -1,10 +1,10 @@
1
1
  ## Examples
2
2
 
3
- To run a one time task with ECS:
3
+ You can use the `--command` or `-c` option to override the Docker container command.
4
4
 
5
- ufo task hi-migrate
5
+ ufo task hi-migrate # default command
6
+ ufo task hi-web --command bin/migrate
7
+ ufo task hi-web --command bin/with_env bundle exec rake db:migrate:redo VERSION=xxx
8
+ ufo task hi-web -c uptime
9
+ ufo task hi-web -c pwd
6
10
 
7
- You can also override the command used by the Docker container in the task definitions via command.
8
-
9
- ufo task hi-web --command bin/migrate
10
- ufo task hi-web --command bin/with_env bundle exec rake db:migrate:redo VERSION=xxx
@@ -9,6 +9,8 @@ module Ufo
9
9
  [:force, type: :boolean, desc: "Bypass overwrite are you sure prompt for existing files."],
10
10
  [:image, required: true, desc: "Docker image name without the tag. Example: tongueroo/hi. Configures ufo/settings.yml"],
11
11
  [:app, required: true, desc: "App name. Preferably one word. Used in the generated ufo/task_definitions.rb."],
12
+ [:launch_type, default: "ec2", desc: "ec2 or fargate."],
13
+ [:execution_role_arn, desc: "execution role arn used by tasks, required for fargate."],
12
14
  [:template, desc: "Custom template to use."],
13
15
  [:template_mode, desc: "Template mode: replace or additive."],
14
16
  ]
@@ -50,11 +52,16 @@ module Ufo
50
52
  # map variables
51
53
  @app = options[:app]
52
54
  @image = options[:image]
55
+ @execution_role_arn_input = get_execution_role_arn_input
53
56
  # copy the files
54
57
  puts "Setting up ufo project..."
58
+ directory ".", exclude_pattern: /(\.git|templates)/
55
59
 
56
- # directory ".ufo", ".ufo"
57
- directory ".", exclude_pattern: /\.git/
60
+ if @options[:launch_type] == "fargate"
61
+ copy_file ".ufo/templates/fargate.json.erb", ".ufo/templates/main.json.erb"
62
+ else
63
+ copy_file ".ufo/templates/main.json.erb"
64
+ end
58
65
  end
59
66
 
60
67
  def upsert_gitignore
@@ -0,0 +1,24 @@
1
+ require 'yaml'
2
+ require 'memoist'
3
+
4
+ module Ufo
5
+ class Param
6
+ extend Memoist
7
+
8
+ def initialize
9
+ @params_path = "#{Ufo.root}/.ufo/params.yml"
10
+ end
11
+
12
+ def helper
13
+ dsl = DSL.new("#{Ufo.root}/.ufo/task_definitions.rb", quiet: true, mute: true)
14
+ dsl.helper
15
+ end
16
+
17
+ def data
18
+ vars = Ufo::TemplateScope.new(helper).assign_instance_variables
19
+ result = RenderMePretty.result(@params_path, vars)
20
+ YAML.load(result)
21
+ end
22
+ memoize :data
23
+ end
24
+ end
@@ -1,6 +1,6 @@
1
1
  module Ufo
2
2
  class Scale
3
- include Default
3
+ include Util
4
4
  include AwsService
5
5
 
6
6
  def initialize(service, count, options={})
@@ -11,6 +11,20 @@ module Ufo
11
11
  end
12
12
 
13
13
  private
14
+ def get_execution_role_arn_input
15
+ return @execution_role_arn if @execution_role_arn
16
+
17
+ if @options[:execution_role_arn]
18
+ @execution_role_arn = @options[:execution_role_arn]
19
+ return @execution_role_arn
20
+ end
21
+
22
+ return unless @options[:launch_type] == "fargate"
23
+ # execution role arn required for fargate
24
+ print "Please provide a execution role arn role for the ecs task: "
25
+ @execution_role_arn = $stdin.gets.strip
26
+ end
27
+
14
28
  def override_source_paths(*paths)
15
29
  # Using string with instance_eval because block doesnt have access to
16
30
  # path at runtime.
@@ -5,7 +5,6 @@ module Ufo
5
5
  class ShipmentOverridden < UfoError; end
6
6
 
7
7
  class Ship
8
- include Default
9
8
  include AwsService
10
9
  include Util
11
10
 
@@ -209,16 +208,14 @@ module Ufo
209
208
  options = {
210
209
  cluster: @cluster,
211
210
  service_name: @service,
212
- desired_count: default_desired_count,
213
- deployment_configuration: {
214
- maximum_percent: default_maximum_percent,
215
- minimum_healthy_percent: default_minimum_healthy_percent
216
- },
217
211
  task_definition: @task_definition
218
212
  }
213
+ options = options.merge(default_params[:create_service])
219
214
  unless target_group.nil? || target_group.empty?
220
215
  add_load_balancer!(container, options, target_group)
221
216
  end
217
+ puts "Creating ECS service with params:"
218
+ display_params(options)
222
219
  response = ecs.create_service(options)
223
220
  service = response.service # must set service here since this might never be called if @wait_for_deployment is false
224
221
  end
@@ -248,6 +245,9 @@ module Ufo
248
245
  service: ecs_service.service_arn, # can use the service name also since it is unique
249
246
  task_definition: @task_definition
250
247
  }
248
+ params = params.merge(default_params[:update_service] || {})
249
+ puts "Updating ECS service with params:"
250
+ display_params(params)
251
251
  response = ecs.update_service(params)
252
252
  service = response.service # must set service here since this might never be called if @wait_for_deployment is false
253
253
  end