summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--knife/knife.gemspec1
-rw-r--r--knife/lib/chef/knife/bootstrap/templates/windows-chef-client-msi.erb10
-rw-r--r--knife/lib/chef/knife/core/windows_bootstrap_context.rb94
-rw-r--r--knife/lib/constants.rb28
-rw-r--r--knife/lib/exceptions.rb565
-rw-r--r--knife/lib/json_compat.rb63
-rw-r--r--knife/lib/mixin/powershell_exec.rb128
-rw-r--r--knife/lib/powershell.rb84
-rw-r--r--knife/lib/pwsh.rb71
-rw-r--r--lib/chef/dsl/chef_vault.rb4
10 files changed, 1008 insertions, 40 deletions
diff --git a/knife/knife.gemspec b/knife/knife.gemspec
index d121343211..98f4a15806 100644
--- a/knife/knife.gemspec
+++ b/knife/knife.gemspec
@@ -32,6 +32,7 @@ Gem::Specification.new do |s|
# disabling this until we get get it to compile on RHEL 7
# s.add_dependency "x25519", ">= 1.0.9" # ed25519 KEX module. 1.0.9+ required to resolve sigill failures
s.add_dependency "highline", ">= 1.6.9", "< 3" # Used in UI to present a list, no other usage.
+ s.add_dependency "ffi-libarchive", "~> 1.0", ">= 1.0.3" # archive_file resource
s.add_dependency "tty-prompt", "~> 0.21" # knife ui.ask prompt
s.add_dependency "tty-screen", "~> 0.6" # knife list
diff --git a/knife/lib/chef/knife/bootstrap/templates/windows-chef-client-msi.erb b/knife/lib/chef/knife/bootstrap/templates/windows-chef-client-msi.erb
index 7aa7be49f8..877d6d5c64 100644
--- a/knife/lib/chef/knife/bootstrap/templates/windows-chef-client-msi.erb
+++ b/knife/lib/chef/knife/bootstrap/templates/windows-chef-client-msi.erb
@@ -32,9 +32,6 @@
@echo Existing directory found, skipping creation.
)
-> <%= bootstrap_directory %>\wget.vbs (
- <%= win_wget %>
-)
> <%= bootstrap_directory %>\wget.ps1 (
<%= win_wget_ps %>
@@ -111,14 +108,15 @@ goto chef_installed
:chef_installed
@echo Checking for existing <%= ChefUtils::Dist::Infra::PRODUCT %> installation
WHERE <%= ChefUtils::Dist::Infra::CLIENT %> >nul 2>nul
+<%#
If !ERRORLEVEL!==0 (
@echo Existing <%= ChefUtils::Dist::Infra::PRODUCT %> installation detected, skipping download
- goto key_create
+<%# goto key_create
) else (
@echo No existing installation of <%= ChefUtils::Dist::Infra::PRODUCT %> detected
goto install
-)
-
+<%# )
+%>
:install
@rem If user has provided the custom installation command, execute it
<% if @config[:bootstrap_install_command] %>
diff --git a/knife/lib/chef/knife/core/windows_bootstrap_context.rb b/knife/lib/chef/knife/core/windows_bootstrap_context.rb
index f93e6572e3..4d79abe81e 100644
--- a/knife/lib/chef/knife/core/windows_bootstrap_context.rb
+++ b/knife/lib/chef/knife/core/windows_bootstrap_context.rb
@@ -15,10 +15,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-
+require "pry"
require_relative "bootstrap_context"
require "chef-config/path_helper" unless defined?(ChefConfig::PathHelper)
require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+require "mixin/powershell_exec"
+# require "chef/mixin/powershell_exec"
+
class Chef
class Knife
@@ -30,6 +33,8 @@ class Chef
# * @run_list - the run list for the node to bootstrap
#
class WindowsBootstrapContext < BootstrapContext
+ # include Chef::Mixin::PowershellExec
+ include Mixin::PowershellExec
attr_accessor :config
attr_accessor :chef_config
attr_accessor :secret
@@ -322,7 +327,10 @@ class Chef
private
def install_command(executor_quote)
- "msiexec /qn /log #{executor_quote}%CHEF_CLIENT_MSI_LOG_PATH%#{executor_quote} /i #{executor_quote}%LOCAL_DESTINATION_MSI_PATH%#{executor_quote}"
+ # "msiexec /qn /log #{executor_quote}%CHEF_CLIENT_MSI_LOG_PATH%#{executor_quote} /i #{executor_quote}%LOCAL_DESTINATION_MSI_PATH%#{executor_quote}"
+ binding.pry
+ powershell_exec("echo hello")
+ "$a = (Start-Process -FilePath \"msiexec\" -Wait -ArgumentList \"/qn /log #{executor_quote}%CHEF_CLIENT_MSI_LOG_PATH%#{executor_quote} /i #{executor_quote}%LOCAL_DESTINATION_MSI_PATH%#{executor_quote} -Passthru).ExitCode"
end
# Returns a string for copying the trusted certificates on the workstation to the system being bootstrapped
@@ -364,39 +372,57 @@ class Chef
# need to be double quoted, schtasks allows the use of single
# quotes that will later be converted to double quotes
command = install_command("'")
+ binding.pry
<<~EOH
- @set MSIERRORCODE=!ERRORLEVEL!
- @if ERRORLEVEL 1 (
- @echo WARNING: Failed to install #{ChefUtils::Dist::Infra::PRODUCT} MSI package in remote context with status code !MSIERRORCODE!.
- @echo WARNING: This may be due to a defect in operating system update KB2918614: http://support.microsoft.com/kb/2918614
- @set OLDLOGLOCATION="%CHEF_CLIENT_MSI_LOG_PATH%-fail.log"
- @move "%CHEF_CLIENT_MSI_LOG_PATH%" "!OLDLOGLOCATION!" > NUL
- @echo WARNING: Saving installation log of failure at !OLDLOGLOCATION!
- @echo WARNING: Retrying installation with local context...
- @schtasks /create /f /sc once /st 00:00:00 /tn chefclientbootstraptask /ru SYSTEM /rl HIGHEST /tr \"cmd /c #{command} & sleep 2 & waitfor /s %computername% /si chefclientinstalldone\"
-
- @if ERRORLEVEL 1 (
- @echo ERROR: Failed to create #{ChefUtils::Dist::Infra::PRODUCT} installation scheduled task with status code !ERRORLEVEL! > "&2"
- ) else (
- @echo Successfully created scheduled task to install #{ChefUtils::Dist::Infra::PRODUCT}.
- @schtasks /run /tn chefclientbootstraptask
- @if ERRORLEVEL 1 (
- @echo ERROR: Failed to execute #{ChefUtils::Dist::Infra::PRODUCT} installation scheduled task with status code !ERRORLEVEL!. > "&2"
- ) else (
- @echo Successfully started #{ChefUtils::Dist::Infra::PRODUCT} installation scheduled task.
- @echo Waiting for installation to complete -- this may take a few minutes...
- waitfor chefclientinstalldone /t 600
- if ERRORLEVEL 1 (
- @echo ERROR: Timed out waiting for #{ChefUtils::Dist::Infra::PRODUCT} package to install
- ) else (
- @echo Finished waiting for #{ChefUtils::Dist::Infra::PRODUCT} package to install.
- )
- @schtasks /delete /f /tn chefclientbootstraptask > NUL
- )
- )
- ) else (
- @echo Successfully installed #{ChefUtils::Dist::Infra::PRODUCT} package.
- )
+ $MSIERRORCODE = $a
+
+
+ Write-Warning "WARNING: Failed to install #{ChefUtils::Dist::Infra::PRODUCT} MSI package in remote context with status code $MSIERRORCODE."
+ Write-Warning "WARNING: This may be due to a defect in operating system update KB2918614: http://support.microsoft.com/kb/2918614"
+ $OLDLOGLOCATION="%CHEF_CLIENT_MSI_LOG_PATH%-fail.log"
+ Move-Item -Path "{CHEF_CLIENT_MSI_LOG_PATH}" -Destination "{OLDLOGLOCATION}"
+ Write-Warning WARNING: "Saving installation log of failure at ${OLDLOGLOCATION}"
+ Write-Warning WARNING: "Retrying installation with local context..."
+
+ $command = "{CHEF_CLIENT_MSI_LOG_PATH}"
+ Write-Warning WARNING: #####################command $OLDLOGLOCATION ###################
+ New-Event -SourceIdentifier chefclientinstalldone -Sender $computername
+ $actions = (New-ScheduledTaskAction -Execute $command), (New-ScheduledTaskAction -Execute Start-Sleep 2)
+ $trigger = New-ScheduledTaskTrigger -Once -At '0:00 AM'
+ $principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -RunLevel Highest
+ $task = Register-ScheduledTask -TaskName "chefclientbootstraptask" -Trigger $trigger -Principal $principal -Action $actions
+ Wait-Event -SourceIdentifier chefclientinstalldone -Timeout 600
+
+ if(ERRORLEVEL -eq 1)
+ {
+ Write-Host "ERROR: Failed to create #{ChefUtils::Dist::Infra::PRODUCT} installation scheduled task with status code ${ERRORLEVEL} > "&2""
+ }
+ else {
+ Write-Host "Successfully created scheduled task to install #{ChefUtils::Dist::Infra::PRODUCT}."
+ Start-ScheduledTask -TaskName "chefclientbootstraptask"
+ }
+ if(ERRORLEVEL -eq 1)
+ {
+ Write-Host "ERROR: Failed to execute #{ChefUtils::Dist::Infra::PRODUCT} installation scheduled task with status code ${ERRORLEVEL}. > "&2""
+ }
+ else
+ {
+ Write-Host "Successfully started #{ChefUtils::Dist::Infra::PRODUCT} installation scheduled task."
+ Write-Host "Waiting for installation to complete -- this may take a few minutes..."
+ waitfor chefclientinstalldone /t 600
+ if(ERRORLEVEL -eq 1)
+ {
+ Write-Host "ERROR: Timed out waiting for #{ChefUtils::Dist::Infra::PRODUCT} package to install"
+ }
+ else {
+ Write-Host "Finished waiting for #{ChefUtils::Dist::Infra::PRODUCT} package to install."
+ }
+ Unregister-ScheduledTask -TaskName 'chefclientbootstraptask' -Confirm:$false
+ }
+ }
+
+ Write-Host "Successfully installed #{ChefUtils::Dist::Infra::PRODUCT} package."
+
EOH
end
end
diff --git a/knife/lib/constants.rb b/knife/lib/constants.rb
new file mode 100644
index 0000000000..0c78c9ee19
--- /dev/null
+++ b/knife/lib/constants.rb
@@ -0,0 +1,28 @@
+#
+# Author:: John Keiser <jkeiser@chef.io>
+# Copyright:: Copyright (c) Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+class Chef
+ NOT_PASSED = Object.new
+ def NOT_PASSED.to_s
+ "NOT_PASSED"
+ end
+
+ def NOT_PASSED.inspect
+ to_s
+ end
+ NOT_PASSED.freeze
+end
diff --git a/knife/lib/exceptions.rb b/knife/lib/exceptions.rb
new file mode 100644
index 0000000000..ffdbdcbaba
--- /dev/null
+++ b/knife/lib/exceptions.rb
@@ -0,0 +1,565 @@
+#
+# Author:: Adam Jacob (<adam@chef.io>)
+# Author:: Seth Falcon (<seth@chef.io>)
+# Author:: Kyle Goodwin (<kgoodwin@primerevenue.com>)
+# Copyright:: Copyright (c) Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "chef-config/exceptions"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+require_relative "constants"
+
+class Chef
+ # == Chef::Exceptions
+ # Chef's custom exceptions are all contained within the Chef::Exceptions
+ # namespace.
+ class Exceptions
+
+ ConfigurationError = ChefConfig::ConfigurationError
+
+ # Backcompat with Chef::ShellOut code:
+ require "mixlib/shellout/exceptions"
+
+ def self.const_missing(const_name)
+ if const_name == :ShellCommandFailed
+ Chef::Log.warn("Chef::Exceptions::ShellCommandFailed is deprecated, use Mixlib::ShellOut::ShellCommandFailed")
+ called_from = caller[0..3].inject("Called from:\n") { |msg, trace_line| msg << " #{trace_line}\n" }
+ Chef::Log.warn(called_from)
+ Mixlib::ShellOut::ShellCommandFailed
+ else
+ super
+ end
+ end
+
+ class Application < RuntimeError; end
+ class SigInt < RuntimeError; end
+ class SigTerm < RuntimeError; end
+ class Cron < RuntimeError; end
+ class WindowsEnv < RuntimeError; end
+ class Exec < RuntimeError; end
+ class Execute < RuntimeError; end
+ class ErlCall < RuntimeError; end
+ class FileNotFound < RuntimeError; end
+ class Package < RuntimeError; end
+ class Service < RuntimeError; end
+ class Script < RuntimeError; end
+ class Route < RuntimeError; end
+ class SearchIndex < RuntimeError; end
+ class Override < RuntimeError; end
+ class UnsupportedAction < RuntimeError; end
+ class MissingLibrary < RuntimeError; end
+
+ class CannotDetermineNodeName < RuntimeError
+ def initialize
+ super "Unable to determine node name: configure node_name or configure the system's hostname and fqdn"
+ end
+ end
+
+ class User < RuntimeError; end
+ class Group < RuntimeError; end
+ class Link < RuntimeError; end
+ class Mount < RuntimeError; end
+ class Reboot < Exception; end # rubocop:disable Lint/InheritException
+ class RebootPending < Exception; end # rubocop:disable Lint/InheritException
+ class RebootFailed < Mixlib::ShellOut::ShellCommandFailed; end
+ class ClientUpgraded < Exception; end # rubocop:disable Lint/InheritException
+ class PrivateKeyMissing < RuntimeError; end
+ class CannotWritePrivateKey < RuntimeError; end
+ class RoleNotFound < RuntimeError; end
+ class DuplicateRole < RuntimeError; end
+ class ValidationFailed < ArgumentError; end
+ class CannotValidateStaticallyError < ArgumentError; end
+ class InvalidPrivateKey < ArgumentError; end
+ class MissingKeyAttribute < ArgumentError; end
+ class KeyCommandInputError < ArgumentError; end
+
+ class BootstrapCommandInputError < ArgumentError
+ def initialize
+ super "You cannot pass both --json-attributes and --json-attribute-file. Please pass one or none."
+ end
+ end
+
+ class InvalidKeyArgument < ArgumentError; end
+ class InvalidKeyAttribute < ArgumentError; end
+ class InvalidUserAttribute < ArgumentError; end
+ class InvalidClientAttribute < ArgumentError; end
+ class RedirectLimitExceeded < RuntimeError; end
+ class AmbiguousRunlistSpecification < ArgumentError; end
+ class CookbookFrozen < ArgumentError; end
+ class CookbookNotFound < RuntimeError; end
+ class OnlyApiVersion0SupportedForAction < RuntimeError; end
+ # Cookbook loader used to raise an argument error when cookbook not found.
+ # for back compat, need to raise an error that inherits from ArgumentError
+ class CookbookNotFoundInRepo < ArgumentError; end
+ class CookbookMergingError < RuntimeError; end
+ class RecipeNotFound < ArgumentError; end
+ # AttributeNotFound really means the attribute file could not be found
+ class AttributeNotFound < RuntimeError; end
+ # NoSuchAttribute is raised on access by node.read!("foo", "bar") when node["foo"]["bar"] does not exist.
+ class NoSuchAttribute < RuntimeError; end
+ # AttributeTypeMismatch is raised by node.write!("foo", "bar", "baz") when e.g. node["foo"] = "bar" (overwriting String with Hash)
+ class AttributeTypeMismatch < RuntimeError; end
+ class MissingCookbookDependency < StandardError; end # CHEF-5120
+ class InvalidCommandOption < RuntimeError; end
+ class CommandTimeout < RuntimeError; end
+ class RequestedUIDUnavailable < RuntimeError; end
+ class InvalidHomeDirectory < ArgumentError; end
+ class DsclCommandFailed < RuntimeError; end
+ class PlistUtilCommandFailed < RuntimeError; end
+ class UserIDNotFound < ArgumentError; end
+ class GroupIDNotFound < ArgumentError; end
+ class ConflictingMembersInGroup < ArgumentError; end
+ class InvalidResourceReference < RuntimeError; end
+ class ResourceNotFound < RuntimeError; end
+ class ProviderNotFound < RuntimeError; end
+ NoProviderAvailable = ProviderNotFound
+ class VerificationNotFound < RuntimeError; end
+ class InvalidEventType < ArgumentError; end
+ class MultipleIdentityError < RuntimeError; end
+ # Used in Resource::ActionClass#load_current_resource to denote that
+ # the resource doesn't actually exist (for example, the file does not exist)
+ class CurrentValueDoesNotExist < RuntimeError; end
+
+ # Can't find a Resource of this type that is valid on this platform.
+ class NoSuchResourceType < NameError
+ def initialize(short_name, node)
+ super "Cannot find a resource for #{short_name} on #{node[:platform]} version #{node[:platform_version]} with target_mode? #{Chef::Config.target_mode?}"
+ end
+ end
+
+ class InvalidPolicybuilderCall < ArgumentError; end
+
+ class InvalidResourceSpecification < ArgumentError; end
+ class SolrConnectionError < RuntimeError; end
+ class IllegalChecksumRevert < RuntimeError; end
+ class CookbookVersionNameMismatch < ArgumentError; end
+ class MissingParentDirectory < RuntimeError; end
+ class UnresolvableGitReference < RuntimeError; end
+ class InvalidRemoteGitReference < RuntimeError; end
+ class InvalidEnvironmentRunListSpecification < ArgumentError; end
+ class InvalidDataBagItemID < ArgumentError; end
+ class InvalidDataBagName < ArgumentError; end
+ class EnclosingDirectoryDoesNotExist < ArgumentError; end
+ # Errors originating from calls to the Win32 API
+ class Win32APIError < RuntimeError; end
+
+ # Thrown when Win32 API layer binds to non-existent Win32 function. Occurs
+ # when older versions of Windows don't support newer Win32 API functions.
+ class Win32APIFunctionNotImplemented < NotImplementedError; end # rubocop:disable Lint/InheritException
+ # Attempting to run windows code on a not-windows node
+ class Win32NotWindows < RuntimeError; end
+ class WindowsNotAdmin < RuntimeError; end
+ # Attempting to access a 64-bit only resource on a 32-bit Windows system
+ class Win32ArchitectureIncorrect < RuntimeError; end
+ class ObsoleteDependencySyntax < ArgumentError; end
+ class InvalidDataBagPath < ArgumentError; end
+ class DuplicateDataBagItem < RuntimeError; end
+
+ class PowershellCmdletException < RuntimeError; end
+ class LCMParser < RuntimeError; end
+
+ class CannotDetermineHomebrewOwner < Package; end
+ class CannotDetermineWindowsInstallerType < Package; end
+ class NoWindowsPackageSource < Package; end
+
+ # for example, if both recipes/default.yml, recipes/default.yaml are present
+ class AmbiguousYAMLFile < RuntimeError; end
+
+ # Can not create staging file during file deployment
+ class FileContentStagingError < RuntimeError
+ def initialize(errors)
+ super "Staging tempfile can not be created during file deployment.\n Errors: #{errors.join('\n')}!"
+ end
+ end
+
+ # A different version of a cookbook was added to a
+ # VersionedRecipeList than the one already there.
+ class CookbookVersionConflict < ArgumentError; end
+
+ # does not follow X.Y.Z format. ArgumentError?
+ class InvalidPlatformVersion < ArgumentError; end
+ class InvalidCookbookVersion < ArgumentError; end
+
+ # version constraint should be a string or array, or it doesn't
+ # match OP VERSION. ArgumentError?
+ class InvalidVersionConstraint < ArgumentError; end
+
+ # Version constraints are not allowed in chef-solo
+ class IllegalVersionConstraint < NotImplementedError; end # rubocop:disable Lint/InheritException
+
+ class MetadataNotValid < StandardError; end
+
+ class MetadataNotFound < StandardError
+ attr_reader :install_path
+ attr_reader :cookbook_name
+
+ def initialize(install_path, cookbook_name)
+ @install_path = install_path
+ @cookbook_name = cookbook_name
+ super "No metadata.rb or metadata.json found for cookbook #{@cookbook_name} in #{@install_path}"
+ end
+ end
+
+ # File operation attempted but no permissions to perform it
+ class InsufficientPermissions < RuntimeError; end
+
+ # Ifconfig failed
+ class Ifconfig < RuntimeError; end
+
+ # Invalid "source" parameter to a remote_file resource
+ class InvalidRemoteFileURI < ArgumentError; end
+
+ # Node::Attribute computes the merged version of of attributes
+ # and makes it read-only. Attempting to modify a read-only
+ # attribute will cause this error.
+ class ImmutableAttributeModification < NoMethodError
+ def initialize
+ super "Node attributes are read-only when you do not specify which precedence level to set. " +
+ %q{To set an attribute use code like `node.default["key"] = "value"'}
+ end
+ end
+
+ # Merged node attributes are invalidated when the component
+ # attributes are updated. Attempting to read from a stale copy
+ # of merged attributes will trigger this error.
+ class StaleAttributeRead < StandardError; end
+
+ # Registry Helper throws the following errors
+ class Win32RegArchitectureIncorrect < Win32ArchitectureIncorrect; end
+ class Win32RegHiveMissing < ArgumentError; end
+ class Win32RegKeyMissing < RuntimeError; end
+ class Win32RegValueMissing < RuntimeError; end
+ class Win32RegDataMissing < RuntimeError; end
+ class Win32RegValueExists < RuntimeError; end
+ class Win32RegNoRecursive < ArgumentError; end
+ class Win32RegTypeDoesNotExist < ArgumentError; end
+ class Win32RegBadType < ArgumentError; end
+ class Win32RegBadValueSize < ArgumentError; end
+ class Win32RegTypesMismatch < ArgumentError; end
+
+ # incorrect input for registry_key create action throws following error
+ class RegKeyValuesTypeMissing < ArgumentError; end
+ class RegKeyValuesDataMissing < ArgumentError; end
+
+ class InvalidEnvironmentPath < ArgumentError; end
+ class EnvironmentNotFound < RuntimeError; end
+
+ # File-like resource found a non-file (socket, pipe, directory, etc) at its destination
+ class FileTypeMismatch < RuntimeError; end
+
+ # File (or descendent) resource configured to manage symlink source, but
+ # the symlink that is there either loops or points to a nonexistent file
+ class InvalidSymlink < RuntimeError; end
+
+ class ChildConvergeError < RuntimeError; end
+
+ class DeprecatedFeatureError < RuntimeError
+ def initialize(message)
+ super("#{message} (raising error due to treat_deprecation_warnings_as_errors being set)")
+ end
+ end
+
+ class MissingRole < RuntimeError
+ attr_reader :expansion
+
+ def initialize(message_or_expansion = NOT_PASSED)
+ @expansion = nil
+ case message_or_expansion
+ when NOT_PASSED
+ super()
+ when String
+ super
+ when RunList::RunListExpansion
+ @expansion = message_or_expansion
+ missing_roles = @expansion.errors.join(", ")
+ super("The expanded run list includes nonexistent roles: #{missing_roles}")
+ end
+ end
+
+ end
+
+ class Secret
+ class RetrievalError < RuntimeError; end
+ class ConfigurationInvalid < RuntimeError; end
+ class FetchFailed < RuntimeError; end
+ class MissingSecretName < RuntimeError; end
+ class InvalidSecretName < RuntimeError; end
+
+ class InvalidFetcherService < RuntimeError
+ def initialize(given, fetcher_service_names)
+ super("#{given} is not a supported secrets service. Supported services are: :#{fetcher_service_names.join(" :")}")
+ end
+ end
+
+ class MissingFetcher < RuntimeError
+ def initialize(fetcher_service_names)
+ super("No secret service provided. Supported services are: :#{fetcher_service_names.join(" :")}")
+ end
+ end
+
+ class Azure
+ class IdentityNotFound < RuntimeError
+ def initialize
+ super("The managed identity could not be found. This could mean one of the following things:\n\n" \
+ " 1. The VM has no system or user assigned identities.\n" \
+ " 2. The managed identity object_id or client_id that was specified is not assigned to the VM.\n")
+ end
+ end
+ end
+ end
+
+ # Exception class for collecting multiple failures. Used when running
+ # delayed notifications so that chef can process each delayed
+ # notification even if chef client or other notifications fail.
+ class MultipleFailures < StandardError
+ def initialize(*args)
+ super
+ @all_failures = []
+ end
+
+ def message
+ base = "Multiple failures occurred:\n"
+ @all_failures.inject(base) do |message, (location, error)|
+ message << "* #{error.class} occurred in #{location}: #{error.message}\n"
+ end
+ end
+
+ def client_run_failure(exception)
+ set_backtrace(exception.backtrace)
+ @all_failures << [ "#{ChefUtils::Dist::Infra::PRODUCT} run", exception ]
+ end
+
+ def notification_failure(exception)
+ @all_failures << [ "delayed notification", exception ]
+ end
+
+ def raise!
+ unless empty?
+ raise for_raise
+ end
+ end
+
+ def empty?
+ @all_failures.empty?
+ end
+
+ def for_raise
+ if @all_failures.size == 1
+ @all_failures[0][1]
+ else
+ self
+ end
+ end
+ end
+
+ class CookbookVersionSelection
+
+ # Compound exception: In run_list expansion and resolution,
+ # run_list items referred to cookbooks that don't exist and/or
+ # have no versions available.
+ class InvalidRunListItems < StandardError
+ attr_reader :non_existent_cookbooks
+ attr_reader :cookbooks_with_no_matching_versions
+
+ def initialize(message, non_existent_cookbooks, cookbooks_with_no_matching_versions)
+ super(message)
+
+ @non_existent_cookbooks = non_existent_cookbooks
+ @cookbooks_with_no_matching_versions = cookbooks_with_no_matching_versions
+ end
+
+ def to_json(*a)
+ result = {
+ "message" => message,
+ "non_existent_cookbooks" => non_existent_cookbooks,
+ "cookbooks_with_no_versions" => cookbooks_with_no_matching_versions,
+ }
+ Chef::JSONCompat.to_json(result, *a)
+ end
+ end
+
+ # In run_list expansion and resolution, a constraint was
+ # unsatisfiable.
+ #
+ # This exception may not be the complete error report. If you
+ # resolve the misconfiguration represented by this exception and
+ # re-solve, you may get another exception
+ class UnsatisfiableRunListItem < StandardError
+ attr_reader :run_list_item
+ attr_reader :non_existent_cookbooks, :most_constrained_cookbooks
+
+ # most_constrained_cookbooks: if I were to remove constraints
+ # regarding these cookbooks, I would get a solution or move on
+ # to the next error (deeper in the graph). An item in this list
+ # may be unsatisfiable, but when resolved may also reveal
+ # further unsatisfiable constraints; this condition would not be
+ # reported.
+ def initialize(message, run_list_item, non_existent_cookbooks, most_constrained_cookbooks)
+ super(message)
+
+ @run_list_item = run_list_item
+ @non_existent_cookbooks = non_existent_cookbooks
+ @most_constrained_cookbooks = most_constrained_cookbooks
+ end
+
+ def to_json(*a)
+ result = {
+ "message" => message,
+ "unsatisfiable_run_list_item" => run_list_item,
+ "non_existent_cookbooks" => non_existent_cookbooks,
+ "most_constrained_cookbooks" => most_constrained_cookbooks,
+ }
+ Chef::JSONCompat.to_json(result, *a)
+ end
+ end
+
+ end # CookbookVersionSelection
+
+ # When the server sends a redirect, RFC 2616 states a user-agent should
+ # not follow it with a method other than GET or HEAD, unless a specific
+ # action is taken by the user. A redirect received as response to a
+ # non-GET and non-HEAD request will thus raise an InvalidRedirect.
+ class InvalidRedirect < StandardError; end
+
+ # Raised when the content length of a download does not match the content
+ # length declared in the http response.
+ class ContentLengthMismatch < RuntimeError
+ def initialize(response_length, content_length)
+ super <<~EOF
+ Response body length #{response_length} does not match HTTP Content-Length header #{content_length}.
+ This error is most often caused by network issues (proxies, etc) outside of #{ChefUtils::Dist::Infra::CLIENT}.
+ EOF
+ end
+ end
+
+ class UnsupportedPlatform < RuntimeError
+ def initialize(platform)
+ super "This functionality is not supported on platform #{platform}."
+ end
+ end
+
+ # Raised when Chef::Config[:run_lock_timeout] is set and some other client run fails
+ # to release the run lock before Chef::Config[:run_lock_timeout] seconds pass.
+ class RunLockTimeout < RuntimeError
+ def initialize(duration, blocking_pid)
+ super "Unable to acquire lock. Waited #{duration} seconds for #{blocking_pid} to release."
+ end
+ end
+
+ class ChecksumMismatch < RuntimeError
+ def initialize(res_cksum, cont_cksum)
+ super "Checksum on resource (#{res_cksum}...) does not match checksum on content (#{cont_cksum}...)"
+ end
+ end
+
+ class BadProxyURI < RuntimeError; end
+
+ # Raised by Chef::JSONCompat
+ class JSON
+ class EncodeError < RuntimeError; end
+ class ParseError < RuntimeError; end
+ end
+
+ class InvalidSearchQuery < ArgumentError; end
+
+ # Raised by Chef::ProviderResolver
+ class AmbiguousProviderResolution < RuntimeError
+ def initialize(resource, classes)
+ super "Found more than one provider for #{resource.resource_name} resource: #{classes}"
+ end
+ end
+
+ # If a converge fails, we want to wrap the output from those errors into 1 error so we can
+ # see both issues in the output. It is possible that nil will be provided. You must call `fill_backtrace`
+ # to correctly populate the backtrace with the wrapped backtraces.
+ class RunFailedWrappingError < RuntimeError
+ attr_reader :wrapped_errors
+
+ def initialize(*errors)
+ errors = errors.compact
+ output = "Found #{errors.size} errors, they are stored in the backtrace"
+ @wrapped_errors = errors
+ super output
+ end
+
+ def fill_backtrace
+ backtrace = []
+ wrapped_errors.each_with_index do |e, i|
+ backtrace << "#{i + 1}) #{e.class} - #{e.message}"
+ backtrace += e.backtrace if e.backtrace
+ backtrace << "" unless i == wrapped_errors.length - 1
+ end
+ set_backtrace(backtrace)
+ end
+ end
+
+ class PIDFileLockfileMatch < RuntimeError
+ def initialize
+ super "PID file and lockfile are not permitted to match. Specify a different location with --pid or --lockfile"
+ end
+ end
+
+ class CookbookChefVersionMismatch < RuntimeError
+ def initialize(chef_version, cookbook_name, cookbook_version, *constraints)
+ constraint_str = constraints.map { |c| c.requirement.as_list.to_s }.join(", ")
+ super "Cookbook '#{cookbook_name}' version '#{cookbook_version}' depends on #{ChefUtils::Dist::Infra::PRODUCT} version #{constraint_str}, but the running #{ChefUtils::Dist::Infra::PRODUCT} version is #{chef_version}"
+ end
+ end
+
+ class CookbookOhaiVersionMismatch < RuntimeError
+ def initialize(ohai_version, cookbook_name, cookbook_version, *constraints)
+ constraint_str = constraints.map { |c| c.requirement.as_list.to_s }.join(", ")
+ super "Cookbook '#{cookbook_name}' version '#{cookbook_version}' depends on ohai version #{constraint_str}, but the running ohai version is #{ohai_version}"
+ end
+ end
+
+ class MultipleDscResourcesFound < RuntimeError
+ attr_reader :resources_found
+
+ def initialize(resources_found)
+ @resources_found = resources_found
+ matches_info = @resources_found.each do |r|
+ if r["Module"].nil?
+ "Resource #{r["Name"]} was found in #{r["Module"]["Name"]}"
+ else
+ "Resource #{r["Name"]} is a binary resource"
+ end
+ end
+ super "Found multiple resources matching #{matches_info[0]["Module"]["Name"]}:\n#{(matches_info.map { |f| f["Module"]["Version"] }).uniq.join("\n")}"
+ end
+ end
+
+ # exception specific to invalid usage of 'dsc_resource' resource
+ class DSCModuleNameMissing < ArgumentError; end
+
+ class GemRequirementConflict < RuntimeError
+ def initialize(gem_name, option, value1, value2)
+ super "Conflicting requirements for gem '#{gem_name}': Both #{value1.inspect} and #{value2.inspect} given for option #{option.inspect}"
+ end
+ end
+
+ class UnifiedModeImmediateSubscriptionEarlierResource < RuntimeError
+ def initialize(notification)
+ super "immediate subscription from #{notification.resource} resource cannot be setup to #{notification.notifying_resource} resource, which has already fired while in unified mode"
+ end
+ end
+
+ class UnifiedModeBeforeSubscriptionEarlierResource < RuntimeError
+ def initialize(notification)
+ super "before subscription from #{notification.resource} resource cannot be setup to #{notification.notifying_resource} resource, which has already fired while in unified mode"
+ end
+ end
+ end
+end
diff --git a/knife/lib/json_compat.rb b/knife/lib/json_compat.rb
new file mode 100644
index 0000000000..2c143ab445
--- /dev/null
+++ b/knife/lib/json_compat.rb
@@ -0,0 +1,63 @@
+#
+# Author:: Tim Hinderliter (<tim@chef.io>)
+# Copyright:: Copyright (c) Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Wrapper class for interacting with JSON.
+
+autoload :FFI_Yajl, "ffi_yajl"
+require_relative "exceptions"
+# We're requiring this to prevent breaking consumers using Hash.to_json
+require "json" unless defined?(JSON)
+
+class Chef
+ class JSONCompat
+
+ class << self
+
+ def parse(source, opts = {})
+ FFI_Yajl::Parser.parse(source, opts)
+ rescue FFI_Yajl::ParseError => e
+ raise Chef::Exceptions::JSON::ParseError, e.message
+ end
+
+ def from_json(source, opts = {})
+ obj = parse(source, opts)
+
+ # JSON gem requires top level object to be a Hash or Array (otherwise
+ # you get the "must contain two octets" error). Yajl doesn't impose the
+ # same limitation. For compatibility, we re-impose this condition.
+ unless obj.is_a?(Hash) || obj.is_a?(Array)
+ raise Chef::Exceptions::JSON::ParseError, "Top level JSON object must be a Hash or Array. (actual: #{obj.class})"
+ end
+
+ obj
+ end
+
+ def to_json(obj, opts = nil)
+ FFI_Yajl::Encoder.encode(obj, opts)
+ rescue FFI_Yajl::EncodeError => e
+ raise Chef::Exceptions::JSON::EncodeError, e.message
+ end
+
+ def to_json_pretty(obj, opts = nil)
+ options_map = { pretty: true }
+ options_map[:indent] = opts[:indent] if opts.respond_to?(:key?) && opts.key?(:indent)
+ to_json(obj, options_map).chomp
+ end
+
+ end
+ end
+end
diff --git a/knife/lib/mixin/powershell_exec.rb b/knife/lib/mixin/powershell_exec.rb
new file mode 100644
index 0000000000..bbf8ae1a69
--- /dev/null
+++ b/knife/lib/mixin/powershell_exec.rb
@@ -0,0 +1,128 @@
+#
+# Author:: Stuart Preston (<stuart@chef.io>)
+# Copyright:: Copyright (c) Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require_relative "../powershell"
+require_relative "../pwsh"
+
+# The powershell_exec mixin provides in-process access to the PowerShell engine.
+#
+# powershell_exec is initialized with a string that should be set to the script
+# to run and also takes an optional interpreter argument which must be either
+# :powershell (Windows PowerShell which is the default) or :pwsh (PowerShell
+# Core). It will return a Chef::PowerShell object that provides 5 methods:
+#
+# .result - returns a hash representing the results returned by executing the
+# PowerShell script block
+# .verbose - this is an array of string containing any messages written to the
+# PowerShell verbose stream during execution
+# .errors - this is an array of string containing any messages written to the
+# PowerShell error stream during execution
+# .error? - returns true if there were error messages written to the PowerShell
+# error stream during execution
+# .error! - raise Chef::PowerShell::CommandFailed if there was an error
+#
+# Some examples of usage:
+#
+# > powershell_exec("(Get-Item c:\\windows\\system32\\w32time.dll).VersionInfo"
+# ).result["FileVersion"]
+# => "10.0.14393.0 (rs1_release.160715-1616)"
+#
+# > powershell_exec("(get-process ruby).Mainmodule").result["FileName"]
+# => C:\\opscode\\chef\\embedded\\bin\\ruby.exe"
+#
+# > powershell_exec("$a = $true; $a").result
+# => true
+#
+# > powershell_exec("$PSVersionTable", :pwsh).result["PSEdition"]
+# => "Core"
+#
+# > powershell_exec("not-found").errors
+# => ["ObjectNotFound: (not-found:String) [], CommandNotFoundException: The
+# term 'not-found' is not recognized as the name of a cmdlet, function, script
+# file, or operable program. Check the spelling of the name, or if a path was
+# included, verify that the path is correct and try again. (at <ScriptBlock>,
+# <No file>: line 1)"]
+#
+# > powershell_exec("not-found").error?
+# => true
+#
+# > powershell_exec("get-item c:\\notfound -erroraction stop")
+# WIN32OLERuntimeError: (in OLE method `ExecuteScript': )
+# OLE error code:80131501 in System.Management.Automation
+# The running command stopped because the preference variable
+# "ErrorActionPreference" or common parameter is set to Stop: Cannot find
+# path 'C:\notfound' because it does not exist.
+#
+# *Why use this and not powershell_out?* Startup time to invoke the PowerShell
+# engine is much faster (over 7X faster in tests) than writing the PowerShell
+# to disk, shelling out to powershell.exe and retrieving the .stdout or .stderr
+# methods afterwards. Additionally we are able to have a higher fidelity
+# conversation with PowerShell because we are now working with the objects that
+# are returned by the script, rather than having to parse the stdout of
+# powershell.exe to get a result.
+#
+# *How does this work?* In .NET terms, when you run a PowerShell script block
+# through the engine, behind the scenes you get a Collection<PSObject> returned
+# and simply we are serializing this, adding any errors that were generated to
+# a custom JSON string transferred in memory to Ruby. The easiest way to
+# develop for this approach is to imagine that the last thing that happens in
+# your PowerShell script block is "ConvertTo-Json". That's exactly what we are
+# doing here behind the scenes.
+#
+# There are a handful of current limitations with this approach:
+# - Windows UAC elevation is controlled by the token assigned to the account
+# that Ruby.exe is running under.
+# - Terminating errors will result in a WIN32OLERuntimeError and typically are
+# handled as an exception.
+# - There are no return/error codes, as we are not shelling out to
+# powershell.exe but calling a method inline, no errors codes are returned.
+# - There is no settable timeout on powershell_exec method execution.
+# - It is not possible to impersonate another user running powershell, the
+# credentials of the user running Chef Client are used.
+#
+
+class Chef
+ module Mixin
+ module PowershellExec
+ # Run a command under PowerShell via a managed (.NET) API.
+ #
+ # Requires: .NET Framework 4.0 or higher on the target machine.
+ #
+ # @param script [String] script to run
+ # @param interpreter [Symbol] the interpreter type, `:powershell` or `:pwsh`
+ # @return [Chef::PowerShell] output
+ def powershell_exec(script, interpreter = :powershell)
+ case interpreter
+ when :powershell
+ Chef::PowerShell.new(script)
+ when :pwsh
+ Chef::Pwsh.new(script)
+ else
+ raise ArgumentError, "Expected interpreter of :powershell or :pwsh"
+ end
+ end
+
+ # The same as the #powershell_exec method except this will raise
+ # Chef::PowerShell::CommandFailed if the command fails
+ def powershell_exec!(script, interpreter = :powershell)
+ cmd = powershell_exec(script, interpreter)
+ cmd.error!
+ cmd
+ end
+ end
+ end
+end
diff --git a/knife/lib/powershell.rb b/knife/lib/powershell.rb
new file mode 100644
index 0000000000..52ab66667f
--- /dev/null
+++ b/knife/lib/powershell.rb
@@ -0,0 +1,84 @@
+#
+# Author:: Stuart Preston (<stuart@chef.io>)
+# Copyright:: Copyright (c) Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "ffi" #unless defined?(FFI)
+require_relative "json_compat"
+require "pry"
+
+class Chef
+ class PowerShell
+ extend FFI::Library
+ ffi_lib FFI::Library::LIBC
+
+
+ attr_reader :result
+ attr_reader :errors
+ attr_reader :verbose
+
+ # Run a command under PowerShell via FFI
+ # This implementation requires the managed dll and native wrapper to be in the library search
+ # path on Windows (i.e. c:\windows\system32 or in the same location as ruby.exe).
+ #
+ # Requires: .NET Framework 4.0 or higher on the target machine.
+ #
+ # @param script [String] script to run
+ # @return [Object] output
+ def initialize(script)
+ binding.pry
+ # This Powershell DLL source lives here: https://github.com/chef/chef-powershell-shim
+ # Every merge into that repo triggers a Habitat build and promotion. Running
+ # the rake :update_chef_exec_dll task in this (chef/chef) repo will pull down
+ # the built packages and copy the binaries to distro/ruby_bin_folder. Bundle install
+ # ensures that the correct architecture binaries are installed into the path.
+ @dll ||= "Chef.PowerShell.Wrapper.dll"
+ exec(script)
+ end
+
+ #
+ # Was there an error running the command
+ #
+ # @return [Boolean]
+ #
+ def error?
+ return true if errors.count > 0
+
+ false
+ end
+
+ class CommandFailed < RuntimeError; end
+
+ #
+ # @raise [Chef::PowerShell::CommandFailed] raise if the command failed
+ #
+ def error!
+ raise Chef::PowerShell::CommandFailed, "Unexpected exit in PowerShell command: #{@errors}" if error?
+ end
+
+ protected
+
+ def exec(script)
+ binding.pry
+ FFI.ffi_lib @dll
+ FFI.attach_function :execute_powershell, :ExecuteScript, [:string], :pointer
+ execution = FFI.execute_powershell(script).read_utf16string
+ hashed_outcome = Chef::JSONCompat.parse(execution)
+ @result = Chef::JSONCompat.parse(hashed_outcome["result"])
+ @errors = hashed_outcome["errors"]
+ @verbose = hashed_outcome["verbose"]
+ end
+ end
+end
diff --git a/knife/lib/pwsh.rb b/knife/lib/pwsh.rb
new file mode 100644
index 0000000000..3d067eb0d6
--- /dev/null
+++ b/knife/lib/pwsh.rb
@@ -0,0 +1,71 @@
+#
+# Author:: Matt Wrock (<mwrock@chef.io>)
+# Copyright:: Copyright (c) Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+class Chef
+ class Pwsh < Chef::PowerShell
+
+ # Run a command under pwsh (powershell core) via FFI
+ # This implementation requires the managed dll, native wrapper and a
+ # published, self contained dotnet core directory tree to exist in the
+ # bindir directory.
+ #
+ # @param script [String] script to run
+ # @return [Object] output
+ def initialize(script)
+ @dll = Pwsh.dll
+ super
+ end
+
+ protected
+
+ def exec(script)
+ # Note that we need to override the location of the shared dotnet core library
+ # location. With most .net core applications, you can simply publish them as a
+ # "self-contained" application allowing consumers of the application to run them
+ # and use its own stand alone version of the .net core runtime. However because
+ # this is simply a dll and not an exe, it will look for the runtime in the shared
+ # .net core installation folder. By setting DOTNET_MULTILEVEL_LOOKUP to 0 we can
+ # override that folder's location with DOTNET_ROOT. To avoid the possibility of
+ # interfering with other .net core processes that might rely on the common shared
+ # location, we revert these variables after the script completes.
+ original_dml = ENV["DOTNET_MULTILEVEL_LOOKUP"]
+ original_dotnet_root = ENV["DOTNET_ROOT"]
+ original_dotnet_root_x86 = ENV["DOTNET_ROOT(x86)"]
+
+ ENV["DOTNET_MULTILEVEL_LOOKUP"] = "0"
+ ENV["DOTNET_ROOT"] = RbConfig::CONFIG["bindir"]
+ ENV["DOTNET_ROOT(x86)"] = RbConfig::CONFIG["bindir"]
+
+ super
+ ensure
+ ENV["DOTNET_MULTILEVEL_LOOKUP"] = original_dml
+ ENV["DOTNET_ROOT"] = original_dotnet_root
+ ENV["DOTNET_ROOT(x86)"] = original_dotnet_root_x86
+ end
+
+ def self.dll
+ # This Powershell DLL source lives here: https://github.com/chef/chef-powershell-shim
+ # Every merge into that repo triggers a Habitat build and promotion. Running
+ # the rake :update_chef_exec_dll task in this (chef/chef) repo will pull down
+ # the built packages and copy the binaries to distro/ruby_bin_folder. Bundle install
+ # ensures that the correct architecture binaries are installed into the path.
+ # Also note that the version of pwsh is determined by which assemblies the dll was
+ # built with. To update powershell, those dependencies must be bumped.
+ @dll ||= Dir.glob("#{RbConfig::CONFIG["bindir"]}/**/Chef.PowerShell.Wrapper.Core.dll").last
+ end
+ end
+end
diff --git a/lib/chef/dsl/chef_vault.rb b/lib/chef/dsl/chef_vault.rb
index 3411f79e41..ffd2538771 100644
--- a/lib/chef/dsl/chef_vault.rb
+++ b/lib/chef/dsl/chef_vault.rb
@@ -19,6 +19,7 @@
autoload :ChefVault, "chef-vault"
require_relative "data_query"
+require "pry"
class Chef
module DSL
@@ -37,6 +38,7 @@ class Chef
# @param [String] bag Name of the data bag to load from.
# @param [String] id Identifier of the data bag item to load.
def chef_vault_item(bag, id)
+ binding.pry
if ::ChefVault::Item.vault?(bag, id)
::ChefVault::Item.load(bag, id)
elsif node["chef-vault"]["databag_fallback"]
@@ -56,6 +58,7 @@ class Chef
# @param [String] bag Name of the data bag to load from.
# @return [Array]
def chef_vault(bag)
+ binding.pry
raise "'#{bag}' is not a vault" unless Chef::DataBag.list.include? bag
pattern = Regexp.new(/_keys$/).freeze
@@ -74,6 +77,7 @@ class Chef
# @param [String] id Identifier of the data bag item to load.
# @return [Hash]
def chef_vault_item_for_environment(bag, id)
+ binding.pry
item = chef_vault_item(bag, id)
return {} unless item[node.chef_environment]