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 +4 -4
- data/.cody/acceptance/bin/build.sh +1 -1
- data/CHANGELOG.md +3 -0
- data/lib/ufo/aws_services/concerns.rb +55 -0
- data/lib/ufo/aws_services.rb +9 -40
- data/lib/ufo/cfn/stack/status.rb +1 -1
- data/lib/ufo/cfn/stack.rb +4 -4
- data/lib/ufo/cli/destroy.rb +1 -1
- data/lib/ufo/config.rb +3 -0
- data/lib/ufo/docker/builder.rb +1 -1
- data/lib/ufo/docker/compiler.rb +3 -3
- data/lib/ufo/docker/state/base.rb +14 -0
- data/lib/ufo/docker/state/bucket.rb +2 -0
- data/lib/ufo/docker/state/file.rb +52 -0
- data/lib/ufo/docker/state/s3.rb +80 -0
- data/lib/ufo/docker/state.rb +16 -50
- data/lib/ufo/info.rb +1 -1
- data/lib/ufo/s3/aws_setup.rb +17 -0
- data/lib/ufo/s3/bucket.rb +174 -0
- data/lib/ufo/s3/rollback.rb +52 -0
- data/lib/ufo/version.rb +1 -1
- data/ufo.gemspec +1 -0
- metadata +23 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5b86133b126853170107907bce7c02f4620387d8b11c4a5e6e0f03176fda1349
|
4
|
+
data.tar.gz: 3c543c7b436ec7214043d5f5cf45a798af2ce054aed970fa5de991848f6a4664
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
data/lib/ufo/aws_services.rb
CHANGED
@@ -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
|
30
|
+
def cfn
|
30
31
|
Aws::CloudFormation::Client.new(aws_options)
|
31
32
|
end
|
32
|
-
memoize :
|
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
|
data/lib/ufo/cfn/stack/status.rb
CHANGED
@@ -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 =
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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}"
|
data/lib/ufo/cli/destroy.rb
CHANGED
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
|
data/lib/ufo/docker/builder.rb
CHANGED
@@ -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(
|
165
|
+
State.new(@options.merge(base_image: docker_image))
|
166
166
|
else
|
167
167
|
Dockerfile.new(docker_image, @options)
|
168
168
|
end
|
data/lib/ufo/docker/compiler.rb
CHANGED
@@ -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
|
-
|
13
|
-
|
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,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
|
data/lib/ufo/docker/state.rb
CHANGED
@@ -1,63 +1,29 @@
|
|
1
1
|
module Ufo::Docker
|
2
2
|
class State
|
3
|
-
|
4
|
-
include Ufo::Utils::Pretty
|
3
|
+
extend Memoist
|
5
4
|
|
6
|
-
def initialize(
|
7
|
-
@
|
5
|
+
def initialize(options={})
|
6
|
+
@options = options
|
8
7
|
end
|
9
8
|
|
10
9
|
def update
|
11
|
-
|
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
|
23
|
-
|
13
|
+
def read
|
14
|
+
storage.read
|
24
15
|
end
|
25
16
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
@@ -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
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.
|
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
|