ufo 6.1.5 → 6.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c5f634a56ad0e21b377ac92fb03f9b745a0f9e889e3a0b2b11f0f4b03c2a40b0
4
- data.tar.gz: 3cbc84d12fd991d62f85f53792319b170fc033e77d7191fc666aa4c124b0d1f9
3
+ metadata.gz: 5b86133b126853170107907bce7c02f4620387d8b11c4a5e6e0f03176fda1349
4
+ data.tar.gz: 3c543c7b436ec7214043d5f5cf45a798af2ce054aed970fa5de991848f6a4664
5
5
  SHA512:
6
- metadata.gz: fb24c7425643f9b9013f54309205c908eae46fcfbd66795d31d5067fba472280b8744b85a0d7ea9de9f684cd00ae4df6e7114c2ff051450d9d00e1ca36999945
7
- data.tar.gz: 6c6cf2c3d47ff6edf45a327503b4590e9edbd38ce7cc2fc9d3b320bb4fb8d50240754fa6d6aa90e7e7e203af141de65b6140fdb98d0c4eeda031204545f558dd
6
+ metadata.gz: f4658807edc6f36b07cf8048f9f7f9c75ed90f3a03cd481ea1b763d4332bdf887a17d9199988021fa3866d73e7d0b0f43b1d03ef031915fdf5e0d3ad00a5904c
7
+ data.tar.gz: d52d10df7faf76256f336abfc89a9ccd1afb999973d52e7cbf660481c17eb54ae8333c09e600de12455020c707fd1821a32beee0a893c72750337c1aecbcfaaa
@@ -22,7 +22,7 @@ REPO=$(aws ecr describe-repositories --repository-name test/demo | jq -r '.repos
22
22
 
23
23
  # DockerHub
24
24
  # toomanyrequests: You have reached your pull rate limit. You may increase the limit by authenticating
25
- docker login --username $DOCKER_USER --password $DOCKER_PASS
25
+ echo "$DOCKER_PASS" | docker login --username $DOCKER_USER --password-stdin
26
26
  TOKEN=$(curl -s --user "$DOCKER_USER:$DOCKER_PASS" "https://auth.docker.io/token?service=registry.docker.io&scope=repository:ratelimitpreview/test:pull" | jq -r .token)
27
27
  echo "Current rate limit:"
28
28
  curl -s --head -H "Authorization: Bearer $TOKEN" https://registry-1.docker.io/v2/ratelimitpreview/test/manifests/latest
data/CHANGELOG.md CHANGED
@@ -3,6 +3,9 @@
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.2.0] - 2022-03-16
7
+ - [#152](https://github.com/tongueroo/ufo/pull/152) ufo docker base: s3 storage support
8
+
6
9
  ## [6.1.5] - 2022-03-16
7
10
  - [#151](https://github.com/tongueroo/ufo/pull/151) fix symlink when .git ext is in the repo
8
11
 
@@ -0,0 +1,55 @@
1
+ require "cfn_status"
2
+
3
+ module Ufo::AwsServices
4
+ module Concerns
5
+ extend Memoist
6
+
7
+ def find_stack(stack_name)
8
+ resp = cfn.describe_stacks(stack_name: stack_name)
9
+ resp.stacks.first
10
+ rescue Aws::CloudFormation::Errors::ValidationError => e
11
+ # example: Stack with id demo-web does not exist
12
+ if e.message =~ /Stack with/ && e.message =~ /does not exist/
13
+ nil
14
+ else
15
+ raise
16
+ end
17
+ end
18
+
19
+ def stack_resources(stack_name)
20
+ resp = cfn.describe_stack_resources(stack_name: stack_name)
21
+ resp.stack_resources
22
+ rescue Aws::CloudFormation::Errors::ValidationError => e
23
+ e.message.include?("does not exist") ? return : raise
24
+ end
25
+
26
+ def task_definition_arns(family, max_items=10)
27
+ resp = ecs.list_task_definitions(
28
+ family_prefix: family,
29
+ sort: "DESC",
30
+ )
31
+ arns = resp.task_definition_arns
32
+ arns = arns.select do |arn|
33
+ task_definition = arn.split('/').last.split(':').first
34
+ task_definition == family
35
+ end
36
+ arns[0..max_items]
37
+ end
38
+
39
+ def status
40
+ CfnStatus.new(@stack_name) # NOTE: @stack_name must be set in the including Class
41
+ end
42
+ memoize :status
43
+
44
+ def find_stack_resources(stack_name)
45
+ resp = cfn.describe_stack_resources(stack_name: stack_name)
46
+ resp.stack_resources
47
+ rescue Aws::CloudFormation::Errors::ValidationError => e
48
+ if e.message.include?("does not exist")
49
+ nil
50
+ else
51
+ raise
52
+ end
53
+ end
54
+ end
55
+ end
@@ -6,15 +6,16 @@ require "aws-sdk-ec2"
6
6
  require "aws-sdk-ecr"
7
7
  require "aws-sdk-ecs"
8
8
  require "aws-sdk-elasticloadbalancingv2"
9
+ require "aws-sdk-s3"
9
10
  require "aws-sdk-ssm"
10
11
  require "aws-sdk-wafv2"
11
12
 
12
13
  require "aws_mfa_secure/ext/aws" # add MFA support
13
- require "cfn_status"
14
14
 
15
15
  module Ufo
16
16
  module AwsServices
17
17
  extend Memoist
18
+ include Concerns
18
19
 
19
20
  def acm
20
21
  Aws::ACM::Client.new(aws_options)
@@ -26,10 +27,10 @@ module Ufo
26
27
  end
27
28
  memoize :applicationautoscaling
28
29
 
29
- def cloudformation
30
+ def cfn
30
31
  Aws::CloudFormation::Client.new(aws_options)
31
32
  end
32
- memoize :cloudformation
33
+ memoize :cfn
33
34
 
34
35
  def cloudwatchlogs
35
36
  Aws::CloudWatchLogs::Client.new(aws_options)
@@ -56,6 +57,11 @@ module Ufo
56
57
  end
57
58
  memoize :elb
58
59
 
60
+ def s3
61
+ Aws::S3::Client.new(aws_options)
62
+ end
63
+ memoize :s3
64
+
59
65
  # ssm is a helper method
60
66
  def ssm_client
61
67
  Aws::SSM::Client.new(aws_options)
@@ -98,42 +104,5 @@ module Ufo
98
104
  ) if ENV['UFO_DEBUG_AWS_SDK']
99
105
  options
100
106
  end
101
-
102
- def find_stack(stack_name)
103
- resp = cloudformation.describe_stacks(stack_name: stack_name)
104
- resp.stacks.first
105
- rescue Aws::CloudFormation::Errors::ValidationError => e
106
- # example: Stack with id demo-web does not exist
107
- if e.message =~ /Stack with/ && e.message =~ /does not exist/
108
- nil
109
- else
110
- raise
111
- end
112
- end
113
-
114
- def stack_resources(stack_name)
115
- resp = cloudformation.describe_stack_resources(stack_name: stack_name)
116
- resp.stack_resources
117
- rescue Aws::CloudFormation::Errors::ValidationError => e
118
- e.message.include?("does not exist") ? return : raise
119
- end
120
-
121
- def task_definition_arns(family, max_items=10)
122
- resp = ecs.list_task_definitions(
123
- family_prefix: family,
124
- sort: "DESC",
125
- )
126
- arns = resp.task_definition_arns
127
- arns = arns.select do |arn|
128
- task_definition = arn.split('/').last.split(':').first
129
- task_definition == family
130
- end
131
- arns[0..max_items]
132
- end
133
-
134
- def status
135
- CfnStatus.new(@stack_name) # NOTE: @stack_name must be set in the including Class
136
- end
137
- memoize :status
138
107
  end
139
108
  end
@@ -122,7 +122,7 @@ class Ufo::Cfn::Stack
122
122
 
123
123
  # refreshes the loaded events in memory
124
124
  def refresh_events
125
- resp = cloudformation.describe_stack_events(stack_name: @stack_name)
125
+ resp = cfn.describe_stack_events(stack_name: @stack_name)
126
126
  @events = resp["stack_events"]
127
127
  rescue Aws::CloudFormation::Errors::ValidationError => e
128
128
  if e.message =~ /Stack .* does not exis/
data/lib/ufo/cfn/stack.rb CHANGED
@@ -31,7 +31,7 @@ module Ufo::Cfn
31
31
  @stack = find_stack(@stack_name)
32
32
  if @stack && rollback_complete?(@stack)
33
33
  logger.info "Existing stack in ROLLBACK_COMPLETE state. Deleting stack before continuing."
34
- cloudformation.delete_stack(stack_name: @stack_name)
34
+ cfn.delete_stack(stack_name: @stack_name)
35
35
  status.wait
36
36
  status.reset
37
37
  @stack = nil # at this point stack has been deleted
@@ -53,7 +53,7 @@ module Ufo::Cfn
53
53
 
54
54
  def perform(action)
55
55
  logger.info "#{action[0..-2].capitalize}ing stack #{@stack_name.color(:green)}"
56
- cloudformation.send("#{action}_stack", stack_options) # Example: cloudformation.send("update_stack", stack_options)
56
+ cfn.send("#{action}_stack", stack_options) # Example: cfn.send("update_stack", stack_options)
57
57
  rescue Aws::CloudFormation::Errors::ValidationError => e
58
58
  handle_stack_error(e)
59
59
  end
@@ -152,10 +152,10 @@ module Ufo::Cfn
152
152
  end
153
153
 
154
154
  if stack.stack_status == "CREATE_IN_PROGRESS"
155
- cloudformation.delete_stack(stack_name: @stack_name)
155
+ cfn.delete_stack(stack_name: @stack_name)
156
156
  logger.info "Canceling stack creation"
157
157
  elsif stack.stack_status == "UPDATE_IN_PROGRESS"
158
- cloudformation.cancel_update_stack(stack_name: @stack_name)
158
+ cfn.cancel_update_stack(stack_name: @stack_name)
159
159
  logger.info "Canceling stack update"
160
160
  else
161
161
  logger.info "The stack is not in a state to that is cancelable: #{stack.stack_status}"
@@ -17,7 +17,7 @@ class Ufo::CLI
17
17
  return
18
18
  end
19
19
 
20
- cloudformation.delete_stack(stack_name: @stack_name)
20
+ cfn.delete_stack(stack_name: @stack_name)
21
21
  puts "Deleting stack #{@stack_name.color(:green)}"
22
22
 
23
23
  return unless @options[:wait]
data/lib/ufo/config.rb CHANGED
@@ -114,7 +114,10 @@ module Ufo
114
114
  config.ship.docker.quiet = false # only affects ufo ship docker commands output
115
115
 
116
116
  config.state = ActiveSupport::OrderedOptions.new
117
+ config.state.bucket = nil # Set to use existing bucket. When not set ufo creates a managed s3 bucket
118
+ config.state.managed = true # false will disable creation of managed bucket entirely
117
119
  config.state.reminder = true
120
+ config.state.storage = "s3" # s3 or file
118
121
 
119
122
  config.waf = ActiveSupport::OrderedOptions.new
120
123
  config.waf.web_acl_arn = nil
@@ -162,7 +162,7 @@ module Ufo::Docker
162
162
 
163
163
  def update_dockerfile
164
164
  updater = if File.exist?("#{Ufo.root}/Dockerfile.erb") # dont use @dockerfile on purpose
165
- State.new(docker_image, @options)
165
+ State.new(@options.merge(base_image: docker_image))
166
166
  else
167
167
  Dockerfile.new(docker_image, @options)
168
168
  end
@@ -9,9 +9,9 @@ module Ufo::Docker
9
9
  return unless File.exist?(@erb_file)
10
10
 
11
11
  puts "Compiled #{File.basename(@erb_file).color(:green)} to #{File.basename(@dockerfile).color(:green)}"
12
- path = "#{Ufo.root}/.ufo/state/data.yml"
13
- vars = YAML.load_file(path)[Ufo.env] if File.exist?(path)
14
- vars ||= {}
12
+
13
+ state = State.new
14
+ vars = state.read
15
15
  result = RenderMePretty.result(@erb_file, vars)
16
16
  comment =<<~EOL.chop # remove the trailing newline
17
17
  # IMPORTANT: This file was generated from #{File.basename(@erb_file)} as a part of running:
@@ -0,0 +1,14 @@
1
+ class Ufo::Docker::State
2
+ class Base
3
+ include Ufo::Utils::Logging
4
+ include Ufo::Utils::Pretty
5
+
6
+ def initialize(options={})
7
+ @options = options
8
+ # base_image only passed in with: ufo docker base
9
+ # State#update uses it.
10
+ # State#read wont have access to it and gets it from stored state
11
+ @base_image = options[:base_image]
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,2 @@
1
+ class Ufo::Docker::State
2
+ class S3 < Base
@@ -0,0 +1,52 @@
1
+ class Ufo::Docker::State
2
+ class File < Base
3
+ def read
4
+ current_data
5
+ end
6
+
7
+ def update
8
+ data = current_data
9
+ data["base_image"] = @base_image
10
+
11
+ pretty_path = state_path.sub("#{Ufo.root}/", "")
12
+ FileUtils.mkdir_p(::File.dirname(state_path))
13
+ IO.write(state_path, YAML.dump(data))
14
+
15
+ logger.info "The #{pretty_path} base_image has been updated with the latest base image:".color(:green)
16
+ logger.info " #{@base_image}".color(:green)
17
+ reminder_message
18
+ end
19
+
20
+ def current_data
21
+ ::File.exist?(state_path) ? YAML.load_file(state_path) : {}
22
+ end
23
+
24
+ def state_path
25
+ "#{Ufo.root}/.ufo/state/#{Ufo.app}/#{Ufo.env}/data.yml"
26
+ end
27
+
28
+ def reminder_message
29
+ return unless Ufo.config.state.reminder
30
+ repo = ENV['UFO_CENTRAL_REPO']
31
+ return unless repo
32
+ logger.info "It looks like you're using a central deployer pattern".color(:yellow)
33
+ logger.info <<~EOL
34
+ Remember to commit the state file:
35
+
36
+ state file: #{pretty_path(state_path)}
37
+ repo: #{repo}
38
+
39
+ EOL
40
+
41
+ logger.info <<~EOL
42
+ You can disable these reminder messages with:
43
+
44
+ .ufo/config.rb
45
+
46
+ Ufo.configure do |config|
47
+ config.state.reminder = false
48
+ end
49
+ EOL
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,80 @@
1
+ class Ufo::Docker::State
2
+ class S3 < Base
3
+ extend Memoist
4
+ include Ufo::AwsServices
5
+
6
+ def read
7
+ current_data
8
+ end
9
+
10
+ def update
11
+ data = current_data
12
+ data["base_image"] = @base_image
13
+
14
+ # write data to s3
15
+ body = YAML.dump(data)
16
+ s3.put_object(
17
+ body: body,
18
+ bucket: s3_bucket,
19
+ key: s3_key,
20
+ )
21
+ logger.info "Updated base image in s3://#{s3_bucket}/#{s3_key}"
22
+ logger.info " #{@base_image}".color(:green)
23
+ end
24
+
25
+ # TODO: edge cases: no bucket, no permission
26
+ def current_data
27
+ resp = s3.get_object(bucket: s3_bucket, key: s3_key)
28
+ YAML.load(resp.body)
29
+ rescue Aws::S3::Errors::NoSuchKey
30
+ logger.debug "WARN: s3 key does not exist: #{s3_key}"
31
+ {}
32
+ rescue Aws::S3::Errors::NoSuchBucket
33
+ logger.error "ERROR: S3 bucket does not exist to store state: #{s3_bucket}".color(:red)
34
+ logger.error <<~EOL
35
+ Please double check the config.
36
+
37
+ See: http://ufoships.com/docs/config/state/
38
+
39
+ EOL
40
+ exit 1
41
+ end
42
+
43
+ def s3_key
44
+ "ufo/state/#{app}/#{Ufo.env}/data.yml"
45
+ end
46
+
47
+ # ufo docker base is called before Ufo.config is loaded. This ensures it is loaded
48
+ def app
49
+ Ufo.config
50
+ Ufo.app
51
+ end
52
+
53
+ def s3_bucket
54
+ state = Ufo.config.state
55
+ if state.bucket
56
+ state.bucket
57
+ elsif state.managed
58
+ ensure_s3_bucket_exist
59
+ Ufo::S3::Bucket.name
60
+ else
61
+ logger.error "ERROR: No s3 bucket to store state".color(:red)
62
+ logger.error <<~EOL
63
+ UFO needs a bucket to store the built docker base image.
64
+
65
+ Configure an existing bucket or enable UFO to create a bucket.
66
+
67
+ See: http://ufoships.com/docs/config/state/
68
+ EOL
69
+ exit 1
70
+ end
71
+ end
72
+
73
+ def ensure_s3_bucket_exist
74
+ bucket = Ufo::S3::Bucket.new
75
+ return if bucket.exist?
76
+ bucket.deploy
77
+ end
78
+ memoize :ensure_s3_bucket_exist
79
+ end
80
+ end
@@ -1,63 +1,29 @@
1
1
  module Ufo::Docker
2
2
  class State
3
- include Ufo::Utils::Logging
4
- include Ufo::Utils::Pretty
3
+ extend Memoist
5
4
 
6
- def initialize(docker_image, options={})
7
- @docker_image, @options = docker_image, options
5
+ def initialize(options={})
6
+ @options = options
8
7
  end
9
8
 
10
9
  def update
11
- data = current_data
12
- data[Ufo.env] ||= {}
13
- data[Ufo.env]["base_image"] = @docker_image
14
- pretty_path = state_path.sub("#{Ufo.root}/", "")
15
- FileUtils.mkdir_p(File.dirname(state_path))
16
- IO.write(state_path, YAML.dump(data))
17
- logger.info "The #{pretty_path} base_image has been updated with the latest base image:".color(:green)
18
- logger.info " #{@docker_image}".color(:green)
19
- reminder_message
10
+ storage.update
20
11
  end
21
12
 
22
- def current_data
23
- File.exist?(state_path) ? YAML.load_file(state_path) : {}
13
+ def read
14
+ storage.read
24
15
  end
25
16
 
26
- def state_path
27
- path = "#{Ufo.root}/.ufo/state"
28
- if ENV['UFO_APP'] # env var activates app path
29
- path = "#{Ufo.root}/.ufo/state/#{Ufo.app}"
30
- end
31
- "#{path}/data.yml"
32
- end
33
-
34
- def reminder_message
35
- return unless Ufo.config.state.reminder
36
- repo = ENV['UFO_CENTRAL_REPO']
37
- return unless repo
38
- logger.info "It looks like you're using a central deployer pattern".color(:yellow)
39
- logger.info <<~EOL
40
- Remember to commit the state file:
41
-
42
- state file: #{pretty_path(state_path)}
43
- repo: #{repo}
44
-
45
- EOL
46
-
47
- unless ENV['UFO_APP']
48
- logger.info "WARN: It also doesnt look like UFO_ENV is set".color(:yellow)
49
- logger.info "UFO_ENV should be set when you're using ufo in a central manner"
50
- end
51
-
52
- logger.info <<~EOL
53
- You can disable these reminder messages with:
54
-
55
- .ufo/config.rb
56
-
57
- Ufo.configure do |config|
58
- config.state.reminder = false
59
- end
60
- EOL
17
+ private
18
+ # Examples:
19
+ # File.new(@docker_image, @options)
20
+ # S3.new(@docker_image, @options)
21
+ def storage
22
+ storage = Ufo.config.state.storage
23
+ class_name = "Ufo::Docker::State::#{storage.camelize}"
24
+ klass = class_name.constantize
25
+ klass.new(@options)
61
26
  end
27
+ memoize :storage
62
28
  end
63
29
  end
data/lib/ufo/info.rb CHANGED
@@ -73,7 +73,7 @@ module Ufo
73
73
  end
74
74
 
75
75
  def stack_resources
76
- resp = cloudformation.describe_stack_resources(stack_name: @stack_name)
76
+ resp = cfn.describe_stack_resources(stack_name: @stack_name)
77
77
  resp.stack_resources
78
78
  end
79
79
  memoize :stack_resources
@@ -0,0 +1,17 @@
1
+ module Ufo::S3
2
+ class AwsSetup
3
+ include Ufo::AwsServices
4
+ include Ufo::Utils::Logging
5
+
6
+ def check!
7
+ s3.config.region
8
+ rescue Aws::Errors::MissingRegionError => e
9
+ logger.info "ERROR: #{e.class}: #{e.message}".color(:red)
10
+ logger.info <<~EOL
11
+ Unable to detect the AWS_REGION to make AWS API calls. This is might be because the AWS access
12
+ has not been set up yet. Please either your ~/.aws files.
13
+ EOL
14
+ exit 1
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,174 @@
1
+ module Ufo::S3
2
+ class Bucket
3
+ extend Memoist
4
+ extend Ufo::AwsServices
5
+ include Ufo::AwsServices
6
+ include Ufo::Utils::Logging
7
+
8
+ STACK_NAME = ENV['UFO_STACK_NAME'] || "ufo"
9
+ def initialize(options={})
10
+ @options = options
11
+ end
12
+
13
+ def deploy
14
+ stack = find_stack
15
+ if rollback.complete?
16
+ logger.info "Existing '#{STACK_NAME}' stack in ROLLBACK_COMPLETE state. Deleting stack before continuing."
17
+ disable_termination_protection
18
+ cfn.delete_stack(stack_name: STACK_NAME)
19
+ status.wait
20
+ stack = nil
21
+ end
22
+
23
+ if stack
24
+ update
25
+ else
26
+ create
27
+ end
28
+ end
29
+
30
+ def exist?
31
+ !!bucket_name
32
+ end
33
+
34
+ def bucket_name
35
+ self.class.name
36
+ end
37
+
38
+ def show
39
+ if bucket_name
40
+ logger.info "UFO bucket name: #{bucket_name}"
41
+ else
42
+ logger.info "UFO bucket does not exist yet."
43
+ end
44
+ end
45
+
46
+ # Launches a cloudformation to create an s3 bucket
47
+ def create
48
+ logger.info "Creating #{STACK_NAME} stack for s3 bucket to store state"
49
+ cfn.create_stack(
50
+ stack_name: STACK_NAME,
51
+ template_body: template_body,
52
+ enable_termination_protection: true,
53
+ )
54
+ success = status.wait
55
+ unless success
56
+ logger.info "ERROR: Unable to create UFO stack with managed s3 bucket".color(:red)
57
+ exit 1
58
+ end
59
+ end
60
+
61
+ def update
62
+ logger.info "Updating #{STACK_NAME} stack with the s3 bucket"
63
+ cfn.update_stack(stack_name: STACK_NAME, template_body: template_body)
64
+ rescue Aws::CloudFormation::Errors::ValidationError => e
65
+ raise unless e.message.include?("No updates are to be performed")
66
+ end
67
+
68
+ def delete
69
+ are_you_sure?
70
+
71
+ logger.info "Deleting #{STACK_NAME} stack with the s3 bucket"
72
+ disable_termination_protection
73
+ empty_bucket!
74
+ cfn.delete_stack(stack_name: STACK_NAME)
75
+ end
76
+
77
+ def disable_termination_protection
78
+ cfn.update_termination_protection(
79
+ stack_name: STACK_NAME,
80
+ enable_termination_protection: false,
81
+ )
82
+ end
83
+
84
+ def find_stack
85
+ resp = cfn.describe_stacks(stack_name: STACK_NAME)
86
+ resp.stacks.first
87
+ rescue Aws::CloudFormation::Errors::ValidationError
88
+ nil
89
+ end
90
+
91
+ def status
92
+ CfnStatus.new(STACK_NAME)
93
+ end
94
+
95
+ private
96
+
97
+ def empty_bucket!
98
+ return unless bucket_name # in case of UFO stack ROLLBACK_COMPLETE from failed bucket creation
99
+
100
+ resp = s3.list_objects(bucket: bucket_name)
101
+ if resp.contents.size > 0
102
+ # IE: objects = [{key: "objectkey1"}, {key: "objectkey2"}]
103
+ objects = resp.contents.map { |item| {key: item.key} }
104
+ s3.delete_objects(
105
+ bucket: bucket_name,
106
+ delete: {
107
+ objects: objects,
108
+ quiet: false,
109
+ }
110
+ )
111
+ empty_bucket! # keep deleting objects until bucket is empty
112
+ end
113
+ end
114
+
115
+
116
+ def are_you_sure?
117
+ return true if @options[:yes]
118
+
119
+ if bucket_name.nil?
120
+ logger.info "The UFO stack and s3 bucket does not exist."
121
+ exit
122
+ end
123
+
124
+ logger.info "Are you yes you want the UFO bucket #{bucket_name.color(:green)} to be emptied and deleted? (y/N)"
125
+ yes = $stdin.gets.strip
126
+ confirmed = yes =~ /^Y/i
127
+ unless confirmed
128
+ logger.info "Phew that was close."
129
+ exit
130
+ end
131
+ end
132
+
133
+ def template_body
134
+ <<~YAML
135
+ Description: UFO managed s3 bucket
136
+ Resources:
137
+ Bucket:
138
+ Type: AWS::S3::Bucket
139
+ Properties:
140
+ BucketEncryption:
141
+ ServerSideEncryptionConfiguration:
142
+ - ServerSideEncryptionByDefault:
143
+ SSEAlgorithm: AES256
144
+ Tags:
145
+ - Key: Name
146
+ Value: UFO
147
+ Outputs:
148
+ Bucket:
149
+ Value:
150
+ Ref: Bucket
151
+ YAML
152
+ end
153
+
154
+ def rollback
155
+ Rollback.new(STACK_NAME)
156
+ end
157
+
158
+ class << self
159
+ @@name = nil
160
+ def name
161
+ return @@name if @@name # only memoize once bucket has been created
162
+
163
+ AwsSetup.new.check!
164
+
165
+ stack = new.find_stack
166
+ return unless stack
167
+
168
+ stack_resources = find_stack_resources(STACK_NAME)
169
+ bucket = stack_resources.find { |r| r.logical_resource_id == "Bucket" }
170
+ @@name = bucket.physical_resource_id # actual bucket name
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,52 @@
1
+ module Ufo::S3
2
+ class Rollback
3
+ extend Memoist
4
+ include Ufo::AwsServices
5
+ include Ufo::Utils::Logging
6
+
7
+ def initialize(stack)
8
+ @stack = stack
9
+ end
10
+
11
+ def delete_stack
12
+ return unless complete?
13
+ logger.info "Existing stack in ROLLBACK_COMPLETE state. Deleting stack before continuing."
14
+ cfn.delete_stack(stack_name: @stack)
15
+ status.wait
16
+ status.reset
17
+ true
18
+ end
19
+
20
+ def continue_update
21
+ continue_update?
22
+ begin
23
+ cfn.continue_update_rollback(stack_name: @stack)
24
+ rescue Aws::CloudFormation::Errors::ValidationError => e
25
+ logger.info "ERROR: Continue update: #{e.message}".color(:red)
26
+ quit 1
27
+ end
28
+ end
29
+
30
+ def continue_update?
31
+ logger.info <<~EOL
32
+ The stack is in the UPDATE_ROLLBACK_FAILED state. More info here: https://amzn.to/2IiEjc5
33
+ Would you like to try to continue the update rollback? (y/N)
34
+ EOL
35
+
36
+ yes = @options[:yes] ? "y" : $stdin.gets
37
+ unless yes =~ /^y/
38
+ logger.info "Exiting without continuing the update rollback."
39
+ quit 0
40
+ end
41
+ end
42
+
43
+ def complete?
44
+ stack&.stack_status == 'ROLLBACK_COMPLETE'
45
+ end
46
+
47
+ def stack
48
+ find_stack(@stack)
49
+ end
50
+ memoize :stack
51
+ end
52
+ end
data/lib/ufo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Ufo
2
- VERSION = "6.1.5"
2
+ VERSION = "6.2.0"
3
3
  end
data/ufo.gemspec CHANGED
@@ -28,6 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.add_dependency "aws-sdk-ecr"
29
29
  spec.add_dependency "aws-sdk-ecs"
30
30
  spec.add_dependency "aws-sdk-elasticloadbalancingv2"
31
+ spec.add_dependency "aws-sdk-s3"
31
32
  spec.add_dependency "aws-sdk-ssm"
32
33
  spec.add_dependency "aws-sdk-wafv2"
33
34
  spec.add_dependency "aws_data"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ufo
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.1.5
4
+ version: 6.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tung Nguyen
@@ -150,6 +150,20 @@ dependencies:
150
150
  - - ">="
151
151
  - !ruby/object:Gem::Version
152
152
  version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: aws-sdk-s3
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :runtime
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
153
167
  - !ruby/object:Gem::Dependency
154
168
  name: aws-sdk-ssm
155
169
  requirement: !ruby/object:Gem::Requirement
@@ -472,6 +486,7 @@ files:
472
486
  - lib/ufo.rb
473
487
  - lib/ufo/autoloader.rb
474
488
  - lib/ufo/aws_services.rb
489
+ - lib/ufo/aws_services/concerns.rb
475
490
  - lib/ufo/booter.rb
476
491
  - lib/ufo/cfn/base.rb
477
492
  - lib/ufo/cfn/deploy.rb
@@ -581,6 +596,10 @@ files:
581
596
  - lib/ufo/docker/dockerfile.rb
582
597
  - lib/ufo/docker/pusher.rb
583
598
  - lib/ufo/docker/state.rb
599
+ - lib/ufo/docker/state/base.rb
600
+ - lib/ufo/docker/state/bucket.rb
601
+ - lib/ufo/docker/state/file.rb
602
+ - lib/ufo/docker/state/s3.rb
584
603
  - lib/ufo/ecr/auth.rb
585
604
  - lib/ufo/ecr/cleaner.rb
586
605
  - lib/ufo/ecs/service.rb
@@ -598,6 +617,9 @@ files:
598
617
  - lib/ufo/logger/formatter.rb
599
618
  - lib/ufo/names.rb
600
619
  - lib/ufo/param.rb
620
+ - lib/ufo/s3/aws_setup.rb
621
+ - lib/ufo/s3/bucket.rb
622
+ - lib/ufo/s3/rollback.rb
601
623
  - lib/ufo/task_definition.rb
602
624
  - lib/ufo/task_definition/builder.rb
603
625
  - lib/ufo/task_definition/context.rb