class CfnDsl::RakeTask

Rake TaskLib rubocop:disable Metrics/ClassLength

Attributes

cfndsl_opts[RW]

@deprecated pre 1.x rake task generator

spec_task[R]

rubocop:enable Metrics/ParameterLists

tasks[R]

rubocop:enable Metrics/ParameterLists

Public Class Methods

new(name = nil) { |self| ... } click to toggle source

Creates Cloudformation generation tasks

@example directory ‘tmp’

namespace :samples do

CfnDsl::RakeTask.new do |t|
  t.specification(file: 'tmp/cloudformation_resources.json')

  desc 'Generate CloudFormation Json'
  t.json(name: :json, files: ["sample/*.rb"], pathmap: 'tmp/%f.json', pretty: true, extras: FileList.new('sample/*.yaml') )

  t.yaml(name: :yaml, files: 'sample/t1.rb', pathmap: 'tmp/%f.yaml', extras: '%X.yaml')
end

end

# File lib/cfndsl/rake_task.rb, line 38
def initialize(name = nil)
  @tasks = []
  @spec_task = nil

  last_desc = ::Rake.application.last_description
  desc nil

  yield self if block_given?

  if cfndsl_opts
    desc last_desc if last_desc
    task(name || :generate) { |_t, _args| cfndsl_opts[:files].each { |opts| generate(opts) } }
  else
    define_base_tasks(name || :generate, last_desc)
  end
end

Public Instance Methods

json(name:, files:, pathmap:, pretty: false, extras: [], **json_opts) click to toggle source

Convert DSL sources to json

Generates file tasks for each of the matching file in the files FileList. Each task is dependant on the source,

the specification file, and the required extras files such that if the timestamp of any of these is earlier
the target file will be regenerated.

Finally task <name> is generated that depends on all the generated targets.

@param [Symbol|String] name the name of a task that is dependant on all the files being converted @param [Rake::FileList | Array<String> ] files source file list @param [String] pathmap expression to map source files to target files @param [Rake::Filelist|Array<String>] extras a list of files to load as external parameters

Note String values containing '%' are treated as a pathmap from the source
The resulting generated FileList is used as a dependency for generation so any entries not
containing '*' MUST exist.

@param [Boolean] pretty use JSON.pretty_generate @param [Hash] json_opts, other options to pass to JSON generator rubocop:disable Metrics/ParameterLists

# File lib/cfndsl/rake_task.rb, line 113
def json(name:, files:, pathmap:, pretty: false, extras: [], **json_opts)
  json_method = pretty ? :pretty_generate : :generate
  generate_model_tasks(name: name, files: files, pathmap: pathmap, extras: extras) do |model, f|
    f.write(JSON.send(json_method, model, **json_opts))
  end
  self
end
specification(file:, name: nil, version: nil) click to toggle source

Use a custom specification file

This specification file will be used for any generation tasks

Creates a file task to download from upstream source, with an optional additional named task that depends on it.

The minimum required version can be specified with the version parameter, which can be overriden by invoking rake with the ‘cfn_spec_task’ argument

@example CfnDsl::RakeTask.new() do |t|

t.specification(name: :update_spec, file: 'tmp/cloudformation_resources.json', version: '6.3'0)

end

# rake update_spec # >> Will ensure ‘tmp/cloudformation_resources.json’ exists and is at least 6.3.0 # rake update_spec # >> Will always try to download the latest available spec

@param [String] file the number of the specification file to use @param [Symbol] name A pretty name for the task to update this file from upstream source @param [String] version The minimum version required, and the default version used when downloading

if not specified and not overriden by 'cfn_spec_version' rake task argument
then any existing file is considered sufficient, and 'latest' is the version used for downloading

@todo Add capability to provide a user spec/patches dir

# File lib/cfndsl/rake_task.rb, line 82
def specification(file:, name: nil, version: nil)
  if name
    desc 'Update Resource Specification' unless ::Rake.application.last_description
    task name, [:cfn_spec_version] => file
  end

  @spec_task = file(file, :cfn_spec_version) do |t, args|
    update_specification(t.name, args.with_defaults(cfn_spec_version: version)[:cfn_spec_version])
  end
  @spec_task.define_singleton_method(:needed?) { true } # We always need to check
  self
end
yaml(name:, files:, pathmap:, extras: [], **yaml_opts) click to toggle source

Convert DSL sources to yaml

Generates file tasks for each of the matching file in the files FileList. Each task is dependant on the source,

the specification file, and the required extras files such that if the timestamp of any of these is earlier
the target file will be regenerated.

Finally task <name> is generated that depends on all the generated targets. @param [Symbol|String] name the name of a task that is dependant on all the files being converted @param [Rake::FileList | Array<String> ] files source file list @param [String] pathmap expression to map source files to target files @param [Rake::Filelist|Array<String>] extras a list of files to load as external parameters

Note String values containing '%' are treated as a pathmap from the source
The resulting generated FileList is used as a dependency for generation so any entries not
containing '*' MUST exist.

@param [Hash] yaml_opts, other options to pass to YAML generator

# File lib/cfndsl/rake_task.rb, line 136
def yaml(name:, files:, pathmap:, extras: [], **yaml_opts)
  generate_model_tasks(name: name, files: files, pathmap: pathmap, extras: extras) do |model, f|
    simple_model = JSON.parse(model.to_json) # convert model to a simple ruby object to avoid yaml tags
    YAML.dump(simple_model, f, **yaml_opts)
  end
  self
end

Private Instance Methods

build_extras_filelist(source, extras) click to toggle source
# File lib/cfndsl/rake_task.rb, line 204
def build_extras_filelist(source, extras)
  extras = [extras] unless extras.respond_to?(:each)
  extras.each_with_object(Rake::FileList.new) do |extra, result|
    case extra
    when Rake::FileList
      result.add(extra)
    when Array
      result.add(*extra)
    when /%/
      result.add(source.pathmap(extra))
    else
      result.add(extra)
    end
  end
end
define_base_tasks(name, description) click to toggle source
# File lib/cfndsl/rake_task.rb, line 149
def define_base_tasks(name, description)
  return if tasks.empty?

  desc description || 'Generate Cloudformation'
  task(name || :generate, [:cfn_spec_version] => tasks)

  load_spec = task load_spec_types: [spec_task].compact do |_t, args|
    CfnDsl.specification_file = spec_task&.name || CfnDsl::LOCAL_SPEC_FILE
    require_relative 'cloudformation'
    CfnDsl::CloudFormationTemplate.check_types(file: spec_task&.name, version: @updated_version || args[:cfn_spec_version])
    verbose&.puts "Using Cloudformation types generated from #{CfnDsl::CloudFormationTemplate.template_types.values_at('File', 'Version').join(' ')}"
  end
  load_spec.define_singleton_method(:timestamp) { Rake::EARLY } # Prevent this task from causing generation tasks.

  tasks.unshift(load_spec)
end
extra() click to toggle source
# File lib/cfndsl/rake_task.rb, line 252
def extra
  cfndsl_opts.fetch(:extras, [])
end
file_output(path, &block) click to toggle source
# File lib/cfndsl/rake_task.rb, line 256
def file_output(path, &block)
  File.open(File.expand_path(path), 'w', &block)
end
generate(opts) click to toggle source
# File lib/cfndsl/rake_task.rb, line 224
def generate(opts)
  log(opts)
  outputter(opts) do |output|
    if cfndsl_opts[:outformat] == 'yaml'
      data = model(opts[:filename]).to_json
      output.puts JSON.parse(data).to_yaml
    else
      output.puts cfndsl_opts[:pretty] ? JSON.pretty_generate(model(opts[:filename])) : model(opts[:filename]).to_json
    end
  end
end
generate_model_tasks(name:, files:, pathmap:, extras:) { |model, f| ... } click to toggle source
# File lib/cfndsl/rake_task.rb, line 185
def generate_model_tasks(name:, files:, pathmap:, extras:)
  files = Rake::FileList.new(*files) unless files.is_a?(Rake::FileList)

  tasks << task(name, [:cfn_spec_version] => files.pathmap(pathmap))

  files.each do |source|
    matched_extras = build_extras_filelist(source, extras)

    file source.pathmap(pathmap) => [source, :load_spec_types, *matched_extras] do |task|
      eval_extras = matched_extras.map { |e| [:yaml, e] } # eval treats yaml and json same
      puts "Generating Cloudformation for #{source} to #{task.name}"
      model = CfnDsl.eval_file_with_extras(source, eval_extras, verbose)
      File.open(task.name, 'w') { |f| yield model, f }
    end
  end

  self
end
log(opts) click to toggle source
# File lib/cfndsl/rake_task.rb, line 236
def log(opts)
  type = opts[:output].nil? ? 'STDOUT' : opts[:output]
  verbose&.puts("Writing to #{type}")
end
model(filename) click to toggle source
# File lib/cfndsl/rake_task.rb, line 245
def model(filename)
  raise "#{filename} doesn't exist" unless File.exist?(filename)

  verbose&.puts("using extras #{extra}")
  CfnDsl.eval_file_with_extras(filename, extra, verbose)
end
outputter(opts) { |$stdout| ... } click to toggle source
# File lib/cfndsl/rake_task.rb, line 241
def outputter(opts, &block)
  opts[:output].nil? ? yield($stdout) : file_output(opts[:output], &block)
end
safe_update_embedded_spec() click to toggle source
# File lib/cfndsl/rake_task.rb, line 179
def safe_update_embedded_spec
  return if File.fnmatch?(File.join(File.dirname(File.expand_path(Rake.application.rakefile)), '*'), CfnDsl::LOCAL_SPEC_FILE)

  raise Error, 'Refusing to update CloudFormation spec embedded in cfndsl gem'
end
update_specification(file, minimum_version) click to toggle source
# File lib/cfndsl/rake_task.rb, line 166
def update_specification(file, minimum_version)
  if CfnDsl::Specification.update_required?(version: minimum_version, file: file)

    safe_update_embedded_spec if file == CfnDsl::LOCAL_SPEC_FILE

    result = CfnDsl.update_specification_file(file: file, version: minimum_version)
    puts "Specification #{result[:file]} updated to version #{result[:version]}"
    @updated_version = result[:version]
  elsif minimum_version
    verbose&.puts "Specification #{file} is already >= #{minimum_version}"
  end
end
verbose() click to toggle source
# File lib/cfndsl/rake_task.rb, line 220
def verbose
  (Rake.verbose? || cfndsl_opts&.fetch(:verbose, false)) && $stderr
end