Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new "pdk console" command #758

Merged
merged 2 commits into from
Oct 8, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ RSpec/NestedGroups:
- 'spec/unit/generate/puppet_object_spec.rb'
- 'spec/unit/template_file_spec.rb'

RSpec/AnyInstance:
Exclude:
- 'spec/unit/pdk/cli/console_spec.rb'

Style/ClassAndModuleChildren:
Enabled: false

Expand Down
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ puppet-syntax | Checks for correct syntax in Puppet manifests, templates, and Hi
puppetlabs_spec_helper | Provides classes, methods, and Rake tasks to help with spec testing Puppet code.
rspec-puppet | Tests the behavior of Puppet when it compiles your manifests into a catalog of Puppet resources.
rspec-puppet-facts | Adds support for running rspec-puppet tests against the facts for your supported operating systems.
puppet-debugger | Provides a REPL based debugger console.


## Installation
Expand Down Expand Up @@ -105,6 +106,46 @@ This command runs all available unit tests.

## Experimental features

### `pdk console` command
Copy link
Contributor

@glennsarti glennsarti Sep 23, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jbondpdx Can you give these docs a quick look.

  • To use just execute avoid just and easy

  • ~/modules/ntp $ ... Avoid shell specific things e.g. a PowerShell user would see this prefixed differently.

Copy link
Contributor Author

@logicminds logicminds Sep 24, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the README with the fixes and also added some extra bits I thought were important.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I didn't see this sooner, @glennsarti , @logicminds ... I think it got lost in a mass of GitHub emails. Thank you for both for these docs!

The pdk console command executes a session of the puppet debugger when inside a module and allows for exploration of puppet code. See the official [puppet debugger site](https://www.puppet-debugger.com) for more info and the official docs [site here.](https://docs.puppet-debugger.com)

To use, execute `pdk console` from inside your module directory. You can also supply the `--puppet-version` or `--pe-version` or `--puppet-dev` to swap out the puppet version when using the console.

Example (from within a module):

* `pdk console --puppet-version=5`
* `pdk console --pe-version=2018.1`

The `pdk console` command will also pass through any puppet debugger arguments you wish to use.

Example:

* `pdk console --no-facterdb`
* `pdk console --play https://gist.github.com/logicminds/4f6bcfd723c92aad1f01f6a800319fa4`
* `pdk console -e "md5('sdfasdfasdf')" --run-once --quiet`

Use `pdk console -h` for a further explanation of pass through arguments.

If you receive the following error you do not have the puppet-debugger gem installed.

```
pdk console -h
Error: Unknown Puppet subcommand 'debugger'
See 'puppet help' for help on available puppet subcommands
logicminds marked this conversation as resolved.
Show resolved Hide resolved
```

To fix this you will need to add the following entry to your .sync.yml file and run pdk update:

```
Gemfile:
required:
":development":
- gem: puppet-debugger
version: "~> 0.14"
```

**NOTE**: The puppet-debugger gem has been added to the [puppet-module-* gems](https://github.com/puppetlabs/puppet-module-gems/pull/117), so once you get the gem update you no longer need the .sync.yml entry.

### `pdk bundle` command

This command executes arbitrary commands in a bundler context within the module you're currently working on. Arguments to this command are passed straight through to bundler. This command is experimental and can lead to errors that can't be resolved by PDK itself.
Expand Down
1 change: 1 addition & 0 deletions lib/pdk/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ def self.puppet_dev_option(dsl)
require 'pdk/cli/update'
require 'pdk/cli/validate'
require 'pdk/cli/module'
require 'pdk/cli/console'

@base_cmd.add_command Cri::Command.new_basic_help
end
145 changes: 145 additions & 0 deletions lib/pdk/cli/console.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
module PDK::CLI
@console_cmd = @base_cmd.define_command do
name 'console'
usage _('console [console_options]')
summary _('(Experimental) Start a session of the puppet debugger console.')
default_subcommand 'help'
description _(<<-EOF
The pdk console runs a interactive session of the puppet debugger tool to test out snippets of code, run
language evaluations, datatype prototyping and much more. A virtual playground for your puppet code!
For usage details see the puppet debugger docs at https://docs.puppet-debugger.com.

EOF
)
PDK::CLI.puppet_version_options(self)
PDK::CLI.puppet_dev_option(self)
# we have to skip option parsing because it is expected the user
# will be passing additional args that are passed to the debugger
skip_option_parsing

# TODO: using -h or --help skips the pdk help and passes to puppet debugger help
run do |_opts, args, _cmd|
require 'pdk/cli/util'
require 'pdk/util'

PDK::CLI::Util.ensure_in_module!(
message: _('Console can only be run from inside a valid module directory'),
log_level: :fatal,
)
processed_options, processed_args = process_opts(args)

PDK::CLI::Util.validate_puppet_version_opts(processed_options)

PDK::CLI::Util.analytics_screen_view('console', args)

# TODO: figure out if we need to remove default configs set by puppet
logicminds marked this conversation as resolved.
Show resolved Hide resolved
# so it is scoped for the module only
# "--environmentpath"...
flags = if PDK::Util.in_module_root?
["--basemodulepath=#{base_module_path}",
"--modulepath=#{base_module_path}"]
else
[]
end
debugger_args = ['debugger'] + processed_args + flags
result = run_in_module(processed_options, debugger_args)

exit result[:exit_code]
end

# Logs a fatal message about the gem missing and how to add it
def inform_user_for_missing_gem(gem_name = 'puppet-debugger', version = '~> 0.14')
PDK.logger.fatal(<<-EOF
Your Gemfile is missing the #{gem_name} gem. You can add the missing gem
by updating your #{File.join(PDK::Util.module_root, '.sync.yml')} file with the following
and running pdk update.

Gemfile:
required:
":development":
- gem: #{gem_name}
version: \"#{version}\"

EOF
)
end

# @return [Boolean] - true if the gem was found in the lockfile
# @param [String] - name of ruby gem to check in bundle lockfile
def gem_in_bundle_lockfile?(gem_name)
require 'bundler'
require 'pdk/util/bundler'

lock_file_path = PDK::Util::Bundler::BundleHelper.new.gemfile_lock
PDK.logger.debug("Checking lockfile #{lock_file_path} for #{gem_name}")
lock_file = ::Bundler::LockfileParser.new(::Bundler.read_file(lock_file_path))
!lock_file.specs.find { |spec| spec.name.eql?(gem_name) }.nil?
rescue ::Bundler::GemfileNotFound => e
PDK.logger.debug _(e.message)
false
end

def check_fixtures_dir
existing_path = base_module_path.split(':').find do |path|
Dir.exist?(path) && Dir.entries(path).length > 2
end
PDK.logger.warn _('Module fixtures not found, please run pdk bundle exec rake spec_prep.') unless existing_path
end

# @return [Array] - array of split options [{:"puppet-version"=>"6.9.0"}, ['--loglevel=debug']]
# options are for the pdk and debugger pass through
def process_opts(opts)
args = opts.map do |e|
if e =~ %r{\A-{2}puppet|pe\-version|dev}
value = e.split('=')
(value.count < 2) ? value + [''] : value
end
end
args = args.compact.to_h
# symbolize keys
args = args.inject({}) do |memo, (k, v)| # rubocop:disable Style/EachWithObject
memo[k.sub('--', '').to_sym] = v
memo
end
# pass through all other args that are bound for puppet debugger
processed_args = opts.map { |e| e unless e =~ %r{\A-{2}puppet|pe\-version|dev} }.compact
[args, processed_args]
end

# @param opts [Hash] - the options passed into the CRI command
# @param bundle_args [Array] array of bundle exec args and puppet debugger args
# @return [Hash] - a command result hash
def run_in_module(opts, bundle_args)
require 'pdk/cli/exec'
require 'pdk/cli/exec/interactive_command'
require 'pdk/util/ruby_version'
require 'pdk/util/bundler'

check_fixtures_dir
output = opts[:debug].nil?
puppet_env = PDK::CLI::Util.puppet_from_opts_or_env(opts, output)
gemfile_env = PDK::Util::Bundler::BundleHelper.gemfile_env(puppet_env[:gemset])
PDK::Util::RubyVersion.use(puppet_env[:ruby_version])
PDK::Util::RubyVersion.instance(puppet_env[:ruby_version])
PDK::Util::Bundler.ensure_bundle!(puppet_env[:gemset])
unless gem_in_bundle_lockfile?('puppet-debugger')
inform_user_for_missing_gem
return { exit_code: 1 }
end

debugger_args = %w[exec puppet] + bundle_args
command = PDK::CLI::Exec::InteractiveCommand.new(PDK::CLI::Exec.bundle_bin, *debugger_args).tap do |c|
c.context = :pwd
c.update_environment(gemfile_env)
end
command.execute!
end

# @return [String] - the basemodulepath of the fixtures and modules from the current module
# also includes ./modules in case librarian puppet is used
def base_module_path
base_module_path = File.join(PDK::Util.module_fixtures_dir, 'modules')
"#{base_module_path}:#{File.join(PDK::Util.module_root, 'modules')}"
end
end
end
74 changes: 74 additions & 0 deletions spec/fixtures/module_gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
source ENV['GEM_SOURCE'] || 'https://rubygems.org'

def location_for(place_or_version, fake_version = nil)
git_url_regex = %r{\A(?<url>(https?|git)[:@][^#]*)(#(?<branch>.*))?}
file_url_regex = %r{\Afile:\/\/(?<path>.*)}

if place_or_version && (git_url = place_or_version.match(git_url_regex))
[fake_version, { git: git_url[:url], branch: git_url[:branch], require: false }].compact
elsif place_or_version && (file_url = place_or_version.match(file_url_regex))
['>= 0', { path: File.expand_path(file_url[:path]), require: false }]
else
[place_or_version, { require: false }]
end
end

ruby_version_segments = Gem::Version.new(RUBY_VERSION.dup).segments
minor_version = ruby_version_segments[0..1].join('.')

group :development do
gem "fast_gettext", '1.1.0', require: false if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.1.0')
gem "fast_gettext", require: false if Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.1.0')
gem "json_pure", '<= 2.0.1', require: false if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.0.0')
gem "json", '= 1.8.1', require: false if Gem::Version.new(RUBY_VERSION.dup) == Gem::Version.new('2.1.9')
gem "json", '= 2.0.4', require: false if Gem::Requirement.create('~> 2.4.2').satisfied_by?(Gem::Version.new(RUBY_VERSION.dup))
gem "json", '= 2.1.0', require: false if Gem::Requirement.create(['>= 2.5.0', '< 2.7.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup))
gem "rb-readline", '= 0.5.5', require: false, platforms: [:mswin, :mingw, :x64_mingw]
gem "puppet-module-posix-default-r#{minor_version}", '~> 0.3', require: false, platforms: [:ruby]
gem "puppet-module-posix-dev-r#{minor_version}", '~> 0.3', require: false, platforms: [:ruby]
gem "puppet-module-win-default-r#{minor_version}", '~> 0.3', require: false, platforms: [:mswin, :mingw, :x64_mingw]
gem "puppet-module-win-dev-r#{minor_version}", '~> 0.3', require: false, platforms: [:mswin, :mingw, :x64_mingw]
gem "puppet-debugger", '~> 0.14.0', require: false
gem "puppet-debugger-playbooks", require: false
end

puppet_version = ENV['PUPPET_GEM_VERSION']
facter_version = ENV['FACTER_GEM_VERSION']
hiera_version = ENV['HIERA_GEM_VERSION']

gems = {}

gems['puppet'] = location_for(puppet_version)

# If facter or hiera versions have been specified via the environment
# variables

gems['facter'] = location_for(facter_version) if facter_version
gems['hiera'] = location_for(hiera_version) if hiera_version

if Gem.win_platform? && puppet_version =~ %r{^(file:///|git://)}
# If we're using a Puppet gem on Windows which handles its own win32-xxx gem
# dependencies (>= 3.5.0), set the maximum versions (see PUP-6445).
gems['win32-dir'] = ['<= 0.4.9', require: false]
gems['win32-eventlog'] = ['<= 0.6.5', require: false]
gems['win32-process'] = ['<= 0.7.5', require: false]
gems['win32-security'] = ['<= 0.2.5', require: false]
gems['win32-service'] = ['0.8.8', require: false]
end

gems.each do |gem_name, gem_params|
gem gem_name, *gem_params
end

# Evaluate Gemfile.local and ~/.gemfile if they exist
extra_gemfiles = [
"#{__FILE__}.local",
File.join(Dir.home, '.gemfile'),
]

extra_gemfiles.each do |gemfile|
if File.file?(gemfile) && File.readable?(gemfile)
eval(File.read(gemfile), binding)
end
end
# vim: syntax=ruby
Loading