class Terradoc

——————————————————————————– # ——————————————————————————– # ——————————————————————————– #

Constants

VERSION

Public Class Methods

new(options = {}) click to toggle source

——————————————————————————– # ——————————————————————————– #

# File lib/terradoc.rb, line 13
def initialize(options = {})
    @command = 'hcl2json'

    raise StandardError.new("Failed to locate #{@command} please install (pip install #{@command})") unless which @command

    @console_only = false
    @console_only = options[:console] unless options[:console].nil?

    @base_path = '.'
    @base_path = options[:path] unless options[:path].nil?
    @base_path.chomp!('/')
    raise StandardError.new("Path #{@base_path} does not exist - Aborting") unless Dir.exist? @base_path

    @output_file = 'README.md'
    @output_file = options[:output] unless options[:output].nil?

    @files   = []
    @pattern = "#{@base_path}/*.tf"

    @config = {
                'data-sources' => {
                                    'start'          => '<!--Terradoc-data-sources-start-->',
                                    'end'            => '<!--Terradoc-data-sources-end-->',
                                    'raw-results'    => [],
                                    'sorted-results' => [],
                                    'data-table'     => []
                                  },
                'modules'      => {
                                    'start'          => '<!--Terradoc-modules-start-->',
                                    'end'            => '<!--Terradoc-modules-end-->',
                                    'raw-results'    => [],
                                    'sorted-results' => [],
                                    'data-table'     => []
                                  },
                'outputs'      => {
                                    'start'          => '<!--Terradoc-outputs-start-->',
                                    'end'            => '<!--Terradoc-outputs-end-->',
                                    'raw-results'    => [],
                                    'sorted-results' => [],
                                    'data-table'     => []
                                  },
                'providers'    => {
                                    'start'          => '<!--Terradoc-providers-start-->',
                                    'end'            => '<!--Terradoc-providers-end-->',
                                    'raw-results'    => [],
                                    'sorted-results' => [],
                                    'data-table'     => []
                                  },
                'resources'    => {
                                    'start'          => '<!--Terradoc-resources-start-->',
                                    'end'            => '<!--Terradoc-resources-end-->',
                                    'raw-results'    => [],
                                    'sorted-results' => [],
                                    'data-table'     => []
                                  },
                'variables'    => {
                                    'start'          => '<!--Terradoc-variables-start-->',
                                    'end'            => '<!--Terradoc-variables-end-->',
                                    'raw-results'    => [],
                                    'sorted-results' => [],
                                    'data-table'     => []
                                  }
              }

    terradoc_main
end

Public Instance Methods

cleanup_array(array) click to toggle source

——————————————————————————– # ——————————————————————————– #

# File lib/terradoc.rb, line 204
def cleanup_array(array)
    array = array.uniq { |k| k[:name] } if array.count.positive?
    array = array.sort_by { |k| k[:name] } if array.count.positive?
    return array
end
generate_data_tables() click to toggle source

——————————————————————————– # ——————————————————————————– #

# File lib/terradoc.rb, line 223
def generate_data_tables
    if @config['data-sources']['sorted-results'].size.positive?
        @config['data-sources']['data-table'] << '| Name |'
        @config['data-sources']['data-table'] << '| ---- |'
        @config['data-sources']['sorted-results'].each do |item|
            @config['data-sources']['data-table'] << "| #{item[:name]} |"
        end
    else
        @config['data-sources']['data-table'] << '> No data sources found'
    end

    if @config['modules']['sorted-results'].size.positive?
        @config['modules']['data-table'] << '| Name |'
        @config['modules']['data-table'] << '| ---- |'
        @config['modules']['sorted-results'] .each do |item|
            @config['modules']['data-table']  << "| #{item[:name]} |"
        end
    else
        @config['modules']['data-table'] << '> No modules found'
    end

    if @config['outputs']['sorted-results'].size.positive?
        @config['outputs']['data-table'] << '| Name | Description |'
        @config['outputs']['data-table'] << '| ---- | ----------- |'
        @config['outputs']['sorted-results'].each do |item|
            @config['outputs']['data-table'] << "| #{item[:name]} | #{item[:description]} |"
        end
    else
        @config['outputs']['data-table'] << '> No outputs found'
    end

    if @config['providers']['sorted-results'].size.positive?
        @config['providers']['data-table'] << '| Name |'
        @config['providers']['data-table'] << '| ---- |'
        @config['providers']['sorted-results'].each do |item|
            @config['providers']['data-table'] << "| #{item[:name]} |"
        end
    else
        @config['providers']['data-table'] << '> No providers found'
    end

    if @config['resources']['sorted-results'].size.positive?
        @config['resources']['data-table'] << '| Name |'
        @config['resources']['data-table'] << '| ---- |'
        @config['resources']['sorted-results'].each do |item|
            @config['resources']['data-table'] << "| #{item[:name]} |"
        end
    else
        @config['resources']['data-table'] << '> No resources found'
    end

    if @config['variables']['sorted-results'].size.positive?
        @config['variables']['data-table'] << '| Name | Description | Type | Default | Required? |'
        @config['variables']['data-table'] << '| ---- | ----------- |:----:|:-------:|:---------:|'
        @config['variables']['sorted-results'].each do |item|
            @config['variables']['data-table'] << "| #{item[:name]} | #{item[:description]} | #{item[:type]} | #{item[:default]} | #{item[:required]} |"
        end
    else
        @config['variables']['data-table'] << '> No variables found'
    end
end
generate_file_list() click to toggle source

——————————————————————————– # ——————————————————————————– #

# File lib/terradoc.rb, line 130
def generate_file_list
    Dir.glob(@pattern).each do |file|
        @files << file
    end
end
generate_output(lines, start_tag, end_tag, data_table) click to toggle source

——————————————————————————– # ——————————————————————————– #

# File lib/terradoc.rb, line 300
def generate_output(lines, start_tag, end_tag, data_table)
    processed = []

    skip_lines = false
    lines.each do |line|
        if line.casecmp?(end_tag) && skip_lines
            processed << line
            skip_lines = false
            next
        end

        next if skip_lines

        processed << line

        next unless line.casecmp?(start_tag)

        skip_lines = true
        data_table.each do |row|
            processed << row
        end
    end

    return processed
end
load_file(filename) click to toggle source

——————————————————————————– # ——————————————————————————– #

# File lib/terradoc.rb, line 287
def load_file(filename)
    lines = []

    begin
        lines = File.readlines(filename).each(&:chomp!)
    rescue SystemCallError
        raise StandardError.new("Failed to open file #{filename} for reading")
    end
    return lines
end
process_files() click to toggle source

——————————————————————————– # ——————————————————————————– #

# File lib/terradoc.rb, line 138
def process_files
    @files.each do |file|
        Open3.popen3("hcl2json #{file}") do |_stdin, stdout, _stderr, _wait_thr|
            stdout_str = stdout.read

            json = JSON.parse(stdout_str, max_nesting: 256)

            process_raw_results(json)
        end
    end
end
process_raw_results(json) click to toggle source

——————————————————————————– # ——————————————————————————– #

# File lib/terradoc.rb, line 152
def process_raw_results(json)
    unless json['data'].nil?
        json['data'].each do |key, _value|
            name = key.gsub('_', '\_')
            @config['data-sources']['raw-results'] << { :name => name }
        end
    end

    unless json['module'].nil?
        json['module'].each do |key, value|
            name = key.gsub('_', '\_')
            @config['modules']['raw-results'] << { :name => name, :source => value['source'], :version => value['version'] }
        end
    end

    unless json['output'].nil?
        json['output'].each do |key, value|
            name = key.gsub('_', '\_')
            @config['outputs']['raw-results'] << { :name => name, :description => value['description'] }
        end
    end

    unless json['provider'].nil?
        json['provider'].each do |key, _value|
            name = key.gsub('_', '\_')
            @config['providers']['raw-results'] << { :name => name }
        end
    end

    unless json['resource'].nil?
        json['resource'].each do |key, _value|
            name = key.gsub('_', '\_')
            @config['resources']['raw-results'] << { :name => name }
        end
    end

    unless json['variable'].nil?
        json['variable'].each do |key, value|
            name = key.gsub('_', '\_')
            default = value['default'].to_s.gsub('_', '\_')
            required = if default.empty?
                           'Yes'
                       else
                           'No'
                       end
            @config['variables']['raw-results'] << { :name => name, :description => value['description'], :type => value['type'], :default => default, :required => required }
        end
    end
end
sort_details() click to toggle source

——————————————————————————– # ——————————————————————————– #

# File lib/terradoc.rb, line 212
def sort_details
    @config['data-sources']['sorted-results'] = cleanup_array(@config['data-sources']['raw-results'])
    @config['modules']['sorted-results'] = cleanup_array(@config['modules']['raw-results'])
    @config['outputs']['sorted-results'] = cleanup_array(@config['outputs']['raw-results'])
    @config['providers']['sorted-results'] = cleanup_array(@config['providers']['raw-results'])
    @config['resources']['sorted-results'] = cleanup_array(@config['resources']['raw-results'])
    @config['variables']['sorted-results'] = cleanup_array(@config['variables']['raw-results'])
end
terradoc_main() click to toggle source

——————————————————————————– # ——————————————————————————– #

# File lib/terradoc.rb, line 95
def terradoc_main
    unless @console_only
        spinners = TTY::Spinner::Multi.new("[:spinner] Terradoc is processing your files (Path: #{@base_path}, Output File: #{@output_file})")

        sp1 = spinners.register '[:spinner] Locating files'
        sp2 = spinners.register '[:spinner] Processing files'
        sp3 = spinners.register '[:spinner] Sorting the results'
        sp4 = spinners.register '[:spinner] Generating data tables'
        sp5 = spinners.register '[:spinner] Writing output'

        sp1.auto_spin
        sp2.auto_spin
        sp3.auto_spin
        sp4.auto_spin
        sp5.auto_spin
    end

    generate_file_list
    sp1.success unless @console_only

    process_files
    sp2.success unless @console_only

    sort_details
    sp3.success unless @console_only

    generate_data_tables
    sp4.success unless @console_only

    write_output
    sp5.success unless @console_only
end
which(cmd) click to toggle source

——————————————————————————– # ——————————————————————————– #

# File lib/terradoc.rb, line 82
def which(cmd)
    exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
    ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
        exts.each do |ext|
            exe = File.join(path, "#{cmd}#{ext}")
            return exe if File.executable?(exe) && !File.directory?(exe)
        end
    end
    return nil
end
write_output() click to toggle source

——————————————————————————– # ——————————————————————————– #

# File lib/terradoc.rb, line 328
def write_output
    permissions = 0o0644

    lines = load_file(@output_file)

    lines = generate_output(lines, @config['data-sources']['start'], @config['data-sources']['end'], @config['data-sources']['data-table'])
    lines = generate_output(lines, @config['modules']['start'], @config['modules']['end'], @config['modules']['data-table'])
    lines = generate_output(lines, @config['outputs']['start'], @config['outputs']['end'], @config['outputs']['data-table'])
    lines = generate_output(lines, @config['providers']['start'], @config['providers']['end'], @config['providers']['data-table'])
    lines = generate_output(lines, @config['resources']['start'], @config['resources']['end'], @config['resources']['data-table'])
    lines = generate_output(lines, @config['variables']['start'], @config['variables']['end'], @config['variables']['data-table'])

    if @console_only
        puts lines
    else
        begin
            File.open(@output_file, 'w') do |f|
                lines.each do |line|
                    f.puts line
                end
                f.chmod(permissions)
            end
        rescue SystemCallError
            raise StandardError.new("Failed to open file #{filename} for writing")
        end
    end
end