Class: Lono::Param::Generator

Inherits:
Object
  • Object
show all
Includes:
Blueprint::Root, Conventions
Defined in:
lib/lono/param/generator.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Conventions

#template_param_convention

Methods included from Blueprint::Root

#bundler_groups, #find_blueprint_root, #require_bundle_gems, #set_blueprint_root

Constructor Details

#initialize(blueprint, options = {}) ⇒ Generator

Returns a new instance of Generator.



7
8
9
10
11
# File 'lib/lono/param/generator.rb', line 7

def initialize(blueprint, options={})
  @blueprint, @options = blueprint, options
  set_blueprint_root(@blueprint)
  @template, @param = template_param_convention(options)
end

Instance Attribute Details

#base_pathObject (readonly)

set when generate is called



6
7
8
# File 'lib/lono/param/generator.rb', line 6

def base_path
  @base_path
end

#env_pathObject (readonly)

set when generate is called



6
7
8
# File 'lib/lono/param/generator.rb', line 6

def env_path
  @env_path
end

Instance Method Details

#contextObject

Context for ERB rendering. This is where we control what references get passed to the ERB rendering.



180
181
182
# File 'lib/lono/param/generator.rb', line 180

def context
  @context ||= Lono::Template::Context.new(@blueprint, @options)
end

#convert_to_cfn_format(contents, casing = :camel) ⇒ Object



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/lono/param/generator.rb', line 195

def convert_to_cfn_format(contents, casing=:camel)
  lines = parse_contents(contents)

  # First use a Hash structure so that overlay env files will override
  # the base param file.
  data = {}
  lines.each do |line|
    key,value = line.strip.split("=").map {|x| x.strip}
    data[key] = value
  end

  # Now build up the aws json format for parameters
  params = []
  data.each do |key,value|
    param = if value == "use_previous_value"
              {
                "ParameterKey": key,
                "UsePreviousValue": true
              }
            elsif value
              {
                "ParameterKey": key,
                "ParameterValue": value
              }
            end
    if param
      param = param.to_snake_keys if casing == :underscore
      params << param
    end
  end
  params
end

#generateObject



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/lono/param/generator.rb', line 98

def generate
  puts "Generating parameter files for blueprint #{@blueprint.color(:green)}:"

  @base_path, @env_path = lookup_paths

  return unless @base_path || @env_path

  # useful option for lono cfn, since some templates dont require params
  return if @options[:allow_not_exists] && !source_exist?

  if source_exist?
    contents = process_erb
    data = convert_to_cfn_format(contents)
    json = JSON.pretty_generate(data)
    write_output(json)
    unless @options[:mute]
      short_output_path = output_path.sub("#{Lono.root}/","")
      puts "  #{short_output_path}"
    end
  else
    puts "#{@base_path} or #{@env_path} could not be found?  Are you sure it exist?"
    exit 1
  end
  json
end

#lookup_param_file(root: Lono.root, env: Lono.env) ⇒ Object

Lookup precedence:

configs/BLUEPRINT/params/development/TEMPLATE/PARAM.txt
configs/BLUEPRINT/params/development/PARAM.txt
configs/BLUEPRINT/params/development.txt


28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/lono/param/generator.rb', line 28

def lookup_param_file(root: Lono.root, env: Lono.env)
  # The docs conver direct_absolute_form and direct_relative_form as the "Direct Form"
  unless env == "base"
    direct_absolute_form = @param # user provided the absolute full path
    direct_relative_form = "#{root}/#{@param}" # user provided the full path within the lono project
    direct_env_form = "#{root}/configs/#{@blueprint}/params/#{env}/#{@param}" # direct lookup is simple
    direct_simple_form = "#{root}/configs/#{@blueprint}/params/#{@param}" # direct lookup is simple
  end
  long_form = "#{root}/configs/#{@blueprint}/params/#{env}/#{@template}/#{@param}"
  medium_form = "#{root}/configs/#{@blueprint}/params/#{env}/#{@param}"
  short_form = "#{root}/configs/#{@blueprint}/params/#{env}"

  if ENV['LONO_PARAM_DEBUG']
    puts "Lono.blueprint_root #{Lono.blueprint_root}"
    puts "direct_absolute_form #{direct_absolute_form}"
    puts "direct_relative_form #{direct_relative_form}"
    puts "direct_env_form #{direct_env_form}"
    puts "direct_simple_form #{direct_simple_form}"
    puts "long_form #{long_form}"
    puts "medium_form #{medium_form}"
    puts "short_form #{short_form}"
  end

  unless env == "base"
    return param_file(direct_absolute_form) if param_file?(direct_absolute_form)
    return param_file(direct_relative_form) if param_file?(direct_relative_form)
    return param_file(direct_env_form) if param_file?(direct_env_form) # consider this first its simple and direct but is scope to env so it's more specific
    return param_file(direct_simple_form) if param_file?(direct_simple_form) # consider this first its simple and direct but is scope to env so it's more specific
  end
  return param_file(long_form) if param_file?(long_form) # consider this first because its more explicit

  # All 3 are the same
  # Also, blueprint and template the same and explicitly specified param
  if @blueprint == @template
    return param_file(medium_form) if param_file?(medium_form) # higher precedence between longer but short form should be encouraged
    return param_file(short_form) if param_file?(short_form)
    return # cannot find a param file
  end

  # Only template and param are the same
  if @template == @param
    return param_file(medium_form) if param_file?(medium_form) # only consider medium form
    return # cannot find a param file
  end
end

#lookup_pathsObject



86
87
88
89
90
91
92
93
94
95
96
# File 'lib/lono/param/generator.rb', line 86

def lookup_paths
  @base_path = lookup_param_file(env: "base")
  @env_path = lookup_param_file(env: Lono.env)

  if ENV['LONO_PARAM_DEBUG']
    puts "  @base_path #{@base_path.inspect}"
    puts "  @env_path #{@env_path.inspect}"
  end

  [@base_path, @env_path]
end

#output_pathObject



228
229
230
231
232
233
234
235
236
237
# File 'lib/lono/param/generator.rb', line 228

def output_path
  output = Lono.config.output_path.sub("#{Lono.root}/","")
  path = if @base_path && !@env_path
           # Handle case when base config exist but the env config does not
           @base_path.sub("configs", output).sub("base", Lono.env)
         else
           @env_path.sub("configs", output)
         end
  path.sub(/\.txt$/,'.json')
end

#param_file(path) ⇒ Object



80
81
82
83
84
# File 'lib/lono/param/generator.rb', line 80

def param_file(path)
  return path if File.file?(path)
  return "#{path}.txt" if File.file?("#{path}.txt")
  return "#{path}.sh" if File.file?("#{path}.sh")
end

#param_file?(path) ⇒ Boolean

Allows user to specify the .txt extension or not to. Also allows user to use other extensions like .sh if they are explicit about it.

Returns:

  • (Boolean)


76
77
78
# File 'lib/lono/param/generator.rb', line 76

def param_file?(path)
  File.file?(path) || File.file?("#{path}.txt") || File.file?("#{path}.sh")
end

#params(casing = :underscore) ⇒ Object

useful for when calling CloudFormation via the aws-sdk gem



134
135
136
137
138
139
140
141
142
# File 'lib/lono/param/generator.rb', line 134

def params(casing = :underscore)
  @base_path, @env_path = lookup_paths

  # useful option for lono cfn
  return {} if @options[:allow_not_exists] && !source_exist?

  contents = process_erb
  convert_to_cfn_format(contents, casing)
end

#parse_contents(contents) ⇒ Object



184
185
186
187
188
189
190
191
192
193
# File 'lib/lono/param/generator.rb', line 184

def parse_contents(contents)
  lines = contents.split("\n")
  # remove comment at the end of the line
  lines.map! { |l| l.sub(/#.*/,'').strip }
  # filter out commented lines
  lines = lines.reject { |l| l =~ /(^|\s)#/i }
  # filter out empty lines
  lines = lines.reject { |l| l.strip.empty? }
  lines
end

#process_erbObject

Reads both the base source and env source and overlay the two Example 1:

params/base/mystack.txt - base path
params/production/mystack.txt - env path

the base/mystack.txt gets combined with the prod/mystack.txt
it produces a final prod/mystack.txt

Example 2:

params/base/mystack.txt - base path

the base/mystack.txt is used to produced a prod/mystack.txt

Example 3:

params/production/mystack.txt - env path

the prod/mystack.txt is used to produced a prod/mystack.txt


161
162
163
164
165
166
167
168
169
# File 'lib/lono/param/generator.rb', line 161

def process_erb
  contents = []
  contents << render_erb(@base_path)
  contents << render_erb(@env_path)
  result = contents.compact.join("\n")
  # puts "process_erb result".color(:yellow)
  # puts result
  result
end

#puts_param_message(type) ⇒ Object



13
14
15
16
17
18
19
20
# File 'lib/lono/param/generator.rb', line 13

def puts_param_message(type)
  path = send("#{type}_path")
  return unless path
  if param_file?(path)
    pretty_path = path.sub("#{Lono.root}/",'')
    puts "Using param for #{type}: #{pretty_path}".color(:yellow)
  end
end

#render_erb(path) ⇒ Object



171
172
173
174
175
176
# File 'lib/lono/param/generator.rb', line 171

def render_erb(path)
  return unless path
  if File.exist?(path)
    RenderMePretty.result(path, context: context)
  end
end

#source_exist?Boolean

Checks both base and source path for existing of the param file. Example:

params/base/mystack.txt - base path
params/production/mystack.txt - source path

Returns:

  • (Boolean)


128
129
130
131
# File 'lib/lono/param/generator.rb', line 128

def source_exist?
  @base_path && File.exist?(@base_path) ||
  @env_path && File.exist?(@env_path)
end

#write_output(json) ⇒ Object



239
240
241
242
243
# File 'lib/lono/param/generator.rb', line 239

def write_output(json)
  dir = File.dirname(output_path)
  FileUtils.mkdir_p(dir)
  IO.write(output_path, json)
end