lono 2.1.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +16 -284
- data/lib/lono.rb +6 -0
- data/lib/lono/cfn.rb +5 -0
- data/lib/lono/cfn/base.rb +86 -9
- data/lib/lono/cfn/create.rb +27 -2
- data/lib/lono/cfn/help.rb +12 -12
- data/lib/lono/cfn/preview.rb +12 -11
- data/lib/lono/cfn/update.rb +3 -2
- data/lib/lono/default/settings.yml +13 -0
- data/lib/lono/env.rb +11 -0
- data/lib/lono/param.rb +1 -1
- data/lib/lono/param/generator.rb +144 -22
- data/lib/lono/settings.rb +28 -0
- data/lib/lono/template.rb +13 -2
- data/lib/lono/template/aws_services.rb +7 -0
- data/lib/lono/template/dsl.rb +24 -33
- data/lib/lono/template/helpers.rb +144 -0
- data/lib/lono/template/template.rb +69 -68
- data/lib/lono/template/upload.rb +75 -0
- data/lib/lono/version.rb +1 -1
- data/lib/starter_projects/json_project/config/{lono.rb → templates/base/blog.rb} +3 -3
- data/lib/starter_projects/{yaml_project/config/lono/api.rb → json_project/config/templates/base/stacks.rb} +9 -9
- data/lib/starter_projects/json_project/templates/{db.json.erb → db.json} +1 -1
- data/lib/starter_projects/json_project/templates/partial/{host_record.json.erb → host_record.json} +0 -0
- data/lib/starter_projects/json_project/templates/partial/{server.json.erb → server.json} +2 -2
- data/lib/starter_projects/json_project/templates/user_data/{app.sh.erb → app.sh} +0 -0
- data/lib/starter_projects/json_project/templates/user_data/{db.sh.erb → db.sh} +0 -0
- data/lib/starter_projects/json_project/templates/user_data/{db2.sh.erb → db2.sh} +0 -0
- data/lib/starter_projects/json_project/templates/user_data/{ruby_script.rb.erb → ruby_script.rb} +0 -0
- data/lib/starter_projects/json_project/templates/{web.json.erb → web.json} +2 -2
- data/lib/starter_projects/yaml_project/config/{lono.rb → templates/base/blog.rb} +4 -8
- data/lib/starter_projects/{json_project/config/lono/api.rb → yaml_project/config/templates/base/stacks.rb} +11 -13
- data/lib/starter_projects/yaml_project/config/templates/prod/stacks.rb +1 -0
- data/lib/starter_projects/yaml_project/config/templates/stag/stacks.rb +1 -0
- data/lib/starter_projects/yaml_project/config/variables/base/variables.rb +4 -0
- data/lib/starter_projects/yaml_project/config/variables/prod/variables.rb +1 -0
- data/lib/starter_projects/yaml_project/config/variables/stag/variables.rb +1 -0
- data/lib/starter_projects/yaml_project/helpers/my_custom_helper.rb +17 -0
- data/lib/starter_projects/yaml_project/params/{api-web-prod.txt → base/api-web-prod.txt} +0 -0
- data/lib/starter_projects/yaml_project/params/{example.txt → base/example.txt} +0 -0
- data/lib/starter_projects/yaml_project/params/prod/example.txt +1 -0
- data/lib/starter_projects/yaml_project/params/stag/example.txt +1 -0
- data/lib/starter_projects/yaml_project/templates/{db.yml.erb → db.yml} +1 -1
- data/lib/starter_projects/yaml_project/templates/{example.yml.erb → example.yml} +0 -0
- data/lib/starter_projects/yaml_project/templates/partial/{host_record.yml.erb → host_record.yml} +0 -0
- data/lib/starter_projects/yaml_project/templates/partial/{server.yml.erb → server.yml} +0 -0
- data/lib/starter_projects/yaml_project/templates/partial/user_data/{bootstrap.sh.erb → bootstrap.sh} +0 -0
- data/lib/starter_projects/yaml_project/templates/{web.yml.erb → web.yml} +2 -2
- data/lono.gemspec +1 -0
- data/spec/fixtures/params/baseonly/params/base/network.txt +1 -0
- data/spec/fixtures/params/envonly/params/prod/network.txt +1 -0
- data/spec/fixtures/params/overlay/params/base/network.txt +1 -0
- data/spec/fixtures/params/overlay/params/prod/network.txt +1 -0
- data/spec/lib/lono/new_spec.rb +1 -1
- data/spec/lib/lono/param/generator_spec.rb +34 -0
- data/spec/lib/lono/template/dsl_spec.rb +1 -1
- data/spec/lib/lono/template_spec.rb +5 -0
- metadata +60 -22
data/lib/lono/template/dsl.rb
CHANGED
@@ -2,7 +2,7 @@ class Lono::Template::DSL
|
|
2
2
|
def initialize(options={})
|
3
3
|
@options = options
|
4
4
|
@project_root = @options[:project_root] || '.'
|
5
|
-
@
|
5
|
+
@config_path = "#{@project_root}/config"
|
6
6
|
Lono::ProjectChecker.check(@project_root)
|
7
7
|
@templates = []
|
8
8
|
@results = {}
|
@@ -15,12 +15,23 @@ class Lono::Template::DSL
|
|
15
15
|
write_output
|
16
16
|
end
|
17
17
|
|
18
|
+
# Instance eval's all the files within each folder under
|
19
|
+
# config/lono/base and config/lono/[LONO_ENV]
|
20
|
+
# Base gets base first and then the LONO_ENV configs get evaluate second.
|
21
|
+
# This means the env specific configs override the base configs.
|
18
22
|
def evaluate_templates
|
19
|
-
|
20
|
-
|
23
|
+
evaluate_folder("base")
|
24
|
+
evaluate_folder(LONO_ENV)
|
21
25
|
@detected_format = detect_format
|
22
26
|
end
|
23
27
|
|
28
|
+
def evaluate_folder(folder)
|
29
|
+
paths = Dir.glob("#{@config_path}/templates/#{folder}/**/*")
|
30
|
+
paths.select{ |e| File.file?(e) }.each do |path|
|
31
|
+
evaluate_template(path)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
24
35
|
def evaluate_template(path)
|
25
36
|
begin
|
26
37
|
instance_eval(File.read(path), path)
|
@@ -55,35 +66,14 @@ class Lono::Template::DSL
|
|
55
66
|
end
|
56
67
|
end
|
57
68
|
|
58
|
-
# Detects the format of the templates.
|
69
|
+
# Detects the format of the templates. Checks the extension of all the
|
59
70
|
# templates files.
|
60
71
|
# All the templates must be of the same format, either all json or all yaml.
|
61
72
|
def detect_format
|
62
|
-
#
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
if formats.size > 1
|
67
|
-
puts "ERROR: Detected multiple formats: #{formats.join(", ")}".colorize(:red)
|
68
|
-
puts "All the source values in the template blocks in the config folder must have the same format extension."
|
69
|
-
exit 1
|
70
|
-
else
|
71
|
-
found_format = formats.first
|
72
|
-
if found_format
|
73
|
-
detected_format = found_format.sub(/^\./,'')
|
74
|
-
detected_format = "yaml" if detected_format == "yml"
|
75
|
-
else # empty templates, no templates defined yet
|
76
|
-
detected_format = "yaml" # defaults to yaml
|
77
|
-
end
|
78
|
-
end
|
79
|
-
detected_format
|
80
|
-
end
|
81
|
-
|
82
|
-
# load any templates defined in project/config/lono/*
|
83
|
-
def load_subfolder
|
84
|
-
Dir.glob("#{File.dirname(@path)}/lono/**/*").select{ |e| File.file? e }.each do |path|
|
85
|
-
evaluate_template(path)
|
86
|
-
end
|
73
|
+
extensions = Dir.glob("#{@project_root}/templates/**/*").map do |path|
|
74
|
+
File.extname(path).sub(/^\./,'')
|
75
|
+
end.reject(&:empty?).uniq
|
76
|
+
extensions.include?('yml') ? 'yml' : 'json' # defaults to yml - falls back to json
|
87
77
|
end
|
88
78
|
|
89
79
|
def template(name, &block)
|
@@ -91,8 +81,9 @@ class Lono::Template::DSL
|
|
91
81
|
end
|
92
82
|
|
93
83
|
def build_templates
|
84
|
+
options = @options.merge(detected_format: @detected_format)
|
94
85
|
@templates.each do |t|
|
95
|
-
@results[t[:name]] = Lono::Template::Template.new(t[:name], t[:block],
|
86
|
+
@results[t[:name]] = Lono::Template::Template.new(t[:name], t[:block], options).build
|
96
87
|
end
|
97
88
|
end
|
98
89
|
|
@@ -102,7 +93,8 @@ class Lono::Template::DSL
|
|
102
93
|
FileUtils.mkdir(output_path) unless File.exist?(output_path)
|
103
94
|
puts "Generating CloudFormation templates:" unless @options[:quiet]
|
104
95
|
@results.each do |name,text|
|
105
|
-
path = "#{output_path}/#{name}".sub(/^\.\//,'')
|
96
|
+
path = "#{output_path}/#{name}".sub(/^\.\//,'') # strip leading '.'
|
97
|
+
path += ".#{@detected_format}"
|
106
98
|
puts " #{path}" unless @options[:quiet]
|
107
99
|
ensure_parent_dir(path)
|
108
100
|
validate(text, path)
|
@@ -112,7 +104,6 @@ class Lono::Template::DSL
|
|
112
104
|
end
|
113
105
|
end
|
114
106
|
|
115
|
-
# TODO: set @detected_format upon DSL.new
|
116
107
|
def validate(text, path)
|
117
108
|
if @detected_format == "json"
|
118
109
|
validate_json(text, path)
|
@@ -156,7 +147,7 @@ class Lono::Template::DSL
|
|
156
147
|
def yaml_format(text)
|
157
148
|
comment =<<~EOS
|
158
149
|
# This file was generated with lono. Do not edit directly, the changes will be lost.
|
159
|
-
# More info:
|
150
|
+
# More info: http://lono.cloud
|
160
151
|
EOS
|
161
152
|
"#{comment}#{remove_blank_lines(text)}"
|
162
153
|
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module Lono::Template::Helpers
|
2
|
+
def template_s3_path(template_name)
|
3
|
+
format = @_detected_format.sub('yaml','yml')
|
4
|
+
template_path = "#{template_name}.#{format}"
|
5
|
+
|
6
|
+
# must have settings.s3_path for this to owrk
|
7
|
+
settings = Lono::Settings.new(@project_root)
|
8
|
+
if settings.s3_path
|
9
|
+
# high jacking Upload for useful s3_https_url method
|
10
|
+
upload = Lono::Template::Upload.new(@_options)
|
11
|
+
upload.s3_https_url(template_path)
|
12
|
+
else
|
13
|
+
message = "template_s3_path helper called but s3.path not configured in lono/settings.yml"
|
14
|
+
puts "WARN: #{message}".colorize(:yellow)
|
15
|
+
message
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def template_params(param_name)
|
20
|
+
param_path = "params/#{LONO_ENV}/#{param_name}.txt"
|
21
|
+
generator_options = {
|
22
|
+
project_root: @_project_root,
|
23
|
+
path: param_path,
|
24
|
+
allow_no_file: true
|
25
|
+
}.merge(@_options)
|
26
|
+
generator = Lono::Param::Generator.new(param_name, generator_options)
|
27
|
+
# do not generate because lono cfn calling logic already generated it we only need the values
|
28
|
+
generator.params # Returns Array in underscore keys format
|
29
|
+
end
|
30
|
+
|
31
|
+
def user_data(path, vars={})
|
32
|
+
path = "#{@_project_root}/templates/user_data/#{path}"
|
33
|
+
template = IO.read(path)
|
34
|
+
variables(vars)
|
35
|
+
result = erb_result(path, template)
|
36
|
+
output = []
|
37
|
+
result.split("\n").each do |line|
|
38
|
+
output += transform(line)
|
39
|
+
end
|
40
|
+
json = output.to_json
|
41
|
+
json[0] = '' # remove first char: [
|
42
|
+
json.chop! # remove last char: ]
|
43
|
+
end
|
44
|
+
|
45
|
+
def ref(name)
|
46
|
+
%Q|{"Ref"=>"#{name}"}|
|
47
|
+
end
|
48
|
+
|
49
|
+
def find_in_map(*args)
|
50
|
+
%Q|{"Fn::FindInMap" => [ #{transform_array(args)} ]}|
|
51
|
+
end
|
52
|
+
|
53
|
+
def base64(value)
|
54
|
+
%Q|{"Fn::Base64"=>"#{value}"}|
|
55
|
+
end
|
56
|
+
|
57
|
+
def get_att(*args)
|
58
|
+
%Q|{"Fn::GetAtt" => [ #{transform_array(args)} ]}|
|
59
|
+
end
|
60
|
+
|
61
|
+
def get_azs(region="AWS::Region")
|
62
|
+
%Q|{"Fn::GetAZs"=>"#{region}"}|
|
63
|
+
end
|
64
|
+
|
65
|
+
def join(delimiter, values)
|
66
|
+
%Q|{"Fn::Join" => ["#{delimiter}", [ #{transform_array(values)} ]]}|
|
67
|
+
end
|
68
|
+
|
69
|
+
def select(index, list)
|
70
|
+
%Q|{"Fn::Select" => ["#{index}", [ #{transform_array(list)} ]]}|
|
71
|
+
end
|
72
|
+
|
73
|
+
def partial_exist?(path)
|
74
|
+
path = partial_path_for(path)
|
75
|
+
path = auto_add_format(path)
|
76
|
+
path && File.exist?(path)
|
77
|
+
end
|
78
|
+
|
79
|
+
# The partial's path is a relative path given without the extension and
|
80
|
+
#
|
81
|
+
# Example:
|
82
|
+
# Given: file in templates/partial/iam/docker.yml
|
83
|
+
# The path should be: iam/docker
|
84
|
+
#
|
85
|
+
# If the user specifies the extension then use that instead of auto-adding
|
86
|
+
# the detected format.
|
87
|
+
def partial(path,vars={}, options={})
|
88
|
+
path = partial_path_for(path)
|
89
|
+
path = auto_add_format(path)
|
90
|
+
|
91
|
+
template = IO.read(path)
|
92
|
+
variables(vars)
|
93
|
+
result = erb_result(path, template)
|
94
|
+
result = indent(result, options[:indent]) if options[:indent]
|
95
|
+
if options[:indent]
|
96
|
+
# Add empty line at beginning because empty lines gets stripped during
|
97
|
+
# processing anyway. This allows the user to call partial without having
|
98
|
+
# to put the partial call at very beginning of the line.
|
99
|
+
# This only should happen if user is using indent option.
|
100
|
+
["\n", result].join("\n")
|
101
|
+
else
|
102
|
+
result
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# add indentation
|
107
|
+
def indent(text, indentation_amount)
|
108
|
+
text.split("\n").map do |line|
|
109
|
+
" " * indentation_amount + line
|
110
|
+
end.join("\n")
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
def partial_path_for(path)
|
115
|
+
"#{@_project_root}/templates/partial/#{path}"
|
116
|
+
end
|
117
|
+
|
118
|
+
def auto_add_format(path)
|
119
|
+
# Return immediately if user provided explicit extension
|
120
|
+
extension = File.extname(path) # current extension
|
121
|
+
return path if !extension.empty?
|
122
|
+
|
123
|
+
# Else let's auto detect
|
124
|
+
paths = Dir.glob("#{path}.*")
|
125
|
+
|
126
|
+
if paths.size == 1 # non-ambiguous match
|
127
|
+
return paths.first
|
128
|
+
end
|
129
|
+
|
130
|
+
if paths.size > 1 # ambiguous match
|
131
|
+
puts "ERROR: Multiple possible partials found:".colorize(:red)
|
132
|
+
paths.each do |path|
|
133
|
+
puts " #{path}"
|
134
|
+
end
|
135
|
+
puts "Please specify an extension in the name to remove the ambiguity.".colorize(:green)
|
136
|
+
exit 1
|
137
|
+
end
|
138
|
+
|
139
|
+
# Account for case when user wants to include a file with no extension at all
|
140
|
+
return path if File.exist?(path) && !File.directory?(path)
|
141
|
+
|
142
|
+
path # original path if this point is reached
|
143
|
+
end
|
144
|
+
end
|
@@ -3,45 +3,83 @@ require 'json'
|
|
3
3
|
require 'base64'
|
4
4
|
|
5
5
|
class Lono::Template::Template
|
6
|
+
include Lono::Template::Helpers
|
6
7
|
include ERB::Util
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
@
|
9
|
+
def initialize(name, block=nil, options={})
|
10
|
+
# Taking care to name instance variables with _ in front because we load the
|
11
|
+
# variables from config/variables and those instance variables can clobber these
|
12
|
+
# instance variables
|
13
|
+
@_name = name
|
14
|
+
@_options = options
|
15
|
+
@_detected_format = options[:detected_format]
|
16
|
+
@_block = block
|
17
|
+
@_project_root = options[:project_root] || '.'
|
18
|
+
@_config_path = "#{@_project_root}/config"
|
19
|
+
@_source = default_source(name)
|
20
|
+
end
|
21
|
+
|
22
|
+
def default_source(name)
|
23
|
+
"#{@_project_root}/templates/#{name}.#{@_detected_format}" # defaults to name, source method overrides
|
13
24
|
end
|
14
25
|
|
15
26
|
def build
|
16
|
-
|
17
|
-
|
18
|
-
|
27
|
+
load_variables
|
28
|
+
load_custom_helpers
|
29
|
+
instance_eval(&@_block) if @_block
|
30
|
+
template = IO.read(@_source)
|
31
|
+
erb_result(@_source, template)
|
19
32
|
end
|
20
33
|
|
21
|
-
def
|
22
|
-
|
34
|
+
def load_variables
|
35
|
+
load_variables_folder("base")
|
36
|
+
load_variables_folder(LONO_ENV)
|
23
37
|
end
|
24
38
|
|
25
|
-
|
26
|
-
|
27
|
-
|
39
|
+
# Load the variables defined in config/variables/* to make available in the
|
40
|
+
# template blocks in config/templates/*.
|
41
|
+
#
|
42
|
+
# Example:
|
43
|
+
#
|
44
|
+
# `config/variables/base/variables.rb`:
|
45
|
+
# @foo = 123
|
46
|
+
#
|
47
|
+
# `config/templates/base/resources.rb`:
|
48
|
+
# template "mytemplate.yml" do
|
49
|
+
# source "mytemplate.yml.erb"
|
50
|
+
# variables(foo: @foo)
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# NOTE: Only able to make instance variables avaialble with instance_eval
|
54
|
+
# Wasnt able to make local variables available.
|
55
|
+
def load_variables_folder(folder)
|
56
|
+
paths = Dir.glob("#{@_config_path}/variables/#{folder}/**/*")
|
57
|
+
paths.select{ |e| File.file? e }.each do |path|
|
58
|
+
instance_eval(IO.read(path))
|
28
59
|
end
|
29
60
|
end
|
30
61
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
62
|
+
# Load custom helper methods from the user's infra repo
|
63
|
+
def load_custom_helpers
|
64
|
+
Dir.glob("#{@_project_root}/helpers/**/*_helper.rb").each do |path|
|
65
|
+
filename = path.sub(%r{.*/},'').sub('.rb','')
|
66
|
+
module_name = filename.classify
|
67
|
+
|
68
|
+
require path
|
69
|
+
self.class.send :include, module_name.constantize
|
70
|
+
end
|
71
|
+
|
38
72
|
end
|
39
73
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
74
|
+
def source(path)
|
75
|
+
@_source = path[0..0] == '/' ? path : "#{@_project_root}/templates/#{path}"
|
76
|
+
@_source += ".#{@_detected_format}"
|
77
|
+
end
|
78
|
+
|
79
|
+
def variables(vars={})
|
80
|
+
vars.each do |var,value|
|
81
|
+
instance_variable_set("@#{var}", value)
|
82
|
+
end
|
45
83
|
end
|
46
84
|
|
47
85
|
def erb_result(path, template)
|
@@ -49,6 +87,7 @@ class Lono::Template::Template
|
|
49
87
|
ERB.new(template, nil, "-").result(binding)
|
50
88
|
rescue Exception => e
|
51
89
|
puts e
|
90
|
+
puts e.backtrace if ENV['DEBUG']
|
52
91
|
|
53
92
|
# how to know where ERB stopped? - https://www.ruby-forum.com/topic/182051
|
54
93
|
# syntax errors have the (erb):xxx info in e.message
|
@@ -57,7 +96,7 @@ class Lono::Template::Template
|
|
57
96
|
error_info ||= e.backtrace.grep(/\(erb\)/)[0]
|
58
97
|
raise unless error_info # unable to find the (erb):xxx: error line
|
59
98
|
line = error_info.split(':')[1].to_i
|
60
|
-
puts "Error evaluating ERB template on line #{line.to_s.colorize(:red)} of: #{path.sub(/^\.\//, '')}"
|
99
|
+
puts "Error evaluating ERB template on line #{line.to_s.colorize(:red)} of: #{path.sub(/^\.\//, '').colorize(:green)}"
|
61
100
|
|
62
101
|
template_lines = template.split("\n")
|
63
102
|
context = 5 # lines of context
|
@@ -75,48 +114,6 @@ class Lono::Template::Template
|
|
75
114
|
end
|
76
115
|
end
|
77
116
|
|
78
|
-
def user_data(path, vars={})
|
79
|
-
path = "#{@options[:project_root]}/templates/user_data/#{path}"
|
80
|
-
template = IO.read(path)
|
81
|
-
variables(vars)
|
82
|
-
result = erb_result(path, template)
|
83
|
-
output = []
|
84
|
-
result.split("\n").each do |line|
|
85
|
-
output += transform(line)
|
86
|
-
end
|
87
|
-
json = output.to_json
|
88
|
-
json[0] = '' # remove first char: [
|
89
|
-
json.chop! # remove last char: ]
|
90
|
-
end
|
91
|
-
|
92
|
-
def ref(name)
|
93
|
-
%Q|{"Ref"=>"#{name}"}|
|
94
|
-
end
|
95
|
-
|
96
|
-
def find_in_map(*args)
|
97
|
-
%Q|{"Fn::FindInMap" => [ #{transform_array(args)} ]}|
|
98
|
-
end
|
99
|
-
|
100
|
-
def base64(value)
|
101
|
-
%Q|{"Fn::Base64"=>"#{value}"}|
|
102
|
-
end
|
103
|
-
|
104
|
-
def get_att(*args)
|
105
|
-
%Q|{"Fn::GetAtt" => [ #{transform_array(args)} ]}|
|
106
|
-
end
|
107
|
-
|
108
|
-
def get_azs(region="AWS::Region")
|
109
|
-
%Q|{"Fn::GetAZs"=>"#{region}"}|
|
110
|
-
end
|
111
|
-
|
112
|
-
def join(delimiter, values)
|
113
|
-
%Q|{"Fn::Join" => ["#{delimiter}", [ #{transform_array(values)} ]]}|
|
114
|
-
end
|
115
|
-
|
116
|
-
def select(index, list)
|
117
|
-
%Q|{"Fn::Select" => ["#{index}", [ #{transform_array(list)} ]]}|
|
118
|
-
end
|
119
|
-
|
120
117
|
def transform_array(arr)
|
121
118
|
arr.map! {|x| x =~ /=>/ ? x : x.inspect }
|
122
119
|
arr.join(',')
|
@@ -248,4 +245,8 @@ class Lono::Template::Template
|
|
248
245
|
def encode_base64(text)
|
249
246
|
Base64.strict_encode64(text).strip
|
250
247
|
end
|
248
|
+
|
249
|
+
def name
|
250
|
+
@_name
|
251
|
+
end
|
251
252
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'json'
|
3
|
+
require 'base64'
|
4
|
+
|
5
|
+
class Lono::Template::Upload
|
6
|
+
include Lono::Template::AwsServices
|
7
|
+
|
8
|
+
def initialize(options={})
|
9
|
+
@options = options
|
10
|
+
@project_root = options[:project_root] || '.'
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
ensure_s3_setup!
|
15
|
+
paths = Dir.glob("#{@project_root}/output/**/*")
|
16
|
+
paths.reject { |p| p =~ %r{output/params} }.
|
17
|
+
select { |p| File.file?(p) }.each do |path|
|
18
|
+
upload(path)
|
19
|
+
end
|
20
|
+
say "Templates uploaded to s3."
|
21
|
+
end
|
22
|
+
|
23
|
+
def upload(path)
|
24
|
+
pretty_path = path.sub(/^\.\//, '')
|
25
|
+
key = "#{s3_path}/#{LONO_ENV}/#{pretty_path.sub(/^output\//,'')}"
|
26
|
+
s3_full_path = "s3://#{s3_bucket}/#{key}"
|
27
|
+
|
28
|
+
resp = s3.put_object(
|
29
|
+
body: IO.read(path),
|
30
|
+
bucket: s3_bucket,
|
31
|
+
key: key,
|
32
|
+
storage_class: "REDUCED_REDUNDANCY"
|
33
|
+
) unless @options[:noop]
|
34
|
+
|
35
|
+
message = "Uploaded: #{pretty_path} to #{s3_full_path}"
|
36
|
+
message = "NOOP: #{message}" if @options[:noop]
|
37
|
+
say message
|
38
|
+
end
|
39
|
+
|
40
|
+
# https://s3.amazonaws.com/mybucket/cloudformation-templates/prod/parent.yml
|
41
|
+
def s3_https_url(template_path)
|
42
|
+
ensure_s3_setup!
|
43
|
+
"https://s3.amazonaws.com/#{s3_bucket}/#{s3_path}/#{LONO_ENV}/#{template_path}"
|
44
|
+
end
|
45
|
+
|
46
|
+
# Example:
|
47
|
+
# s3_bucket('s3://mybucket/templates/storage/path') => mybucket
|
48
|
+
def s3_bucket
|
49
|
+
@s3_full_path.sub('s3://','').split('/').first
|
50
|
+
end
|
51
|
+
|
52
|
+
# Example:
|
53
|
+
# s3_bucket('s3://mybucket/templates/storage/path') => templates/storage/path
|
54
|
+
def s3_path
|
55
|
+
@s3_full_path.sub('s3://','').split('/')[1..-1].join('/')
|
56
|
+
end
|
57
|
+
|
58
|
+
# nice warning if the s3 path not found
|
59
|
+
def ensure_s3_setup!
|
60
|
+
return if @options[:noop]
|
61
|
+
|
62
|
+
settings = Lono::Settings.new(@project_root)
|
63
|
+
if settings.s3_path
|
64
|
+
@s3_full_path = settings.s3_path
|
65
|
+
else
|
66
|
+
say "Unable to upload templates to s3 because you have not configured the s3.path option in .lono/settings.yml.".colorize(:red)
|
67
|
+
say "Please configure .lono/settings.yml with s3.path. Refer to http://lono.cloud/docs/settings/ for more help.".colorize(:red)
|
68
|
+
exit 1
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def say(message)
|
73
|
+
puts message unless @options[:quiet]
|
74
|
+
end
|
75
|
+
end
|