Move inspec init to v2 plugins (#3407)

* Move inspec init to v2 plugins.
* Revert inspec run command env change.
* Allow prefix and env for run_inspec_process.
* Update unit tests to use new functionality.

Signed-off-by: Jared Quick <jquick@chef.io>
This commit is contained in:
Jared Quick 2018-09-18 15:54:33 -04:00 committed by GitHub
parent 4c3b03da19
commit 544204a44c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 160 additions and 157 deletions

View file

@ -1,12 +0,0 @@
# encoding: utf-8
# author: Christoph Hartmann
# author: Dominik Richter
libdir = File.dirname(__FILE__)
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
module Init
autoload :Profile, 'inspec-init/profile'
end
require 'inspec-init/cli'

View file

@ -1,39 +0,0 @@
# encoding: utf-8
require 'pathname'
require_relative 'renderer'
require 'inspec/base_cli'
module Init
class CLI < Inspec::BaseCLI
namespace 'init'
# TODO: find another solution, once https://github.com/erikhuda/thor/issues/261 is fixed
def self.banner(command, _namespace = nil, _subcommand = false)
"#{basename} #{subcommand_prefix} #{command.usage}"
end
def self.subcommand_prefix
namespace
end
# Look in the 'template' directory, and register a subcommand
# for each template directory found there.
template_dir = File.join(File.dirname(__FILE__), 'templates')
Dir.glob(File.join(template_dir, '*')) do |template|
template_name = Pathname.new(template).relative_path_from(Pathname.new(template_dir)).to_s
# register command for the template
desc "#{template_name} NAME", "Create a new #{template_name}"
option :overwrite, type: :boolean, default: false,
desc: 'Overwrites existing directory'
define_method template_name.to_sym do |name_for_new_structure|
renderer = Init::Renderer.new(self, options)
renderer.render_with_values(template_name, name: name_for_new_structure)
end
end
end
# register the subcommand to Inspec CLI registry
Inspec::Plugins::CLI.add_subcommand(Init::CLI, 'init', 'init TEMPLATE ...', 'Scaffolds a new project', {})
end

View file

@ -1,79 +0,0 @@
require 'fileutils'
require 'erb'
module Init
class Renderer
# Creates a renderer able to render the given template type
# 1. iterate over all files
# 2. read content in erb
# 3. write to full_destination_root_path
attr_reader :overwrite_mode, :ui
def initialize(cli_ui, cli_options = {})
@ui = cli_ui
@overwrite_mode = cli_options['overwrite']
end
# rubocop: disable Metrics/AbcSize
def render_with_values(template_type, template_values = {})
# look for template directory
base_dir = File.join(File.dirname(__FILE__), 'templates', template_type)
# prepare glob for all subdirectories and files
template_glob = File.join(base_dir, '**', '{*,.*}')
# Use the name attribute to define the path to the profile.
profile_path = template_values[:name]
# Use slashes (\, /) to split up the name into an Array then use the last entry
# to reset the name of the profile.
template_values[:name] = template_values[:name].split(%r{\\|\/}).last
# Generate the full full_destination_root_path path on disk
full_destination_root_path = Pathname.new(Dir.pwd).join(profile_path)
ui.plain_text "Create new #{template_type} at #{ui.mark_text(full_destination_root_path)}"
# check that the directory does not exist
if File.exist?(full_destination_root_path) && !overwrite_mode
ui.plain_text "#{ui.mark_text(full_destination_root_path)} exists already, use --overwrite"
ui.exit(1)
end
# ensure that full_destination_root_path directory is available
FileUtils.mkdir_p(full_destination_root_path)
# iterate over files and write to full_destination_root_path
Dir.glob(template_glob) do |file|
relative_destination_item_path = Pathname.new(file).relative_path_from(Pathname.new(base_dir))
full_destination_item_path = Pathname.new(full_destination_root_path).join(relative_destination_item_path)
if File.directory?(file)
ui.li "Create directory #{ui.mark_text(relative_destination_item_path)}"
FileUtils.mkdir_p(full_destination_item_path)
elsif File.file?(file)
ui.li "Create file #{ui.mark_text(relative_destination_item_path)}"
# read & render content
content = render(File.read(file), template_values)
# write file content
File.write(full_destination_item_path, content)
else
ui.plain_text "Ignore #{file}, because its not an file or directoy"
end
end
end
# rubocop: enable Metrics/AbcSize
# This is a render helper to bind hash values to a ERB template
# ERB provides result_with_hash in ruby 2.5.0+, which does exactly this
def render(template_content, hash)
# create a new binding class
cls = Class.new do
hash.each do |key, value|
define_method key.to_sym do
value
end
end
# expose binding
define_method :bind do
binding
end
end
ERB.new(template_content).result(cls.new.bind)
end
end
end

View file

@ -0,0 +1,12 @@
module InspecPlugins
module Init
class Plugin < Inspec.plugin(2)
plugin_name :'inspec-init'
cli_command :init do
require_relative 'inspec-init/cli'
InspecPlugins::Init::CLI
end
end
end
end

View file

@ -0,0 +1,28 @@
# encoding: utf-8
require 'pathname'
require_relative 'renderer'
module InspecPlugins
module Init
class CLI < Inspec.plugin(2, :cli_command)
subcommand_desc 'init SUBCOMMAND', 'Initialize InSpec objects'
# Look in the 'template' directory, and register a subcommand
# for each template directory found there.
template_dir = File.join(File.dirname(__FILE__), 'templates')
Dir.glob(File.join(template_dir, '*')) do |template|
template_name = Pathname.new(template).relative_path_from(Pathname.new(template_dir)).to_s
# register command for the template
desc "#{template_name} NAME", "Create a new #{template_name}"
option :overwrite, type: :boolean, default: false,
desc: 'Overwrites existing directory'
define_method template_name.to_sym do |name_for_new_structure|
renderer = InspecPlugins::Init::Renderer.new(self, options)
renderer.render_with_values(template_name, name: name_for_new_structure)
end
end
end
end
end

View file

@ -0,0 +1,81 @@
require 'fileutils'
require 'erb'
module InspecPlugins
module Init
class Renderer
# Creates a renderer able to render the given template type
# 1. iterate over all files
# 2. read content in erb
# 3. write to full_destination_root_path
attr_reader :overwrite_mode, :ui
def initialize(cli_ui, cli_options = {})
@ui = cli_ui
@overwrite_mode = cli_options['overwrite']
end
# rubocop: disable Metrics/AbcSize
def render_with_values(template_type, template_values = {})
# look for template directory
base_dir = File.join(File.dirname(__FILE__), 'templates', template_type)
# prepare glob for all subdirectories and files
template_glob = File.join(base_dir, '**', '{*,.*}')
# Use the name attribute to define the path to the profile.
profile_path = template_values[:name]
# Use slashes (\, /) to split up the name into an Array then use the last entry
# to reset the name of the profile.
template_values[:name] = template_values[:name].split(%r{\\|\/}).last
# Generate the full full_destination_root_path path on disk
full_destination_root_path = Pathname.new(Dir.pwd).join(profile_path)
ui.plain_text "Create new #{template_type} at #{ui.mark_text(full_destination_root_path)}"
# check that the directory does not exist
if File.exist?(full_destination_root_path) && !overwrite_mode
ui.plain_text "#{ui.mark_text(full_destination_root_path)} exists already, use --overwrite"
ui.exit(1)
end
# ensure that full_destination_root_path directory is available
FileUtils.mkdir_p(full_destination_root_path)
# iterate over files and write to full_destination_root_path
Dir.glob(template_glob) do |file|
relative_destination_item_path = Pathname.new(file).relative_path_from(Pathname.new(base_dir))
full_destination_item_path = Pathname.new(full_destination_root_path).join(relative_destination_item_path)
if File.directory?(file)
ui.li "Create directory #{ui.mark_text(relative_destination_item_path)}"
FileUtils.mkdir_p(full_destination_item_path)
elsif File.file?(file)
ui.li "Create file #{ui.mark_text(relative_destination_item_path)}"
# read & render content
content = render(File.read(file), template_values)
# write file content
File.write(full_destination_item_path, content)
else
ui.plain_text "Ignore #{file}, because its not an file or directoy"
end
end
end
# rubocop: enable Metrics/AbcSize
# This is a render helper to bind hash values to a ERB template
# ERB provides result_with_hash in ruby 2.5.0+, which does exactly this
def render(template_content, hash)
# create a new binding class
cls = Class.new do
hash.each do |key, value|
define_method key.to_sym do
value
end
end
# expose binding
define_method :bind do
binding
end
end
ERB.new(template_content).result(cls.new.bind)
end
end
end
end

View file

@ -0,0 +1,30 @@
# encoding: utf-8
require_relative '../../../shared/core_plugin_test_helper.rb'
class InitCli < MiniTest::Test
include CorePluginFunctionalHelper
def test_generating_inspec_profile
Dir.mktmpdir do |dir|
profile = File.join(dir, 'test-profile')
out = run_inspec_process("init profile test-profile", prefix: "cd #{dir} &&")
assert_equal 0, out.exit_status
assert_includes out.stdout, 'Create new profile at'
assert_includes out.stdout, profile
assert_includes Dir.entries(profile).join, 'inspec.yml'
assert_includes Dir.entries(profile).join, 'README.md'
end
end
def test_profile_with_slash_name
Dir.mktmpdir do |dir|
profile = dir + '/test/deeper/profile'
out = run_inspec_process("init profile test/deeper/profile", prefix: "cd #{dir} &&")
assert_equal 0, out.exit_status
assert_equal true, File.exist?(profile)
profile = YAML.load_file("#{profile}/inspec.yml")
assert_equal 'profile', profile['name']
end
end
end

View file

@ -33,9 +33,14 @@ module CorePluginFunctionalHelper
require 'train'
TRAIN_CONNECTION = Train.create('local', command_runner: :generic).connection
def run_inspec_process(command_line, env = {})
env_prefix = env.to_a.map { |assignment| "#{assignment[0]}=#{assignment[1]}" }.join(' ')
TRAIN_CONNECTION.run_command("#{env_prefix} #{inspec_bin_path} #{command_line}")
def run_inspec_process(command_line, opts = {})
prefix = ''
if opts.key?(:prefix)
prefix = opts[:prefix]
elsif opts.key?(:env)
prefix = opts[:env].to_a.map { |assignment| "#{assignment[0]}=#{assignment[1]}" }.join(' ')
end
TRAIN_CONNECTION.run_command("#{prefix} #{inspec_bin_path} #{command_line}")
end
end

View file

@ -1,23 +0,0 @@
require 'functional/helper'
require 'fileutils'
require 'tmpdir'
require 'yaml'
describe 'inspec init' do
include FunctionalHelper
tmpdir = Dir.tmpdir
describe 'inspec init profile with/slash' do
it 'names profile with string after last slash' do
slash_profile = "#{tmpdir}/inspecwith/slash"
out = inspec("init profile #{slash_profile}")
out.exit_status.must_equal 0
File.exist?(slash_profile).must_equal true
profile = YAML.load_file("#{slash_profile}/inspec.yml")
profile['name'].must_equal 'slash'
end
end
Dir.glob("#{tmpdir}/inspecwith*").each {|i| FileUtils.remove_entry_secure(i) }
end

View file

@ -25,11 +25,11 @@ class PluginLoaderTests < MiniTest::Test
@bundled_plugins = [
:artifact,
:compliance,
:init,
:supermarket,
]
@core_plugins = [
:'inspec-habitat',
:'inspec-init',
]
end