Skip to content

Commit

Permalink
Merge pull request #299 from rodjek/pdk-468
Browse files Browse the repository at this point in the history
(PDK-468) Task generation
  • Loading branch information
bmjen committed Sep 25, 2017
2 parents f29b071 + 8e43f00 commit 61691e7
Show file tree
Hide file tree
Showing 10 changed files with 347 additions and 5 deletions.
2 changes: 1 addition & 1 deletion lib/pdk/cli/new.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

module PDK::CLI
@new_cmd = @base_cmd.define_command do
name 'new'
Expand All @@ -14,3 +13,4 @@ module PDK::CLI
require 'pdk/cli/new/class'
require 'pdk/cli/new/defined_type'
require 'pdk/cli/new/module'
require 'pdk/cli/new/task'
28 changes: 28 additions & 0 deletions lib/pdk/cli/new/task.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module PDK::CLI
@new_task_cmd = @new_cmd.define_command do
name 'task'
usage _('task [options] <name>')
summary _('Create a new task named <name> using given options')

PDK::CLI.template_url_option(self)
option nil, :description, _('A short description of the purpose of the task'), argument: :required

run do |opts, args, _cmd|
PDK::CLI::Util.ensure_in_module!

task_name = args[0]
module_dir = Dir.pwd

if task_name.nil? || task_name.empty?
puts command.help
exit 1
end

unless Util::OptionValidator.valid_task_name?(task_name)
raise PDK::CLI::ExitWithError, _("'%{name}' is not a valid task name") % { name: task_name }
end

PDK::Generate::Task.new(module_dir, task_name, opts).run
end
end
end
1 change: 1 addition & 0 deletions lib/pdk/cli/util/option_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def self.enum(val, valid_entries, _options = {})
def self.valid_module_name?(string)
!(string =~ %r{\A[a-z][a-z0-9_]*\Z}).nil?
end
singleton_class.send(:alias_method, :valid_task_name?, :valid_module_name?)

# Validate a Puppet namespace against the regular expression in the
# documentation: https://docs.puppet.com/puppet/4.10/lang_reserved.html#classes-and-defined-resource-types
Expand Down
1 change: 1 addition & 0 deletions lib/pdk/generate.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'pdk/generators/module'
require 'pdk/generators/defined_type'
require 'pdk/generators/puppet_class'
require 'pdk/generators/task'
require 'pdk/module/metadata'
require 'pdk/module/templatedir'

Expand Down
1 change: 1 addition & 0 deletions lib/pdk/generators/module.rb
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ def self.prepare_module_directory(target_dir)
[
File.join(target_dir, 'manifests'),
File.join(target_dir, 'templates'),
File.join(target_dir, 'tasks'),
].each do |dir|
begin
FileUtils.mkdir_p(dir)
Expand Down
13 changes: 9 additions & 4 deletions lib/pdk/generators/puppet_object.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module Generate
class PuppetObject
attr_reader :module_dir
attr_reader :object_name
attr_reader :options

# Initialises the PDK::Generate::PuppetObject object.
#
Expand All @@ -30,6 +31,7 @@ class PuppetObject
def initialize(module_dir, object_name, options = {})
@module_dir = module_dir
@options = options
@object_name = object_name

if [:class, :defined_type].include?(object_type) # rubocop:disable Style/GuardClause
object_name_parts = object_name.split('::')
Expand Down Expand Up @@ -83,10 +85,13 @@ def object_type
#
# @api public
def run
[target_object_path, target_spec_path].each do |target_file|
if File.exist?(target_file)
raise PDK::CLI::ExitWithError, _("Unable to generate %{object_type}; '%{file}' already exists.") % { file: target_file, object_type: object_type }
end
[target_object_path, target_spec_path].compact.each do |target_file|
next unless File.exist?(target_file)

raise PDK::CLI::ExitWithError, _("Unable to generate %{object_type}; '%{file}' already exists.") % {
file: target_file,
object_type: object_type,
}
end

with_templates do |template_path, config_hash|
Expand Down
86 changes: 86 additions & 0 deletions lib/pdk/generators/task.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
require 'pdk/generators/puppet_object'

module PDK
module Generate
class Task < PuppetObject
OBJECT_TYPE = :task

# Prepares the data needed to render the new task template.
#
# @return [Hash{Symbol => Object}] a hash of information that will be
# provided to the task template during rendering. Additionally, this hash
# (with the :name key removed) makes up the task metadata.
def template_data
{
name: object_name,
puppet_task_version: 1,
supports_noop: true,
description: options.fetch(:description, 'A short description of this task'),
}
end

# Calculates the path to the file where the new task will be written.
#
# @return [String] the path to the task file.
def target_object_path
@target_object_path ||= File.join(module_dir, 'tasks', "#{task_name}.sh")
end

# Calculates the path to the file where the tests for the new task will
# be written.
#
# @return [nil] as there is currently no test framework for Tasks.
def target_spec_path
nil
end

def run
check_if_task_already_exists

super

write_task_metadata
end

# Checks that the task has not already been defined with a different
# extension.
#
# @raise [PDK::CLI::ExitWithError] if files with the same name as the
# task exist in the <module>/tasks/ directory
#
# @api private
def check_if_task_already_exists
error = _("A task named '%{name}' already exists in this module; defined in %{file}")
allowed_extensions = %w[.md .conf]

Dir.glob(File.join(module_dir, 'tasks', "#{task_name}.*")).each do |file|
next if allowed_extensions.include?(File.extname(file))

raise PDK::CLI::ExitWithError, error % { name: task_name, file: file }
end
end

# Writes the <module>/tasks/<task_name>.json metadata file for the task.
#
# @api private
def write_task_metadata
task_metadata = template_data.dup
task_metadata.delete(:name)

File.open(File.join(module_dir, 'tasks', "#{task_name}.json"), 'w') do |f|
f.write(JSON.pretty_generate(task_metadata))
end
end

# Calculates the file name of the task files ('init' if the task has the
# same name as the module, otherwise use the specified task name).
#
# @return [String] the base name of the file(s) for the task.
#
# @api private
def task_name
(object_name == module_name) ? 'init' : object_name
end
end
end
end
101 changes: 101 additions & 0 deletions spec/unit/pdk/cli/new/task_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
require 'spec_helper'

describe 'PDK::CLI new task' do
let(:help_text) { a_string_matching(%r{^USAGE\s+pdk new task}m) }

before(:each) do
allow(PDK::Util).to receive(:module_root).and_return(module_root)
end

shared_examples 'it exits non-zero and prints the help text' do
it 'exits non-zero and prints the `pdk new task` help' do
expect {
PDK::CLI.run(args)
}.to raise_error(SystemExit) { |error|
expect(error.status).not_to be_zero
}.and output(help_text).to_stdout
end
end

shared_examples 'it exits with an error' do |expected_error|
it 'exits with an error' do
expect(logger).to receive(:error).with(a_string_matching(expected_error))

expect {
PDK::CLI.run(args)
}.to raise_error(SystemExit) { |error|
expect(error.status).not_to be_zero
}
end
end

context 'when not run from inside a module' do
let(:module_root) { nil }
let(:args) { %w[new task test_task] }

it_behaves_like 'it exits with an error', %r{must be run from inside a valid module}
end

context 'when run from inside a module' do
let(:module_root) { '/path/to/test/module' }

context 'and not provided with a name for the new task' do
let(:args) { %w[new task] }

it_behaves_like 'it exits non-zero and prints the help text'
end

context 'and provided an empty string as the task name' do
let(:args) { ['new', 'task', ''] }

it_behaves_like 'it exits non-zero and prints the help text'
end

context 'and provided an invalid task name' do
let(:args) { %w[new task test-task] }

it_behaves_like 'it exits with an error', %r{'test-task' is not a valid task name}
end

context 'and provided a valid task name' do
let(:generator) { PDK::Generate::Task }
let(:generator_double) { instance_double(generator) }
let(:generator_opts) { instance_of(Hash) }

before(:each) do
allow(generator).to receive(:new).with(anything, 'test_task', generator_opts).and_return(generator_double)
end

it 'generates the task' do
expect(generator_double).to receive(:run)

PDK::CLI.run(%w[new task test_task])
end

context 'and a custom template URL' do
let(:generator_opts) { { :'template-url' => 'https://custom/template' } }

it 'generates the task from the custom template' do
expect(generator_double).to receive(:run)

PDK::CLI.run(%w[new task test_task --template-url https://custom/template])
end
end

context 'and provided a description for the task' do
let(:generator_opts) do
{
:description => 'test_task description',
:'template-url' => anything,
}
end

it 'generates the task with the specified description' do
expect(generator_double).to receive(:run)

PDK::CLI.run(['new', 'task', 'test_task', '--description', 'test_task description'])
end
end
end
end
end
1 change: 1 addition & 0 deletions spec/unit/pdk/generate/module_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,7 @@
it 'creates a skeleton directory structure' do
expect(FileUtils).to receive(:mkdir_p).with(File.join(path, 'manifests'))
expect(FileUtils).to receive(:mkdir_p).with(File.join(path, 'templates'))
expect(FileUtils).to receive(:mkdir_p).with(File.join(path, 'tasks'))

described_class.prepare_module_directory(path)
end
Expand Down
Loading

0 comments on commit 61691e7

Please sign in to comment.