lono 1.1.3 → 2.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/.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
|