Class: Lono::Param::Generator

Inherits:
Object
  • Object
show all
Includes:
CurrentRegion
Defined in:
lib/lono/param/generator.rb

Class Method Summary collapse

Instance Method Summary collapse

Methods included from CurrentRegion

#current_region

Constructor Details

#initialize(name, options) ⇒ Generator

Returns a new instance of Generator.



27
28
29
30
31
32
# File 'lib/lono/param/generator.rb', line 27

def initialize(name, options)
  @_name = "#{Lono.env}/#{name}"
  @_options = options
  @_env_path = options[:path] || "#{Lono.root}/params/#{@_name}.txt"
  @_base_path = @_env_path.sub("/#{Lono.env}/", "/base/")
end

Class Method Details

.generate_all(options) ⇒ Object



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

def self.generate_all(options)
  puts "Generating params files"

  params = param_names("base") + param_names(Lono.env)
  params.uniq.each do |name|
    param = Lono::Param::Generator.new(name, options)
    param.generate
  end
end

.param_names(folder) ⇒ Object

Returns param names Example: Given params:

params/base/a.txt params/base/b.txt params/base/c.txt

Returns:

param_names("base") => ["a", "b", "c"]


20
21
22
23
24
25
# File 'lib/lono/param/generator.rb', line 20

def self.param_names(folder)
  base_folder = "#{Lono.root}/params/#{folder}" # Example: "./params/base"
  Dir.glob("#{base_folder}/**/*.txt").map do |path|
    path.sub("#{base_folder}/", '').sub('.txt','')
  end
end

Instance Method Details

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



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/lono/param/generator.rb', line 146

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

#erb_result(path, template) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/lono/param/generator.rb', line 85

def erb_result(path, template)
  load_variables
  begin
    ERB.new(template, nil, "-").result(binding)
  rescue Exception => e
    puts e
    puts e.backtrace if ENV['DEBUG']

    # how to know where ERB stopped? - https://www.ruby-forum.com/topic/182051
    # syntax errors have the (erb):xxx info in e.message
    # undefined variables have (erb):xxx info in e.backtrac
    error_info = e.message.split("\n").grep(/\(erb\)/)[0]
    error_info ||= e.backtrace.grep(/\(erb\)/)[0]
    raise unless error_info # unable to find the (erb):xxx: error line
    line = error_info.split(':')[1].to_i
    puts "Error evaluating ERB template on line #{line.to_s.colorize(:red)} of: #{path.sub(/^\.\//, '').colorize(:green)}"

    template_lines = template.split("\n")
    context = 5 # lines of context
    top, bottom = [line-context-1, 0].max, line+context-1
    spacing = template_lines.size.to_s.size
    template_lines[top..bottom].each_with_index do |line_content, index|
      line_number = top+index+1
      if line_number == line
        printf("%#{spacing}d %s\n".colorize(:red), line_number, line_content)
      else
        printf("%#{spacing}d %s\n", line_number, line_content)
      end
    end
    exit 1 unless ENV['TEST']
  end
end

#generateObject



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/lono/param/generator.rb', line 34

def generate
  # useful option for lono cfn
  return if @_options[:allow_no_file] && !source_exist?

  if source_exist?
    contents = overlay_sources
    data = convert_to_cfn_format(contents)
    json = JSON.pretty_generate(data)
    write_output(json)
    # Example: @_name = stag/ecs/private
    #          pretty_name = ecs/private
    pretty_name = @_name.sub("#{Lono.env}/", '')
    puts "Params file generated for #{pretty_name} at #{output_path}" unless @_options[:mute]
  else
    puts "#{@_base_path} or #{@_env_path} could not be found?  Are you sure it exist?"
    exit 1
  end
  json
end

#load_variablesObject



190
191
192
193
# File 'lib/lono/param/generator.rb', line 190

def load_variables
  load_variables_folder("base")
  load_variables_folder(Lono.env)
end

#load_variables_folder(folder) ⇒ Object

Load the variables defined in config/variables/* to make available the params/*.txt files

Example:

`config/variables/base/variables.rb`:
   @ami = 123

`params/ecs/private.txt`:
  AmiId=<%= @ami %>


205
206
207
208
209
210
# File 'lib/lono/param/generator.rb', line 205

def load_variables_folder(folder)
  paths = Dir.glob("#{Lono.root}/config/variables/#{folder}/**/*")
  paths.select{ |e| File.file? e }.each do |path|
    instance_eval(IO.read(path))
  end
end

#output_pathObject



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

def output_path
  "#{Lono.root}/output/params/#{@_name}.json".sub(/\.\//,'')
end

#overlay_sourcesObject

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

params/base/mystack.txt - base path
params/prod/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/prod/mystack.txt - env path

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


71
72
73
74
75
76
# File 'lib/lono/param/generator.rb', line 71

def overlay_sources
  contents = []
  contents << process_erb(@_base_path)
  contents << process_erb(@_env_path)
  contents.compact.join("\n")
end

#params(casing = :underscore) ⇒ Object

useful for when calling CloudFormation via the aws-sdk gem



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

def params(casing = :underscore)
  # useful option for lono cfn
  return {} if @_options[:allow_no_file] && !source_exist?

  contents = overlay_sources
  convert_to_cfn_format(contents, casing)
end

#parse_contents(contents) ⇒ Object



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

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_erb(path) ⇒ Object



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

def process_erb(path)
  if File.exist?(path)
    template = IO.read(path)
    erb_result(path, template)
  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/prod/mystack.txt - source path

Returns:

  • (Boolean)


122
123
124
# File 'lib/lono/param/generator.rb', line 122

def source_exist?
  File.exist?(@_base_path) || File.exist?(@_env_path)
end

#write_output(json) ⇒ Object



183
184
185
186
187
# File 'lib/lono/param/generator.rb', line 183

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