lono 1.1.3 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitmodules +3 -0
- data/CHANGELOG.md +8 -0
- data/README.md +150 -39
- data/bin/lono +2 -2
- data/circle.yml +4 -0
- data/lib/lono.rb +16 -7
- data/lib/lono/cfn.rb +64 -0
- data/lib/lono/cfn/aws_services.rb +37 -0
- data/lib/lono/cfn/base.rb +144 -0
- data/lib/lono/cfn/create.rb +34 -0
- data/lib/lono/cfn/delete.rb +26 -0
- data/lib/lono/cfn/diff.rb +43 -0
- data/lib/lono/cfn/help.rb +93 -0
- data/lib/lono/cfn/preview.rb +133 -0
- data/lib/lono/cfn/update.rb +62 -0
- data/lib/lono/cfn/util.rb +21 -0
- data/lib/lono/cli.rb +19 -10
- data/lib/lono/command.rb +25 -0
- data/lib/lono/help.rb +59 -0
- data/lib/lono/new.rb +3 -2
- data/lib/lono/param.rb +20 -0
- data/lib/lono/param/generator.rb +90 -0
- data/lib/lono/param/help.rb +15 -0
- data/lib/lono/project_checker.rb +44 -0
- data/lib/lono/template.rb +22 -248
- data/lib/lono/template/bashify.rb +39 -0
- data/lib/lono/template/dsl.rb +139 -0
- data/lib/lono/template/help.rb +25 -0
- data/lib/lono/template/template.rb +251 -0
- data/lib/lono/version.rb +1 -1
- data/lib/{starter_project_yaml → starter_projects/json_project}/Gemfile +0 -1
- data/lib/{starter_project_json → starter_projects/json_project}/Guardfile +0 -0
- data/lib/{starter_project_json → starter_projects/json_project}/config/lono.rb +0 -0
- data/lib/{starter_project_json → starter_projects/json_project}/config/lono/api.rb +0 -0
- data/lib/starter_projects/json_project/params/api-web-prod.txt +20 -0
- data/lib/{starter_project_json → starter_projects/json_project}/templates/db.json.erb +0 -0
- data/lib/{starter_project_json → starter_projects/json_project}/templates/partial/host_record.json.erb +0 -0
- data/lib/{starter_project_json → starter_projects/json_project}/templates/partial/server.json.erb +0 -0
- data/lib/{starter_project_json → starter_projects/json_project}/templates/user_data/app.sh.erb +0 -0
- data/lib/{starter_project_json → starter_projects/json_project}/templates/user_data/db.sh.erb +0 -0
- data/lib/{starter_project_json → starter_projects/json_project}/templates/user_data/db2.sh.erb +0 -0
- data/lib/{starter_project_json → starter_projects/json_project}/templates/user_data/ruby_script.rb.erb +0 -0
- data/lib/{starter_project_json → starter_projects/json_project}/templates/web.json.erb +0 -0
- data/lib/{starter_project_json → starter_projects/yaml_project}/Gemfile +0 -1
- data/lib/{starter_project_yaml → starter_projects/yaml_project}/Guardfile +0 -0
- data/lib/{starter_project_yaml → starter_projects/yaml_project}/config/lono.rb +0 -0
- data/lib/{starter_project_yaml → starter_projects/yaml_project}/config/lono/api.rb +0 -0
- data/lib/starter_projects/yaml_project/params/api-web-prod.txt +20 -0
- data/lib/{starter_project_yaml → starter_projects/yaml_project}/templates/db.yml.erb +0 -0
- data/lib/{starter_project_yaml → starter_projects/yaml_project}/templates/partial/host_record.yml.erb +0 -0
- data/lib/{starter_project_yaml → starter_projects/yaml_project}/templates/partial/server.yml.erb +0 -0
- data/lib/{starter_project_yaml → starter_projects/yaml_project}/templates/partial/user_data/bootstrap.sh.erb +0 -0
- data/lib/{starter_project_yaml → starter_projects/yaml_project}/templates/web.yml.erb +0 -0
- data/lono.gemspec +15 -10
- data/spec/fixtures/my_project/config/lono.rb +1 -0
- data/spec/fixtures/my_project/params/my-stack.txt +3 -0
- data/spec/fixtures/my_project/templates/.gitkeep +0 -0
- data/spec/fixtures/my_project/templates/my-stack.yml.erb +0 -0
- data/spec/lib/lono/cfn_spec.rb +35 -0
- data/spec/lib/lono/new_spec.rb +3 -3
- data/spec/lib/lono/param_spec.rb +15 -0
- data/spec/lib/lono/{dsl_spec.rb → template/dsl_spec.rb} +9 -9
- data/spec/lib/lono/template/template_spec.rb +104 -0
- data/spec/lib/lono/template_spec.rb +22 -37
- data/spec/lib/lono_spec.rb +6 -83
- data/vendor/plissken/Gemfile +14 -0
- data/vendor/plissken/LICENSE.txt +20 -0
- data/vendor/plissken/README.md +46 -0
- data/vendor/plissken/Rakefile +56 -0
- data/vendor/plissken/VERSION +1 -0
- data/vendor/plissken/lib/plissken.rb +1 -0
- data/vendor/plissken/lib/plissken/ext/hash/to_snake_keys.rb +45 -0
- data/vendor/plissken/plissken.gemspec +61 -0
- data/vendor/plissken/spec/lib/to_snake_keys_spec.rb +177 -0
- data/vendor/plissken/spec/spec_helper.rb +90 -0
- data/vendor/plissken/test/helper.rb +20 -0
- data/vendor/plissken/test/plissken/ext/hash/to_snake_keys_test.rb +184 -0
- data/vendor/plissken/test/test_plissken.rb +2 -0
- metadata +115 -39
- data/lib/lono/bashify.rb +0 -41
- data/lib/lono/cli/help.rb +0 -37
- data/lib/lono/dsl.rb +0 -132
data/lib/lono/command.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Lono
|
4
|
+
class Command < Thor
|
5
|
+
class << self
|
6
|
+
def dispatch(m, args, options, config)
|
7
|
+
# Allow calling for help via:
|
8
|
+
# lono generate help
|
9
|
+
# lono generate -h
|
10
|
+
# lono generate --help
|
11
|
+
# lono generate -D
|
12
|
+
#
|
13
|
+
# as well thor's nomral setting as
|
14
|
+
#
|
15
|
+
# lono help generate
|
16
|
+
help_flags = Thor::HELP_MAPPINGS + ["help"]
|
17
|
+
if args.length > 1 && !(args & help_flags).empty?
|
18
|
+
args -= help_flags
|
19
|
+
args.insert(-2, "help")
|
20
|
+
end
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/lono/help.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
module Lono::Help
|
2
|
+
def new_long_desc
|
3
|
+
<<-EOL
|
4
|
+
Examples:
|
5
|
+
|
6
|
+
$ lono new project
|
7
|
+
|
8
|
+
$ lono new lono
|
9
|
+
EOL
|
10
|
+
end
|
11
|
+
|
12
|
+
def generate
|
13
|
+
<<-EOL
|
14
|
+
Examples:
|
15
|
+
|
16
|
+
$ lono generate
|
17
|
+
|
18
|
+
$ lono g -c # shortcut
|
19
|
+
|
20
|
+
Builds both CloudFormation template and parameter files based on lono project and writes them to the output folder on the filesystem.
|
21
|
+
EOL
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def template
|
26
|
+
<<-EOL
|
27
|
+
Examples:
|
28
|
+
|
29
|
+
$ lono template generate --help
|
30
|
+
|
31
|
+
$ lono template bashify --help
|
32
|
+
EOL
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
def cfn
|
37
|
+
<<-EOL
|
38
|
+
Examples:
|
39
|
+
|
40
|
+
$ lono cfn create my-stack
|
41
|
+
|
42
|
+
$ lono cfn preview my-stack
|
43
|
+
|
44
|
+
$ lono cfn update my-stack
|
45
|
+
|
46
|
+
$ lono cfn delete my-stack
|
47
|
+
EOL
|
48
|
+
end
|
49
|
+
|
50
|
+
def param
|
51
|
+
<<-EOL
|
52
|
+
Examples:
|
53
|
+
|
54
|
+
$ lono param generate
|
55
|
+
EOL
|
56
|
+
end
|
57
|
+
|
58
|
+
extend self
|
59
|
+
end
|
data/lib/lono/new.rb
CHANGED
@@ -9,11 +9,12 @@ module Lono
|
|
9
9
|
|
10
10
|
def run
|
11
11
|
puts "Setting up lono project" unless options[:quiet]
|
12
|
-
source_root = File.expand_path("../../
|
12
|
+
source_root = File.expand_path("../../starter_projects/#{@format}_project", __FILE__)
|
13
13
|
paths = Dir.glob("#{source_root}/**/*").
|
14
14
|
select {|p| File.file?(p) }
|
15
15
|
paths.each do |src|
|
16
|
-
|
16
|
+
# starter_projects/yaml_project/ ->
|
17
|
+
regexp = Regexp.new(".*starter_projects/#{@format}_project/")
|
17
18
|
dest = src.gsub(regexp,'')
|
18
19
|
dest = "#{@project_root}/#{dest}"
|
19
20
|
|
data/lib/lono/param.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "thor"
|
2
|
+
|
3
|
+
module Lono
|
4
|
+
class Param < Command
|
5
|
+
autoload :Help, 'lono/param/help'
|
6
|
+
autoload :Generator, 'lono/param/generator'
|
7
|
+
|
8
|
+
class_option :verbose, type: :boolean
|
9
|
+
class_option :noop, type: :boolean
|
10
|
+
class_option :mute, type: :boolean
|
11
|
+
class_option :project_root, desc: "project root to use", default: '.'
|
12
|
+
|
13
|
+
desc "generate NAME", "generate parameter json file for NAME"
|
14
|
+
long_desc Help.generate
|
15
|
+
option :path, desc: "Name of the source that maps to the params txt file. name -> params/NAME.txt. Use this to override the params/NAME.txt convention"
|
16
|
+
def generate
|
17
|
+
Generator.generate_all(options)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
class Lono::Param::Generator
|
2
|
+
def self.generate_all(options)
|
3
|
+
puts "Generating params files"
|
4
|
+
project_root = options[:project_root] || '.'
|
5
|
+
Dir.glob("#{project_root}/params/**/*.txt").each do |path|
|
6
|
+
next if File.directory?(path)
|
7
|
+
name = path.sub(/.*params\//, '').sub('.txt', '')
|
8
|
+
param = Lono::Param::Generator.new(name, options)
|
9
|
+
param.generate
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(name, options)
|
14
|
+
@name = name
|
15
|
+
@options = options
|
16
|
+
@project_root = options[:project_root] || '.'
|
17
|
+
@source_path = options[:path] || "#{@project_root}/params/#{@name}.txt"
|
18
|
+
end
|
19
|
+
|
20
|
+
def generate
|
21
|
+
# useful option for lono cfn
|
22
|
+
return if @options[:allow_no_file] && !File.exist?(@source_path)
|
23
|
+
|
24
|
+
if File.exist?(@source_path)
|
25
|
+
contents = IO.read(@source_path)
|
26
|
+
data = convert_to_cfn_format(contents)
|
27
|
+
json = JSON.pretty_generate(data)
|
28
|
+
write_output(json)
|
29
|
+
puts "Params file generated for #{@name} at #{output_path}" unless @options[:mute]
|
30
|
+
else
|
31
|
+
puts "#{@source_path} could not be found? Are you sure it exist?"
|
32
|
+
exit 1
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# useful for when calling CloudFormation via the aws-sdk gem
|
37
|
+
def params
|
38
|
+
# useful option for lono cfn
|
39
|
+
return {} if @options[:allow_no_file] && !File.exist?(@source_path)
|
40
|
+
|
41
|
+
contents = IO.read(@source_path)
|
42
|
+
convert_to_cfn_format(contents, :underscore)
|
43
|
+
end
|
44
|
+
|
45
|
+
def parse_contents(contents)
|
46
|
+
lines = contents.split("\n")
|
47
|
+
# remove comment at the end of the line
|
48
|
+
lines.map! { |l| l.sub(/#.*/,'').strip }
|
49
|
+
# filter out commented lines
|
50
|
+
lines = lines.reject { |l| l =~ /(^|\s)#/i }
|
51
|
+
# filter out empty lines
|
52
|
+
lines = lines.reject { |l| l.strip.empty? }
|
53
|
+
lines
|
54
|
+
end
|
55
|
+
|
56
|
+
def convert_to_cfn_format(contents, casing=:camel)
|
57
|
+
lines = parse_contents(contents)
|
58
|
+
params = []
|
59
|
+
lines.each do |line|
|
60
|
+
key,value = line.strip.split("=").map {|x| x.strip}
|
61
|
+
param = if value == "use_previous_value"
|
62
|
+
{
|
63
|
+
ParameterKey: key,
|
64
|
+
UsePreviousValue: true
|
65
|
+
}
|
66
|
+
elsif value
|
67
|
+
{
|
68
|
+
ParameterKey: key,
|
69
|
+
ParameterValue: value
|
70
|
+
}
|
71
|
+
end
|
72
|
+
if param
|
73
|
+
param = param.to_snake_keys if casing == :underscore
|
74
|
+
params << param
|
75
|
+
end
|
76
|
+
end
|
77
|
+
params
|
78
|
+
end
|
79
|
+
|
80
|
+
def output_path
|
81
|
+
"#{@project_root}/output/params/#{@name}.json".sub(/\.\//,'')
|
82
|
+
end
|
83
|
+
|
84
|
+
def write_output(json)
|
85
|
+
dir = File.dirname(output_path)
|
86
|
+
FileUtils.mkdir_p(dir) unless File.exist?(dir)
|
87
|
+
IO.write(output_path, json)
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Lono::Param::Help
|
2
|
+
class << self
|
3
|
+
def generate
|
4
|
+
<<-EOL
|
5
|
+
Example:
|
6
|
+
|
7
|
+
To generate a CloudFormation json parameter files in the params folder to the output/params folder.
|
8
|
+
|
9
|
+
$ lono-params generate
|
10
|
+
|
11
|
+
If you have params/my-stack.txt. It will generate a CloudFormation json file in output/params/my-stack.json.
|
12
|
+
EOL
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Lono
|
2
|
+
class ProjectChecker
|
3
|
+
# Checks to see command is running in a lono project.
|
4
|
+
# If not, provide a friendly message and exit.
|
5
|
+
def self.check(project_root)
|
6
|
+
new(project_root).check
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(project_root)
|
10
|
+
@project_root = project_root
|
11
|
+
end
|
12
|
+
|
13
|
+
def check
|
14
|
+
config_folder_exist
|
15
|
+
templates_folder_exist
|
16
|
+
empty_folders
|
17
|
+
end
|
18
|
+
|
19
|
+
def config_folder_exist
|
20
|
+
unless File.exist?("#{@project_root}/config")
|
21
|
+
puts "The config folder does not exist in this project. Are you sure this is a lono project?"
|
22
|
+
exit 1
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def templates_folder_exist
|
27
|
+
unless File.exist?("#{@project_root}/templates")
|
28
|
+
puts "The templates folder does not exist in this project. Are you sure this is a lono project?"
|
29
|
+
exit 1
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def empty_folders
|
34
|
+
if Dir["#{@project_root}/config/**/*.rb"].empty?
|
35
|
+
puts "The config folder does not contain any lono template definitions."
|
36
|
+
exit 1
|
37
|
+
end
|
38
|
+
if Dir["#{@project_root}/templates/**/*"].empty?
|
39
|
+
puts "The templates folder does not contain any lono template definitions."
|
40
|
+
exit 1
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/lono/template.rb
CHANGED
@@ -1,253 +1,27 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
require 'base64'
|
1
|
+
require "thor"
|
2
|
+
require_relative "command"
|
4
3
|
|
5
4
|
module Lono
|
6
|
-
class Template
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
def variables(vars={})
|
27
|
-
vars.each do |var,value|
|
28
|
-
instance_variable_set("@#{var}", value)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def partial(path,vars={}, options={})
|
33
|
-
path = "#{@options[:project_root]}/templates/partial/#{path}"
|
34
|
-
template = IO.read(path)
|
35
|
-
variables(vars)
|
36
|
-
result = erb_result(path, template)
|
37
|
-
result = indent(result, options[:indent]) if options[:indent]
|
38
|
-
result
|
39
|
-
end
|
40
|
-
|
41
|
-
# add indentation
|
42
|
-
def indent(result, indentation_amount)
|
43
|
-
result.split("\n").map do |line|
|
44
|
-
" " * indentation_amount + line
|
45
|
-
end.join("\n")
|
46
|
-
end
|
47
|
-
|
48
|
-
def erb_result(path, template)
|
49
|
-
begin
|
50
|
-
ERB.new(template, nil, "-").result(binding)
|
51
|
-
rescue Exception => e
|
52
|
-
puts e
|
53
|
-
|
54
|
-
# how to know where ERB stopped? - https://www.ruby-forum.com/topic/182051
|
55
|
-
# syntax errors have the (erb):xxx info in e.message
|
56
|
-
# undefined variables have (erb):xxx info in e.backtrac
|
57
|
-
error_info = e.message.split("\n").grep(/\(erb\)/)[0]
|
58
|
-
error_info ||= e.backtrace.grep(/\(erb\)/)[0]
|
59
|
-
raise unless error_info # unable to find the (erb):xxx: error line
|
60
|
-
line = error_info.split(':')[1].to_i
|
61
|
-
puts "Error evaluating ERB template on line #{line.to_s.colorize(:red)} of: #{path.sub(/^\.\//, '')}"
|
62
|
-
|
63
|
-
template_lines = template.split("\n")
|
64
|
-
context = 5 # lines of context
|
65
|
-
top, bottom = [line-context-1, 0].max, line+context-1
|
66
|
-
spacing = template_lines.size.to_s.size
|
67
|
-
template_lines[top..bottom].each_with_index do |line_content, index|
|
68
|
-
line_number = top+index+1
|
69
|
-
if line_number == line
|
70
|
-
printf("%#{spacing}d %s\n".colorize(:red), line_number, line_content)
|
71
|
-
else
|
72
|
-
printf("%#{spacing}d %s\n", line_number, line_content)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
exit 1 unless ENV['TEST']
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def user_data(path, vars={})
|
80
|
-
path = "#{@options[:project_root]}/templates/user_data/#{path}"
|
81
|
-
template = IO.read(path)
|
82
|
-
variables(vars)
|
83
|
-
result = erb_result(path, template)
|
84
|
-
output = []
|
85
|
-
result.split("\n").each do |line|
|
86
|
-
output += transform(line)
|
87
|
-
end
|
88
|
-
json = output.to_json
|
89
|
-
json[0] = '' # remove first char: [
|
90
|
-
json.chop! # remove last char: ]
|
91
|
-
end
|
92
|
-
|
93
|
-
def ref(name)
|
94
|
-
%Q|{"Ref"=>"#{name}"}|
|
95
|
-
end
|
96
|
-
|
97
|
-
def find_in_map(*args)
|
98
|
-
%Q|{"Fn::FindInMap" => [ #{transform_array(args)} ]}|
|
99
|
-
end
|
100
|
-
|
101
|
-
def base64(value)
|
102
|
-
%Q|{"Fn::Base64"=>"#{value}"}|
|
103
|
-
end
|
104
|
-
|
105
|
-
def get_att(*args)
|
106
|
-
%Q|{"Fn::GetAtt" => [ #{transform_array(args)} ]}|
|
107
|
-
end
|
108
|
-
|
109
|
-
def get_azs(region="AWS::Region")
|
110
|
-
%Q|{"Fn::GetAZs"=>"#{region}"}|
|
111
|
-
end
|
112
|
-
|
113
|
-
def join(delimiter, values)
|
114
|
-
%Q|{"Fn::Join" => ["#{delimiter}", [ #{transform_array(values)} ]]}|
|
115
|
-
end
|
116
|
-
|
117
|
-
def select(index, list)
|
118
|
-
%Q|{"Fn::Select" => ["#{index}", [ #{transform_array(list)} ]]}|
|
119
|
-
end
|
120
|
-
|
121
|
-
def transform_array(arr)
|
122
|
-
arr.map! {|x| x =~ /=>/ ? x : x.inspect }
|
123
|
-
arr.join(',')
|
124
|
-
end
|
125
|
-
|
126
|
-
# transform each line of bash script to array with cloudformation template objects
|
127
|
-
def transform(data)
|
128
|
-
data = evaluate(data)
|
129
|
-
if data[-1].is_a?(String)
|
130
|
-
data[0..-2] + ["#{data[-1]}\n"]
|
131
|
-
else
|
132
|
-
data + ["\n"]
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
# Input:
|
137
|
-
# String
|
138
|
-
# Output:
|
139
|
-
# Array of parse positions
|
140
|
-
#
|
141
|
-
# The positions of tokens taking into account when brackets start and close,
|
142
|
-
# handles nested brackets.
|
143
|
-
def bracket_positions(line)
|
144
|
-
positions,pair,count = [],[],0
|
145
|
-
|
146
|
-
line.split('').each_with_index do |char,i|
|
147
|
-
pair << i if pair.empty?
|
148
|
-
|
149
|
-
first_pair_char = line[pair[0]]
|
150
|
-
if first_pair_char == '{' # object logic
|
151
|
-
if char == '{'
|
152
|
-
count += 1
|
153
|
-
end
|
154
|
-
|
155
|
-
if char == '}'
|
156
|
-
count -= 1
|
157
|
-
if count == 0
|
158
|
-
pair << i
|
159
|
-
positions << pair
|
160
|
-
pair = []
|
161
|
-
end
|
162
|
-
end
|
163
|
-
else # string logic
|
164
|
-
lookahead = line[i+1]
|
165
|
-
if lookahead == '{'
|
166
|
-
pair << i
|
167
|
-
positions << pair
|
168
|
-
pair = []
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end # end of loop
|
172
|
-
|
173
|
-
# for string logic when lookahead does not contain a object token
|
174
|
-
# need to clear out what's left to match the final pair
|
175
|
-
if !pair.empty?
|
176
|
-
pair << line.size - 1
|
177
|
-
positions << pair
|
178
|
-
end
|
179
|
-
|
180
|
-
positions
|
181
|
-
end
|
182
|
-
|
183
|
-
# Input:
|
184
|
-
# Array - bracket_positions
|
185
|
-
# Ouput:
|
186
|
-
# Array - positions that can be use to determine what to parse
|
187
|
-
def parse_positions(line)
|
188
|
-
positions = bracket_positions(line)
|
189
|
-
positions.flatten
|
190
|
-
end
|
191
|
-
|
192
|
-
# Input
|
193
|
-
# String line of code to decompose into chunks, some can be transformed into objects
|
194
|
-
# Output
|
195
|
-
# Array of strings, some can be transformed into objects
|
196
|
-
#
|
197
|
-
# Example:
|
198
|
-
# line = 'a{b}c{d{d}d}e' # nested brackets
|
199
|
-
# template.decompose(line).should == ['a','{b}','c','{d{d}d}','e']
|
200
|
-
def decompose(line)
|
201
|
-
positions = parse_positions(line)
|
202
|
-
return [line] if positions.empty?
|
203
|
-
|
204
|
-
result = []
|
205
|
-
str = ''
|
206
|
-
until positions.empty?
|
207
|
-
left = positions.shift
|
208
|
-
right = positions.shift
|
209
|
-
token = line[left..right]
|
210
|
-
# if cfn object, add to the result set but after clearing out
|
211
|
-
# the temp str that is being built up when the token is just a string
|
212
|
-
if cfn_object?(token)
|
213
|
-
unless str.empty? # first token might be a object
|
214
|
-
result << str
|
215
|
-
str = ''
|
216
|
-
end
|
217
|
-
result << token
|
218
|
-
else
|
219
|
-
str << token # keeps building up the string
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
# at the of the loop there's a leftover string, unless the last token
|
224
|
-
# is an object
|
225
|
-
result << str unless str.empty?
|
226
|
-
|
227
|
-
result
|
228
|
-
end
|
229
|
-
|
230
|
-
def cfn_object?(s)
|
231
|
-
exact = %w[Ref]
|
232
|
-
pattern = %w[Fn::]
|
233
|
-
exact_match = !!exact.detect {|word| s.include?(word)}
|
234
|
-
pattern_match = !!pattern.detect {|p| s =~ Regexp.new(p)}
|
235
|
-
(exact_match || pattern_match) && s =~ /^{/ && s =~ /=>/
|
236
|
-
end
|
237
|
-
|
238
|
-
def recompose(decomposition)
|
239
|
-
decomposition.map { |s| cfn_object?(s) ? eval(s) : s }
|
240
|
-
end
|
241
|
-
|
242
|
-
def evaluate(line)
|
243
|
-
recompose(decompose(line))
|
244
|
-
end
|
245
|
-
|
246
|
-
# For simple just parameters files that can also be generated with lono, the CFN
|
247
|
-
# Fn::Base64 function is not available and as lono is not being used in the context
|
248
|
-
# of CloudFormation. So this can be used in it's place.
|
249
|
-
def encode_base64(text)
|
250
|
-
Base64.strict_encode64(text).strip
|
5
|
+
class Template < Command
|
6
|
+
autoload :Help, 'lono/template/help'
|
7
|
+
autoload :Bashify, 'lono/template/bashify'
|
8
|
+
autoload :DSL, 'lono/template/dsl'
|
9
|
+
autoload :Template, 'lono/template/template'
|
10
|
+
|
11
|
+
desc "generate", "Generate the CloudFormation templates"
|
12
|
+
Help.generate
|
13
|
+
option :clean, type: :boolean, aliases: "-c", desc: "remove all output files before generating"
|
14
|
+
option :project_root, default: ".", aliases: "-r", desc: "project root"
|
15
|
+
option :quiet, type: :boolean, aliases: "-q", desc: "silence the output"
|
16
|
+
option :pretty, type: :boolean, default: true, desc: "json pretty the output. only applies with json format"
|
17
|
+
def generate
|
18
|
+
DSL.new(options.clone).run
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "bashify [URL-OR-PATH]", "Convert the UserData section of an existing CloudFormation Template to a starter bash script that is compatiable with lono"
|
22
|
+
Help.bashify
|
23
|
+
def bashify(path)
|
24
|
+
Bashify.new(path: path).run
|
251
25
|
end
|
252
26
|
end
|
253
27
|
end
|