forger 2.0.5 → 3.0.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/.circleci/config.yml +1 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile.lock +40 -29
- data/README.md +6 -37
- data/docs/example/config/variables/development.rb +2 -0
- data/docs/example/profiles/default.yml +2 -2
- data/docs/extract-scripts.md +40 -0
- data/docs/layouts.md +35 -0
- data/docs/profiles.md +79 -0
- data/docs/variables.md +53 -0
- data/forger.gemspec +6 -3
- data/lib/forger.rb +4 -23
- data/lib/forger/autoloader.rb +21 -0
- data/lib/forger/aws_services.rb +22 -0
- data/lib/forger/clean.rb +0 -2
- data/lib/forger/cleaner.rb +0 -1
- data/lib/forger/cleaner/ami.rb +1 -1
- data/lib/forger/cli.rb +10 -6
- data/lib/forger/completer.rb +0 -2
- data/lib/forger/core.rb +28 -12
- data/lib/forger/create.rb +2 -23
- data/lib/forger/create/info.rb +10 -4
- data/lib/forger/create/waiter.rb +1 -1
- data/lib/forger/destroy.rb +1 -1
- data/lib/forger/help/upload.md +1 -13
- data/lib/forger/network.rb +2 -2
- data/lib/forger/new.rb +5 -6
- data/lib/forger/profile.rb +15 -3
- data/lib/forger/s3.rb +23 -0
- data/lib/forger/s3/bucket.rb +131 -0
- data/lib/forger/script.rb +0 -4
- data/lib/forger/script/upload.rb +12 -42
- data/lib/forger/scripts/cloudwatch.sh +2 -2
- data/lib/forger/scripts/shared/functions.sh +1 -1
- data/lib/forger/setting.rb +0 -32
- data/lib/forger/template.rb +0 -3
- data/lib/forger/template/context.rb +16 -1
- data/lib/forger/template/helper.rb +9 -9
- data/lib/forger/template/helper/ami_helper.rb +1 -1
- data/lib/forger/template/helper/core_helper.rb +7 -6
- data/lib/forger/template/helper/script_helper.rb +3 -9
- data/lib/forger/version.rb +1 -1
- data/lib/forger/wait.rb +0 -2
- data/lib/forger/waiter.rb +0 -1
- data/lib/forger/waiter/ami.rb +1 -1
- data/lib/templates/default/app/user_data/bootstrap.sh.tt +6 -9
- data/lib/templates/default/app/user_data/layouts/default.sh.tt +1 -5
- data/lib/templates/default/config/settings.yml.tt +1 -11
- data/lib/templates/default/config/{development.yml.tt → variables/development.yml.tt} +0 -0
- data/spec/fixtures/demo_project/app/user_data/bootstrap.sh +2 -2
- data/spec/fixtures/demo_project/config/settings.yml +2 -4
- data/spec/fixtures/demo_project/config/variables/test.rb +4 -0
- data/spec/fixtures/demo_project/profiles/default.yml +2 -2
- metadata +59 -13
- data/docs/example/config/development.yml +0 -7
- data/lib/forger/aws_service.rb +0 -7
- data/lib/forger/config.rb +0 -25
- data/spec/fixtures/demo_project/config/test.yml +0 -8
@@ -0,0 +1,131 @@
|
|
1
|
+
require "cfn/status"
|
2
|
+
|
3
|
+
class Forger::S3
|
4
|
+
class Bucket
|
5
|
+
STACK_NAME = ENV['FORGER_STACK_NAME'] || "forger"
|
6
|
+
include Forger::AwsServices
|
7
|
+
extend Forger::AwsServices
|
8
|
+
|
9
|
+
class << self
|
10
|
+
@@name = nil
|
11
|
+
def name
|
12
|
+
return @@name if @@name # only memoize once bucket has been created
|
13
|
+
|
14
|
+
stack = new.find_stack
|
15
|
+
return unless stack
|
16
|
+
|
17
|
+
resp = cfn.describe_stack_resources(stack_name: STACK_NAME)
|
18
|
+
bucket = resp.stack_resources.find { |r| r.logical_resource_id == "Bucket" }
|
19
|
+
@@name = bucket.physical_resource_id # actual bucket name
|
20
|
+
end
|
21
|
+
|
22
|
+
def ensure_exists!
|
23
|
+
bucket = new
|
24
|
+
return if bucket.exist?
|
25
|
+
bucket.create
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(options={})
|
30
|
+
@options = options
|
31
|
+
end
|
32
|
+
|
33
|
+
def deploy
|
34
|
+
stack = find_stack
|
35
|
+
if stack
|
36
|
+
update
|
37
|
+
else
|
38
|
+
create
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def exist?
|
43
|
+
!!bucket_name
|
44
|
+
end
|
45
|
+
|
46
|
+
def bucket_name
|
47
|
+
self.class.name
|
48
|
+
end
|
49
|
+
|
50
|
+
def show
|
51
|
+
if bucket_name
|
52
|
+
puts "Forger bucket name: #{bucket_name}"
|
53
|
+
else
|
54
|
+
puts "Forger bucket does not exist yet."
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Launches a cloudformation to create an s3 bucket
|
59
|
+
def create
|
60
|
+
puts "Creating #{STACK_NAME} stack with the s3 bucket"
|
61
|
+
cfn.create_stack(stack_name: STACK_NAME, template_body: template_body)
|
62
|
+
status = ::Cfn::Status.new(STACK_NAME)
|
63
|
+
status.wait
|
64
|
+
end
|
65
|
+
|
66
|
+
def update
|
67
|
+
puts "Updating #{STACK_NAME} stack with the s3 bucket"
|
68
|
+
cfn.update_stack(stack_name: STACK_NAME, template_body: template_body)
|
69
|
+
rescue Aws::CloudFormation::Errors::ValidationError => e
|
70
|
+
raise unless e.message.include?("No updates are to be performed")
|
71
|
+
end
|
72
|
+
|
73
|
+
def delete
|
74
|
+
are_you_sure?
|
75
|
+
|
76
|
+
puts "Deleting #{STACK_NAME} stack with the s3 bucket"
|
77
|
+
empty_bucket!
|
78
|
+
cfn.delete_stack(stack_name: STACK_NAME)
|
79
|
+
end
|
80
|
+
|
81
|
+
def find_stack
|
82
|
+
resp = cfn.describe_stacks(stack_name: STACK_NAME)
|
83
|
+
resp.stacks.first
|
84
|
+
rescue Aws::CloudFormation::Errors::ValidationError
|
85
|
+
nil
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def empty_bucket!
|
91
|
+
resp = s3.list_objects(bucket: bucket_name)
|
92
|
+
if resp.contents.size > 0
|
93
|
+
# IE: objects = [{key: "objectkey1"}, {key: "objectkey2"}]
|
94
|
+
objects = resp.contents.map { |item| {key: item.key} }
|
95
|
+
s3.delete_objects(
|
96
|
+
bucket: bucket_name,
|
97
|
+
delete: {
|
98
|
+
objects: objects,
|
99
|
+
quiet: false,
|
100
|
+
}
|
101
|
+
)
|
102
|
+
empty_bucket! # keep deleting objects until bucket is empty
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
def are_you_sure?
|
108
|
+
return true if @options[:sure]
|
109
|
+
|
110
|
+
puts "Are you sure you want the forger bucket #{bucket_name.color(:green)} to be emptied and deleted? (y/N)"
|
111
|
+
sure = $stdin.gets.strip
|
112
|
+
yes = sure =~ /^Y/i
|
113
|
+
unless yes
|
114
|
+
puts "Phew that was close."
|
115
|
+
exit
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def template_body
|
120
|
+
<<~YAML
|
121
|
+
Resources:
|
122
|
+
Bucket:
|
123
|
+
Type: AWS::S3::Bucket
|
124
|
+
Properties:
|
125
|
+
Tags:
|
126
|
+
- Key: Name
|
127
|
+
Value: forger
|
128
|
+
YAML
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
data/lib/forger/script.rb
CHANGED
data/lib/forger/script/upload.rb
CHANGED
@@ -15,12 +15,17 @@ class Forger::Script
|
|
15
15
|
|
16
16
|
def run
|
17
17
|
compiler.compile_scripts if @compile
|
18
|
+
ensure_bucket_exists
|
18
19
|
compressor.compress
|
19
20
|
upload(tarball_path)
|
20
21
|
compressor.clean
|
21
22
|
compiler.clean if @compile and Forger.settings["compile_clean"]
|
22
23
|
end
|
23
24
|
|
25
|
+
def ensure_bucket_exists
|
26
|
+
Forger::S3::Bucket.ensure_exists! if Forger::Template::Helper.extract_scripts_registered?
|
27
|
+
end
|
28
|
+
|
24
29
|
def upload(tarball_path)
|
25
30
|
if @options[:noop]
|
26
31
|
puts "NOOP: Not uploading file to s3"
|
@@ -28,32 +33,13 @@ class Forger::Script
|
|
28
33
|
end
|
29
34
|
|
30
35
|
puts "Uploading scripts.tgz (#{filesize}) to #{s3_dest}".color(:green)
|
31
|
-
obj = s3_resource.bucket(bucket_name).object(
|
36
|
+
obj = s3_resource.bucket(bucket_name).object(s3_key)
|
32
37
|
start_time = Time.now
|
33
|
-
|
38
|
+
obj.upload_file(tarball_path)
|
34
39
|
time_took = pretty_time(Time.now-start_time).color(:green)
|
35
40
|
puts "Time to upload code to s3: #{time_took}"
|
36
41
|
end
|
37
42
|
|
38
|
-
def upload_to_s3(obj, tarball_path)
|
39
|
-
obj.upload_file(tarball_path)
|
40
|
-
rescue Aws::S3::Errors::PermanentRedirect => e
|
41
|
-
puts "ERROR: #{e.class} #{e.message}".color(:red)
|
42
|
-
puts "The bucket you are trying to upload scripts to is in a different region than the region the instance is being launched in."
|
43
|
-
puts "You must configured FORGER_S3_ENDPOINT env variable to prevent this error. Example:"
|
44
|
-
puts " FORGER_S3_ENDPOINT=https://s3.us-west-2.amazonaws.com"
|
45
|
-
puts "Check your ~/.aws/config for the region being used for the ec2 instance."
|
46
|
-
exit 1
|
47
|
-
rescue Aws::S3::Errors::AccessDenied, Aws::S3::Errors::AllAccessDisabled
|
48
|
-
e = $!
|
49
|
-
puts "ERROR: #{e.class} #{e.message}".color(:red)
|
50
|
-
puts "You do not have permission to upload scripts to this bucket: #{bucket_name}. Are you sure the right bucket is configured?"
|
51
|
-
if ENV['AWS_PROFILE']
|
52
|
-
puts "Also maybe check your AWS_PROFILE env. Current AWS_PROFILE=#{ENV['AWS_PROFILE']}"
|
53
|
-
end
|
54
|
-
exit 1
|
55
|
-
end
|
56
|
-
|
57
43
|
def empty?
|
58
44
|
Dir.glob("#{Forger.root}/app/scripts/**/*").select do |path|
|
59
45
|
File.file?(path)
|
@@ -69,33 +55,17 @@ class Forger::Script
|
|
69
55
|
end
|
70
56
|
|
71
57
|
def s3_dest
|
72
|
-
"s3://#{bucket_name}/#{
|
58
|
+
"s3://#{bucket_name}/#{s3_key}"
|
73
59
|
end
|
74
60
|
|
75
|
-
def
|
76
|
-
# Example
|
61
|
+
def s3_key
|
62
|
+
# Example s3_key: ec2/development/scripts/scripts-md5
|
63
|
+
dest_folder = "#{Forger.env}/scripts"
|
77
64
|
"#{dest_folder}/#{File.basename(tarball_path)}"
|
78
65
|
end
|
79
66
|
|
80
|
-
# Example:
|
81
|
-
# s3_folder: s3://infra-bucket/ec2
|
82
|
-
# bucket_name: infra-bucket
|
83
67
|
def bucket_name
|
84
|
-
|
85
|
-
end
|
86
|
-
|
87
|
-
# Removes s3://bucket-name and adds Forger.env. Example:
|
88
|
-
# s3_folder: s3://infra-bucket/ec2
|
89
|
-
# bucket_name: ec2/development/scripts
|
90
|
-
def dest_folder
|
91
|
-
folder = s3_folder.sub('s3://','').split('/')[1..-1].join('/')
|
92
|
-
folder = nil if folder == ''
|
93
|
-
[folder, "#{Forger.env}/scripts"].compact.join('/')
|
94
|
-
end
|
95
|
-
|
96
|
-
# s3_folder example:
|
97
|
-
def s3_folder
|
98
|
-
Forger.settings["s3_folder"]
|
68
|
+
Forger::S3::Bucket.name
|
99
69
|
end
|
100
70
|
|
101
71
|
def s3_resource
|
@@ -13,8 +13,8 @@ LOG_GROUP_NAME=$1
|
|
13
13
|
# shellcheck disable=SC1091
|
14
14
|
source "/opt/forger/shared/functions.sh"
|
15
15
|
OS=$(os_name)
|
16
|
-
if [ "$OS" != "amzn" ] && [ "$OS" != "
|
17
|
-
echo "Sorry, cloudwatch logging with the forger tool is supported for
|
16
|
+
if [ "$OS" != "amzn" ] && [ "$OS" != "amzn2" ] && [ "$OS" != "ubuntu" ] ; then
|
17
|
+
echo "Sorry, cloudwatch logging with the forger tool is supported for amzn2 and ubuntu only"
|
18
18
|
exit
|
19
19
|
fi
|
20
20
|
|
@@ -63,7 +63,7 @@ function os_name() {
|
|
63
63
|
OS="${OS#\"}" # remove leading "
|
64
64
|
OS="${OS%\"}" # remove trailing "
|
65
65
|
|
66
|
-
if [ "$OS" == "
|
66
|
+
if [ "$OS" == "amzn" ]; then
|
67
67
|
VERSION=$(gawk -F= '/^VERSION/{print $2}' /etc/os-release)
|
68
68
|
VERSION="${VERSION#\"}" # remove leading "
|
69
69
|
if [[ "$VERSION" =~ ^2 ]] ; then
|
data/lib/forger/setting.rb
CHANGED
@@ -20,41 +20,9 @@ module Forger
|
|
20
20
|
all_envs = load_file(project_settings_path)
|
21
21
|
all_envs = merge_base(all_envs)
|
22
22
|
@@data = all_envs[Forger.env] || all_envs["base"] || {}
|
23
|
-
@@data = flatten_s3_folder(@@data)
|
24
23
|
@@data
|
25
24
|
end
|
26
25
|
|
27
|
-
# So we can access settings['s3_folder'] transparently
|
28
|
-
def flatten_s3_folder(data)
|
29
|
-
# data = data.clone
|
30
|
-
if data['s3_folder'].is_a?(Hash)
|
31
|
-
data['s3_folder'] = s3_folder
|
32
|
-
end
|
33
|
-
data
|
34
|
-
end
|
35
|
-
|
36
|
-
# Special helper method to support multiple formats for s3_folder setting.
|
37
|
-
# Format 1: Simple String
|
38
|
-
#
|
39
|
-
# development:
|
40
|
-
# s3_folder: mybucket/path/to/folder
|
41
|
-
#
|
42
|
-
# Format 2: Hash
|
43
|
-
#
|
44
|
-
# development:
|
45
|
-
# s3_folder:
|
46
|
-
# default: mybucket/path/to/folder
|
47
|
-
# dev_profile1: mybucket/path/to/folder
|
48
|
-
# dev_profile1: another-bucket/storage/path
|
49
|
-
#
|
50
|
-
def s3_folder
|
51
|
-
s3_folder = data['s3_folder']
|
52
|
-
return s3_folder if s3_folder.nil? or s3_folder.is_a?(String)
|
53
|
-
|
54
|
-
# If reach here then the s3_folder is a Hash
|
55
|
-
s3_folder[ENV['AWS_PROFILE']] || s3_folder["default"]
|
56
|
-
end
|
57
|
-
|
58
26
|
private
|
59
27
|
def load_file(path)
|
60
28
|
return Hash.new({}) unless File.exist?(path)
|
data/lib/forger/template.rb
CHANGED
@@ -8,15 +8,30 @@ module Forger::Template
|
|
8
8
|
|
9
9
|
def initialize(options={})
|
10
10
|
@options = options
|
11
|
+
load_variables
|
11
12
|
load_custom_helpers
|
12
13
|
end
|
13
14
|
|
14
15
|
private
|
16
|
+
# Load variables from:
|
17
|
+
# config/variables/development.rb
|
18
|
+
# config/variables/production.rb
|
19
|
+
# etc
|
20
|
+
def load_variables
|
21
|
+
load_variables_file(:base)
|
22
|
+
load_variables_file(Forger.env)
|
23
|
+
end
|
24
|
+
|
25
|
+
def load_variables_file(type)
|
26
|
+
path = "#{Forger.root}/config/variables/#{type}.rb"
|
27
|
+
instance_eval(IO.read(path), path) if File.exist?(path)
|
28
|
+
end
|
29
|
+
|
15
30
|
# Load custom helper methods from project
|
16
31
|
def load_custom_helpers
|
17
32
|
Dir.glob("#{Forger.root}/app/helpers/**/*_helper.rb").each do |path|
|
18
33
|
filename = path.sub(%r{.*/},'').sub('.rb','')
|
19
|
-
module_name = filename.
|
34
|
+
module_name = filename.camelize
|
20
35
|
|
21
36
|
# Prepend a period so require works FORGER_ROOT is set to a relative path
|
22
37
|
# without a period.
|
@@ -2,16 +2,16 @@ require "active_support/core_ext/string"
|
|
2
2
|
|
3
3
|
module Forger::Template
|
4
4
|
module Helper
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
include AmiHelper
|
6
|
+
include CoreHelper
|
7
|
+
include PartialHelper
|
8
|
+
include ScriptHelper
|
9
|
+
include SshKeyHelper
|
9
10
|
extend self
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
autoinclude :SshKeyHelper
|
12
|
+
@@extract_scripts_registered = false
|
13
|
+
def extract_scripts_registered?
|
14
|
+
@@extract_scripts_registered
|
15
|
+
end
|
16
16
|
end
|
17
17
|
end
|
@@ -10,7 +10,7 @@ module Forger::Template::Helper::CoreHelper
|
|
10
10
|
path = "#{Forger.root}/app/user_data/#{name}"
|
11
11
|
unless File.exist?(path)
|
12
12
|
puts "ERROR: user-data script #{path.color(:red)} does not exist"
|
13
|
-
exit
|
13
|
+
exit 1
|
14
14
|
end
|
15
15
|
result = RenderMePretty.result(path, context: self, layout: layout_path)
|
16
16
|
# Must prepend and append scripts in user_data here because we need to
|
@@ -58,11 +58,12 @@ module Forger::Template::Helper::CoreHelper
|
|
58
58
|
end
|
59
59
|
|
60
60
|
# provides access to config/* settings as variables
|
61
|
-
# FORGER_ENV=development => config/development.
|
62
|
-
# FORGER_ENV=production
|
63
|
-
def
|
64
|
-
Forger.
|
61
|
+
# FORGER_ENV=development => config/variables/development.rb
|
62
|
+
# FORGER_ENV=production => config/variables/production.rb
|
63
|
+
def vars
|
64
|
+
Forger.vars
|
65
65
|
end
|
66
|
+
alias_method :variables, :vars
|
66
67
|
|
67
68
|
# provides access to config/settings.yml as variables
|
68
69
|
def settings
|
@@ -122,7 +123,7 @@ private
|
|
122
123
|
def load_custom_helpers
|
123
124
|
Dir.glob("#{Forger.root}/app/helpers/**/*_helper.rb").each do |path|
|
124
125
|
filename = path.sub(%r{.*/},'').sub('.rb','')
|
125
|
-
module_name = filename.
|
126
|
+
module_name = filename.camelize
|
126
127
|
|
127
128
|
require path
|
128
129
|
self.class.send :include, module_name.constantize
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Forger::Template::Helper::ScriptHelper
|
2
2
|
# Bash code that is meant to included in user-data
|
3
3
|
def extract_scripts(options={})
|
4
|
-
|
4
|
+
@@extract_scripts_registered = true
|
5
5
|
|
6
6
|
settings_options = settings["extract_scripts"] || {}
|
7
7
|
options = settings_options.merge(options)
|
@@ -23,6 +23,7 @@ mkdir -p #{to}
|
|
23
23
|
aws s3 cp #{scripts_s3_path} #{to}/
|
24
24
|
(
|
25
25
|
cd #{to}
|
26
|
+
rm -rf #{to}/scripts # cleanup
|
26
27
|
tar zxf #{to}/#{scripts_name}
|
27
28
|
chmod -R a+x #{to}/scripts
|
28
29
|
chown -R #{user}:#{user} #{to}/scripts
|
@@ -31,15 +32,8 @@ BASH_CODE
|
|
31
32
|
end
|
32
33
|
|
33
34
|
private
|
34
|
-
def
|
35
|
-
return if settings["s3_folder"]
|
35
|
+
def register_extract_scripts
|
36
36
|
|
37
|
-
puts "The extract_scripts helper method aws called. It requires the s3_folder to be set at:"
|
38
|
-
lines = caller.reject { |l| l =~ %r{lib/forger} } # hide internal forger trace
|
39
|
-
puts " #{lines[0]}"
|
40
|
-
|
41
|
-
puts "Please configure your config/settings.yml with an s3_folder.".color(:red)
|
42
|
-
exit 1
|
43
37
|
end
|
44
38
|
|
45
39
|
def scripts_name
|
data/lib/forger/version.rb
CHANGED