lono 5.1.1 → 5.2.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.
@@ -1,213 +1,215 @@
1
- class Lono::Param::Generator
2
- include Lono::Blueprint::Root
3
- include Lono::Conventions
4
-
5
- attr_reader :env_path, :base_path # set when generate is called
6
- def initialize(blueprint, options={})
7
- @blueprint, @options = blueprint, options
8
- set_blueprint_root(@blueprint)
9
- @template, @param = template_param_convention(options)
10
- end
11
-
12
- def puts_param_message(type)
13
- path = send("#{type}_path")
14
- return unless path
15
- if File.exist?(path)
16
- pretty_path = path.sub("#{Lono.root}/",'')
17
- puts "Using param: #{pretty_path}".color(:yellow)
1
+ class Lono::Param
2
+ class Generator
3
+ include Lono::Blueprint::Root
4
+ include Lono::Conventions
5
+
6
+ attr_reader :env_path, :base_path # set when generate is called
7
+ def initialize(blueprint, options={})
8
+ @blueprint, @options = blueprint, options
9
+ set_blueprint_root(@blueprint)
10
+ @template, @param = template_param_convention(options)
18
11
  end
19
- end
20
12
 
21
- # Lookup precedence:
22
- #
23
- # configs/BLUEPRINT/params/development/TEMPLATE/PARAM.txt
24
- # configs/BLUEPRINT/params/development/PARAM.txt
25
- # configs/BLUEPRINT/params/development.txt
26
- #
27
- def lookup_param_file(root: Lono.root, env: Lono.env)
28
- long_form = "#{root}/configs/#{@blueprint}/params/#{env}/#{@template}/#{@param}.txt"
29
- medium_form = "#{root}/configs/#{@blueprint}/params/#{env}/#{@param}.txt"
30
- short_form = "#{root}/configs/#{@blueprint}/params/#{env}.txt"
31
-
32
- if ENV['LONO_PARAM_DEBUG']
33
- puts "Lono.blueprint_root #{Lono.blueprint_root}"
34
- puts "long_form #{long_form}"
35
- puts "medium_form #{medium_form}"
36
- puts "short_form #{short_form}"
13
+ def puts_param_message(type)
14
+ path = send("#{type}_path")
15
+ return unless path
16
+ if File.exist?(path)
17
+ pretty_path = path.sub("#{Lono.root}/",'')
18
+ puts "Using param: #{pretty_path}".color(:yellow)
19
+ end
37
20
  end
38
21
 
39
- return long_form if File.exist?(long_form) # always consider this first because its so explicit
22
+ # Lookup precedence:
23
+ #
24
+ # configs/BLUEPRINT/params/development/TEMPLATE/PARAM.txt
25
+ # configs/BLUEPRINT/params/development/PARAM.txt
26
+ # configs/BLUEPRINT/params/development.txt
27
+ #
28
+ def lookup_param_file(root: Lono.root, env: Lono.env)
29
+ long_form = "#{root}/configs/#{@blueprint}/params/#{env}/#{@template}/#{@param}.txt"
30
+ medium_form = "#{root}/configs/#{@blueprint}/params/#{env}/#{@param}.txt"
31
+ short_form = "#{root}/configs/#{@blueprint}/params/#{env}.txt"
32
+
33
+ if ENV['LONO_PARAM_DEBUG']
34
+ puts "Lono.blueprint_root #{Lono.blueprint_root}"
35
+ puts "long_form #{long_form}"
36
+ puts "medium_form #{medium_form}"
37
+ puts "short_form #{short_form}"
38
+ end
40
39
 
41
- # All 3 are the same
42
- if @blueprint == @template && @template == @param
43
- return medium_form if File.exist?(medium_form) # higher precedence between longer but short form should be encouraged
44
- return short_form if File.exist?(short_form)
45
- return # cannot find a param file
46
- end
40
+ return long_form if File.exist?(long_form) # always consider this first because its so explicit
41
+
42
+ # All 3 are the same
43
+ if @blueprint == @template && @template == @param
44
+ return medium_form if File.exist?(medium_form) # higher precedence between longer but short form should be encouraged
45
+ return short_form if File.exist?(short_form)
46
+ return # cannot find a param file
47
+ end
47
48
 
48
- # Only template and param are the same
49
- if @template == @param
50
- return medium_form if File.exist?(medium_form) # only consider medium form
51
- return # cannot find a param file
49
+ # Only template and param are the same
50
+ if @template == @param
51
+ return medium_form if File.exist?(medium_form) # only consider medium form
52
+ return # cannot find a param file
53
+ end
52
54
  end
53
- end
54
55
 
55
- def lookup_paths
56
- @base_path = lookup_param_file(env: "base")
57
- @env_path = lookup_param_file(env: Lono.env)
56
+ def lookup_paths
57
+ @base_path = lookup_param_file(env: "base")
58
+ @env_path = lookup_param_file(env: Lono.env)
58
59
 
59
- if ENV['LONO_PARAM_DEBUG']
60
- puts " @base_path #{@base_path.inspect}"
61
- puts " @env_path #{@env_path.inspect}"
62
- end
60
+ if ENV['LONO_PARAM_DEBUG']
61
+ puts " @base_path #{@base_path.inspect}"
62
+ puts " @env_path #{@env_path.inspect}"
63
+ end
63
64
 
64
- [@base_path, @env_path]
65
- end
65
+ [@base_path, @env_path]
66
+ end
66
67
 
67
- def generate
68
- puts "Generating parameter files for blueprint #{@blueprint.color(:green)}:"
68
+ def generate
69
+ puts "Generating parameter files for blueprint #{@blueprint.color(:green)}:"
69
70
 
70
- @base_path, @env_path = lookup_paths
71
+ @base_path, @env_path = lookup_paths
71
72
 
72
- return unless @base_path || @env_path
73
+ return unless @base_path || @env_path
73
74
 
74
- # useful option for lono cfn, since some templates dont require params
75
- return if @options[:allow_not_exists] && !source_exist?
75
+ # useful option for lono cfn, since some templates dont require params
76
+ return if @options[:allow_not_exists] && !source_exist?
76
77
 
77
- if source_exist?
78
- contents = process_erb
79
- data = convert_to_cfn_format(contents)
80
- json = JSON.pretty_generate(data)
81
- write_output(json)
82
- unless @options[:mute]
83
- short_output_path = output_path.sub("#{Lono.root}/","")
84
- puts " #{short_output_path}"
78
+ if source_exist?
79
+ contents = process_erb
80
+ data = convert_to_cfn_format(contents)
81
+ json = JSON.pretty_generate(data)
82
+ write_output(json)
83
+ unless @options[:mute]
84
+ short_output_path = output_path.sub("#{Lono.root}/","")
85
+ puts " #{short_output_path}"
86
+ end
87
+ else
88
+ puts "#{@base_path} or #{@env_path} could not be found? Are you sure it exist?"
89
+ exit 1
85
90
  end
86
- else
87
- puts "#{@base_path} or #{@env_path} could not be found? Are you sure it exist?"
88
- exit 1
91
+ json
89
92
  end
90
- json
91
- end
92
93
 
93
- # Checks both base and source path for existing of the param file.
94
- # Example:
95
- # params/base/mystack.txt - base path
96
- # params/production/mystack.txt - source path
97
- def source_exist?
98
- @base_path && File.exist?(@base_path) ||
99
- @env_path && File.exist?(@env_path)
100
- end
94
+ # Checks both base and source path for existing of the param file.
95
+ # Example:
96
+ # params/base/mystack.txt - base path
97
+ # params/production/mystack.txt - source path
98
+ def source_exist?
99
+ @base_path && File.exist?(@base_path) ||
100
+ @env_path && File.exist?(@env_path)
101
+ end
101
102
 
102
- # useful for when calling CloudFormation via the aws-sdk gem
103
- def params(casing = :underscore)
104
- @base_path, @env_path = lookup_paths
103
+ # useful for when calling CloudFormation via the aws-sdk gem
104
+ def params(casing = :underscore)
105
+ @base_path, @env_path = lookup_paths
105
106
 
106
- # useful option for lono cfn
107
- return {} if @options[:allow_not_exists] && !source_exist?
107
+ # useful option for lono cfn
108
+ return {} if @options[:allow_not_exists] && !source_exist?
108
109
 
109
- contents = process_erb
110
- convert_to_cfn_format(contents, casing)
111
- end
110
+ contents = process_erb
111
+ convert_to_cfn_format(contents, casing)
112
+ end
112
113
 
113
- # Reads both the base source and env source and overlay the two
114
- # Example 1:
115
- # params/base/mystack.txt - base path
116
- # params/production/mystack.txt - env path
117
- #
118
- # the base/mystack.txt gets combined with the prod/mystack.txt
119
- # it produces a final prod/mystack.txt
120
- #
121
- # Example 2:
122
- # params/base/mystack.txt - base path
123
- #
124
- # the base/mystack.txt is used to produced a prod/mystack.txt
125
- #
126
- # Example 3:
127
- # params/production/mystack.txt - env path
128
- #
129
- # the prod/mystack.txt is used to produced a prod/mystack.txt
130
- def process_erb
131
- contents = []
132
- contents << render_erb(@base_path)
133
- contents << render_erb(@env_path)
134
- result = contents.compact.join("\n")
135
- # puts "process_erb result".color(:yellow)
136
- # puts result
137
- result
138
- end
114
+ # Reads both the base source and env source and overlay the two
115
+ # Example 1:
116
+ # params/base/mystack.txt - base path
117
+ # params/production/mystack.txt - env path
118
+ #
119
+ # the base/mystack.txt gets combined with the prod/mystack.txt
120
+ # it produces a final prod/mystack.txt
121
+ #
122
+ # Example 2:
123
+ # params/base/mystack.txt - base path
124
+ #
125
+ # the base/mystack.txt is used to produced a prod/mystack.txt
126
+ #
127
+ # Example 3:
128
+ # params/production/mystack.txt - env path
129
+ #
130
+ # the prod/mystack.txt is used to produced a prod/mystack.txt
131
+ def process_erb
132
+ contents = []
133
+ contents << render_erb(@base_path)
134
+ contents << render_erb(@env_path)
135
+ result = contents.compact.join("\n")
136
+ # puts "process_erb result".color(:yellow)
137
+ # puts result
138
+ result
139
+ end
139
140
 
140
- def render_erb(path)
141
- return unless path
142
- if File.exist?(path)
143
- RenderMePretty.result(path, context: context)
141
+ def render_erb(path)
142
+ return unless path
143
+ if File.exist?(path)
144
+ RenderMePretty.result(path, context: context)
145
+ end
144
146
  end
145
- end
146
147
 
147
- # Context for ERB rendering.
148
- # This is where we control what references get passed to the ERB rendering.
149
- def context
150
- @context ||= Lono::Template::Context.new(@blueprint, @options)
151
- end
148
+ # Context for ERB rendering.
149
+ # This is where we control what references get passed to the ERB rendering.
150
+ def context
151
+ @context ||= Lono::Template::Context.new(@blueprint, @options)
152
+ end
152
153
 
153
- def parse_contents(contents)
154
- lines = contents.split("\n")
155
- # remove comment at the end of the line
156
- lines.map! { |l| l.sub(/#.*/,'').strip }
157
- # filter out commented lines
158
- lines = lines.reject { |l| l =~ /(^|\s)#/i }
159
- # filter out empty lines
160
- lines = lines.reject { |l| l.strip.empty? }
161
- lines
162
- end
154
+ def parse_contents(contents)
155
+ lines = contents.split("\n")
156
+ # remove comment at the end of the line
157
+ lines.map! { |l| l.sub(/#.*/,'').strip }
158
+ # filter out commented lines
159
+ lines = lines.reject { |l| l =~ /(^|\s)#/i }
160
+ # filter out empty lines
161
+ lines = lines.reject { |l| l.strip.empty? }
162
+ lines
163
+ end
163
164
 
164
- def convert_to_cfn_format(contents, casing=:camel)
165
- lines = parse_contents(contents)
165
+ def convert_to_cfn_format(contents, casing=:camel)
166
+ lines = parse_contents(contents)
166
167
 
167
- # First use a Hash structure so that overlay env files will override
168
- # the base param file.
169
- data = {}
170
- lines.each do |line|
171
- key,value = line.strip.split("=").map {|x| x.strip}
172
- data[key] = value
173
- end
168
+ # First use a Hash structure so that overlay env files will override
169
+ # the base param file.
170
+ data = {}
171
+ lines.each do |line|
172
+ key,value = line.strip.split("=").map {|x| x.strip}
173
+ data[key] = value
174
+ end
174
175
 
175
- # Now build up the aws json format for parameters
176
- params = []
177
- data.each do |key,value|
178
- param = if value == "use_previous_value"
179
- {
180
- "ParameterKey": key,
181
- "UsePreviousValue": true
182
- }
183
- elsif value
184
- {
185
- "ParameterKey": key,
186
- "ParameterValue": value
187
- }
188
- end
189
- if param
190
- param = param.to_snake_keys if casing == :underscore
191
- params << param
176
+ # Now build up the aws json format for parameters
177
+ params = []
178
+ data.each do |key,value|
179
+ param = if value == "use_previous_value"
180
+ {
181
+ "ParameterKey": key,
182
+ "UsePreviousValue": true
183
+ }
184
+ elsif value
185
+ {
186
+ "ParameterKey": key,
187
+ "ParameterValue": value
188
+ }
189
+ end
190
+ if param
191
+ param = param.to_snake_keys if casing == :underscore
192
+ params << param
193
+ end
192
194
  end
195
+ params
193
196
  end
194
- params
195
- end
196
197
 
197
- def output_path
198
- output = Lono.config.output_path.sub("#{Lono.root}/","")
199
- path = if @base_path && !@env_path
200
- # Handle case when base config exist but the env config does not
201
- @base_path.sub("configs", output).sub("base", Lono.env)
202
- else
203
- @env_path.sub("configs", output)
204
- end
205
- path.sub(/\.txt$/,'.json')
206
- end
198
+ def output_path
199
+ output = Lono.config.output_path.sub("#{Lono.root}/","")
200
+ path = if @base_path && !@env_path
201
+ # Handle case when base config exist but the env config does not
202
+ @base_path.sub("configs", output).sub("base", Lono.env)
203
+ else
204
+ @env_path.sub("configs", output)
205
+ end
206
+ path.sub(/\.txt$/,'.json')
207
+ end
207
208
 
208
- def write_output(json)
209
- dir = File.dirname(output_path)
210
- FileUtils.mkdir_p(dir)
211
- IO.write(output_path, json)
209
+ def write_output(json)
210
+ dir = File.dirname(output_path)
211
+ FileUtils.mkdir_p(dir)
212
+ IO.write(output_path, json)
213
+ end
212
214
  end
213
- end
215
+ end
data/lib/lono/param.rb CHANGED
@@ -1,12 +1,10 @@
1
- require "thor"
2
-
3
1
  module Lono
4
- class Param < Lono::Command
2
+ class Param < Command
5
3
  class_option :verbose, type: :boolean
6
4
  class_option :noop, type: :boolean
7
5
  class_option :mute, type: :boolean
8
6
 
9
- desc "generate", "Generate all parameter files to `output/params`."
7
+ desc "generate", "Generate parameter output files to `output/params`."
10
8
  long_desc Lono::Help.text("param/generate")
11
9
  def generate(blueprint=nil)
12
10
  Blueprint::Find.one_or_all(blueprint).each do |b|
@@ -3,6 +3,7 @@ class Lono::S3
3
3
  STACK_NAME = ENV['LONO_STACK_NAME'] || "lono"
4
4
  include Lono::AwsServices
5
5
  extend Lono::AwsServices
6
+ extend Memoist
6
7
 
7
8
  class << self
8
9
  @@name = nil
@@ -24,6 +25,14 @@ class Lono::S3
24
25
 
25
26
  def deploy
26
27
  stack = find_stack
28
+ if rollback_complete?(stack)
29
+ puts "Existing '#{STACK_NAME}' stack in ROLLBACK_COMPLETE state. Deleting stack before continuing."
30
+ cfn.delete_stack(stack_name: STACK_NAME)
31
+ status.wait
32
+ status.reset
33
+ stack = nil
34
+ end
35
+
27
36
  if stack
28
37
  update
29
38
  else
@@ -50,9 +59,17 @@ class Lono::S3
50
59
  # Launches a cloudformation to create an s3 bucket
51
60
  def create
52
61
  puts "Creating #{STACK_NAME} stack with the s3 bucket"
53
- cfn.create_stack(stack_name: STACK_NAME, template_body: template_body)
54
- status = ::Cfn::Status.new(STACK_NAME)
55
- status.wait
62
+ cfn.create_stack(
63
+ stack_name: STACK_NAME,
64
+ template_body: template_body,
65
+ enable_termination_protection: true,
66
+ )
67
+ success = status.wait
68
+ status.reset
69
+ unless success
70
+ puts "ERROR: Unable to create lono stack with managed s3 bucket".color(:red)
71
+ exit 1
72
+ end
56
73
  end
57
74
 
58
75
  def update
@@ -66,10 +83,18 @@ class Lono::S3
66
83
  are_you_sure?
67
84
 
68
85
  puts "Deleting #{STACK_NAME} stack with the s3 bucket"
86
+ disable_termination_protect
69
87
  empty_bucket!
70
88
  cfn.delete_stack(stack_name: STACK_NAME)
71
89
  end
72
90
 
91
+ def disable_termination_protect
92
+ cfn.update_termination_protection(
93
+ stack_name: STACK_NAME,
94
+ enable_termination_protection: false,
95
+ )
96
+ end
97
+
73
98
  def find_stack
74
99
  resp = cfn.describe_stacks(stack_name: STACK_NAME)
75
100
  resp.stacks.first
@@ -77,6 +102,11 @@ class Lono::S3
77
102
  nil
78
103
  end
79
104
 
105
+ def status
106
+ ::Cfn::Status.new(STACK_NAME)
107
+ end
108
+ memoize :status
109
+
80
110
  private
81
111
 
82
112
  def empty_bucket!
@@ -99,6 +129,11 @@ class Lono::S3
99
129
  def are_you_sure?
100
130
  return true if @options[:sure]
101
131
 
132
+ if bucket_name.nil?
133
+ puts "The lono stack and s3 bucket does not exist."
134
+ exit
135
+ end
136
+
102
137
  puts "Are you sure you want the lono bucket #{bucket_name.color(:green)} to be emptied and deleted? (y/N)"
103
138
  sure = $stdin.gets.strip
104
139
  yes = sure =~ /^Y/i
@@ -3,7 +3,7 @@ class Lono::Script
3
3
  SCRIPTS_INFO_PATH = "#{Lono.config.output_path}/data/scripts_info.txt"
4
4
  include Lono::Blueprint::Root
5
5
 
6
- def initialize(blueprint, options = {})
6
+ def initialize(blueprint, options={})
7
7
  @blueprint, @options = blueprint, options
8
8
  @template = @options[:template] || @blueprint
9
9
  Lono::ProjectChecker.check
@@ -0,0 +1,148 @@
1
+ require "fileutils"
2
+ require "memoist"
3
+ require "yaml"
4
+
5
+ # Subclasses should implement:
6
+ #
7
+ # variables - Returns String with content of varibles files.
8
+ # setup - Hook to do extra things like create IAM service roles.
9
+ # finish - Finish hook after config files have been created.
10
+ #
11
+ # Note there is no params method to hook. The Base class handles params well.
12
+ #
13
+ class Lono::Seed
14
+ class Base
15
+ include Lono::Blueprint::Root
16
+ include Lono::AwsServices
17
+ include Lono::Conventions
18
+ extend Memoist
19
+
20
+ def initialize(blueprint, options)
21
+ @blueprint, @options = blueprint, options
22
+ @template, @param = template_param_convention(options)
23
+ end
24
+
25
+ def run
26
+ check_dsl_type!
27
+ setup
28
+ create_params
29
+ create_variables
30
+ finish
31
+ end
32
+
33
+ # Always create params files
34
+ def create_params
35
+ with_each_template do |path|
36
+ create_param_file(path)
37
+ end
38
+ end
39
+
40
+ def create_variables
41
+ return unless variables
42
+ dest_path = "configs/#{@blueprint}/variables/#{Lono.env}.rb"
43
+ write(dest_path, variables)
44
+ puts "Starter variables created: #{dest_path}"
45
+ end
46
+
47
+ def check_dsl_type!
48
+ dsl_type = template_type == 'dsl'
49
+ unless dsl_type
50
+ puts "Detected template_type: #{template_type}"
51
+ puts "lono seed only supports dsl template types currently."
52
+ exit 1
53
+ end
54
+ end
55
+
56
+ def template_type
57
+ blueprint_root = find_blueprint_root(@blueprint)
58
+ meta_config = "#{blueprint_root}/.meta/config.yml"
59
+ return false unless File.exist?(meta_config)
60
+
61
+ meta = YAML.load_file(meta_config)
62
+ meta['template_type']
63
+ end
64
+
65
+ def setup; end
66
+ def finish; end
67
+
68
+ # Meant to be overriden by subclass
69
+ # Return String with contents of variables file.
70
+ def variables
71
+ false
72
+ end
73
+
74
+ def create_param_file(app_template_path)
75
+ parameters = parameters(app_template_path)
76
+
77
+ lines = []
78
+ required = required(parameters)
79
+ lines << "# Required parameters:" unless required.empty?
80
+ required.each do |name, data|
81
+ example = description_example(data["Description"])
82
+ lines << "#{name}=#{example}"
83
+ end
84
+ optional = optional(parameters)
85
+ lines << "# Optional parameters:" unless optional.empty?
86
+ optional.each do |name, data|
87
+ value = default_value(data)
88
+ lines << "# #{name}=#{value}"
89
+ end
90
+
91
+ if lines.empty?
92
+ puts "Template has no parameters."
93
+ return
94
+ end
95
+
96
+ content = lines.join("\n") + "\n"
97
+ dest_path = "configs/#{@blueprint}/params/#{Lono.env}.txt" # only support environment level parameters for now
98
+ write(dest_path, content)
99
+ puts "Starter params created: #{dest_path}"
100
+ end
101
+
102
+ def write(path, content)
103
+ FileUtils.mkdir_p(File.dirname(path))
104
+ IO.write(path, content)
105
+ end
106
+
107
+ def description_example(description)
108
+ default = '...'
109
+ return default unless description
110
+ md = description.match(/(Example|IE): (.*)/)
111
+ return default unless md
112
+ md[2]
113
+ end
114
+
115
+ def default_value(data)
116
+ value = data["Default"]
117
+ if value.blank?
118
+ description_example(data["Description"])
119
+ else
120
+ value
121
+ end
122
+ end
123
+
124
+ def parameters(app_template_path)
125
+ builder = Lono::Template::Dsl::Builder.new(app_template_path, @blueprint, quiet: false)
126
+ template = builder.template
127
+ template["Parameters"] || []
128
+ end
129
+ memoize :parameters
130
+
131
+ def required(parameters)
132
+ parameters.reject { |logical_id, p| p["Default"] }
133
+ end
134
+
135
+ def optional(parameters)
136
+ parameters.select { |logical_id, p| p["Default"] }
137
+ end
138
+
139
+ private
140
+ def with_each_template
141
+ paths = Dir.glob("#{Lono.config.templates_path}/**/*.rb")
142
+ files = paths.select{ |e| File.file?(e) }
143
+ files.each do |path|
144
+ yield(path)
145
+ end
146
+ end
147
+ end
148
+ end