lono 3.5.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +15 -4
- data/.rspec +1 -0
- data/CHANGELOG.md +15 -1
- data/Gemfile +3 -3
- data/Guardfile +17 -8
- data/{LICENSE → LICENSE.txt} +1 -1
- data/README.md +20 -12
- data/Rakefile +1 -2
- data/{bin → exe}/lono +1 -0
- data/lib/lono.rb +12 -9
- data/lib/lono/cfn.rb +7 -9
- data/lib/lono/cfn/{aws_services.rb → aws_service.rb} +1 -1
- data/lib/lono/cfn/base.rb +41 -38
- data/lib/lono/cfn/create.rb +6 -2
- data/lib/lono/cfn/delete.rb +2 -2
- data/lib/lono/cfn/diff.rb +1 -1
- data/lib/lono/cfn/preview.rb +26 -15
- data/lib/lono/cfn/update.rb +11 -9
- data/lib/lono/cfn/util.rb +3 -3
- data/lib/lono/clean.rb +1 -1
- data/lib/lono/cli.rb +71 -39
- data/lib/lono/command.rb +42 -18
- data/lib/lono/completer.rb +162 -0
- data/lib/lono/completer/script.rb +6 -0
- data/lib/lono/completer/script.sh +10 -0
- data/lib/lono/completion.rb +15 -0
- data/lib/lono/core.rb +23 -9
- data/lib/lono/core/config.rb +20 -0
- data/lib/lono/default/settings.yml +33 -13
- data/lib/lono/help.rb +6 -79
- data/lib/lono/help/cfn.md +6 -0
- data/lib/lono/help/cfn/create.md +22 -0
- data/lib/lono/help/cfn/delete.md +5 -0
- data/lib/lono/help/cfn/diff.md +5 -0
- data/lib/lono/help/cfn/download.md +5 -0
- data/lib/lono/help/cfn/preview.md +11 -0
- data/lib/lono/help/cfn/update.md +21 -0
- data/lib/lono/help/completion.md +22 -0
- data/lib/lono/help/completion_script.md +3 -0
- data/lib/lono/help/generate.md +7 -0
- data/lib/lono/help/hello.md +5 -0
- data/lib/lono/help/import.md +7 -0
- data/lib/lono/help/inspect.md +4 -0
- data/lib/lono/help/inspect/depends.md +3 -0
- data/lib/lono/help/inspect/summary.md +3 -0
- data/lib/lono/help/new.md +8 -0
- data/lib/lono/help/param.md +3 -0
- data/lib/lono/{param/help.rb → help/param/generate.md} +1 -9
- data/lib/lono/help/script/build.md +5 -0
- data/lib/lono/help/script/upload.md +8 -0
- data/lib/lono/help/template.md +4 -0
- data/lib/lono/help/template/bashify.md +4 -0
- data/lib/lono/help/template/generate.md +7 -0
- data/lib/lono/help/user_data.md +3 -0
- data/lib/lono/importer.rb +43 -20
- data/lib/lono/inspector.rb +2 -19
- data/lib/lono/inspector/base.rb +2 -2
- data/lib/lono/inspector/{depends.rb → graph.rb} +3 -3
- data/lib/lono/inspector/summary.rb +1 -1
- data/lib/lono/new.rb +79 -26
- data/lib/lono/new/helper.rb +16 -0
- data/lib/lono/new/message.rb +35 -0
- data/lib/lono/param.rb +1 -2
- data/lib/lono/param/generator.rb +34 -86
- data/lib/lono/project_checker.rb +35 -40
- data/lib/lono/script.rb +19 -0
- data/lib/lono/script/base.rb +9 -0
- data/lib/lono/script/build.rb +73 -0
- data/lib/lono/script/upload.rb +81 -0
- data/lib/lono/sequence.rb +33 -0
- data/lib/lono/setting.rb +83 -0
- data/lib/lono/template.rb +8 -9
- data/lib/lono/template/{aws_services.rb → aws_service.rb} +1 -1
- data/lib/lono/template/context.rb +73 -0
- data/lib/lono/template/dsl.rb +63 -64
- data/lib/lono/template/helper.rb +201 -0
- data/lib/lono/template/template.rb +29 -221
- data/lib/lono/template/upload.rb +41 -33
- data/lib/lono/upgrade4.rb +175 -0
- data/lib/lono/user_data.rb +31 -0
- data/lib/lono/version.rb +1 -1
- data/lib/starter_projects/autoscaling/.gitignore +1 -0
- data/lib/starter_projects/{json_project → autoscaling}/Gemfile +0 -0
- data/lib/starter_projects/{yaml_project → autoscaling}/Guardfile +0 -0
- data/lib/starter_projects/autoscaling/README.md +118 -0
- data/lib/starter_projects/autoscaling/app/definitions/base.rb +2 -0
- data/lib/starter_projects/autoscaling/app/templates/autoscaling.yml +682 -0
- data/lib/starter_projects/autoscaling/config/params/base/autoscaling.txt +6 -0
- data/lib/starter_projects/autoscaling/config/settings.yml +33 -0
- data/lib/starter_projects/ec2/.gitignore +1 -0
- data/lib/starter_projects/{yaml_project → ec2}/Gemfile +0 -0
- data/lib/starter_projects/{json_project → ec2}/Guardfile +1 -1
- data/lib/starter_projects/ec2/README.md +86 -0
- data/lib/starter_projects/ec2/app/definitions/base.rb +2 -0
- data/lib/starter_projects/ec2/app/definitions/development.rb +1 -0
- data/lib/starter_projects/ec2/app/definitions/production.rb +1 -0
- data/lib/starter_projects/{yaml_project → ec2/app}/helpers/my_custom_helper.rb +0 -0
- data/lib/starter_projects/{json_project/templates/user_data/app.sh → ec2/app/partials/user_data/bootstrap.sh} +1 -2
- data/lib/starter_projects/{yaml_project → ec2/app}/templates/example.yml +0 -0
- data/lib/starter_projects/{json_project/params/base/api-web.txt → ec2/config/params/base/example.txt} +0 -0
- data/lib/starter_projects/ec2/config/params/development/example.txt +3 -0
- data/lib/starter_projects/ec2/config/params/production/example.txt +2 -0
- data/lib/starter_projects/ec2/config/settings.yml +33 -0
- data/lib/starter_projects/ec2/config/variables/base.rb +3 -0
- data/lib/starter_projects/ec2/config/variables/development.rb +2 -0
- data/lib/starter_projects/ec2/config/variables/production.rb +2 -0
- data/lib/starter_projects/ec2/welcome.txt +8 -0
- data/lib/starter_projects/skeleton/.gitignore +1 -0
- data/lib/starter_projects/skeleton/Gemfile +3 -0
- data/lib/starter_projects/skeleton/Guardfile +12 -0
- data/lib/starter_projects/skeleton/README.md +53 -0
- data/{spec/fixtures/my_project/templates/.gitkeep → lib/starter_projects/skeleton/app/definitions/base.rb} +0 -0
- data/lib/starter_projects/skeleton/config/settings.yml +33 -0
- data/lib/starter_projects/skeleton/welcome.txt +7 -0
- data/lono.gemspec +12 -10
- data/spec/fixtures/lono_project/.gitignore +1 -0
- data/spec/fixtures/lono_project/Gemfile +3 -0
- data/spec/fixtures/lono_project/Guardfile +12 -0
- data/spec/fixtures/lono_project/app/definitions/base.rb +10 -0
- data/spec/fixtures/lono_project/app/definitions/base/more.rb +7 -0
- data/spec/fixtures/lono_project/app/definitions/development.rb +1 -0
- data/spec/fixtures/lono_project/app/definitions/production.rb +1 -0
- data/spec/fixtures/lono_project/app/helpers/custom_helper.rb +5 -0
- data/spec/fixtures/lono_project/app/partials/security_group.yml +10 -0
- data/{lib/starter_projects/yaml_project/templates/partial → spec/fixtures/lono_project/app/partials}/user_data/bootstrap.sh +8 -2
- data/spec/fixtures/lono_project/app/templates/example.yml +50 -0
- data/{lib/starter_projects/yaml_project/params/base/api-web-prod.txt → spec/fixtures/lono_project/config/params/base/example.txt} +1 -0
- data/spec/fixtures/lono_project/config/params/development/example.txt +1 -0
- data/spec/fixtures/lono_project/config/params/production/example.txt +1 -0
- data/spec/fixtures/lono_project/config/settings.yml +31 -0
- data/spec/fixtures/lono_project/config/variables/base.rb +3 -0
- data/spec/fixtures/lono_project/config/variables/development.rb +1 -0
- data/spec/fixtures/lono_project/config/variables/production.rb +1 -0
- data/spec/fixtures/params/envonly/params/{prod → development}/network.txt +0 -0
- data/spec/fixtures/params/overlay/params/{prod → development}/network.txt +0 -0
- data/spec/fixtures/raw_templates/aws-waf-security-automations.template +2 -2
- data/spec/lib/lono/cfn_spec.rb +6 -9
- data/spec/lib/lono/cli_spec.rb +44 -0
- data/spec/lib/lono/completion_spec.rb +17 -0
- data/spec/lib/lono/inspect_spec.rb +6 -15
- data/spec/lib/lono/param/generator_spec.rb +45 -26
- data/spec/lib/lono/param_spec.rb +1 -3
- data/spec/lib/lono/setting_spec.rb +47 -0
- data/spec/lib/lono/template/dsl_spec.rb +33 -157
- data/spec/lib/lono/template_spec.rb +4 -16
- data/spec/spec_helper.rb +45 -14
- metadata +168 -82
- data/.coveralls.yml +0 -1
- data/lib/lono/cfn/help.rb +0 -103
- data/lib/lono/current_region.rb +0 -42
- data/lib/lono/inspector/help.rb +0 -21
- data/lib/lono/settings.rb +0 -45
- data/lib/lono/template/help.rb +0 -25
- data/lib/lono/template/helpers.rb +0 -136
- data/lib/starter_projects/json_project/.gitignore +0 -1
- data/lib/starter_projects/json_project/config/templates/base/blog.rb +0 -20
- data/lib/starter_projects/json_project/config/templates/base/stacks.rb +0 -58
- data/lib/starter_projects/json_project/templates/db.json +0 -212
- data/lib/starter_projects/json_project/templates/partial/host_record.json +0 -28
- data/lib/starter_projects/json_project/templates/partial/server.json +0 -45
- data/lib/starter_projects/json_project/templates/user_data/db.sh +0 -39
- data/lib/starter_projects/json_project/templates/user_data/db2.sh +0 -2
- data/lib/starter_projects/json_project/templates/user_data/ruby_script.rb +0 -5
- data/lib/starter_projects/json_project/templates/web.json +0 -386
- data/lib/starter_projects/yaml_project/.gitignore +0 -1
- data/lib/starter_projects/yaml_project/config/templates/base/blog.rb +0 -20
- data/lib/starter_projects/yaml_project/config/templates/base/stacks.rb +0 -56
- data/lib/starter_projects/yaml_project/config/templates/prod/stacks.rb +0 -1
- data/lib/starter_projects/yaml_project/config/templates/stag/stacks.rb +0 -1
- data/lib/starter_projects/yaml_project/config/variables/base/variables.rb +0 -4
- data/lib/starter_projects/yaml_project/config/variables/prod/variables.rb +0 -1
- data/lib/starter_projects/yaml_project/config/variables/stag/variables.rb +0 -1
- data/lib/starter_projects/yaml_project/params/base/example.txt +0 -2
- data/lib/starter_projects/yaml_project/params/prod/example.txt +0 -1
- data/lib/starter_projects/yaml_project/params/stag/example.txt +0 -1
- data/lib/starter_projects/yaml_project/templates/db.yml +0 -148
- data/lib/starter_projects/yaml_project/templates/partial/host_record.yml +0 -14
- data/lib/starter_projects/yaml_project/templates/partial/server.yml +0 -59
- data/lib/starter_projects/yaml_project/templates/web.yml +0 -206
- data/spec/fixtures/my_project/config/templates/base/stacks.rb +0 -3
- data/spec/fixtures/my_project/params/my-stack.txt +0 -3
- data/spec/fixtures/my_project/templates/my-stack.yml +0 -0
- data/spec/lib/lono/new_spec.rb +0 -59
- data/spec/lib/lono/template/template_spec.rb +0 -104
- data/spec/lib/lono_spec.rb +0 -27
data/lib/lono/template/dsl.rb
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
class Lono::Template::DSL
|
2
2
|
def initialize(options={})
|
3
3
|
@options = options
|
4
|
-
@config_path = "#{Lono.root}/config"
|
5
4
|
Lono::ProjectChecker.check
|
5
|
+
Lono::ProjectChecker.empty_templates
|
6
6
|
@templates = []
|
7
7
|
@results = {}
|
8
|
-
@detected_format = nil
|
9
8
|
end
|
10
9
|
|
11
10
|
def run(options={})
|
@@ -14,24 +13,29 @@ class Lono::Template::DSL
|
|
14
13
|
write_output
|
15
14
|
end
|
16
15
|
|
17
|
-
# Instance eval's
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
16
|
+
# Instance eval's the template declarations in app/definitions in this order:
|
17
|
+
#
|
18
|
+
# app/definitions/base.rb
|
19
|
+
# app/definitions/base - all files in folder
|
20
|
+
# app/definitions/[Lono.env].rb
|
21
|
+
# app/definitions/[Lono.env] - all files in folder
|
22
|
+
#
|
23
|
+
# So Lono.env specific template declarations override base template declarations.
|
21
24
|
def evaluate_templates
|
25
|
+
evaluate_template("base")
|
22
26
|
evaluate_folder("base")
|
27
|
+
evaluate_template(Lono.env)
|
23
28
|
evaluate_folder(Lono.env)
|
24
|
-
@detected_format = detect_format
|
25
29
|
end
|
26
30
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
evaluate_template(path)
|
31
|
-
end
|
31
|
+
def evaluate_template(name)
|
32
|
+
path = "#{Lono.config.definitions_path}/#{name}.rb"
|
33
|
+
evaluate_template_path(path)
|
32
34
|
end
|
33
35
|
|
34
|
-
def
|
36
|
+
def evaluate_template_path(path)
|
37
|
+
return unless File.exist?(path)
|
38
|
+
|
35
39
|
begin
|
36
40
|
instance_eval(File.read(path), path)
|
37
41
|
rescue Exception => e
|
@@ -41,6 +45,13 @@ class Lono::Template::DSL
|
|
41
45
|
end
|
42
46
|
end
|
43
47
|
|
48
|
+
def evaluate_folder(folder)
|
49
|
+
paths = Dir.glob("#{Lono.config.definitions_path}/#{folder}/**/*")
|
50
|
+
paths.select{ |e| File.file?(e) }.each do |path|
|
51
|
+
evaluate_template_path(path)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
44
55
|
# Prints out a user friendly task_definition error message
|
45
56
|
def template_evaluation_error(e)
|
46
57
|
error_info = e.backtrace.first
|
@@ -65,85 +76,73 @@ class Lono::Template::DSL
|
|
65
76
|
end
|
66
77
|
end
|
67
78
|
|
68
|
-
# Detects the format of the templates. Checks the extension of all the
|
69
|
-
# templates files.
|
70
|
-
# All the templates must be of the same format, either all json or all yaml.
|
71
|
-
def detect_format
|
72
|
-
extensions = Dir.glob("#{Lono.root}/templates/**/*").map do |path|
|
73
|
-
File.extname(path).sub(/^\./,'')
|
74
|
-
end.reject(&:empty?).uniq
|
75
|
-
extensions.include?('yml') ? 'yml' : 'json' # defaults to yml - falls back to json
|
76
|
-
end
|
77
|
-
|
78
79
|
def template(name, &block)
|
79
80
|
@templates << {name: name, block: block}
|
80
81
|
end
|
81
82
|
|
82
83
|
def build_templates
|
83
|
-
options = @options.merge(detected_format: @detected_format)
|
84
84
|
@templates.each do |t|
|
85
|
-
@results[t[:name]] = Lono::Template::Template.new(t[:name], t[:block], options).build
|
85
|
+
@results[t[:name]] = Lono::Template::Template.new(t[:name], t[:block], @options).build
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
89
|
def write_output
|
90
|
-
output_path = "#{Lono.
|
90
|
+
output_path = "#{Lono.config.output_path}/templates"
|
91
91
|
FileUtils.rm_rf(output_path) if @options[:clean]
|
92
|
-
FileUtils.
|
92
|
+
FileUtils.mkdir_p(output_path)
|
93
93
|
puts "Generating CloudFormation templates:" unless @options[:quiet]
|
94
94
|
@results.each do |name,text|
|
95
95
|
path = "#{output_path}/#{name}".sub(/^\.\//,'') # strip leading '.'
|
96
|
-
path += "
|
96
|
+
path += ".yml"
|
97
97
|
puts " #{path}" unless @options[:quiet]
|
98
98
|
ensure_parent_dir(path)
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
def validate(text, path)
|
107
|
-
if @detected_format == "json"
|
108
|
-
validate_json(text, path)
|
109
|
-
else
|
110
|
-
validate_yaml(text, path)
|
99
|
+
text = commented(text)
|
100
|
+
IO.write(path, text) # write file first so validate method is simpler
|
101
|
+
validate(path)
|
111
102
|
end
|
112
103
|
end
|
113
104
|
|
114
|
-
def
|
105
|
+
def validate(path)
|
106
|
+
text = IO.read(path)
|
115
107
|
begin
|
116
|
-
YAML.load(
|
108
|
+
YAML.load(text)
|
117
109
|
rescue Psych::SyntaxError => e
|
118
|
-
|
119
|
-
puts "ERROR: #{e.message}".colorize(:red)
|
120
|
-
File.open(path, 'w') {|f| f.write(yaml) }
|
121
|
-
exit 1
|
110
|
+
handle_yaml_syntax_error(e, path)
|
122
111
|
end
|
123
112
|
end
|
124
113
|
|
125
|
-
def
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
puts "Invalid json. Output written to #{path} for debugging".colorize(:red)
|
130
|
-
puts "ERROR: #{e.message}".colorize(:red)
|
131
|
-
File.open(path, 'w') {|f| f.write(json) }
|
132
|
-
exit 1
|
133
|
-
end
|
134
|
-
end
|
114
|
+
def handle_yaml_syntax_error(e, path)
|
115
|
+
io = StringIO.new
|
116
|
+
io.puts "Invalid yaml. Output written to debugging: #{path}".colorize(:red)
|
117
|
+
io.puts "ERROR: #{e.message}".colorize(:red)
|
135
118
|
|
136
|
-
|
137
|
-
|
138
|
-
|
119
|
+
# Grab line info. Example error:
|
120
|
+
# ERROR: (<unknown>): could not find expected ':' while scanning a simple key at line 2 column 1
|
121
|
+
md = e.message.match(/at line (\d+) column (\d+)/)
|
122
|
+
line = md[1].to_i
|
139
123
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
124
|
+
lines = IO.read(path).split("\n")
|
125
|
+
context = 5 # lines of context
|
126
|
+
top, bottom = [line-context-1, 0].max, line+context-1
|
127
|
+
spacing = lines.size.to_s.size
|
128
|
+
lines[top..bottom].each_with_index do |line_content, index|
|
129
|
+
line_number = top+index+1
|
130
|
+
if line_number == line
|
131
|
+
io.printf("%#{spacing}d %s\n".colorize(:red), line_number, line_content)
|
132
|
+
else
|
133
|
+
io.printf("%#{spacing}d %s\n", line_number, line_content)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
if ENV['TEST']
|
138
|
+
io.string
|
139
|
+
else
|
140
|
+
puts io.string
|
141
|
+
exit 1
|
142
|
+
end
|
144
143
|
end
|
145
144
|
|
146
|
-
def
|
145
|
+
def commented(text)
|
147
146
|
comment =<<~EOS
|
148
147
|
# This file was generated with lono. Do not edit directly, the changes will be lost.
|
149
148
|
# More info: http://lono.cloud
|
@@ -0,0 +1,201 @@
|
|
1
|
+
# This is included into Lono::Template::Context.
|
2
|
+
# It has access to the original thor CLI options via @options.
|
3
|
+
#
|
4
|
+
# @options gets passed into:
|
5
|
+
#
|
6
|
+
# Lono::Template::Context.new(@options)
|
7
|
+
module Lono::Template::Helper
|
8
|
+
# Bash code that is meant to included in user-data
|
9
|
+
def extract_scripts(options={})
|
10
|
+
check_s3_folder_settings!
|
11
|
+
|
12
|
+
settings = setting.data["extract_scripts"] || {}
|
13
|
+
options = settings.merge(options)
|
14
|
+
# defaults also here in case they are removed from settings
|
15
|
+
to = options[:to] || "/opt"
|
16
|
+
user = options[:as] || "ec2-user"
|
17
|
+
|
18
|
+
if Dir.glob("#{Lono.config.scripts_path}/*").empty?
|
19
|
+
puts "WARN: you are using the extract_scripts helper method but you do not have any app/scripts.".colorize(:yellow)
|
20
|
+
calling_line = caller[0].split(':')[0..1].join(':')
|
21
|
+
puts "Called from: #{calling_line}"
|
22
|
+
return ""
|
23
|
+
end
|
24
|
+
|
25
|
+
<<-BASH_CODE
|
26
|
+
# Generated from the lono extract_scripts helper.
|
27
|
+
# Downloads scripts from s3, extract them, and setup.
|
28
|
+
mkdir -p #{to}
|
29
|
+
aws s3 cp #{scripts_s3_path} #{to}/
|
30
|
+
cd #{to}
|
31
|
+
tar zxf #{to}/#{scripts_name}
|
32
|
+
chmod -R a+x #{to}/scripts
|
33
|
+
chown -R #{user}:#{user} #{to}/scripts
|
34
|
+
BASH_CODE
|
35
|
+
end
|
36
|
+
|
37
|
+
def check_s3_folder_settings!
|
38
|
+
return if setting.s3_folder
|
39
|
+
|
40
|
+
puts "Helper method called that requires the s3_folder to be set at:"
|
41
|
+
lines = caller.reject { |l| l =~ %r{lib/lono} } # hide internal lono trace
|
42
|
+
puts " #{lines[0]}"
|
43
|
+
|
44
|
+
puts "Please configure your settings.yml with an s3_folder.".colorize(:red)
|
45
|
+
puts "Detected AWS_PROFILE #{ENV['AWS_PROFILE'].inspect}"
|
46
|
+
exit 1
|
47
|
+
end
|
48
|
+
|
49
|
+
def scripts_name
|
50
|
+
File.basename(scripts_s3_path)
|
51
|
+
end
|
52
|
+
|
53
|
+
def scripts_s3_path
|
54
|
+
upload = Lono::Script::Upload.new
|
55
|
+
upload.s3_dest
|
56
|
+
end
|
57
|
+
|
58
|
+
def template_s3_path(template_name)
|
59
|
+
check_s3_folder_settings!
|
60
|
+
# high jacking Upload for useful s3_https_url method
|
61
|
+
template_path = "#{template_name}.yml"
|
62
|
+
upload = Lono::Template::Upload.new(@options)
|
63
|
+
upload.s3_https_url(template_path)
|
64
|
+
end
|
65
|
+
|
66
|
+
def template_params(param_name)
|
67
|
+
generator_options = {
|
68
|
+
allow_no_file: true
|
69
|
+
}.merge(@options)
|
70
|
+
generator = Lono::Param::Generator.new(param_name, generator_options)
|
71
|
+
# do not generate because lono cfn calling logic already generated it we only need the values
|
72
|
+
generator.params # Returns Array in underscore keys format
|
73
|
+
end
|
74
|
+
|
75
|
+
# Adjust the partial path so that it will use app/user_data
|
76
|
+
def user_data(path,vars={}, options={})
|
77
|
+
options.merge!(user_data: true)
|
78
|
+
partial(path,vars, options)
|
79
|
+
end
|
80
|
+
|
81
|
+
# The partial's path is a relative path.
|
82
|
+
#
|
83
|
+
# Example:
|
84
|
+
# Given file in app/partials/iam/docker.yml
|
85
|
+
#
|
86
|
+
# <%= partial("iam/docker", {}, indent: 10) %>
|
87
|
+
# <%= partial("iam/docker.yml", {}, indent: 10) %>
|
88
|
+
#
|
89
|
+
# If the user specifies the extension then use that instead of auto-adding
|
90
|
+
# the detected format.
|
91
|
+
def partial(path,vars={}, options={})
|
92
|
+
path = options[:user_data] ?
|
93
|
+
user_data_path_for(path) :
|
94
|
+
partial_path_for(path)
|
95
|
+
path = auto_add_format(path)
|
96
|
+
|
97
|
+
instance_variables!(vars)
|
98
|
+
result = render_path(path)
|
99
|
+
|
100
|
+
result = indent(result, options[:indent]) if options[:indent]
|
101
|
+
result + "\n"
|
102
|
+
end
|
103
|
+
|
104
|
+
# add indentation
|
105
|
+
def indent(text, indentation_amount)
|
106
|
+
text.split("\n").map do |line|
|
107
|
+
" " * indentation_amount + line
|
108
|
+
end.join("\n")
|
109
|
+
end
|
110
|
+
|
111
|
+
def partial_exist?(path)
|
112
|
+
path = partial_path_for(path)
|
113
|
+
path = auto_add_format(path)
|
114
|
+
path && File.exist?(path)
|
115
|
+
end
|
116
|
+
|
117
|
+
def current_region
|
118
|
+
region = Aws.config[:region]
|
119
|
+
region ||= ENV['AWS_REGION']
|
120
|
+
return region if region
|
121
|
+
|
122
|
+
default_region = 'us-east-1' # fallback if default not found in ~/.aws/config
|
123
|
+
if ENV['AWS_PROFILE']
|
124
|
+
path = "#{ENV['HOME']}/.aws/config"
|
125
|
+
if File.exist?(path)
|
126
|
+
lines = IO.readlines(path)
|
127
|
+
capture_default, capture_current = false, false
|
128
|
+
lines.each do | line|
|
129
|
+
if line.include?('[default]')
|
130
|
+
capture_default = true # next line
|
131
|
+
next
|
132
|
+
end
|
133
|
+
if capture_default && line.match(/region = /)
|
134
|
+
# over default from above
|
135
|
+
default_region = line.split(' = ').last.strip
|
136
|
+
capture_default = false
|
137
|
+
end
|
138
|
+
|
139
|
+
md = line.match(/\[profile (.*)\]/)
|
140
|
+
if md && md[1] == ENV['AWS_PROFILE']
|
141
|
+
capture_current = true
|
142
|
+
next
|
143
|
+
end
|
144
|
+
if capture_current && line.match(/region = /)
|
145
|
+
region = line.split(' = ').last.strip
|
146
|
+
capture_current = false
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
region ||= default_region
|
152
|
+
return region if region
|
153
|
+
end
|
154
|
+
|
155
|
+
'us-east-1' # default
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
def render_path(path)
|
160
|
+
RenderMePretty.result(path, context: self)
|
161
|
+
end
|
162
|
+
|
163
|
+
def user_data_path_for(path)
|
164
|
+
"#{Lono.config.user_data_path}/#{path}"
|
165
|
+
end
|
166
|
+
|
167
|
+
def partial_path_for(path)
|
168
|
+
"#{Lono.config.partials_path}/#{path}"
|
169
|
+
end
|
170
|
+
|
171
|
+
def auto_add_format(path)
|
172
|
+
# Return immediately if user provided explicit extension
|
173
|
+
extension = File.extname(path) # current extension
|
174
|
+
return path if !extension.empty?
|
175
|
+
|
176
|
+
# Else let's auto detect
|
177
|
+
paths = Dir.glob("#{path}.*")
|
178
|
+
|
179
|
+
if paths.size == 1 # non-ambiguous match
|
180
|
+
return paths.first
|
181
|
+
end
|
182
|
+
|
183
|
+
if paths.size > 1 # ambiguous match
|
184
|
+
puts "ERROR: Multiple possible partials found:".colorize(:red)
|
185
|
+
paths.each do |path|
|
186
|
+
puts " #{path}"
|
187
|
+
end
|
188
|
+
puts "Please specify an extension in the name to remove the ambiguity.".colorize(:green)
|
189
|
+
exit 1
|
190
|
+
end
|
191
|
+
|
192
|
+
# Account for case when user wants to include a file with no extension at all
|
193
|
+
return path if File.exist?(path) && !File.directory?(path)
|
194
|
+
|
195
|
+
path # original path if this point is reached
|
196
|
+
end
|
197
|
+
|
198
|
+
def setting
|
199
|
+
@setting ||= Lono::Setting.new
|
200
|
+
end
|
201
|
+
end
|
@@ -3,249 +3,57 @@ require 'json'
|
|
3
3
|
require 'base64'
|
4
4
|
|
5
5
|
class Lono::Template::Template
|
6
|
-
include Lono::Template::Helpers
|
7
|
-
include Lono::CurrentRegion
|
8
6
|
include ERB::Util
|
9
7
|
|
8
|
+
# Main template DSL methods are: source and variables
|
9
|
+
#
|
10
|
+
# template "example-2" do
|
11
|
+
# source "example"
|
12
|
+
# variables(test: 1)
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
attr_reader :name
|
10
16
|
def initialize(name, block=nil, options={})
|
11
17
|
# Taking care to name instance variables with _ in front because we load the
|
12
18
|
# variables from config/variables and those instance variables can clobber these
|
13
19
|
# instance variables
|
14
|
-
@
|
15
|
-
@
|
16
|
-
@
|
17
|
-
@
|
18
|
-
@_config_path = "#{Lono.root}/config"
|
19
|
-
@_source = default_source(name)
|
20
|
-
end
|
21
|
-
|
22
|
-
def default_source(name)
|
23
|
-
"#{Lono.root}/templates/#{name}.#{@_detected_format}" # defaults to name, source method overrides
|
24
|
-
end
|
25
|
-
|
26
|
-
def build
|
27
|
-
load_variables
|
28
|
-
load_custom_helpers
|
29
|
-
instance_eval(&@_block) if @_block
|
30
|
-
template = IO.read(@_source)
|
31
|
-
erb_result(@_source, template)
|
32
|
-
end
|
33
|
-
|
34
|
-
def load_variables
|
35
|
-
load_variables_folder("base")
|
36
|
-
load_variables_folder(Lono.env)
|
37
|
-
end
|
38
|
-
|
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))
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
# Load custom helper methods from the user's infra repo
|
63
|
-
def load_custom_helpers
|
64
|
-
Dir.glob("#{Lono.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
|
20
|
+
@name = name
|
21
|
+
@block = block
|
22
|
+
@options = options
|
23
|
+
@source_path = default_source_path(name)
|
71
24
|
end
|
72
25
|
|
26
|
+
# Returns path, example: ./app/templates/example.yml
|
73
27
|
def source(path)
|
74
|
-
@
|
75
|
-
@
|
28
|
+
@source_path = path[0..0] == '/' ? path : "#{Lono.config.templates_path}/#{path}"
|
29
|
+
@source_path += ".yml"
|
76
30
|
end
|
77
31
|
|
78
32
|
def variables(vars={})
|
79
33
|
vars.each do |var,value|
|
80
|
-
instance_variable_set("@#{var}", value)
|
34
|
+
context.instance_variable_set("@#{var}", value)
|
81
35
|
end
|
82
36
|
end
|
83
37
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
rescue Exception => e
|
88
|
-
puts e
|
89
|
-
puts e.backtrace if ENV['DEBUG']
|
90
|
-
|
91
|
-
# how to know where ERB stopped? - https://www.ruby-forum.com/topic/182051
|
92
|
-
# syntax errors have the (erb):xxx info in e.message
|
93
|
-
# undefined variables have (erb):xxx info in e.backtrac
|
94
|
-
error_info = e.message.split("\n").grep(/\(erb\)/)[0]
|
95
|
-
error_info ||= e.backtrace.grep(/\(erb\)/)[0]
|
96
|
-
raise unless error_info # unable to find the (erb):xxx: error line
|
97
|
-
line = error_info.split(':')[1].to_i
|
98
|
-
puts "Error evaluating ERB template on line #{line.to_s.colorize(:red)} of: #{path.sub(/^\.\//, '').colorize(:green)}"
|
99
|
-
|
100
|
-
template_lines = template.split("\n")
|
101
|
-
context = 5 # lines of context
|
102
|
-
top, bottom = [line-context-1, 0].max, line+context-1
|
103
|
-
spacing = template_lines.size.to_s.size
|
104
|
-
template_lines[top..bottom].each_with_index do |line_content, index|
|
105
|
-
line_number = top+index+1
|
106
|
-
if line_number == line
|
107
|
-
printf("%#{spacing}d %s\n".colorize(:red), line_number, line_content)
|
108
|
-
else
|
109
|
-
printf("%#{spacing}d %s\n", line_number, line_content)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
exit 1 unless ENV['TEST']
|
113
|
-
end
|
38
|
+
# internal methods
|
39
|
+
def default_source_path(name)
|
40
|
+
"#{Lono.config.templates_path}/#{name}.yml" # defaults to name, source method overrides
|
114
41
|
end
|
115
42
|
|
116
|
-
def
|
117
|
-
|
118
|
-
arr.join(',')
|
119
|
-
end
|
43
|
+
def build
|
44
|
+
instance_eval(&@block) if @block
|
120
45
|
|
121
|
-
|
122
|
-
|
123
|
-
data = evaluate(data)
|
124
|
-
if data[-1].is_a?(String)
|
125
|
-
data[0..-2] + ["#{data[-1]}\n"]
|
46
|
+
if File.exist?(@source_path)
|
47
|
+
RenderMePretty.result(@source_path, context: context)
|
126
48
|
else
|
127
|
-
|
49
|
+
puts "ERROR: #{@source_path} does not exist, but it was used as a template source.".colorize(:red)
|
50
|
+
exit 1
|
128
51
|
end
|
129
52
|
end
|
130
53
|
|
131
|
-
#
|
132
|
-
#
|
133
|
-
|
134
|
-
|
135
|
-
#
|
136
|
-
# The positions of tokens taking into account when brackets start and close,
|
137
|
-
# handles nested brackets.
|
138
|
-
def bracket_positions(line)
|
139
|
-
positions,pair,count = [],[],0
|
140
|
-
|
141
|
-
line.split('').each_with_index do |char,i|
|
142
|
-
pair << i if pair.empty?
|
143
|
-
|
144
|
-
first_pair_char = line[pair[0]]
|
145
|
-
if first_pair_char == '{' # object logic
|
146
|
-
if char == '{'
|
147
|
-
count += 1
|
148
|
-
end
|
149
|
-
|
150
|
-
if char == '}'
|
151
|
-
count -= 1
|
152
|
-
if count == 0
|
153
|
-
pair << i
|
154
|
-
positions << pair
|
155
|
-
pair = []
|
156
|
-
end
|
157
|
-
end
|
158
|
-
else # string logic
|
159
|
-
lookahead = line[i+1]
|
160
|
-
if lookahead == '{'
|
161
|
-
pair << i
|
162
|
-
positions << pair
|
163
|
-
pair = []
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end # end of loop
|
167
|
-
|
168
|
-
# for string logic when lookahead does not contain a object token
|
169
|
-
# need to clear out what's left to match the final pair
|
170
|
-
if !pair.empty?
|
171
|
-
pair << line.size - 1
|
172
|
-
positions << pair
|
173
|
-
end
|
174
|
-
|
175
|
-
positions
|
176
|
-
end
|
177
|
-
|
178
|
-
# Input:
|
179
|
-
# Array - bracket_positions
|
180
|
-
# Ouput:
|
181
|
-
# Array - positions that can be use to determine what to parse
|
182
|
-
def parse_positions(line)
|
183
|
-
positions = bracket_positions(line)
|
184
|
-
positions.flatten
|
185
|
-
end
|
186
|
-
|
187
|
-
# Input
|
188
|
-
# String line of code to decompose into chunks, some can be transformed into objects
|
189
|
-
# Output
|
190
|
-
# Array of strings, some can be transformed into objects
|
191
|
-
#
|
192
|
-
# Example:
|
193
|
-
# line = 'a{b}c{d{d}d}e' # nested brackets
|
194
|
-
# template.decompose(line).should == ['a','{b}','c','{d{d}d}','e']
|
195
|
-
def decompose(line)
|
196
|
-
positions = parse_positions(line)
|
197
|
-
return [line] if positions.empty?
|
198
|
-
|
199
|
-
result = []
|
200
|
-
str = ''
|
201
|
-
until positions.empty?
|
202
|
-
left = positions.shift
|
203
|
-
right = positions.shift
|
204
|
-
token = line[left..right]
|
205
|
-
# if cfn object, add to the result set but after clearing out
|
206
|
-
# the temp str that is being built up when the token is just a string
|
207
|
-
if cfn_object?(token)
|
208
|
-
unless str.empty? # first token might be a object
|
209
|
-
result << str
|
210
|
-
str = ''
|
211
|
-
end
|
212
|
-
result << token
|
213
|
-
else
|
214
|
-
str << token # keeps building up the string
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
|
-
# at the of the loop there's a leftover string, unless the last token
|
219
|
-
# is an object
|
220
|
-
result << str unless str.empty?
|
221
|
-
|
222
|
-
result
|
223
|
-
end
|
224
|
-
|
225
|
-
def cfn_object?(s)
|
226
|
-
exact = %w[Ref]
|
227
|
-
pattern = %w[Fn::]
|
228
|
-
exact_match = !!exact.detect {|word| s.include?(word)}
|
229
|
-
pattern_match = !!pattern.detect {|p| s =~ Regexp.new(p)}
|
230
|
-
(exact_match || pattern_match) && s =~ /^{/ && s =~ /=>/
|
231
|
-
end
|
232
|
-
|
233
|
-
def recompose(decomposition)
|
234
|
-
decomposition.map { |s| cfn_object?(s) ? eval(s) : s }
|
235
|
-
end
|
236
|
-
|
237
|
-
def evaluate(line)
|
238
|
-
recompose(decompose(line))
|
239
|
-
end
|
240
|
-
|
241
|
-
# For simple just parameters files that can also be generated with lono, the CFN
|
242
|
-
# Fn::Base64 function is not available and as lono is not being used in the context
|
243
|
-
# of CloudFormation. So this can be used in it's place.
|
244
|
-
def encode_base64(text)
|
245
|
-
Base64.strict_encode64(text).strip
|
246
|
-
end
|
247
|
-
|
248
|
-
def name
|
249
|
-
@_name
|
54
|
+
# Context for ERB rendering.
|
55
|
+
# This is where we control what references get passed to the ERB rendering.
|
56
|
+
def context
|
57
|
+
@context ||= Lono::Template::Context.new(@options)
|
250
58
|
end
|
251
59
|
end
|