lono 5.1.1 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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