summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md7
-rw-r--r--DOC_CHANGES.md77
-rw-r--r--MAINTAINERS.md7
-rw-r--r--RELEASE_NOTES.md145
-rw-r--r--appveyor.yml2
-rw-r--r--chef.gemspec2
-rw-r--r--lib/chef/application/client.rb4
-rw-r--r--lib/chef/audit/audit_reporter.rb2
-rw-r--r--lib/chef/audit/runner.rb17
-rw-r--r--lib/chef/client.rb2
-rw-r--r--lib/chef/config.rb10
-rw-r--r--lib/chef/dsl/powershell.rb29
-rw-r--r--lib/chef/exceptions.rb21
-rw-r--r--lib/chef/formatters/doc.rb4
-rw-r--r--lib/chef/knife/bootstrap.rb3
-rw-r--r--lib/chef/knife/bootstrap/templates/chef-full.erb2
-rw-r--r--lib/chef/knife/core/subcommand_loader.rb18
-rw-r--r--lib/chef/knife/exec.rb3
-rw-r--r--lib/chef/knife/ssh.rb14
-rw-r--r--lib/chef/mixin/params_validate.rb61
-rw-r--r--lib/chef/mixin/powershell_type_coercions.rb82
-rw-r--r--lib/chef/mixin/windows_architecture_helper.rb8
-rw-r--r--lib/chef/node.rb2
-rw-r--r--lib/chef/platform/provider_mapping.rb107
-rw-r--r--lib/chef/platform/query_helpers.rb7
-rw-r--r--lib/chef/provider/batch.rb2
-rw-r--r--lib/chef/provider/cron.rb2
-rw-r--r--lib/chef/provider/cron/aix.rb2
-rw-r--r--lib/chef/provider/deploy.rb191
-rw-r--r--lib/chef/provider/dsc_resource.rb157
-rw-r--r--lib/chef/provider/env.rb2
-rw-r--r--lib/chef/provider/env/windows.rb2
-rw-r--r--lib/chef/provider/git.rb4
-rw-r--r--lib/chef/provider/group.rb10
-rw-r--r--lib/chef/provider/group/dscl.rb2
-rw-r--r--lib/chef/provider/group/groupmod.rb2
-rw-r--r--lib/chef/provider/group/usermod.rb2
-rw-r--r--lib/chef/provider/group/windows.rb2
-rw-r--r--lib/chef/provider/mdadm.rb2
-rw-r--r--lib/chef/provider/mount/windows.rb2
-rw-r--r--lib/chef/provider/package/homebrew.rb2
-rw-r--r--lib/chef/provider/package/openbsd.rb67
-rw-r--r--lib/chef/provider/powershell_script.rb2
-rw-r--r--lib/chef/provider/service/macosx.rb3
-rw-r--r--lib/chef/provider/user/dscl.rb8
-rw-r--r--lib/chef/provider/user/windows.rb2
-rw-r--r--lib/chef/providers.rb1
-rw-r--r--lib/chef/recipe.rb2
-rw-r--r--lib/chef/resource.rb9
-rw-r--r--lib/chef/resource/batch.rb2
-rw-r--r--lib/chef/resource/cron.rb6
-rw-r--r--lib/chef/resource/deploy.rb269
-rw-r--r--lib/chef/resource/dsc_resource.rb83
-rw-r--r--lib/chef/resource/env.rb2
-rw-r--r--lib/chef/resource/git.rb2
-rw-r--r--lib/chef/resource/group.rb2
-rw-r--r--lib/chef/resource/homebrew_package.rb2
-rw-r--r--lib/chef/resource/lwrp_base.rb8
-rw-r--r--lib/chef/resource/mdadm.rb2
-rw-r--r--lib/chef/resource/mount.rb2
-rw-r--r--lib/chef/resource/powershell_script.rb2
-rw-r--r--lib/chef/resource/user.rb2
-rw-r--r--lib/chef/resources.rb1
-rw-r--r--lib/chef/run_context.rb2
-rw-r--r--lib/chef/shell.rb12
-rw-r--r--lib/chef/util/dsc/resource_store.rb110
-rw-r--r--lib/chef/util/path_helper.rb76
-rw-r--r--lib/chef/util/powershell/cmdlet.rb48
-rw-r--r--lib/chef/util/powershell/cmdlet_result.rb21
-rw-r--r--lib/chef/util/powershell/ps_credential.rb38
-rw-r--r--lib/chef/version.rb2
-rw-r--r--lib/chef/win32/api.rb2
-rw-r--r--lib/chef/win32/api/crypto.rb63
-rw-r--r--lib/chef/win32/api/installer.rb2
-rw-r--r--lib/chef/win32/crypto.rb49
-rw-r--r--lib/chef/workstation_config_loader.rb7
-rw-r--r--spec/functional/file_content_management/deploy_strategies_spec.rb2
-rw-r--r--spec/functional/resource/cookbook_file_spec.rb2
-rw-r--r--spec/functional/resource/deploy_revision_spec.rb35
-rw-r--r--spec/functional/resource/directory_spec.rb2
-rw-r--r--spec/functional/resource/dsc_resource_spec.rb93
-rwxr-xr-xspec/functional/resource/env_spec.rb7
-rw-r--r--spec/functional/resource/file_spec.rb2
-rw-r--r--spec/functional/resource/powershell_spec.rb3
-rw-r--r--spec/functional/resource/remote_directory_spec.rb2
-rw-r--r--spec/functional/resource/remote_file_spec.rb2
-rw-r--r--spec/functional/resource/template_spec.rb2
-rw-r--r--spec/functional/resource/user/dscl_spec.rb3
-rw-r--r--spec/functional/resource/user/useradd_spec.rb40
-rw-r--r--spec/functional/win32/crypto_spec.rb57
-rw-r--r--spec/spec_helper.rb3
-rw-r--r--spec/support/platform_helpers.rb14
-rw-r--r--spec/support/shared/functional/securable_resource_with_reporting.rb10
-rw-r--r--spec/unit/application/client_spec.rb8
-rw-r--r--spec/unit/audit/audit_reporter_spec.rb2
-rw-r--r--spec/unit/audit/runner_spec.rb10
-rw-r--r--spec/unit/config_spec.rb10
-rw-r--r--spec/unit/knife/bootstrap_spec.rb28
-rw-r--r--spec/unit/knife/core/subcommand_loader_spec.rb58
-rw-r--r--spec/unit/mixin/params_validate_spec.rb136
-rw-r--r--spec/unit/mixin/powershell_type_coercions_spec.rb72
-rw-r--r--spec/unit/platform/query_helpers_spec.rb22
-rw-r--r--spec/unit/platform_spec.rb5
-rw-r--r--spec/unit/provider/dsc_resource_spec.rb84
-rw-r--r--spec/unit/provider/package/openbsd_spec.rb122
-rw-r--r--spec/unit/provider/service/macosx_spec.rb6
-rw-r--r--spec/unit/provider_resolver_spec.rb132
-rw-r--r--spec/unit/recipe_spec.rb4
-rw-r--r--spec/unit/resource/deploy_spec.rb27
-rw-r--r--spec/unit/resource/dsc_resource_spec.rb85
-rw-r--r--spec/unit/shell_spec.rb2
-rw-r--r--spec/unit/util/dsc/resource_store.rb76
-rw-r--r--spec/unit/util/powershell/ps_credential_spec.rb37
-rw-r--r--spec/unit/workstation_config_loader_spec.rb2
114 files changed, 2449 insertions, 691 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9d959832c0..74c6746816 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,6 @@
-## Unreleased (12.2.0)
+## Unreleased
+
+## 12.2.0
* make deploy resource attributes nillable (`symlink_before_migrate nil`) works now
* mixin the LWRP attribute DSL method into Chef::Resource directly
@@ -7,9 +9,12 @@
fail-fast rather than moving on to the next machine.
* migrate macosx, windows, openbsd, and netbsd resources to dynamic resolution
* migrate cron and mdadm resources to dynamic resolution
+* [Issue 3096](https://github.com/chef/chef/issues/3096) Fix OpenBSD package provider installation issues
+* New `dsc_resource` resource to invoke Powershell DSC resources
## 12.1.2
* [Issue 3022](https://github.com/chef/chef/issues/3022): Homebrew Cask install fails
+ FIXME (remove on 12.2.0 release): 3022 was only merged to 12-stable and #3077 or its descendant should fix this
* [Issue 3059](https://github.com/chef/chef/issues/3059): Chef 12.1.1 yum_package silently fails
* [Issue 3078](https://github.com/chef/chef/issues/3078): Compat break in audit-mode changes
diff --git a/DOC_CHANGES.md b/DOC_CHANGES.md
index 6b58871418..de7fe6bf92 100644
--- a/DOC_CHANGES.md
+++ b/DOC_CHANGES.md
@@ -5,3 +5,80 @@ Example Doc Change:
### Headline for the required change
Description of the required change.
-->
+
+### knife ssh has --exit-on-error option
+`knife ssh` now has an --exit-on-error option that will cause it to
+fail immediately in the face of an SSH connection error. The default
+behavior is move on to the next node.
+
+### DSC Resource
+
+The `dsc_resource` resource for Windows systems that allows cookbook authors to invoke [PowerShell Desired
+State Configuration](http://technet.microsoft.com/en-us/library/dn249912.aspx) resources in Chef DSL.
+
+#### Prerequisites
+
+* **Windows Management Framework 5** February Preview
+* **Local Configuration Manager** must be set to have a `RefreshMode` of `Disabled`
+
+#### Syntax
+
+```ruby
+dsc_resource "description" do
+ resource "resource_name"
+ property :property_name, property_value
+ ...
+ property :property_name, property_value
+end
+```
+
+#### Attributes
+
+- `resource`: The friendly name of the DSC resource
+
+- `property`: `:property_name`, `property_value` pair for each property that must be set for the DSC resource.
+`property_name` must be of the `Symbol`. The following types are supported for `property_value`, along with
+their conversion into Powershell:
+
+| Ruby Type | Powershell Type |
+|-------------------------------------|-----------------|
+| Fixnum | Integer |
+| Float | Double |
+| FalseClass | bool($false) |
+| TrueClass | bool($true) |
+| Chef::Util::Powershell:PSCredential | PSCredential |
+| Hash | Hashtable |
+| Array | Object[] |
+
+- `module_name` is the name of the module that the DSC resource comes from. If it is not provided, it will
+ be inferred.
+
+#### Actions
+
+|Action|Description|
+|------|------------------------|
+|`:run`| Invoke the DSC resource|
+
+#### Example
+
+```ruby
+dsc_resource "demogroupremove" do
+ resource :group
+ property :groupname, 'demo1'
+ property :ensure, 'present'
+end
+
+dsc_resource "useradd" do
+ resource :user
+ property :username, "Foobar1"
+ property :fullname, "Foobar1"
+ property :password, ps_credential("P@assword!")
+ property :ensure, 'present'
+end
+
+dsc_resource "AddFoobar1ToUsers" do
+ resource :Group
+ property :GroupName, "demo1"
+ property :MembersToInclude, ["Foobar1"]
+end
+```
diff --git a/MAINTAINERS.md b/MAINTAINERS.md
index 883e882a64..65eb9b69fd 100644
--- a/MAINTAINERS.md
+++ b/MAINTAINERS.md
@@ -21,6 +21,7 @@ infrastructure, and the Chef applications. Includes anything not covered by
another component.
### Lieutenant
+* [Thom May](https://github.com/thommay)
### Maintainers
@@ -34,6 +35,7 @@ another component.
* [Bryan McLellan](http://github.com/btm)
* [Ranjib Dey](http://github.com/ranjib)
* [AJ Christensen](https://github.com/fujin)
+* [Steven Murawski](https://github.com/smurawski)
## Dev Tools
@@ -70,10 +72,12 @@ The specific components of Chef related to a given platform - including (but not
### Lieutenant
+* [Jon Cowie](http://github.com/jonlives)
+
### Maintainers
-* [Jon Cowie](http://github.com/jonlives)
* [Lamont Granquist](http://github.com/lamont-granquist)
+* [Phil Dibowitz](https://github.com/jaymzh)
## Ubuntu
@@ -83,6 +87,7 @@ The specific components of Chef related to a given platform - including (but not
* [Lamont Granquist](http://github.com/lamont-granquist)
* [Ranjib Dey](http://github.com/ranjib)
+* [Thom May](https://github.com/thommay)
## Windows
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 5076ec0f4a..2a59d97736 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,2 +1,145 @@
-# Chef Client Release Notes 12.1.1:
+# Chef Client Release Notes 12.2.0:
+## Desired State Configuration (DSC) Resource
+
+If you are using `Windows Management Framework(WMF) 5`, you can now take advantage of the new `dsc_resource`.
+This new functionality takes advantage of WMF 5's `Invoke-DscResource` cmdlet to
+directly invoke resources.
+
+### Prerequisites
+
+To use this new resource, you must have the February preview of WMF 5.
+This can be installed using the Powershell cookbook. It is also required that
+the Local Configuration Manager(LCM) be configured with a `RefreshMode` of `Disabled`.
+Doing this will preclude you from using `dsc_script`. Below we provide an example
+DSC configuration:
+
+```powershell
+# create a configuration command to generate a meta.mof to set Local Configuration Manager settings
+
+Configuration LCMSettings {
+ Node localhost {
+ LocalConfigurationManager {
+ RefreshMode = 'Disabled'
+ }
+ }
+}
+
+# Run the configuration command and generate the meta.mof to configure a local configuration manager
+LCMSettings
+# Apply the local configuration manager settings found in the LCMSettings folder (by default configurations are generated
+# to a folder in the current working directory named for the configuration command name
+Set-DscLocalConfigurationManager -path ./LCMSettings
+```
+
+Running this script tells the LCM not to do document management, allowing Chef to
+take over that role. While you may be able to switch this to other values mid-run,
+you should not be doing this to run both `dsc_script` and `dsc_resource` resources.
+
+### Usage
+
+Once the LCM is correctly configured, you can begin using `dsc_resource` in your recipes.
+You can get a list of available by running the `Get-DscResource` command. You will be
+able to use any resource that does not have an `ImplementedAs` property with value
+`Composite`.
+
+As an example, let's consider the `User` dsc resource. Start by taking a look
+at what a DSC `User` resource would look like
+
+```
+> Get-DscResource User
+
+ImplementedAs Name Module Properties
+------------- ---- ------ ----------
+PowerShell User PSDesiredStateConfiguration {UserName, DependsOn, Descr...
+
+```
+
+We see here that is `ImplementedAs` is not equal to `Composite`, so it is a resource that can
+be used with `dsc_resource`. We can what properties are accpeted by the `User` resource by
+running
+
+```
+> Get-DscResource User -Syntax
+
+User [string] #ResourceName
+{
+ UserName = [string]
+ [ DependsOn = [string[]] ]
+ [ Description = [string] ]
+ [ Disabled = [bool] ]
+ [ Ensure = [string] { Absent | Present } ]
+ [ FullName = [string] ]
+ [ Password = [PSCredential] ]
+ [ PasswordChangeNotAllowed = [bool] ]
+ [ PasswordChangeRequired = [bool] ]
+ [ PasswordNeverExpires = [bool] ]
+}
+```
+
+From above, the `User` resource has a require property `UserName`, however we're probably
+also going to want to prover at the very least a `Password`. From above, we can see the `UserName`
+property must be of type string, and `Password` needs to be of type `PSCredential`. Since there
+is no native Ruby type that maps to a Powershell PSCredential, a dsl method `ps_credential` is
+provided that makes creating this simple. `ps_credential` can be called as `ps_credential(password)`
+or `ps_credential(username, password)`. Under the hood, this creates a
+`Chef::Util::Powershell::PSCredential` which gets serialized into a Powershell PSCredential.
+
+The following type translations are supported:
+
+| Ruby Type | Powershell Type |
+|-------------------------------------|-----------------|
+| Fixnum | Integer |
+| Float | Double |
+| FalseClass | bool($false) |
+| TrueClass | bool($true) |
+| Chef::Util::Powershell:PSCredential | PSCredential |
+| Hash | Hashtable |
+| Array | Object[] |
+
+With this information in hand, we can now construct a Chef `dsc_resource` resource that creates
+a user.
+
+```ruby
+dsc_resource 'create foo user' do
+ resource :User
+ property :UserName, 'FooUser'
+ property :Password, ps_credential("P@ssword!")
+ property :Ensure, 'Present'
+end
+```
+
+#### Third Party Resources
+`dsc_resource` also supports the use of 3rd party DSC resources, for example the DSC Resource Kit. These
+resources can be used just like you would use any `PSDesiredStateConfiguration` resource like `User`. Since
+the implementation of `dsc_resource` knows how to talk to DSC resources that are visible through the
+`Get-DscResource` cmdlet, it should just work. For example, if we wanted to use `xSmbShare`, we could
+construct the powershell resource as
+
+```ruby
+dsc_resource 'create smb share' do
+ resource :xSmbShare
+ property :Name, 'Foo'
+ property :Path, 'C:\Foo'
+end
+```
+
+This would execute
+
+```
+> Get-DscResource xSmbShare
+
+ImplementedAs Name Module Properties
+------------- ---- ------ ----------
+PowerShell xSmbShare xSmbShare {Name, Path, ChangeAccess, ...
+```
+
+to look up the module name, and in this case use `xSmbShare`. However, this lookup process can slow down
+the process. It is also possible that there are multiple DSC resources with that name. To address these
+cases, `dsc_resource` provides an aditional attribute `module_name`. You can pass the name of the module
+that the resource comes from, and `dsc_resource` will make sure that it uses that module. This will
+short-circuit any logic to lookup the module name, shortening the time it takes to execute the resource.
+
+## Notes
+
+- The implementation of `dsc_resource` is base on the experimental Invoke-DscResource cmdlet
diff --git a/appveyor.yml b/appveyor.yml
index 4efdeadf84..5ba6d30b81 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -26,7 +26,7 @@ install:
- bundler --version
build_script:
- - bundle install
+ - bundle install || bundle install || bundle install
test_script:
- SET SPEC_OPTS=--format progress
diff --git a/chef.gemspec b/chef.gemspec
index d32e0005d6..17861bc4a9 100644
--- a/chef.gemspec
+++ b/chef.gemspec
@@ -22,7 +22,7 @@ Gem::Specification.new do |s|
s.add_dependency "mixlib-shellout", ">= 2.0.0.rc.0", "< 3.0"
s.add_dependency "ohai", "~> 8.0"
- s.add_dependency "ffi-yajl", "~> 1.2"
+ s.add_dependency "ffi-yajl", ">= 1.2", "< 3.0"
s.add_dependency "net-ssh", "~> 2.6"
s.add_dependency "net-ssh-multi", "~> 1.1"
# CHEF-3027: The knife-cloud plugins require newer features from highline, core chef should not.
diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb
index d5dc936f83..03fd07e9f0 100644
--- a/lib/chef/application/client.rb
+++ b/lib/chef/application/client.rb
@@ -451,9 +451,9 @@ class Chef::Application::Client < Chef::Application
def audit_mode_experimental_message
msg = if Chef::Config[:audit_mode] == :audit_only
- "Chef-client has been configured to skip converge and run only audits."
+ "Chef-client has been configured to skip converge and only audit."
else
- "Chef-client has been configured to run audits after it converges."
+ "Chef-client has been configured to audit after it converges."
end
msg += " Audit mode is an experimental feature currently under development. API changes may occur. Use at your own risk."
msg += audit_mode_settings_explaination
diff --git a/lib/chef/audit/audit_reporter.rb b/lib/chef/audit/audit_reporter.rb
index a5dd9a6c48..a4f84ed7eb 100644
--- a/lib/chef/audit/audit_reporter.rb
+++ b/lib/chef/audit/audit_reporter.rb
@@ -105,7 +105,7 @@ class Chef
end
unless run_status
- Chef::Log.debug("Run failed before audits were initialized, not sending audit report to server")
+ Chef::Log.debug("Run failed before audit mode was initialized, not sending audit report to server")
return
end
diff --git a/lib/chef/audit/runner.rb b/lib/chef/audit/runner.rb
index e7d1657d69..13c2823dca 100644
--- a/lib/chef/audit/runner.rb
+++ b/lib/chef/audit/runner.rb
@@ -45,6 +45,10 @@ class Chef
RSpec.world.reporter.examples.size
end
+ def exclusion_pattern
+ Regexp.new(".+[\\\/]lib[\\\/]chef[\\\/]")
+ end
+
private
# Prepare to run audits:
# - Require files
@@ -75,11 +79,15 @@ class Chef
require 'rspec'
require 'rspec/its'
require 'specinfra'
+ require 'specinfra/helper'
+ require 'specinfra/helper/set'
require 'serverspec/helper'
require 'serverspec/matcher'
require 'serverspec/subject'
require 'chef/audit/audit_event_proxy'
require 'chef/audit/rspec_formatter'
+
+ Specinfra::Backend::Cmd.send(:include, Specinfra::Helper::Set)
end
# Configure RSpec just the way we like it:
@@ -96,6 +104,7 @@ class Chef
RSpec.configure do |c|
c.color = Chef::Config[:color]
c.expose_dsl_globally = false
+ c.backtrace_exclusion_patterns << exclusion_pattern
end
end
@@ -131,9 +140,13 @@ class Chef
end
end
- # Set up the backend for Specinfra/Serverspec. :exec is the local system.
+ # Set up the backend for Specinfra/Serverspec. :exec is the local system; on Windows, it is :cmd
def configure_specinfra
- Specinfra.configuration.backend = :exec
+ if Chef::Platform.windows?
+ Specinfra.configuration.backend = :cmd
+ else
+ Specinfra.configuration.backend = :exec
+ end
end
# Iterates through the control groups registered to this run_context, builds an
diff --git a/lib/chef/client.rb b/lib/chef/client.rb
index db7cb1bbab..f5cde4bfb3 100644
--- a/lib/chef/client.rb
+++ b/lib/chef/client.rb
@@ -451,7 +451,7 @@ class Chef
if Chef::Config[:why_run] == true
# why_run should probably be renamed to why_converge
- Chef::Log.debug("Not running audits in 'why_run' mode - this mode is used to see potential converge changes")
+ Chef::Log.debug("Not running controls in 'why_run' mode - this mode is used to see potential converge changes")
elsif Chef::Config[:audit_mode] != :disabled
audit_error = run_audits(run_context)
end
diff --git a/lib/chef/config.rb b/lib/chef/config.rb
index 2eb9870a64..a9fa9f1552 100644
--- a/lib/chef/config.rb
+++ b/lib/chef/config.rb
@@ -497,7 +497,8 @@ class Chef
default(:syntax_check_cache_path) { cache_options[:path] }
# Deprecated:
- default(:cache_options) { { :path => PathHelper.join(file_cache_path, "checksums") } }
+ # Move this to the default value of syntax_cache_path when this is removed.
+ default(:cache_options) { { :path => PathHelper.join(config_dir, "syntaxcache") } }
# Whether errors should be raised for deprecation warnings. When set to
# `false` (the default setting), a warning is emitted but code using
@@ -570,11 +571,12 @@ class Chef
end
def self.windows_home_path
- env['SYSTEMDRIVE'] + env['HOMEPATH'] if env['SYSTEMDRIVE'] && env['HOMEPATH']
+ Chef::Log.deprecation("Chef::Config.windows_home_path is now deprecated. Consider using Chef::Util::PathHelper.home instead.")
+ PathHelper.home
end
# returns a platform specific path to the user home dir if set, otherwise default to current directory.
- default( :user_home ) { env['HOME'] || windows_home_path || env['USERPROFILE'] || Dir.pwd }
+ default( :user_home ) { PathHelper.home || Dir.pwd }
# Enable file permission fixup for selinux. Fixup will be done
# only if selinux is enabled in the system.
@@ -627,7 +629,7 @@ class Chef
#
default :no_lazy_load, true
- # Default for the chef_gem compile_time attribute. Nil is the same as false but will emit
+ # Default for the chef_gem compile_time attribute. Nil is the same as true but will emit
# warnings on every use of chef_gem prompting the user to be explicit. If the user sets this to
# true then the user will get backcompat behavior but with a single nag warning that cookbooks
# may break with this setting in the future. The false setting is the recommended setting and
diff --git a/lib/chef/dsl/powershell.rb b/lib/chef/dsl/powershell.rb
new file mode 100644
index 0000000000..a17971c689
--- /dev/null
+++ b/lib/chef/dsl/powershell.rb
@@ -0,0 +1,29 @@
+#
+# Author:: Jay Mundrawala (<jdm@chef.io>)
+# Copyright:: Copyright (c) 2015 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/util/powershell/ps_credential'
+
+class Chef
+ module DSL
+ module Powershell
+ def ps_credential(username='placeholder', password)
+ Chef::Util::Powershell::PSCredential.new(username, password)
+ end
+ end
+ end
+end
diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb
index ecd84c5ba5..eea6a2f239 100644
--- a/lib/chef/exceptions.rb
+++ b/lib/chef/exceptions.rb
@@ -399,18 +399,18 @@ class Chef
class AuditControlGroupDuplicate < RuntimeError
def initialize(name)
- super "Audit control group with name '#{name}' has already been defined"
+ super "Control group with name '#{name}' has already been defined"
end
end
class AuditNameMissing < RuntimeError; end
class NoAuditsProvided < RuntimeError
def initialize
- super "You must provide a block with audits"
+ super "You must provide a block with controls"
end
end
class AuditsFailed < RuntimeError
def initialize(num_failed, num_total)
- super "Audit phase found failures - #{num_failed}/#{num_total} audits failed"
+ super "Audit phase found failures - #{num_failed}/#{num_total} controls failed"
end
end
@@ -442,5 +442,20 @@ class Chef
super "PID file and lockfile are not permitted to match. Specify a different location with --pid or --lockfile"
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 matching resources. #{matches_info.join("\n")}"
+ end
+ end
end
end
diff --git a/lib/chef/formatters/doc.rb b/lib/chef/formatters/doc.rb
index 489888db8f..7144d00b5d 100644
--- a/lib/chef/formatters/doc.rb
+++ b/lib/chef/formatters/doc.rb
@@ -47,7 +47,7 @@ class Chef
else
puts_line "Chef Client finished, #{@updated_resources}/#{total_resources} resources updated in #{elapsed_time} seconds"
if total_audits > 0
- puts_line " #{successful_audits}/#{total_audits} Audits succeeded"
+ puts_line " #{successful_audits}/#{total_audits} controls succeeded"
end
end
end
@@ -59,7 +59,7 @@ class Chef
else
puts_line "Chef Client failed. #{@updated_resources} resources updated in #{elapsed_time} seconds"
if total_audits > 0
- puts_line " #{successful_audits} Audits succeeded"
+ puts_line " #{successful_audits} controls succeeded"
end
end
end
diff --git a/lib/chef/knife/bootstrap.rb b/lib/chef/knife/bootstrap.rb
index e168a6bd9b..64d1d0c378 100644
--- a/lib/chef/knife/bootstrap.rb
+++ b/lib/chef/knife/bootstrap.rb
@@ -21,6 +21,7 @@ require 'chef/knife/data_bag_secret_options'
require 'erubis'
require 'chef/knife/bootstrap/chef_vault_handler'
require 'chef/knife/bootstrap/client_builder'
+require 'chef/util/path_helper'
class Chef
class Knife
@@ -268,7 +269,7 @@ class Chef
bootstrap_files = []
bootstrap_files << File.join(File.dirname(__FILE__), 'bootstrap/templates', "#{template}.erb")
bootstrap_files << File.join(Knife.chef_config_dir, "bootstrap", "#{template}.erb") if Chef::Knife.chef_config_dir
- bootstrap_files << File.join(ENV['HOME'], '.chef', 'bootstrap', "#{template}.erb") if ENV['HOME']
+ Chef::Util::PathHelper.home('.chef', 'bootstrap', "#{template}.erb") {|p| bootstrap_files << p}
bootstrap_files << Gem.find_files(File.join("chef","knife","bootstrap","#{template}.erb"))
bootstrap_files.flatten!
diff --git a/lib/chef/knife/bootstrap/templates/chef-full.erb b/lib/chef/knife/bootstrap/templates/chef-full.erb
index 17d7a9e3b5..a87ab8e544 100644
--- a/lib/chef/knife/bootstrap/templates/chef-full.erb
+++ b/lib/chef/knife/bootstrap/templates/chef-full.erb
@@ -22,7 +22,7 @@ exists() {
<% if knife_config[:bootstrap_install_command] %>
<%= knife_config[:bootstrap_install_command] %>
<% else %>
- install_sh="<%= knife_config[:bootstrap_url] ? knife_config[:bootstrap_url] : "https://www.chef.io/chef/install.sh" %>"
+ install_sh="<%= knife_config[:bootstrap_url] ? knife_config[:bootstrap_url] : "https://www.opscode.com/chef/install.sh" %>"
if ! exists /usr/bin/chef-client; then
echo "Installing Chef Client..."
if exists wget; then
diff --git a/lib/chef/knife/core/subcommand_loader.rb b/lib/chef/knife/core/subcommand_loader.rb
index f9b8f5008e..1f59515f44 100644
--- a/lib/chef/knife/core/subcommand_loader.rb
+++ b/lib/chef/knife/core/subcommand_loader.rb
@@ -28,9 +28,15 @@ class Chef
attr_reader :chef_config_dir
attr_reader :env
- def initialize(chef_config_dir, env=ENV)
- @chef_config_dir, @env = chef_config_dir, env
+ def initialize(chef_config_dir, env=nil)
+ @chef_config_dir = chef_config_dir
@forced_activate = {}
+
+ # Deprecated and un-used instance variable.
+ @env = env
+ unless env.nil?
+ Chef::Log.deprecation("The env argument to Chef::Knife::SubcommandLoader is deprecated. If you are using env to inject/mock HOME, consider mocking Chef::Util::PathHelper.home instead.")
+ end
end
# Load all the sub-commands
@@ -49,7 +55,9 @@ class Chef
end
# finally search ~/.chef/plugins/knife/*.rb
- user_specific_files.concat Dir.glob(File.join(Chef::Util::PathHelper.escape_glob(env['HOME'], '.chef', 'plugins', 'knife'), '*.rb')) if env['HOME']
+ Chef::Util::PathHelper.home('.chef', 'plugins', 'knife') do |p|
+ user_specific_files.concat Dir.glob(File.join(Chef::Util::PathHelper.escape_glob(p), '*.rb'))
+ end
user_specific_files
end
@@ -140,7 +148,7 @@ class Chef
end
def have_plugin_manifest?
- ENV["HOME"] && File.exist?(plugin_manifest_path)
+ plugin_manifest_path && File.exist?(plugin_manifest_path)
end
def plugin_manifest
@@ -148,7 +156,7 @@ class Chef
end
def plugin_manifest_path
- File.join(ENV['HOME'], '.chef', 'plugin_manifest.json')
+ Chef::Util::PathHelper.home('.chef', 'plugin_manifest.json')
end
private
diff --git a/lib/chef/knife/exec.rb b/lib/chef/knife/exec.rb
index 3e8196c616..ace4ee2300 100644
--- a/lib/chef/knife/exec.rb
+++ b/lib/chef/knife/exec.rb
@@ -17,6 +17,7 @@
#
require 'chef/knife'
+require 'chef/util/path_helper'
class Chef::Knife::Exec < Chef::Knife
@@ -42,7 +43,7 @@ class Chef::Knife::Exec < Chef::Knife
# Default script paths are chef-repo/.chef/scripts and ~/.chef/scripts
config[:script_path] << File.join(Chef::Knife.chef_config_dir, 'scripts') if Chef::Knife.chef_config_dir
- config[:script_path] << File.join(ENV['HOME'], '.chef', 'scripts') if ENV['HOME']
+ Chef::Util::PathHelper.home('.chef', 'scripts') { |p| config[:script_path] << p }
scripts = Array(name_args)
context = Object.new
diff --git a/lib/chef/knife/ssh.rb b/lib/chef/knife/ssh.rb
index 4569cc097e..50fedd0e49 100644
--- a/lib/chef/knife/ssh.rb
+++ b/lib/chef/knife/ssh.rb
@@ -31,6 +31,7 @@ class Chef
require 'chef/search/query'
require 'chef/mixin/shell_out'
require 'chef/mixin/command'
+ require 'chef/util/path_helper'
require 'mixlib/shellout'
end
@@ -103,6 +104,13 @@ class Chef
:boolean => true,
:default => true
+ option :on_error,
+ :short => '-e',
+ :long => '--exit-on-error',
+ :description => "Immediately exit if an error is encountered",
+ :boolean => true,
+ :proc => Proc.new { :raise }
+
def session
config[:on_error] ||= :skip
ssh_error_handler = Proc.new do |server|
@@ -335,8 +343,10 @@ class Chef
def screen
tf = Tempfile.new("knife-ssh-screen")
- if File.exist? "#{ENV["HOME"]}/.screenrc"
- tf.puts("source #{ENV["HOME"]}/.screenrc")
+ Chef::Util::PathHelper.home('.screenrc') do |screenrc_path|
+ if File.exist? screenrc_path
+ tf.puts("source #{screenrc_path}")
+ end
end
tf.puts("caption always '%-Lw%{= BW}%50>%n%f* %t%{-}%+Lw%<'")
tf.puts("hardstatus alwayslastline 'knife ssh #{@name_args[0]}'")
diff --git a/lib/chef/mixin/params_validate.rb b/lib/chef/mixin/params_validate.rb
index 78d72dc801..baf210bfc5 100644
--- a/lib/chef/mixin/params_validate.rb
+++ b/lib/chef/mixin/params_validate.rb
@@ -81,34 +81,58 @@ class Chef
DelayedEvaluator.new(&block)
end
- def set_or_return(symbol, arg, validation)
+ NULL_ARG = Object.new
+
+ def nillable_set_or_return(symbol, arg, validation)
iv_symbol = "@#{symbol.to_s}".to_sym
- if arg == nil && self.instance_variable_defined?(iv_symbol) == true
- ivar = self.instance_variable_get(iv_symbol)
- if(ivar.is_a?(DelayedEvaluator))
- validate({ symbol => ivar.call }, { symbol => validation })[symbol]
+ if NULL_ARG.equal?(arg)
+ if self.instance_variable_defined?(iv_symbol) == true
+ get_ivar(iv_symbol, symbol, validation)
else
- ivar
+ # on access we create the iv and set it to nil for back-compat
+ set_ivar(iv_symbol, symbol, nil, validation)
end
else
- if(arg.is_a?(DelayedEvaluator))
- val = arg
- else
- val = validate({ symbol => arg }, { symbol => validation })[symbol]
+ set_ivar(iv_symbol, symbol, arg, validation)
+ end
+ end
- # Handle the case where the "default" was a DelayedEvaluator. In
- # this case, the block yields an optional parameter of +self+,
- # which is the equivalent of "new_resource"
- if val.is_a?(DelayedEvaluator)
- val = val.call(self)
- end
- end
- self.instance_variable_set(iv_symbol, val)
+ def set_or_return(symbol, arg, validation)
+ iv_symbol = "@#{symbol.to_s}".to_sym
+ if arg == nil && self.instance_variable_defined?(iv_symbol) == true
+ get_ivar(iv_symbol, symbol, validation)
+ else
+ set_ivar(iv_symbol, symbol, arg, validation)
end
end
private
+ def get_ivar(iv_symbol, symbol, validation)
+ ivar = self.instance_variable_get(iv_symbol)
+ if(ivar.is_a?(DelayedEvaluator))
+ validate({ symbol => ivar.call }, { symbol => validation })[symbol]
+ else
+ ivar
+ end
+ end
+
+ def set_ivar(iv_symbol, symbol, arg, validation)
+ if(arg.is_a?(DelayedEvaluator))
+ val = arg
+ else
+ val = validate({ symbol => arg }, { symbol => validation })[symbol]
+
+ # Handle the case where the "default" was a DelayedEvaluator. In
+ # this case, the block yields an optional parameter of +self+,
+ # which is the equivalent of "new_resource"
+ if val.is_a?(DelayedEvaluator)
+ val = val.call(self)
+ end
+ end
+ self.instance_variable_set(iv_symbol, val)
+ end
+
# Return the value of a parameter, or nil if it doesn't exist.
def _pv_opts_lookup(opts, key)
if opts.has_key?(key.to_s)
@@ -239,4 +263,3 @@ class Chef
end
end
end
-
diff --git a/lib/chef/mixin/powershell_type_coercions.rb b/lib/chef/mixin/powershell_type_coercions.rb
new file mode 100644
index 0000000000..75b3276c84
--- /dev/null
+++ b/lib/chef/mixin/powershell_type_coercions.rb
@@ -0,0 +1,82 @@
+#
+# Author:: Adam Edwards (<adamed@opscode.com>)
+# Author:: Jay Mundrawala (<jdm@chef.io>)
+# Copyright:: Copyright (c) 2015 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
+ module Mixin
+ module PowershellTypeCoercions
+
+ def type_coercions
+ @type_coercions ||= {
+ Fixnum => { :type => lambda { |x| x.to_s }},
+ Float => { :type => lambda { |x| x.to_s }},
+ FalseClass => { :type => lambda { |x| '$false' }},
+ TrueClass => { :type => lambda { |x| '$true' }},
+ Hash => {:type => Proc.new { |x| translate_hash(x)}},
+ Array => {:type => Proc.new { |x| translate_array(x)}}
+ }
+ end
+
+ def translate_type(value)
+ translation = type_coercions[value.class]
+
+ if translation
+ translation[:type].call(value)
+ elsif value.respond_to? :to_psobject
+ "(#{value.to_psobject})"
+ else
+ safe_string(value.to_s)
+ end
+ end
+
+ private
+
+ def translate_hash(x)
+ translated = x.inject([]) do |memo, (k,v)|
+ memo << "#{k}=#{translate_type(v)}"
+ end
+ "@{#{translated.join(';')}}"
+ end
+
+ def translate_array(x)
+ translated = x.map do |v|
+ translate_type(v)
+ end
+ "@(#{translated.join(',')})"
+ end
+
+ def unsafe?(s)
+ ["'", '#', '`', '"'].any? do |x|
+ s.include? x
+ end
+ end
+
+ def safe_string(s)
+ # do we need to worry about binary data?
+ if unsafe?(s)
+ encoded_str = Base64.strict_encode64(s.encode("UTF-8"))
+ "([System.Text.Encoding]::UTF8.GetString("\
+ "[System.Convert]::FromBase64String('#{encoded_str}')"\
+ "))"
+ else
+ "'#{s}'"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/mixin/windows_architecture_helper.rb b/lib/chef/mixin/windows_architecture_helper.rb
index 65ad042910..a0ac34f627 100644
--- a/lib/chef/mixin/windows_architecture_helper.rb
+++ b/lib/chef/mixin/windows_architecture_helper.rb
@@ -43,6 +43,14 @@ class Chef
end
def with_os_architecture(node)
+ node ||= begin
+ os_arch = ENV['PROCESSOR_ARCHITEW6432'] ||
+ ENV['PROCESSOR_ARCHITECTURE']
+ Hash.new.tap do |n|
+ n[:kernel] = Hash.new
+ n[:kernel][:machine] = os_arch == 'AMD64' ? :x86_64 : :i386
+ end
+ end
wow64_redirection_state = nil
if wow64_architecture_override_required?(node, node_windows_architecture(node))
diff --git a/lib/chef/node.rb b/lib/chef/node.rb
index f9f6416f14..9823185ede 100644
--- a/lib/chef/node.rb
+++ b/lib/chef/node.rb
@@ -323,7 +323,7 @@ class Chef
if attrs.key?("recipes") || attrs.key?("run_list")
raise Chef::Exceptions::AmbiguousRunlistSpecification, "please set the node's run list using the 'run_list' attribute only."
end
- Chef::Log.info("Setting the run_list to #{new_run_list.inspect} from CLI options")
+ Chef::Log.info("Setting the run_list to #{new_run_list.to_s} from CLI options")
run_list(new_run_list)
end
attrs
diff --git a/lib/chef/platform/provider_mapping.rb b/lib/chef/platform/provider_mapping.rb
index 3d8212e24f..0d7285729f 100644
--- a/lib/chef/platform/provider_mapping.rb
+++ b/lib/chef/platform/provider_mapping.rb
@@ -38,33 +38,16 @@ class Chef
require 'chef/providers'
{
- :mac_os_x => {
- :default => {
- :package => Chef::Provider::Package::Homebrew,
- :user => Chef::Provider::User::Dscl,
- :group => Chef::Provider::Group::Dscl
- }
- },
- :mac_os_x_server => {
- :default => {
- :package => Chef::Provider::Package::Homebrew,
- :user => Chef::Provider::User::Dscl,
- :group => Chef::Provider::Group::Dscl
- }
- },
:freebsd => {
:default => {
:group => Chef::Provider::Group::Pw,
:user => Chef::Provider::User::Pw,
- :cron => Chef::Provider::Cron
}
},
:ubuntu => {
:default => {
:package => Chef::Provider::Package::Apt,
:service => Chef::Provider::Service::Debian,
- :cron => Chef::Provider::Cron,
- :mdadm => Chef::Provider::Mdadm
},
">= 11.10" => {
:ifconfig => Chef::Provider::Ifconfig::Debian
@@ -79,40 +62,30 @@ class Chef
:default => {
:package => Chef::Provider::Package::Apt,
:service => Chef::Provider::Service::Debian,
- :cron => Chef::Provider::Cron,
- :mdadm => Chef::Provider::Mdadm
}
},
:linaro => {
:default => {
:package => Chef::Provider::Package::Apt,
:service => Chef::Provider::Service::Debian,
- :cron => Chef::Provider::Cron,
- :mdadm => Chef::Provider::Mdadm
}
},
:raspbian => {
:default => {
:package => Chef::Provider::Package::Apt,
:service => Chef::Provider::Service::Debian,
- :cron => Chef::Provider::Cron,
- :mdadm => Chef::Provider::Mdadm
}
},
:linuxmint => {
:default => {
:package => Chef::Provider::Package::Apt,
:service => Chef::Provider::Service::Upstart,
- :cron => Chef::Provider::Cron,
- :mdadm => Chef::Provider::Mdadm
}
},
:debian => {
:default => {
:package => Chef::Provider::Package::Apt,
:service => Chef::Provider::Service::Debian,
- :cron => Chef::Provider::Cron,
- :mdadm => Chef::Provider::Mdadm
},
">= 6.0" => {
:service => Chef::Provider::Service::Insserv
@@ -124,25 +97,19 @@ class Chef
:xenserver => {
:default => {
:service => Chef::Provider::Service::Redhat,
- :cron => Chef::Provider::Cron,
:package => Chef::Provider::Package::Yum,
- :mdadm => Chef::Provider::Mdadm
}
},
:xcp => {
:default => {
:service => Chef::Provider::Service::Redhat,
- :cron => Chef::Provider::Cron,
:package => Chef::Provider::Package::Yum,
- :mdadm => Chef::Provider::Mdadm
}
},
:centos => {
:default => {
:service => Chef::Provider::Service::Systemd,
- :cron => Chef::Provider::Cron,
:package => Chef::Provider::Package::Yum,
- :mdadm => Chef::Provider::Mdadm,
:ifconfig => Chef::Provider::Ifconfig::Redhat
},
"< 7" => {
@@ -152,17 +119,13 @@ class Chef
:amazon => {
:default => {
:service => Chef::Provider::Service::Redhat,
- :cron => Chef::Provider::Cron,
:package => Chef::Provider::Package::Yum,
- :mdadm => Chef::Provider::Mdadm
}
},
:scientific => {
:default => {
:service => Chef::Provider::Service::Systemd,
- :cron => Chef::Provider::Cron,
:package => Chef::Provider::Package::Yum,
- :mdadm => Chef::Provider::Mdadm
},
"< 7" => {
:service => Chef::Provider::Service::Redhat
@@ -171,9 +134,7 @@ class Chef
:fedora => {
:default => {
:service => Chef::Provider::Service::Systemd,
- :cron => Chef::Provider::Cron,
:package => Chef::Provider::Package::Yum,
- :mdadm => Chef::Provider::Mdadm,
:ifconfig => Chef::Provider::Ifconfig::Redhat
},
"< 15" => {
@@ -183,7 +144,6 @@ class Chef
:opensuse => {
:default => {
:service => Chef::Provider::Service::Redhat,
- :cron => Chef::Provider::Cron,
:package => Chef::Provider::Package::Zypper,
:group => Chef::Provider::Group::Suse
},
@@ -195,7 +155,6 @@ class Chef
:suse => {
:default => {
:service => Chef::Provider::Service::Systemd,
- :cron => Chef::Provider::Cron,
:package => Chef::Provider::Package::Zypper,
:group => Chef::Provider::Group::Gpasswd
},
@@ -207,9 +166,7 @@ class Chef
:oracle => {
:default => {
:service => Chef::Provider::Service::Systemd,
- :cron => Chef::Provider::Cron,
:package => Chef::Provider::Package::Yum,
- :mdadm => Chef::Provider::Mdadm
},
"< 7" => {
:service => Chef::Provider::Service::Redhat
@@ -218,9 +175,7 @@ class Chef
:redhat => {
:default => {
:service => Chef::Provider::Service::Systemd,
- :cron => Chef::Provider::Cron,
:package => Chef::Provider::Package::Yum,
- :mdadm => Chef::Provider::Mdadm,
:ifconfig => Chef::Provider::Ifconfig::Redhat
},
"< 7" => {
@@ -230,27 +185,21 @@ class Chef
:ibm_powerkvm => {
:default => {
:service => Chef::Provider::Service::Redhat,
- :cron => Chef::Provider::Cron,
:package => Chef::Provider::Package::Yum,
- :mdadm => Chef::Provider::Mdadm,
:ifconfig => Chef::Provider::Ifconfig::Redhat
}
},
:cloudlinux => {
:default => {
:service => Chef::Provider::Service::Redhat,
- :cron => Chef::Provider::Cron,
:package => Chef::Provider::Package::Yum,
- :mdadm => Chef::Provider::Mdadm,
:ifconfig => Chef::Provider::Ifconfig::Redhat
}
},
:parallels => {
:default => {
:service => Chef::Provider::Service::Redhat,
- :cron => Chef::Provider::Cron,
:package => Chef::Provider::Package::Yum,
- :mdadm => Chef::Provider::Mdadm,
:ifconfig => Chef::Provider::Ifconfig::Redhat
}
},
@@ -258,46 +207,12 @@ class Chef
:default => {
:package => Chef::Provider::Package::Portage,
:service => Chef::Provider::Service::Gentoo,
- :cron => Chef::Provider::Cron,
- :mdadm => Chef::Provider::Mdadm
}
},
:arch => {
:default => {
:package => Chef::Provider::Package::Pacman,
:service => Chef::Provider::Service::Systemd,
- :cron => Chef::Provider::Cron,
- :mdadm => Chef::Provider::Mdadm
- }
- },
- :mswin => {
- :default => {
- :env => Chef::Provider::Env::Windows,
- :user => Chef::Provider::User::Windows,
- :group => Chef::Provider::Group::Windows,
- :mount => Chef::Provider::Mount::Windows,
- :batch => Chef::Provider::Batch,
- :powershell_script => Chef::Provider::PowershellScript
- }
- },
- :mingw32 => {
- :default => {
- :env => Chef::Provider::Env::Windows,
- :user => Chef::Provider::User::Windows,
- :group => Chef::Provider::Group::Windows,
- :mount => Chef::Provider::Mount::Windows,
- :batch => Chef::Provider::Batch,
- :powershell_script => Chef::Provider::PowershellScript
- }
- },
- :windows => {
- :default => {
- :env => Chef::Provider::Env::Windows,
- :user => Chef::Provider::User::Windows,
- :group => Chef::Provider::Group::Windows,
- :mount => Chef::Provider::Mount::Windows,
- :batch => Chef::Provider::Batch,
- :powershell_script => Chef::Provider::PowershellScript
}
},
:solaris => {},
@@ -305,7 +220,6 @@ class Chef
:default => {
:mount => Chef::Provider::Mount::Solaris,
:package => Chef::Provider::Package::Ips,
- :cron => Chef::Provider::Cron::Solaris,
:group => Chef::Provider::Group::Usermod
}
},
@@ -313,7 +227,6 @@ class Chef
:default => {
:mount => Chef::Provider::Mount::Solaris,
:package => Chef::Provider::Package::Ips,
- :cron => Chef::Provider::Cron::Solaris,
:group => Chef::Provider::Group::Usermod
}
},
@@ -321,7 +234,6 @@ class Chef
:default => {
:mount => Chef::Provider::Mount::Solaris,
:package => Chef::Provider::Package::Solaris,
- :cron => Chef::Provider::Cron::Solaris,
:group => Chef::Provider::Group::Usermod
}
},
@@ -329,7 +241,6 @@ class Chef
:default => {
:mount => Chef::Provider::Mount::Solaris,
:package => Chef::Provider::Package::Ips,
- :cron => Chef::Provider::Cron::Solaris,
:group => Chef::Provider::Group::Usermod,
:user => Chef::Provider::User::Solaris,
}
@@ -338,14 +249,12 @@ class Chef
:default => {
:mount => Chef::Provider::Mount::Solaris,
:package => Chef::Provider::Package::Ips,
- :cron => Chef::Provider::Cron::Solaris,
:group => Chef::Provider::Group::Usermod,
:user => Chef::Provider::User::Solaris,
},
"< 5.11" => {
:mount => Chef::Provider::Mount::Solaris,
:package => Chef::Provider::Package::Solaris,
- :cron => Chef::Provider::Cron::Solaris,
:group => Chef::Provider::Group::Usermod,
:user => Chef::Provider::User::Solaris,
}
@@ -354,22 +263,9 @@ class Chef
:default => {
:mount => Chef::Provider::Mount::Solaris,
:package => Chef::Provider::Package::SmartOS,
- :cron => Chef::Provider::Cron::Solaris,
:group => Chef::Provider::Group::Usermod
}
},
- :netbsd => {
- :default => {
- :group => Chef::Provider::Group::Groupmod
- }
- },
- :openbsd => {
- :default => {
- :group => Chef::Provider::Group::Usermod,
- :package => Chef::Provider::Package::Openbsd,
- :service => Chef::Provider::Service::Openbsd
- }
- },
:hpux => {
:default => {
:group => Chef::Provider::Group::Usermod
@@ -380,7 +276,6 @@ class Chef
:group => Chef::Provider::Group::Aix,
:mount => Chef::Provider::Mount::Aix,
:ifconfig => Chef::Provider::Ifconfig::Aix,
- :cron => Chef::Provider::Cron::Aix,
:package => Chef::Provider::Package::Aix,
:user => Chef::Provider::User::Aix,
:service => Chef::Provider::Service::Aix
@@ -390,8 +285,6 @@ class Chef
:default => {
:package => Chef::Provider::Package::Paludis,
:service => Chef::Provider::Service::Systemd,
- :cron => Chef::Provider::Cron,
- :mdadm => Chef::Provider::Mdadm
}
},
:default => {
diff --git a/lib/chef/platform/query_helpers.rb b/lib/chef/platform/query_helpers.rb
index ff83c871fa..f7c85fbe23 100644
--- a/lib/chef/platform/query_helpers.rb
+++ b/lib/chef/platform/query_helpers.rb
@@ -47,6 +47,13 @@ class Chef
node[:languages] && node[:languages][:powershell] &&
node[:languages][:powershell][:version].to_i >= 4
end
+
+ def supports_dsc_invoke_resource?(node)
+ require 'rubygems'
+ supports_dsc?(node) &&
+ Gem::Version.new(node[:languages][:powershell][:version]) >=
+ Gem::Version.new("5.0.10018.0")
+ end
end
end
end
diff --git a/lib/chef/provider/batch.rb b/lib/chef/provider/batch.rb
index 354a640e59..b6b386e5a8 100644
--- a/lib/chef/provider/batch.rb
+++ b/lib/chef/provider/batch.rb
@@ -22,6 +22,8 @@ class Chef
class Provider
class Batch < Chef::Provider::WindowsScript
+ provides :batch, os: "windows"
+
def initialize (new_resource, run_context)
super(new_resource, run_context, '.bat')
end
diff --git a/lib/chef/provider/cron.rb b/lib/chef/provider/cron.rb
index 1590c624f6..4b7836947e 100644
--- a/lib/chef/provider/cron.rb
+++ b/lib/chef/provider/cron.rb
@@ -25,6 +25,8 @@ class Chef
class Cron < Chef::Provider
include Chef::Mixin::Command
+ provides :cron, os: "!aix"
+
SPECIAL_TIME_VALUES = [:reboot, :yearly, :annually, :monthly, :weekly, :daily, :midnight, :hourly]
CRON_ATTRIBUTES = [:minute, :hour, :day, :month, :weekday, :time, :command, :mailto, :path, :shell, :home, :environment]
WEEKDAY_SYMBOLS = [:sunday, :monday, :tuesday, :wednesday, :thursday, :friday, :saturday]
diff --git a/lib/chef/provider/cron/aix.rb b/lib/chef/provider/cron/aix.rb
index 473700bf2f..9cacbc6ec9 100644
--- a/lib/chef/provider/cron/aix.rb
+++ b/lib/chef/provider/cron/aix.rb
@@ -23,6 +23,8 @@ class Chef
class Cron
class Aix < Chef::Provider::Cron::Unix
+ provides :cron, os: "aix"
+
private
# For AIX we ignore env vars/[ :mailto, :path, :shell, :home ]
diff --git a/lib/chef/provider/deploy.rb b/lib/chef/provider/deploy.rb
index 19e7c01ab1..4449e44689 100644
--- a/lib/chef/provider/deploy.rb
+++ b/lib/chef/provider/deploy.rb
@@ -42,18 +42,38 @@ class Chef
# @configuration is not used by Deploy, it is only for backwards compat with
# chef-deploy or capistrano hooks that might use it to get environment information
- @configuration = @new_resource.to_hash
+ @configuration = new_resource.to_hash
@configuration[:environment] = @configuration[:environment] && @configuration[:environment]["RAILS_ENV"]
end
+ # @return [Array] create_dirs_before_symlink parameter set on the resource
+ def create_dirs_before_symlink
+ new_resource.create_dirs_before_symlink || []
+ end
+
+ # @return [Array] purge_before_symlink paremeter set on the resource
+ def purge_before_symlink
+ new_resource.purge_before_symlink || []
+ end
+
+ # @return [Hash] symlinks parameter set on the resource
+ def symlinks
+ new_resource.symlinks || {}
+ end
+
+ # @return [Hash] symlink_before_migrate parameter set on the resource
+ def symlink_before_migrate
+ new_resource.symlink_before_migrate || {}
+ end
+
def whyrun_supported?
true
end
def load_current_resource
- @scm_provider.load_current_resource
- @release_path = @new_resource.deploy_to + "/releases/#{release_slug}"
- @shared_path = @new_resource.shared_path
+ scm_provider.load_current_resource
+ @release_path = new_resource.deploy_to + "/releases/#{release_slug}"
+ @shared_path = new_resource.shared_path
end
def sudo(command,&block)
@@ -62,10 +82,10 @@ class Chef
def run(command, &block)
exec = execute(command, &block)
- exec.user(@new_resource.user) if @new_resource.user
- exec.group(@new_resource.group) if @new_resource.group
+ exec.user(new_resource.user) if new_resource.user
+ exec.group(new_resource.group) if new_resource.group
exec.cwd(release_path) unless exec.cwd
- exec.environment(@new_resource.environment) unless exec.environment
+ exec.environment(new_resource.environment) unless exec.environment
converge_by("execute #{command}") do
exec
end
@@ -78,8 +98,8 @@ class Chef
#There is no reason to assume 2 deployments in a single chef run, hence fails in whyrun.
end
- [ @new_resource.before_migrate, @new_resource.before_symlink,
- @new_resource.before_restart, @new_resource.after_restart ].each do |script|
+ [ new_resource.before_migrate, new_resource.before_symlink,
+ new_resource.before_restart, new_resource.after_restart ].each do |script|
requirements.assert(:deploy, :force_deploy) do |a|
callback_file = "#{release_path}/#{script}"
a.assertion do
@@ -100,7 +120,7 @@ class Chef
save_release_state
if deployed?(release_path )
if current_release?(release_path )
- Chef::Log.debug("#{@new_resource} is the latest version")
+ Chef::Log.debug("#{new_resource} is the latest version")
else
rollback_to release_path
end
@@ -117,7 +137,7 @@ class Chef
converge_by("delete deployed app at #{release_path} prior to force-deploy") do
Chef::Log.info("Already deployed app at #{release_path}, forcing.")
FileUtils.rm_rf(release_path)
- Chef::Log.info("#{@new_resource} forcing deploy of already deployed app at #{release_path}")
+ Chef::Log.info("#{new_resource} forcing deploy of already deployed app at #{release_path}")
end
end
@@ -143,7 +163,7 @@ class Chef
releases_to_nuke.each do |i|
converge_by("roll back by removing release #{i}") do
- Chef::Log.info "#{@new_resource} removing release: #{i}"
+ Chef::Log.info "#{new_resource} removing release: #{i}"
FileUtils.rm_rf i
end
release_deleted(i)
@@ -157,21 +177,21 @@ class Chef
copy_cached_repo
install_gems
enforce_ownership
- callback(:before_migrate, @new_resource.before_migrate)
+ callback(:before_migrate, new_resource.before_migrate)
migrate
- callback(:before_symlink, @new_resource.before_symlink)
+ callback(:before_symlink, new_resource.before_symlink)
symlink
- callback(:before_restart, @new_resource.before_restart)
+ callback(:before_restart, new_resource.before_restart)
restart
- callback(:after_restart, @new_resource.after_restart)
+ callback(:after_restart, new_resource.after_restart)
cleanup!
- Chef::Log.info "#{@new_resource} deployed to #{@new_resource.deploy_to}"
+ Chef::Log.info "#{new_resource} deployed to #{new_resource.deploy_to}"
end
def rollback
- Chef::Log.info "#{@new_resource} rolling back to previous release #{release_path}"
+ Chef::Log.info "#{new_resource} rolling back to previous release #{release_path}"
symlink
- Chef::Log.info "#{@new_resource} restarting with previous release"
+ Chef::Log.info "#{new_resource} restarting with previous release"
restart
end
@@ -179,7 +199,7 @@ class Chef
@collection = Chef::ResourceCollection.new
case callback_code
when Proc
- Chef::Log.info "#{@new_resource} running callback #{what}"
+ Chef::Log.info "#{new_resource} running callback #{what}"
recipe_eval(&callback_code)
when String
run_callback_from_file("#{release_path}/#{callback_code}")
@@ -191,17 +211,17 @@ class Chef
def migrate
run_symlinks_before_migrate
- if @new_resource.migrate
+ if new_resource.migrate
enforce_ownership
- environment = @new_resource.environment
+ environment = new_resource.environment
env_info = environment && environment.map do |key_and_val|
"#{key_and_val.first}='#{key_and_val.last}'"
end.join(" ")
- converge_by("execute migration command #{@new_resource.migration_command}") do
- Chef::Log.info "#{@new_resource} migrating #{@new_resource.user} with environment #{env_info}"
- run_command(run_options(:command => @new_resource.migration_command, :cwd=>release_path, :log_level => :info))
+ converge_by("execute migration command #{new_resource.migration_command}") do
+ Chef::Log.info "#{new_resource} migrating #{new_resource.user} with environment #{env_info}"
+ run_command(run_options(:command => new_resource.migration_command, :cwd=>release_path, :log_level => :info))
end
end
end
@@ -210,18 +230,18 @@ class Chef
purge_tempfiles_from_current_release
link_tempfiles_to_current_release
link_current_release_to_production
- Chef::Log.info "#{@new_resource} updated symlinks"
+ Chef::Log.info "#{new_resource} updated symlinks"
end
def restart
- if restart_cmd = @new_resource.restart_command
+ if restart_cmd = new_resource.restart_command
if restart_cmd.kind_of?(Proc)
- Chef::Log.info("#{@new_resource} restarting app with embedded recipe")
+ Chef::Log.info("#{new_resource} restarting app with embedded recipe")
recipe_eval(&restart_cmd)
else
- converge_by("restart app using command #{@new_resource.restart_command}") do
- Chef::Log.info("#{@new_resource} restarting app")
- run_command(run_options(:command => @new_resource.restart_command, :cwd => @new_resource.current_path))
+ converge_by("restart app using command #{new_resource.restart_command}") do
+ Chef::Log.info("#{new_resource} restarting app")
+ run_command(run_options(:command => new_resource.restart_command, :cwd => new_resource.current_path))
end
end
end
@@ -232,10 +252,10 @@ class Chef
release_created(release_path)
end
- chop = -1 - @new_resource.keep_releases
+ chop = -1 - new_resource.keep_releases
all_releases[0..chop].each do |old_release|
converge_by("remove old release #{old_release}") do
- Chef::Log.info "#{@new_resource} removing old release #{old_release}"
+ Chef::Log.info "#{new_resource} removing old release #{old_release}"
FileUtils.rm_rf(old_release)
end
release_deleted(old_release)
@@ -243,11 +263,11 @@ class Chef
end
def all_releases
- Dir.glob(Chef::Util::PathHelper.escape_glob(@new_resource.deploy_to) + "/releases/*").sort
+ Dir.glob(Chef::Util::PathHelper.escape_glob(new_resource.deploy_to) + "/releases/*").sort
end
def update_cached_repo
- if @new_resource.svn_force_export
+ if new_resource.svn_force_export
# TODO assertion, non-recoverable - @scm_provider must be svn if force_export?
svn_force_export
else
@@ -256,95 +276,92 @@ class Chef
end
def run_scm_sync
- @scm_provider.run_action(:sync)
+ scm_provider.run_action(:sync)
end
def svn_force_export
- Chef::Log.info "#{@new_resource} exporting source repository"
- @scm_provider.run_action(:force_export)
+ Chef::Log.info "#{new_resource} exporting source repository"
+ scm_provider.run_action(:force_export)
end
def copy_cached_repo
- target_dir_path = @new_resource.deploy_to + "/releases"
+ target_dir_path = new_resource.deploy_to + "/releases"
converge_by("deploy from repo to #{target_dir_path} ") do
FileUtils.rm_rf(release_path) if ::File.exist?(release_path)
FileUtils.mkdir_p(target_dir_path)
- FileUtils.cp_r(::File.join(@new_resource.destination, "."), release_path, :preserve => true)
- Chef::Log.info "#{@new_resource} copied the cached checkout to #{release_path}"
+ FileUtils.cp_r(::File.join(new_resource.destination, "."), release_path, :preserve => true)
+ Chef::Log.info "#{new_resource} copied the cached checkout to #{release_path}"
end
end
def enforce_ownership
- converge_by("force ownership of #{@new_resource.deploy_to} to #{@new_resource.group}:#{@new_resource.user}") do
- FileUtils.chown_R(@new_resource.user, @new_resource.group, @new_resource.deploy_to)
- Chef::Log.info("#{@new_resource} set user to #{@new_resource.user}") if @new_resource.user
- Chef::Log.info("#{@new_resource} set group to #{@new_resource.group}") if @new_resource.group
+ converge_by("force ownership of #{new_resource.deploy_to} to #{new_resource.group}:#{new_resource.user}") do
+ FileUtils.chown_R(new_resource.user, new_resource.group, new_resource.deploy_to)
+ Chef::Log.info("#{new_resource} set user to #{new_resource.user}") if new_resource.user
+ Chef::Log.info("#{new_resource} set group to #{new_resource.group}") if new_resource.group
end
end
def verify_directories_exist
- create_dir_unless_exists(@new_resource.deploy_to)
- create_dir_unless_exists(@new_resource.shared_path)
+ create_dir_unless_exists(new_resource.deploy_to)
+ create_dir_unless_exists(new_resource.shared_path)
end
def link_current_release_to_production
- converge_by(["remove existing link at #{@new_resource.current_path}",
- "link release #{release_path} into production at #{@new_resource.current_path}"]) do
- FileUtils.rm_f(@new_resource.current_path)
+ converge_by(["remove existing link at #{new_resource.current_path}",
+ "link release #{release_path} into production at #{new_resource.current_path}"]) do
+ FileUtils.rm_f(new_resource.current_path)
begin
- FileUtils.ln_sf(release_path, @new_resource.current_path)
+ FileUtils.ln_sf(release_path, new_resource.current_path)
rescue => e
raise Chef::Exceptions::FileNotFound.new("Cannot symlink current release to production: #{e.message}")
end
- Chef::Log.info "#{@new_resource} linked release #{release_path} into production at #{@new_resource.current_path}"
+ Chef::Log.info "#{new_resource} linked release #{release_path} into production at #{new_resource.current_path}"
end
enforce_ownership
end
def run_symlinks_before_migrate
- links_info = @new_resource.symlink_before_migrate.map { |src, dst| "#{src} => #{dst}" }.join(", ")
+ links_info = symlink_before_migrate.map { |src, dst| "#{src} => #{dst}" }.join(", ")
converge_by("make pre-migration symlinks: #{links_info}") do
- @new_resource.symlink_before_migrate.each do |src, dest|
+ symlink_before_migrate.each do |src, dest|
begin
- FileUtils.ln_sf(@new_resource.shared_path + "/#{src}", release_path + "/#{dest}")
+ FileUtils.ln_sf(new_resource.shared_path + "/#{src}", release_path + "/#{dest}")
rescue => e
- raise Chef::Exceptions::FileNotFound.new("Cannot symlink #{@new_resource.shared_path}/#{src} to #{release_path}/#{dest} before migrate: #{e.message}")
+ raise Chef::Exceptions::FileNotFound.new("Cannot symlink #{new_resource.shared_path}/#{src} to #{release_path}/#{dest} before migrate: #{e.message}")
end
end
- Chef::Log.info "#{@new_resource} made pre-migration symlinks"
+ Chef::Log.info "#{new_resource} made pre-migration symlinks"
end
end
def link_tempfiles_to_current_release
- dirs_info = @new_resource.create_dirs_before_symlink.join(",")
- @new_resource.create_dirs_before_symlink.each do |dir|
+ dirs_info = create_dirs_before_symlink.join(",")
+ create_dirs_before_symlink.each do |dir|
create_dir_unless_exists(release_path + "/#{dir}")
end
- Chef::Log.info("#{@new_resource} created directories before symlinking: #{dirs_info}")
+ Chef::Log.info("#{new_resource} created directories before symlinking: #{dirs_info}")
- links_info = @new_resource.symlinks.map { |src, dst| "#{src} => #{dst}" }.join(", ")
+ links_info = symlinks.map { |src, dst| "#{src} => #{dst}" }.join(", ")
converge_by("link shared paths into current release: #{links_info}") do
- @new_resource.symlinks.each do |src, dest|
+ symlinks.each do |src, dest|
begin
- FileUtils.ln_sf(::File.join(@new_resource.shared_path, src), ::File.join(release_path, dest))
+ FileUtils.ln_sf(::File.join(new_resource.shared_path, src), ::File.join(release_path, dest))
rescue => e
- raise Chef::Exceptions::FileNotFound.new("Cannot symlink shared data #{::File.join(@new_resource.shared_path, src)} to #{::File.join(release_path, dest)}: #{e.message}")
+ raise Chef::Exceptions::FileNotFound.new("Cannot symlink shared data #{::File.join(new_resource.shared_path, src)} to #{::File.join(release_path, dest)}: #{e.message}")
end
end
- Chef::Log.info("#{@new_resource} linked shared paths into current release: #{links_info}")
+ Chef::Log.info("#{new_resource} linked shared paths into current release: #{links_info}")
end
run_symlinks_before_migrate
enforce_ownership
end
- def create_dirs_before_symlink
- end
-
def purge_tempfiles_from_current_release
- log_info = @new_resource.purge_before_symlink.join(", ")
+ log_info = purge_before_symlink.join(", ")
converge_by("purge directories in checkout #{log_info}") do
- @new_resource.purge_before_symlink.each { |dir| FileUtils.rm_rf(release_path + "/#{dir}") }
- Chef::Log.info("#{@new_resource} purged directories in checkout #{log_info}")
+ purge_before_symlink.each { |dir| FileUtils.rm_rf(release_path + "/#{dir}") }
+ Chef::Log.info("#{new_resource} purged directories in checkout #{log_info}")
end
end
@@ -394,10 +411,10 @@ class Chef
end
def run_options(run_opts={})
- run_opts[:user] = @new_resource.user if @new_resource.user
- run_opts[:group] = @new_resource.group if @new_resource.group
- run_opts[:environment] = @new_resource.environment if @new_resource.environment
- run_opts[:log_tag] = @new_resource.to_s
+ run_opts[:user] = new_resource.user if new_resource.user
+ run_opts[:group] = new_resource.group if new_resource.group
+ run_opts[:environment] = new_resource.environment if new_resource.environment
+ run_opts[:log_tag] = new_resource.to_s
run_opts[:log_level] ||= :debug
if run_opts[:log_level] == :info
if STDOUT.tty? && !Chef::Config[:daemon] && Chef::Log.info?
@@ -408,7 +425,7 @@ class Chef
end
def run_callback_from_file(callback_file)
- Chef::Log.info "#{@new_resource} queueing checkdeploy hook #{callback_file}"
+ Chef::Log.info "#{new_resource} queueing checkdeploy hook #{callback_file}"
recipe_eval do
Dir.chdir(release_path) do
from_file(callback_file) if ::File.exist?(callback_file)
@@ -418,20 +435,20 @@ class Chef
def create_dir_unless_exists(dir)
if ::File.directory?(dir)
- Chef::Log.debug "#{@new_resource} not creating #{dir} because it already exists"
+ Chef::Log.debug "#{new_resource} not creating #{dir} because it already exists"
return false
end
converge_by("create new directory #{dir}") do
begin
FileUtils.mkdir_p(dir)
- Chef::Log.debug "#{@new_resource} created directory #{dir}"
- if @new_resource.user
- FileUtils.chown(@new_resource.user, nil, dir)
- Chef::Log.debug("#{@new_resource} set user to #{@new_resource.user} for #{dir}")
+ Chef::Log.debug "#{new_resource} created directory #{dir}"
+ if new_resource.user
+ FileUtils.chown(new_resource.user, nil, dir)
+ Chef::Log.debug("#{new_resource} set user to #{new_resource.user} for #{dir}")
end
- if @new_resource.group
- FileUtils.chown(nil, @new_resource.group, dir)
- Chef::Log.debug("#{@new_resource} set group to #{@new_resource.group} for #{dir}")
+ if new_resource.group
+ FileUtils.chown(nil, new_resource.group, dir)
+ Chef::Log.debug("#{new_resource} set group to #{new_resource.group} for #{dir}")
end
rescue => e
raise Chef::Exceptions::FileNotFound.new("Cannot create directory #{dir}: #{e.message}")
@@ -442,7 +459,7 @@ class Chef
def with_rollback_on_error
yield
rescue ::Exception => e
- if @new_resource.rollback_on_error
+ if new_resource.rollback_on_error
Chef::Log.warn "Error on deploying #{release_path}: #{e.message}"
failed_release = release_path
@@ -461,8 +478,8 @@ class Chef
end
def save_release_state
- if ::File.exists?(@new_resource.current_path)
- release = ::File.readlink(@new_resource.current_path)
+ if ::File.exists?(new_resource.current_path)
+ release = ::File.readlink(new_resource.current_path)
@previous_release_path = release if ::File.exists?(release)
end
end
diff --git a/lib/chef/provider/dsc_resource.rb b/lib/chef/provider/dsc_resource.rb
new file mode 100644
index 0000000000..fabb695803
--- /dev/null
+++ b/lib/chef/provider/dsc_resource.rb
@@ -0,0 +1,157 @@
+#
+# Author:: Adam Edwards (<adamed@getchef.com>)
+#
+# Copyright:: 2014, Chef Software, Inc.
+#
+# 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/util/powershell/cmdlet'
+require 'chef/util/dsc/local_configuration_manager'
+require 'chef/mixin/powershell_type_coercions'
+require 'chef/util/dsc/resource_store'
+
+class Chef
+ class Provider
+ class DscResource < Chef::Provider
+ include Chef::Mixin::PowershellTypeCoercions
+
+ provides :dsc_resource, os: "windows"
+
+ def initialize(new_resource, run_context)
+ super
+ @new_resource = new_resource
+ @module_name = new_resource.module_name
+ end
+
+ def action_run
+ if ! test_resource
+ converge_by(generate_description) do
+ result = set_resource
+ end
+ end
+ end
+
+ def load_current_resource
+ end
+
+ def whyrun_supported?
+ true
+ end
+
+ def define_resource_requirements
+ requirements.assert(:run) do |a|
+ a.assertion { supports_dsc_invoke_resource? }
+ err = ["You must have Powershell version >= 5.0.10018.0 to use dsc_resource."]
+ a.failure_message Chef::Exceptions::NoProviderAvailable,
+ err
+ a.whyrun err + ["Assuming a previous resource installs Powershell 5.0.10018.0 or higher."]
+ a.block_action!
+ end
+ requirements.assert(:run) do |a|
+ a.assertion {
+ meta_configuration['RefreshMode'] == 'Disabled'
+ }
+ err = ["The LCM must have its RefreshMode set to Disabled. "]
+ a.failure_message Chef::Exceptions::NoProviderAvailable, err.join(' ')
+ a.whyrun err + ["Assuming a previous resource sets the RefreshMode."]
+ a.block_action!
+ end
+ end
+
+ protected
+
+ def local_configuration_manager
+ @local_configuration_manager ||= Chef::Util::DSC::LocalConfigurationManager.new(
+ node,
+ nil
+ )
+ end
+
+ def resource_store
+ Chef::Util::DSC::ResourceStore.instance
+ end
+
+ def supports_dsc_invoke_resource?
+ run_context && Chef::Platform.supports_dsc_invoke_resource?(node)
+ end
+
+ def generate_description
+ @converge_description
+ end
+
+ def dsc_resource_name
+ new_resource.resource.to_s
+ end
+
+ def module_name
+ @module_name ||= begin
+ found = resource_store.find(dsc_resource_name)
+
+ r = case found.length
+ when 0
+ raise Chef::Exceptions::ResourceNotFound,
+ "Could not find #{dsc_resource_name}. Check to make "\
+ "sure that it shows up when running Get-DscResource"
+ when 1
+ if found[0]['Module'].nil?
+ :none
+ else
+ found[0]['Module']
+ end
+ else
+ raise Chef::Exceptions::MultipleDscResourcesFound, found
+ end
+ end
+ end
+
+ def test_resource
+ result = invoke_resource(:test)
+ # We really want this information from the verbose stream,
+ # however Invoke-DscResource is not correctly writing to that
+ # stream and instead just dumping to stdout
+ @converge_description = result.stdout
+ result.return_value[0]["InDesiredState"]
+ end
+
+ def set_resource
+ result = invoke_resource(:set)
+ result.return_value
+ end
+
+ def invoke_resource(method, output_format=:object)
+ properties = translate_type(@new_resource.properties)
+ switches = "-Method #{method.to_s} -Name #{@new_resource.resource}"\
+ " -Property #{properties} -Verbose"
+
+ if module_name != :none
+ switches += " -Module #{module_name}"
+ end
+
+ cmdlet = Chef::Util::Powershell::Cmdlet.new(
+ node,
+ "Invoke-DscResource #{switches}",
+ output_format
+ )
+ cmdlet.run!
+ end
+
+ def meta_configuration
+ cmdlet = Chef::Util::Powershell::Cmdlet.new(node, "Get-DscLocalConfigurationManager", :object)
+ result = cmdlet.run!
+ result.return_value
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/provider/env.rb b/lib/chef/provider/env.rb
index 815a19bc0c..cf75ff7d85 100644
--- a/lib/chef/provider/env.rb
+++ b/lib/chef/provider/env.rb
@@ -26,6 +26,8 @@ class Chef
include Chef::Mixin::Command
attr_accessor :key_exists
+ provides :env, os: "!windows"
+
def initialize(new_resource, run_context)
super
@key_exists = true
diff --git a/lib/chef/provider/env/windows.rb b/lib/chef/provider/env/windows.rb
index dd7cb1bc46..56cebdb888 100644
--- a/lib/chef/provider/env/windows.rb
+++ b/lib/chef/provider/env/windows.rb
@@ -24,6 +24,8 @@ class Chef
class Windows < Chef::Provider::Env
include Chef::Mixin::WindowsEnvHelper
+ provides :env, os: "windows"
+
def create_env
obj = env_obj(@new_resource.key_name)
unless obj
diff --git a/lib/chef/provider/git.rb b/lib/chef/provider/git.rb
index 8418f22933..693dd3f2b2 100644
--- a/lib/chef/provider/git.rb
+++ b/lib/chef/provider/git.rb
@@ -39,6 +39,10 @@ class Chef
end
end
+ def additional_remotes
+ new_resource.additional_remotes || {}
+ end
+
def define_resource_requirements
# Parent directory of the target must exist.
requirements.assert(:checkout, :sync) do |a|
diff --git a/lib/chef/provider/group.rb b/lib/chef/provider/group.rb
index 29738cc046..a802758dce 100644
--- a/lib/chef/provider/group.rb
+++ b/lib/chef/provider/group.rb
@@ -125,13 +125,13 @@ class Chef
def action_create
case @group_exists
when false
- converge_by("create #{@new_resource}") do
+ converge_by("create #{@new_resource.group_name}") do
create_group
Chef::Log.info("#{@new_resource} created")
end
else
if compare_group
- converge_by(["alter group #{@new_resource}"] + change_desc) do
+ converge_by(["alter group #{@new_resource.group_name}"] + change_desc) do
manage_group
Chef::Log.info("#{@new_resource} altered")
end
@@ -141,7 +141,7 @@ class Chef
def action_remove
if @group_exists
- converge_by("remove group #{@new_resource}") do
+ converge_by("remove group #{@new_resource.group_name}") do
remove_group
Chef::Log.info("#{@new_resource} removed")
end
@@ -150,7 +150,7 @@ class Chef
def action_manage
if @group_exists && compare_group
- converge_by(["manage group #{@new_resource}"] + change_desc) do
+ converge_by(["manage group #{@new_resource.group_name}"] + change_desc) do
manage_group
Chef::Log.info("#{@new_resource} managed")
end
@@ -159,7 +159,7 @@ class Chef
def action_modify
if compare_group
- converge_by(["modify group #{@new_resource}"] + change_desc) do
+ converge_by(["modify group #{@new_resource.group_name}"] + change_desc) do
manage_group
Chef::Log.info("#{@new_resource} modified")
end
diff --git a/lib/chef/provider/group/dscl.rb b/lib/chef/provider/group/dscl.rb
index a59a94aa98..d7e8f2e827 100644
--- a/lib/chef/provider/group/dscl.rb
+++ b/lib/chef/provider/group/dscl.rb
@@ -21,6 +21,8 @@ class Chef
class Group
class Dscl < Chef::Provider::Group
+ provides :group, os: "darwin"
+
def dscl(*args)
host = "."
stdout_result = ""; stderr_result = ""; cmd = "dscl #{host} -#{args.join(' ')}"
diff --git a/lib/chef/provider/group/groupmod.rb b/lib/chef/provider/group/groupmod.rb
index 7ad762af8d..f9299546c8 100644
--- a/lib/chef/provider/group/groupmod.rb
+++ b/lib/chef/provider/group/groupmod.rb
@@ -21,6 +21,8 @@ class Chef
class Group
class Groupmod < Chef::Provider::Group
+ provides :group, os: "netbsd"
+
def load_current_resource
super
[ "group", "user" ].each do |binary|
diff --git a/lib/chef/provider/group/usermod.rb b/lib/chef/provider/group/usermod.rb
index e9dcc38b43..e50e13c443 100644
--- a/lib/chef/provider/group/usermod.rb
+++ b/lib/chef/provider/group/usermod.rb
@@ -23,6 +23,8 @@ class Chef
class Group
class Usermod < Chef::Provider::Group::Groupadd
+ provides :group, os: "openbsd"
+
def load_current_resource
super
end
diff --git a/lib/chef/provider/group/windows.rb b/lib/chef/provider/group/windows.rb
index c9c3da29e0..54e49b0e06 100644
--- a/lib/chef/provider/group/windows.rb
+++ b/lib/chef/provider/group/windows.rb
@@ -26,6 +26,8 @@ class Chef
class Group
class Windows < Chef::Provider::Group
+ provides :group, os: "windows"
+
def initialize(new_resource,run_context)
super
@net_group = Chef::Util::Windows::NetGroup.new(@new_resource.group_name)
diff --git a/lib/chef/provider/mdadm.rb b/lib/chef/provider/mdadm.rb
index d156e49d48..325f1b5977 100644
--- a/lib/chef/provider/mdadm.rb
+++ b/lib/chef/provider/mdadm.rb
@@ -23,6 +23,8 @@ class Chef
class Provider
class Mdadm < Chef::Provider
+ provides :mdadm
+
def popen4
raise Exception, "deprecated"
end
diff --git a/lib/chef/provider/mount/windows.rb b/lib/chef/provider/mount/windows.rb
index 02aa78919a..87873474b3 100644
--- a/lib/chef/provider/mount/windows.rb
+++ b/lib/chef/provider/mount/windows.rb
@@ -27,6 +27,8 @@ class Chef
class Mount
class Windows < Chef::Provider::Mount
+ provides :mount, os: "windows"
+
def is_volume(name)
name =~ /^\\\\\?\\Volume\{[\w-]+\}\\$/ ? true : false
end
diff --git a/lib/chef/provider/package/homebrew.rb b/lib/chef/provider/package/homebrew.rb
index e043c01f56..603899646f 100644
--- a/lib/chef/provider/package/homebrew.rb
+++ b/lib/chef/provider/package/homebrew.rb
@@ -27,7 +27,7 @@ class Chef
class Homebrew < Chef::Provider::Package
provides :homebrew_package
- provides :package, os: ["mac_os_x", "darwin"]
+ provides :package, os: "darwin"
include Chef::Mixin::HomebrewUser
diff --git a/lib/chef/provider/package/openbsd.rb b/lib/chef/provider/package/openbsd.rb
index f0931d7555..82048c3bd4 100644
--- a/lib/chef/provider/package/openbsd.rb
+++ b/lib/chef/provider/package/openbsd.rb
@@ -24,6 +24,7 @@ require 'chef/resource/package'
require 'chef/provider/package'
require 'chef/mixin/shell_out'
require 'chef/mixin/get_source_from_package'
+require 'chef/exceptions'
class Chef
class Provider
@@ -37,25 +38,42 @@ class Chef
def initialize(*args)
super
- @current_resource = Chef::Resource::Package.new(@new_resource.name)
- @new_resource.source(pkg_path) if !@new_resource.source
+ @current_resource = Chef::Resource::Package.new(new_resource.name)
end
def load_current_resource
- @current_resource.package_name(@new_resource.package_name)
+ @current_resource.package_name(new_resource.package_name)
@current_resource.version(installed_version)
@current_resource
end
+ def define_resource_requirements
+ super
+
+ # Below are incomplete/missing features for this package provider
+ requirements.assert(:all_actions) do |a|
+ a.assertion { !new_resource.source }
+ a.failure_message(Chef::Exceptions::Package, 'The openbsd package provider does not support the source attribute')
+ end
+ requirements.assert(:all_actions) do |a|
+ a.assertion do
+ if new_resource.package_name =~ /^(.+?)--(.+)/
+ !new_resource.version
+ else
+ true
+ end
+ end
+ a.failure_message(Chef::Exceptions::Package, 'The openbsd package provider does not support providing a version and flavor')
+ end
+ end
+
def install_package(name, version)
unless @current_resource.version
- version_string = ''
- version_string += "-#{version}" if version
if parts = name.match(/^(.+?)--(.+)/) # use double-dash for stems with flavors, see man page for pkg_add
name = parts[1]
end
- shell_out!("pkg_add -r #{name}#{version_string}", :env => {"PKG_PATH" => @new_resource.source}).status
- Chef::Log.debug("#{@new_resource} installed from: #{@new_resource.source}")
+ shell_out!("pkg_add -r #{name}#{version_string}", :env => {"PKG_PATH" => pkg_path}).status
+ Chef::Log.debug("#{new_resource.package_name} installed")
end
end
@@ -71,32 +89,45 @@ class Chef
private
def installed_version
- if parts = @new_resource.package_name.match(/^(.+?)--(.+)/)
+ if parts = new_resource.package_name.match(/^(.+?)--(.+)/)
name = parts[1]
else
- name = @new_resource.package_name
+ name = new_resource.package_name
end
pkg_info = shell_out!("pkg_info -e \"#{name}->0\"", :env => nil, :returns => [0,1])
result = pkg_info.stdout[/^inst:#{Regexp.escape(name)}-(.+?)\s/, 1]
- Chef::Log.debug("installed_version of '#{@new_resource.package_name}' is '#{result}'")
+ Chef::Log.debug("installed_version of '#{new_resource.package_name}' is '#{result}'")
result
end
def candidate_version
@candidate_version ||= begin
- version_string = ''
- version_string += "-#{version}" if @new_resource.version
- pkg_info = shell_out!("pkg_info -I \"#{@new_resource.package_name}#{version_string}\"", :env => nil, :returns => [0,1])
- if parts = @new_resource.package_name.match(/^(.+?)--(.+)/)
- result = pkg_info.stdout[/^#{Regexp.escape(parts[1])}-(.+?)\s/, 1]
+ results = []
+ shell_out!("pkg_info -I \"#{new_resource.package_name}#{version_string}\"", :env => nil, :returns => [0,1]).stdout.each_line do |line|
+ if parts = new_resource.package_name.match(/^(.+?)--(.+)/)
+ results << line[/^#{Regexp.escape(parts[1])}-(.+?)\s/, 1]
+ else
+ results << line[/^#{Regexp.escape(new_resource.package_name)}-(.+?)\s/, 1]
+ end
+ end
+ results = results.reject(&:nil?)
+ Chef::Log.debug("candidate versions of '#{new_resource.package_name}' are '#{results}'")
+ case results.length
+ when 0
+ []
+ when 1
+ results[0]
else
- result = pkg_info.stdout[/^#{Regexp.escape(@new_resource.package_name)}-(.+?)\s/, 1]
+ raise Chef::Exceptions::Package, "#{new_resource.name} has multiple matching candidates. Please use a more specific name" if results.length > 1
end
- Chef::Log.debug("candidate_version of '#{@new_resource.package_name}' is '#{result}'")
- result
end
end
+ def version_string
+ ver = ''
+ ver += "-#{new_resource.version}" if new_resource.version
+ end
+
def pkg_path
ENV['PKG_PATH'] || "http://ftp.OpenBSD.org/pub/#{node.kernel.name}/#{node.kernel.release}/packages/#{node.kernel.machine}/"
end
diff --git a/lib/chef/provider/powershell_script.rb b/lib/chef/provider/powershell_script.rb
index 8c79b384e9..f9dcd6d80c 100644
--- a/lib/chef/provider/powershell_script.rb
+++ b/lib/chef/provider/powershell_script.rb
@@ -22,6 +22,8 @@ class Chef
class Provider
class PowershellScript < Chef::Provider::WindowsScript
+ provides :powershell_script, os: "windows"
+
protected
EXIT_STATUS_EXCEPTION_HANDLER = "\ntrap [Exception] {write-error -exception ($_.Exception.Message);exit 1}".freeze
EXIT_STATUS_NORMALIZATION_SCRIPT = "\nif ($? -ne $true) { if ( $LASTEXITCODE ) {exit $LASTEXITCODE} else { exit 1 }}".freeze
diff --git a/lib/chef/provider/service/macosx.rb b/lib/chef/provider/service/macosx.rb
index 10ad1aa29d..df5be54fda 100644
--- a/lib/chef/provider/service/macosx.rb
+++ b/lib/chef/provider/service/macosx.rb
@@ -33,8 +33,7 @@ class Chef
/Library/LaunchDaemons
/System/Library/LaunchAgents
/System/Library/LaunchDaemons }
-
- locations << "#{ENV['HOME']}/Library/LaunchAgents" if ENV['HOME']
+ Chef::Util::PathHelper.home('Library', 'LaunchAgents') { |p| locations << p }
locations
end
diff --git a/lib/chef/provider/user/dscl.rb b/lib/chef/provider/user/dscl.rb
index debf36f771..0c0c85e18b 100644
--- a/lib/chef/provider/user/dscl.rb
+++ b/lib/chef/provider/user/dscl.rb
@@ -44,6 +44,8 @@ class Chef
# This provider only supports Mac OSX versions 10.7 and above
class Dscl < Chef::Provider::User
+ provides :user, os: "darwin"
+
def define_resource_requirements
super
@@ -650,7 +652,11 @@ user password using shadow hash.")
def run_plutil(*args)
result = shell_out("plutil -#{args.join(' ')}")
raise(Chef::Exceptions::PlistUtilCommandFailed,"plutil error: #{result.inspect}") unless result.exitstatus == 0
- result.stdout
+ if result.stdout.encoding == Encoding::ASCII_8BIT
+ result.stdout.encode("utf-8", "binary", :undef => :replace, :invalid => :replace, :replace => '?')
+ else
+ result.stdout
+ end
end
def convert_binary_plist_to_xml(binary_plist_string)
diff --git a/lib/chef/provider/user/windows.rb b/lib/chef/provider/user/windows.rb
index 66ef30c349..e282a11d45 100644
--- a/lib/chef/provider/user/windows.rb
+++ b/lib/chef/provider/user/windows.rb
@@ -27,6 +27,8 @@ class Chef
class User
class Windows < Chef::Provider::User
+ provides :user, os: "windows"
+
def initialize(new_resource,run_context)
super
@net_user = Chef::Util::Windows::NetUser.new(@new_resource.username)
diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb
index 796a0f8fa6..a5f5386de3 100644
--- a/lib/chef/providers.rb
+++ b/lib/chef/providers.rb
@@ -25,6 +25,7 @@ require 'chef/provider/cron/aix'
require 'chef/provider/deploy'
require 'chef/provider/directory'
require 'chef/provider/dsc_script'
+require 'chef/provider/dsc_resource'
require 'chef/provider/env'
require 'chef/provider/erl_call'
require 'chef/provider/execute'
diff --git a/lib/chef/recipe.rb b/lib/chef/recipe.rb
index 91f7f30aa9..b4d37c2d61 100644
--- a/lib/chef/recipe.rb
+++ b/lib/chef/recipe.rb
@@ -25,6 +25,7 @@ require 'chef/dsl/include_recipe'
require 'chef/dsl/registry_helper'
require 'chef/dsl/reboot_pending'
require 'chef/dsl/audit'
+require 'chef/dsl/powershell'
require 'chef/mixin/from_file'
@@ -42,6 +43,7 @@ class Chef
include Chef::DSL::RegistryHelper
include Chef::DSL::RebootPending
include Chef::DSL::Audit
+ include Chef::DSL::Powershell
include Chef::Mixin::FromFile
include Chef::Mixin::Deprecation
diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb
index ea220b6c70..2df73a52e8 100644
--- a/lib/chef/resource.rb
+++ b/lib/chef/resource.rb
@@ -997,6 +997,15 @@ class Chef
end
#
+ # DSL method used to define attribute on a resource wrapping params_validate
+ #
+ def self.attribute(attr_name, validation_opts={})
+ define_method(attr_name) do |arg=NULL_ARG|
+ nillable_set_or_return(attr_name.to_sym, arg, validation_opts)
+ end
+ end
+
+ #
# The cookbook in which this Resource was defined (if any).
#
# @return Chef::CookbookVersion The cookbook in which this Resource was defined.
diff --git a/lib/chef/resource/batch.rb b/lib/chef/resource/batch.rb
index 576e6b4c6b..c091ec56b6 100644
--- a/lib/chef/resource/batch.rb
+++ b/lib/chef/resource/batch.rb
@@ -22,6 +22,8 @@ class Chef
class Resource
class Batch < Chef::Resource::WindowsScript
+ provides :batch, os: "windows"
+
def initialize(name, run_context=nil)
super(name, run_context, :batch, "cmd.exe")
end
diff --git a/lib/chef/resource/cron.rb b/lib/chef/resource/cron.rb
index 9c04658bf3..cb16506012 100644
--- a/lib/chef/resource/cron.rb
+++ b/lib/chef/resource/cron.rb
@@ -27,6 +27,8 @@ class Chef
state_attrs :minute, :hour, :day, :month, :weekday, :user
+ provides :cron
+
def initialize(name, run_context=nil)
super
@resource_name = :cron
@@ -138,7 +140,7 @@ class Chef
:kind_of => [String, Symbol]
)
end
-
+
def time(arg=nil)
set_or_return(
:time,
@@ -214,5 +216,3 @@ class Chef
end
end
end
-
-
diff --git a/lib/chef/resource/deploy.rb b/lib/chef/resource/deploy.rb
index 4252aa230f..fade82a972 100644
--- a/lib/chef/resource/deploy.rb
+++ b/lib/chef/resource/deploy.rb
@@ -63,6 +63,7 @@ class Chef
@deploy_to = name
@environment = nil
@repository_cache = 'cached-copy'
+ # XXX: if copy_exclude is a kind_of String why is initialized to an array???
@copy_exclude = []
@purge_before_symlink = %w{log tmp/pids public/system}
@create_dirs_before_symlink = %w{tmp public config}
@@ -78,10 +79,11 @@ class Chef
@scm_provider = Chef::Provider::Git
@svn_force_export = false
@allowed_actions.push(:force_deploy, :deploy, :rollback)
- @additional_remotes = Hash[]
+ @additional_remotes = {}
@keep_releases = 5
@enable_checkout = true
@checkout_branch = "deploy"
+ @timeout = nil
end
# where the checked out/cloned code goes
@@ -104,42 +106,18 @@ class Chef
end
# note: deploy_to is your application "meta-root."
- def deploy_to(arg=nil)
- set_or_return(
- :deploy_to,
- arg,
- :kind_of => [ String ]
- )
- end
+ attribute :deploy_to, :kind_of => [ String ]
- def repo(arg=nil)
- set_or_return(
- :repo,
- arg,
- :kind_of => [ String ]
- )
- end
+ attribute :repo, :kind_of => [ String ]
alias :repository :repo
- def remote(arg=nil)
- set_or_return(
- :remote,
- arg,
- :kind_of => [ String ]
- )
- end
+ attribute :remote, :kind_of => [ String ]
- def role(arg=nil)
- set_or_return(
- :role,
- arg,
- :kind_of => [ String ]
- )
- end
+ attribute :role, :kind_of => [ String ]
- def restart_command(arg=nil, &block)
- arg ||= block
- set_or_return(
+ def restart_command(arg=NULL_ARG, &block)
+ arg = block if block_given?
+ nillable_set_or_return(
:restart_command,
arg,
:kind_of => [ String, Proc ]
@@ -147,155 +125,60 @@ class Chef
end
alias :restart :restart_command
- def migrate(arg=nil)
- set_or_return(
- :migrate,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ attribute :migrate, :kind_of => [ TrueClass, FalseClass ]
- def migration_command(arg=nil)
- set_or_return(
- :migration_command,
- arg,
- :kind_of => [ String ]
- )
- end
+ attribute :migration_command, kind_of: String
- def rollback_on_error(arg=nil)
- set_or_return(
- :rollback_on_error,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ attribute :rollback_on_error, :kind_of => [ TrueClass, FalseClass ]
- def user(arg=nil)
- set_or_return(
- :user,
- arg,
- :kind_of => [ String ]
- )
- end
+ attribute :user, kind_of: String
- def group(arg=nil)
- set_or_return(
- :group,
- arg,
- :kind_of => [ String ]
- )
- end
+ attribute :group, kind_of: [ String ]
- def enable_submodules(arg=nil)
- set_or_return(
- :enable_submodules,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ attribute :enable_submodules, kind_of: [ TrueClass, FalseClass ]
- def shallow_clone(arg=nil)
- set_or_return(
- :shallow_clone,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ attribute :shallow_clone, kind_of: [ TrueClass, FalseClass ]
- def repository_cache(arg=nil)
- set_or_return(
- :repository_cache,
- arg,
- :kind_of => [ String ]
- )
- end
+ attribute :repository_cache, kind_of: String
- def copy_exclude(arg=nil)
- set_or_return(
- :copy_exclude,
- arg,
- :kind_of => [ String ]
- )
- end
+ attribute :copy_exclude, kind_of: String
- def revision(arg=nil)
- set_or_return(
- :revision,
- arg,
- :kind_of => [ String ]
- )
- end
+ attribute :revision, kind_of: String
alias :branch :revision
- def git_ssh_wrapper(arg=nil)
- set_or_return(
- :git_ssh_wrapper,
- arg,
- :kind_of => [ String ]
- )
- end
+ attribute :git_ssh_wrapper, kind_of: String
alias :ssh_wrapper :git_ssh_wrapper
- def svn_username(arg=nil)
- set_or_return(
- :svn_username,
- arg,
- :kind_of => [ String ]
- )
- end
+ attribute :svn_username, kind_of: String
- def svn_password(arg=nil)
- set_or_return(
- :svn_password,
- arg,
- :kind_of => [ String ]
- )
- end
+ attribute :svn_password, kind_of: String
- def svn_arguments(arg=nil)
- set_or_return(
- :svn_arguments,
- arg,
- :kind_of => [ String ]
- )
- end
+ attribute :svn_arguments, kind_of: String
- def svn_info_args(arg=nil)
- set_or_return(
- :svn_arguments,
- arg,
- :kind_of => [ String ])
- end
+ attribute :svn_info_args, kind_of: String
- def scm_provider(arg=nil)
+ def scm_provider(arg=NULL_ARG)
klass = if arg.kind_of?(String) || arg.kind_of?(Symbol)
lookup_provider_constant(arg)
else
arg
end
- set_or_return(
+ nillable_set_or_return(
:scm_provider,
klass,
:kind_of => [ Class ]
)
end
- def svn_force_export(arg=nil)
- set_or_return(
- :svn_force_export,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ attribute :svn_force_export, kind_of: [ TrueClass, FalseClass ]
- def environment(arg=nil)
+ def environment(arg=NULL_ARG)
if arg.is_a?(String)
Chef::Log.debug "Setting RAILS_ENV, RACK_ENV, and MERB_ENV to `#{arg}'"
Chef::Log.warn "[DEPRECATED] please modify your deploy recipe or attributes to set the environment using a hash"
arg = {"RAILS_ENV"=>arg,"MERB_ENV"=>arg,"RACK_ENV"=>arg}
end
- set_or_return(
+ nillable_set_or_return(
:environment,
arg,
:kind_of => [ Hash ]
@@ -303,8 +186,8 @@ class Chef
end
# The number of old release directories to keep around after cleanup
- def keep_releases(arg=nil)
- [set_or_return(
+ def keep_releases(arg=NULL_ARG)
+ [nillable_set_or_return(
:keep_releases,
arg,
:kind_of => [ Integer ]), 1].max
@@ -314,13 +197,7 @@ class Chef
# SCM clone/checkout before symlinking. Use this to get rid of files and
# directories you want to be shared between releases.
# Default: ["log", "tmp/pids", "public/system"]
- def purge_before_symlink(arg=nil)
- set_or_return(
- :purge_before_symlink,
- arg,
- :kind_of => Array
- )
- end
+ attribute :purge_before_symlink, kind_of: Array
# An array of paths, relative to your app's root, where you expect dirs to
# exist before symlinking. This runs after #purge_before_symlink, so you
@@ -330,13 +207,7 @@ class Chef
# then specify tmp here so that the tmp directory will exist when you
# symlink the pids directory in to the current release.
# Default: ["tmp", "public", "config"]
- def create_dirs_before_symlink(arg=nil)
- set_or_return(
- :create_dirs_before_symlink,
- arg,
- :kind_of => Array
- )
- end
+ attribute :create_dirs_before_symlink, kind_of: Array
# A Hash of shared/dir/path => release/dir/path. This attribute determines
# which files and dirs in the shared directory get symlinked to the current
@@ -344,13 +215,7 @@ class Chef
# $shared/pids that you would like to symlink as $current_release/tmp/pids
# you specify it as "pids" => "tmp/pids"
# Default {"system" => "public/system", "pids" => "tmp/pids", "log" => "log"}
- def symlinks(arg=nil)
- set_or_return(
- :symlinks,
- arg,
- :kind_of => Hash
- )
- end
+ attribute :symlinks, kind_of: Hash
# A Hash of shared/dir/path => release/dir/path. This attribute determines
# which files in the shared directory get symlinked to the current release
@@ -359,74 +224,44 @@ class Chef
# For a rails/merb app, this is used to link in a known good database.yml
# (with the production db password) before running migrate.
# Default {"config/database.yml" => "config/database.yml"}
- def symlink_before_migrate(arg=nil)
- set_or_return(
- :symlink_before_migrate,
- arg,
- :kind_of => Hash
- )
- end
+ attribute :symlink_before_migrate, kind_of: Hash
# Callback fires before migration is run.
- def before_migrate(arg=nil, &block)
- arg ||= block
- set_or_return(:before_migrate, arg, :kind_of => [Proc, String])
+ def before_migrate(arg=NULL_ARG, &block)
+ arg = block if block_given?
+ nillable_set_or_return(:before_migrate, arg, kind_of: [Proc, String])
end
# Callback fires before symlinking
- def before_symlink(arg=nil, &block)
- arg ||= block
- set_or_return(:before_symlink, arg, :kind_of => [Proc, String])
+ def before_symlink(arg=NULL_ARG, &block)
+ arg = block if block_given?
+ nillable_set_or_return(:before_symlink, arg, kind_of: [Proc, String])
end
# Callback fires before restart
- def before_restart(arg=nil, &block)
- arg ||= block
- set_or_return(:before_restart, arg, :kind_of => [Proc, String])
+ def before_restart(arg=NULL_ARG, &block)
+ arg = block if block_given?
+ nillable_set_or_return(:before_restart, arg, kind_of: [Proc, String])
end
# Callback fires after restart
- def after_restart(arg=nil, &block)
- arg ||= block
- set_or_return(:after_restart, arg, :kind_of => [Proc, String])
+ def after_restart(arg=NULL_ARG, &block)
+ arg = block if block_given?
+ nillable_set_or_return(:after_restart, arg, kind_of: [Proc, String])
end
- def additional_remotes(arg=nil)
- set_or_return(
- :additional_remotes,
- arg,
- :kind_of => Hash
- )
- end
+ attribute :additional_remotes, kind_of: Hash
- def enable_checkout(arg=nil)
- set_or_return(
- :enable_checkout,
- arg,
- :kind_of => [TrueClass, FalseClass]
- )
- end
+ attribute :enable_checkout, kind_of: [ TrueClass, FalseClass ]
- def checkout_branch(arg=nil)
- set_or_return(
- :checkout_branch,
- arg,
- :kind_of => String
- )
- end
+ attribute :checkout_branch, kind_of: String
# FIXME The Deploy resource may be passed to an SCM provider as its
# resource. The SCM provider knows that SCM resources can specify a
# timeout for SCM operations. The deploy resource must therefore support
# a timeout method, but the timeout it describes is for SCM operations,
# not the overall deployment. This is potentially confusing.
- def timeout(arg=nil)
- set_or_return(
- :timeout,
- arg,
- :kind_of => Integer
- )
- end
+ attribute :timeout, kind_of: Integer
end
end
diff --git a/lib/chef/resource/dsc_resource.rb b/lib/chef/resource/dsc_resource.rb
new file mode 100644
index 0000000000..912b683434
--- /dev/null
+++ b/lib/chef/resource/dsc_resource.rb
@@ -0,0 +1,83 @@
+#
+# Author:: Adam Edwards (<adamed@getchef.com>)
+#
+# Copyright:: 2014, Opscode, Inc.
+#
+# 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/dsl/powershell'
+
+class Chef
+ class Resource
+ class DscResource < Chef::Resource
+
+ provides :dsc_resource, os: "windows"
+
+ include Chef::DSL::Powershell
+
+ def initialize(name, run_context)
+ super
+ @properties = {}
+ @resource_name = :dsc_resource
+ @resource = nil
+ @allowed_actions.push(:run)
+ @action = :run
+ end
+
+ def resource(value=nil)
+ if value
+ @resource = value
+ else
+ @resource
+ end
+ end
+
+ def module_name(value=nil)
+ if value
+ @module_name = value
+ else
+ @module_name
+ end
+ end
+
+ def property(property_name, value=nil)
+ if not property_name.is_a?(Symbol)
+ raise TypeError, "A property name of type Symbol must be specified, '#{property_name.to_s}' of type #{property_name.class.to_s} was given"
+ end
+
+ if value.nil?
+ value_of(@properties[property_name])
+ else
+ @properties[property_name] = value
+ end
+ end
+
+ def properties
+ @properties.reduce({}) do |memo, (k, v)|
+ memo[k] = value_of(v)
+ memo
+ end
+ end
+
+ private
+
+ def value_of(value)
+ if value.is_a?(DelayedEvaluator)
+ value.call
+ else
+ value
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/env.rb b/lib/chef/resource/env.rb
index 4b5fe6cc09..2072ae5d80 100644
--- a/lib/chef/resource/env.rb
+++ b/lib/chef/resource/env.rb
@@ -25,6 +25,8 @@ class Chef
state_attrs :value
+ provides :env, os: "windows"
+
def initialize(name, run_context=nil)
super
@resource_name = :env
diff --git a/lib/chef/resource/git.rb b/lib/chef/resource/git.rb
index 7156873315..306efe633d 100644
--- a/lib/chef/resource/git.rb
+++ b/lib/chef/resource/git.rb
@@ -27,7 +27,7 @@ class Chef
def initialize(name, run_context=nil)
super
@resource_name = :git
- @additional_remotes = Hash[]
+ @additional_remotes = {}
end
def additional_remotes(arg=nil)
diff --git a/lib/chef/resource/group.rb b/lib/chef/resource/group.rb
index daf851fac6..9e8f1309b0 100644
--- a/lib/chef/resource/group.rb
+++ b/lib/chef/resource/group.rb
@@ -25,6 +25,8 @@ class Chef
state_attrs :members
+ provides :group
+
def initialize(name, run_context=nil)
super
@resource_name = :group
diff --git a/lib/chef/resource/homebrew_package.rb b/lib/chef/resource/homebrew_package.rb
index 3bd5dc16dc..73409b13ac 100644
--- a/lib/chef/resource/homebrew_package.rb
+++ b/lib/chef/resource/homebrew_package.rb
@@ -26,7 +26,7 @@ class Chef
class HomebrewPackage < Chef::Resource::Package
provides :homebrew_package
- provides :package, os: ["mac_os_x", "darwin"]
+ provides :package, os: "darwin"
def initialize(name, run_context=nil)
super
diff --git a/lib/chef/resource/lwrp_base.rb b/lib/chef/resource/lwrp_base.rb
index ce72e98028..a777c511f0 100644
--- a/lib/chef/resource/lwrp_base.rb
+++ b/lib/chef/resource/lwrp_base.rb
@@ -70,14 +70,6 @@ class Chef
alias_method :resource_name=, :resource_name
end
- # Define an attribute on this resource, including optional validation
- # parameters.
- def self.attribute(attr_name, validation_opts={})
- define_method(attr_name) do |arg=nil|
- set_or_return(attr_name.to_sym, arg, validation_opts)
- end
- end
-
# Sets the default action
def self.default_action(action_name=NULL_ARG)
unless action_name.equal?(NULL_ARG)
diff --git a/lib/chef/resource/mdadm.rb b/lib/chef/resource/mdadm.rb
index 46a85b2475..971b6c51b4 100644
--- a/lib/chef/resource/mdadm.rb
+++ b/lib/chef/resource/mdadm.rb
@@ -27,6 +27,8 @@ class Chef
state_attrs :devices, :level, :chunk
+ provides :mdadm
+
def initialize(name, run_context=nil)
super
@resource_name = :mdadm
diff --git a/lib/chef/resource/mount.rb b/lib/chef/resource/mount.rb
index 275c069f61..142dec87f7 100644
--- a/lib/chef/resource/mount.rb
+++ b/lib/chef/resource/mount.rb
@@ -27,6 +27,8 @@ class Chef
state_attrs :mount_point, :device_type, :fstype, :username, :password, :domain
+ provides :mount
+
def initialize(name, run_context=nil)
super
@resource_name = :mount
diff --git a/lib/chef/resource/powershell_script.rb b/lib/chef/resource/powershell_script.rb
index 1b8aef94a2..43aafe4df2 100644
--- a/lib/chef/resource/powershell_script.rb
+++ b/lib/chef/resource/powershell_script.rb
@@ -21,6 +21,8 @@ class Chef
class Resource
class PowershellScript < Chef::Resource::WindowsScript
+ provides :powershell_script, os: "windows"
+
def initialize(name, run_context=nil)
super(name, run_context, :powershell_script, "powershell.exe")
@convert_boolean_return = false
diff --git a/lib/chef/resource/user.rb b/lib/chef/resource/user.rb
index 9d6e857de7..7d2ec25596 100644
--- a/lib/chef/resource/user.rb
+++ b/lib/chef/resource/user.rb
@@ -26,6 +26,8 @@ class Chef
state_attrs :uid, :gid, :home
+ provides :user
+
def initialize(name, run_context=nil)
super
@resource_name = :user
diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb
index 680b393741..40b12a7c5f 100644
--- a/lib/chef/resources.rb
+++ b/lib/chef/resources.rb
@@ -29,6 +29,7 @@ require 'chef/resource/deploy_revision'
require 'chef/resource/directory'
require 'chef/resource/dpkg_package'
require 'chef/resource/dsc_script'
+require 'chef/resource/dsc_resource'
require 'chef/resource/easy_install_package'
require 'chef/resource/env'
require 'chef/resource/erl_call'
diff --git a/lib/chef/run_context.rb b/lib/chef/run_context.rb
index fc54506407..4f0215bfd4 100644
--- a/lib/chef/run_context.rb
+++ b/lib/chef/run_context.rb
@@ -50,7 +50,7 @@ class Chef
# recipes, which is triggered by #load. (See also: CookbookCompiler)
attr_accessor :resource_collection
- # The list of audits (control groups) to execute during the audit phase
+ # The list of control groups to execute during the audit phase
attr_accessor :audits
# A Hash containing the immediate notifications triggered by resources
diff --git a/lib/chef/shell.rb b/lib/chef/shell.rb
index fed32b3795..ee4fe78808 100644
--- a/lib/chef/shell.rb
+++ b/lib/chef/shell.rb
@@ -29,6 +29,7 @@ require 'chef/config_fetcher'
require 'chef/shell/shell_session'
require 'chef/shell/ext'
require 'chef/json_compat'
+require 'chef/util/path_helper'
# = Shell
# Shell is Chef in an IRB session. Shell can interact with a Chef server via the
@@ -101,7 +102,7 @@ module Shell
end
def self.configure_irb
- irb_conf[:HISTORY_FILE] = "~/.chef/chef_shell_history"
+ irb_conf[:HISTORY_FILE] = Chef::Util::PathHelper.home(".chef", "chef_shell_history")
irb_conf[:SAVE_HISTORY] = 1000
irb_conf[:IRB_RC] = lambda do |conf|
@@ -295,18 +296,19 @@ FOOTER
private
def config_file_for_shell_mode(environment)
+ dot_chef_dir = Chef::Util::PathHelper.home('.chef')
if config[:config_file]
config[:config_file]
- elsif environment && ENV['HOME']
+ elsif environment
Shell.env = environment
- config_file_to_try = ::File.join(ENV['HOME'], '.chef', environment, 'chef_shell.rb')
+ config_file_to_try = ::File.join(dot_chef_dir, environment, 'chef_shell.rb')
unless ::File.exist?(config_file_to_try)
puts "could not find chef-shell config for environment #{environment} at #{config_file_to_try}"
exit 1
end
config_file_to_try
- elsif ENV['HOME'] && ::File.exist?(File.join(ENV['HOME'], '.chef', 'chef_shell.rb'))
- File.join(ENV['HOME'], '.chef', 'chef_shell.rb')
+ elsif dot_chef_dir && ::File.exist?(File.join(dot_chef_dir, 'chef_shell.rb'))
+ File.join(dot_chef_dir, 'chef_shell.rb')
elsif config[:solo]
Chef::Config.platform_specific_path("/etc/chef/solo.rb")
elsif config[:client]
diff --git a/lib/chef/util/dsc/resource_store.rb b/lib/chef/util/dsc/resource_store.rb
new file mode 100644
index 0000000000..fdcecc2b3c
--- /dev/null
+++ b/lib/chef/util/dsc/resource_store.rb
@@ -0,0 +1,110 @@
+#
+# Author:: Jay Mundrawala (<jdm@chef.io>)
+#
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+#
+# 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/util/powershell/cmdlet'
+require 'chef/util/powershell/cmdlet_result'
+require 'chef/exceptions'
+
+class Chef
+class Util
+class DSC
+ class ResourceStore
+
+ def self.instance
+ @@instance ||= ResourceStore.new.tap do |store|
+ store.send(:populate_cache)
+ end
+ end
+
+ def resources
+ @resources ||= []
+ end
+
+ def find(name, module_name=nil)
+ found = find_resources(name, module_name, resources)
+
+ # We don't have it, query for the resource...it might
+ # have been added since we last queried
+ if found.length == 0
+ rs = query_resource(name)
+ add_resources(rs)
+ found = find_resources(name, module_name, rs)
+ end
+
+ found
+ end
+
+ private
+
+ def add_resource(new_r)
+ count = resources.count do |r|
+ r['ResourceType'].casecmp(new_r['ResourceType']) == 0
+ end
+ if count == 0
+ resources << new_r
+ end
+ end
+
+ def add_resources(rs)
+ rs.each do |r|
+ add_resource(r)
+ end
+ end
+
+ def populate_cache
+ @resources = query_resources
+ end
+
+ def find_resources(name, module_name, rs)
+ found = rs.find_all do |r|
+ name_matches = r['Name'].casecmp(name) == 0
+ if name_matches
+ module_name == nil || (r['Module'] and r['Module']['Name'].casecmp(module_name) == 0)
+ else
+ false
+ end
+ end
+ end
+
+
+ # Returns a list of dsc resources
+ def query_resources
+ cmdlet = Chef::Util::Powershell::Cmdlet.new(nil, 'get-dscresource',
+ :object)
+ result = cmdlet.run
+ result.return_value
+ end
+
+ # Returns a list of dsc resources matching the provided name
+ def query_resource(resource_name)
+ cmdlet = Chef::Util::Powershell::Cmdlet.new(nil, "get-dscresource #{resource_name}",
+ :object)
+ result = cmdlet.run
+ ret_val = result.return_value
+ if ret_val.nil?
+ []
+ elsif ret_val.is_a? Array
+ ret_val
+ else
+ [ret_val]
+ end
+ end
+ end
+end
+end
+end
diff --git a/lib/chef/util/path_helper.rb b/lib/chef/util/path_helper.rb
index 1ae489598d..17c7175331 100644
--- a/lib/chef/util/path_helper.rb
+++ b/lib/chef/util/path_helper.rb
@@ -142,6 +142,82 @@ class Chef
def self.relative_path_from(from, to)
pathname = Pathname.new(Chef::Util::PathHelper.cleanpath(to)).relative_path_from(Pathname.new(Chef::Util::PathHelper.cleanpath(from)))
end
+
+ # Retrieves the "home directory" of the current user while trying to ascertain the existence
+ # of said directory. The path returned uses / for all separators (the ruby standard format).
+ # If the home directory doesn't exist or an error is otherwise encountered, nil is returned.
+ #
+ # If a set of path elements is provided, they are appended as-is to the home path if the
+ # homepath exists.
+ #
+ # If an optional block is provided, the joined path is passed to that block if the home path is
+ # valid and the result of the block is returned instead.
+ #
+ # Home-path discovery is performed once. If a path is discovered, that value is memoized so
+ # that subsequent calls to home_dir don't bounce around.
+ #
+ # See self.all_homes.
+ def self.home(*args)
+ @@home_dir ||= self.all_homes { |p| break p }
+ if @@home_dir
+ path = File.join(@@home_dir, *args)
+ block_given? ? (yield path) : path
+ end
+ end
+
+ # See self.home. This method performs a similar operation except that it yields all the different
+ # possible values of 'HOME' that one could have on this platform. Hence, on windows, if
+ # HOMEDRIVE\HOMEPATH and USERPROFILE are different, the provided block will be called twice.
+ # This method goes out and checks the existence of each location at the time of the call.
+ #
+ # The return is a list of all the returned values from each block invocation or a list of paths
+ # if no block is provided.
+ def self.all_homes(*args)
+ paths = []
+ if Chef::Platform.windows?
+ # By default, Ruby uses the the following environment variables to determine Dir.home:
+ # HOME
+ # HOMEDRIVE HOMEPATH
+ # USERPROFILE
+ # Ruby only checks to see if the variable is specified - not if the directory actually exists.
+ # On Windows, HOMEDRIVE HOMEPATH can point to a different location (such as an unavailable network mounted drive)
+ # while USERPROFILE points to the location where the user application settings and profile are stored. HOME
+ # is not defined as an environment variable (usually). If the home path actually uses UNC, then the prefix is
+ # HOMESHARE instead of HOMEDRIVE.
+ #
+ # We instead walk down the following and only include paths that actually exist.
+ # HOME
+ # HOMEDRIVE HOMEPATH
+ # HOMESHARE HOMEPATH
+ # USERPROFILE
+
+ paths << ENV['HOME']
+ paths << ENV['HOMEDRIVE'] + ENV['HOMEPATH'] if ENV['HOMEDRIVE'] && ENV['HOMEPATH']
+ paths << ENV['HOMESHARE'] + ENV['HOMEPATH'] if ENV['HOMESHARE'] && ENV['HOMEPATH']
+ paths << ENV['USERPROFILE']
+ end
+ paths << Dir.home
+
+ # Depending on what environment variables we're using, the slashes can go in any which way.
+ # Just change them all to / to keep things consistent.
+ # Note: Maybe this is a bad idea on some unixy systems where \ might be a valid character depending on
+ # the particular brand of kool-aid you consume. This code assumes that \ and / are both
+ # path separators on any system being used.
+ paths = paths.map { |home_path| home_path.gsub(path_separator, ::File::SEPARATOR) if home_path }
+
+ # Filter out duplicate paths and paths that don't exist.
+ valid_paths = paths.select { |home_path| home_path && Dir.exists?(home_path) }
+ valid_paths = valid_paths.uniq
+
+ # Join all optional path elements at the end.
+ # If a block is provided, invoke it - otherwise just return what we've got.
+ joined_paths = valid_paths.map { |home_path| File.join(home_path, *args) }
+ if block_given?
+ joined_paths.each { |p| yield p }
+ else
+ joined_paths
+ end
+ end
end
end
end
diff --git a/lib/chef/util/powershell/cmdlet.rb b/lib/chef/util/powershell/cmdlet.rb
index 40edbb13c6..47d63a2b85 100644
--- a/lib/chef/util/powershell/cmdlet.rb
+++ b/lib/chef/util/powershell/cmdlet.rb
@@ -20,7 +20,9 @@ require 'mixlib/shellout'
require 'chef/mixin/windows_architecture_helper'
require 'chef/util/powershell/cmdlet_result'
-class Chef::Util::Powershell
+class Chef
+class Util
+class Powershell
class Cmdlet
def initialize(node, cmdlet, output_format=nil, output_format_options={})
@output_format = output_format
@@ -46,6 +48,10 @@ class Chef::Util::Powershell
attr_reader :output_format
def run(switches={}, execution_options={}, *arguments)
+ streams = { :json => CmdletStream.new('json'),
+ :verbose => CmdletStream.new('verbose'),
+ }
+
arguments_string = arguments.join(' ')
switches_string = command_switches_string(switches)
@@ -56,21 +62,25 @@ class Chef::Util::Powershell
json_depth = @output_format_options[:depth]
end
- json_command = @json_format ? " | convertto-json -compress -depth #{json_depth}" : ""
- command_string = "powershell.exe -executionpolicy bypass -noprofile -noninteractive -command \"trap [Exception] {write-error -exception ($_.Exception.Message);exit 1};#{@cmdlet} #{switches_string} #{arguments_string}#{json_command}\";if ( ! $? ) { exit 1 }"
+ json_command = @json_format ? " | convertto-json -compress -depth #{json_depth} "\
+ "> #{streams[:json].path}" : ""
+ redirections = "4> '#{streams[:verbose].path}'"
+ command_string = "powershell.exe -executionpolicy bypass -noprofile -noninteractive "\
+ "-command \"trap [Exception] {write-error -exception "\
+ "($_.Exception.Message);exit 1};#{@cmdlet} #{switches_string} "\
+ "#{arguments_string} #{redirections}"\
+ "#{json_command}\";if ( ! $? ) { exit 1 }"
augmented_options = {:returns => [0], :live_stream => false}.merge(execution_options)
command = Mixlib::ShellOut.new(command_string, augmented_options)
- os_architecture = "#{ENV['PROCESSOR_ARCHITEW6432']}" == 'AMD64' ? :x86_64 : :i386
-
status = nil
with_os_architecture(@node) do
status = command.run_command
end
- CmdletResult.new(status, @output_format)
+ CmdletResult.new(status, streams, @output_format)
end
def run!(switches={}, execution_options={}, *arguments)
@@ -131,6 +141,30 @@ class Chef::Util::Powershell
command_switches.join(' ')
end
+
+ class CmdletStream
+ def initialize(name)
+ @filename = Dir::Tmpname.create(name) {}
+ ObjectSpace.define_finalizer(self, self.class.destroy(@filename))
+ end
+
+ def path
+ @filename
+ end
+
+ def read
+ if File.exist? @filename
+ File.open(@filename, 'rb:bom|UTF-16LE') do |f|
+ f.read.encode('UTF-8')
+ end
+ end
+ end
+
+ def self.destroy(name)
+ proc { File.delete(name) if File.exists? name }
+ end
+ end
end
end
-
+end
+end
diff --git a/lib/chef/util/powershell/cmdlet_result.rb b/lib/chef/util/powershell/cmdlet_result.rb
index 246701a7bc..f1fdd968b1 100644
--- a/lib/chef/util/powershell/cmdlet_result.rb
+++ b/lib/chef/util/powershell/cmdlet_result.rb
@@ -18,22 +18,35 @@
require 'chef/json_compat'
-class Chef::Util::Powershell
+class Chef
+class Util
+class Powershell
class CmdletResult
attr_reader :output_format
- def initialize(status, output_format)
+ def initialize(status, streams, output_format)
@status = status
@output_format = output_format
+ @streams = streams
end
+ def stdout
+ @status.stdout
+ end
+
def stderr
@status.stderr
end
+ def stream(name)
+ @streams[name].read
+ end
+
def return_value
if output_format == :object
- Chef::JSONCompat.parse(@status.stdout)
+ Chef::JSONCompat.parse(stream(:json))
+ elsif output_format == :json
+ stream(:json)
else
@status.stdout
end
@@ -44,3 +57,5 @@ class Chef::Util::Powershell
end
end
end
+end
+end
diff --git a/lib/chef/util/powershell/ps_credential.rb b/lib/chef/util/powershell/ps_credential.rb
new file mode 100644
index 0000000000..01f8c27b6c
--- /dev/null
+++ b/lib/chef/util/powershell/ps_credential.rb
@@ -0,0 +1,38 @@
+#
+# Author:: Jay Mundrawala (<jdm@chef.io>)
+#
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+#
+# 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/win32/crypto' if Chef::Platform.windows?
+
+class Chef::Util::Powershell
+ class PSCredential
+ def initialize(username, password)
+ @username = username
+ @password = password
+ end
+
+ def to_psobject
+ "New-Object System.Management.Automation.PSCredential('#{@username}',('#{encrypt(@password)}' | ConvertTo-SecureString))"
+ end
+
+ private
+
+ def encrypt(str)
+ Chef::ReservedNames::Win32::Crypto.encrypt(str)
+ end
+ end
+end
diff --git a/lib/chef/version.rb b/lib/chef/version.rb
index e56720fd33..5763f6a138 100644
--- a/lib/chef/version.rb
+++ b/lib/chef/version.rb
@@ -17,7 +17,7 @@
class Chef
CHEF_ROOT = File.dirname(File.expand_path(File.dirname(__FILE__)))
- VERSION = '12.1.2'
+ VERSION = '12.2.0.rc.0'
end
#
diff --git a/lib/chef/win32/api.rb b/lib/chef/win32/api.rb
index 8b81947c9b..efa632f454 100644
--- a/lib/chef/win32/api.rb
+++ b/lib/chef/win32/api.rb
@@ -184,6 +184,8 @@ class Chef
host.typedef :pointer, :PSTR # Pointer to a null-terminated string of 8-bit Windows (ANSI) characters. For more information, see Character Sets Used By Fonts.
host.typedef :pointer, :PTBYTE # Pointer to a TBYTE.
host.typedef :pointer, :PTCHAR # Pointer to a TCHAR.
+ host.typedef :pointer, :PCRYPTPROTECT_PROMPTSTRUCT # Pointer to a CRYPTOPROTECT_PROMPTSTRUCT.
+ host.typedef :pointer, :PDATA_BLOB # Pointer to a DATA_BLOB.
host.typedef :pointer, :PTSTR # A PWSTR if UNICODE is defined, a PSTR otherwise.
host.typedef :pointer, :PUCHAR # Pointer to a UCHAR.
host.typedef :pointer, :PUHALF_PTR # Pointer to a UHALF_PTR.
diff --git a/lib/chef/win32/api/crypto.rb b/lib/chef/win32/api/crypto.rb
new file mode 100644
index 0000000000..1837a57557
--- /dev/null
+++ b/lib/chef/win32/api/crypto.rb
@@ -0,0 +1,63 @@
+#
+# Author:: Jay Mundrawala (<jdm@chef.io>)
+# Copyright:: Copyright 2015 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/win32/api'
+
+class Chef
+ module ReservedNames::Win32
+ module API
+ module Crypto
+ extend Chef::ReservedNames::Win32::API
+
+ ###############################################
+ # Win32 API Bindings
+ ###############################################
+
+ ffi_lib 'Crypt32'
+
+ CRYPTPROTECT_UI_FORBIDDEN = 0x1
+ CRYPTPROTECT_LOCAL_MACHINE = 0x4
+ CRYPTPROTECT_AUDIT = 0x10
+
+ class CRYPT_INTEGER_BLOB < FFI::Struct
+ layout :cbData, :DWORD, # Count, in bytes, of data
+ :pbData, :pointer # Pointer to data buffer
+ def initialize(str=nil)
+ super(nil)
+ if str
+ self[:pbData] = FFI::MemoryPointer.from_string(str)
+ self[:cbData] = str.bytesize
+ end
+ end
+
+ end
+
+ safe_attach_function :CryptProtectData, [
+ :PDATA_BLOB,
+ :LPCWSTR,
+ :PDATA_BLOB,
+ :pointer,
+ :PCRYPTPROTECT_PROMPTSTRUCT,
+ :DWORD,
+ :PDATA_BLOB
+ ], :BOOL
+
+ end
+ end
+ end
+end
diff --git a/lib/chef/win32/api/installer.rb b/lib/chef/win32/api/installer.rb
index 745802d260..6864a26e7d 100644
--- a/lib/chef/win32/api/installer.rb
+++ b/lib/chef/win32/api/installer.rb
@@ -108,7 +108,7 @@ UINT MsiCloseHandle(
end
msi_close_handle(pkg_ptr.read_pointer)
- return buffer
+ return buffer.chomp(0.chr)
end
# Opens a Microsoft Installer (MSI) file from an absolute path and returns a pointer to a handle
diff --git a/lib/chef/win32/crypto.rb b/lib/chef/win32/crypto.rb
new file mode 100644
index 0000000000..79cf51b002
--- /dev/null
+++ b/lib/chef/win32/crypto.rb
@@ -0,0 +1,49 @@
+#
+# Author:: Jay Mundrawala (<jdm@chef.io>)
+# Copyright:: Copyright 2015 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/win32/error'
+require 'chef/win32/api/memory'
+require 'chef/win32/api/crypto'
+require 'digest'
+
+class Chef
+ module ReservedNames::Win32
+ class Crypto
+ include Chef::ReservedNames::Win32::API::Crypto
+ extend Chef::ReservedNames::Win32::API::Crypto
+
+ def self.encrypt(str, &block)
+ data_blob = CRYPT_INTEGER_BLOB.new
+ unless CryptProtectData(CRYPT_INTEGER_BLOB.new(str.to_wstring), nil, nil, nil, nil, 0, data_blob)
+ Chef::ReservedNames::Win32::Error.raise!
+ end
+ bytes = data_blob[:pbData].get_bytes(0, data_blob[:cbData])
+ if block
+ block.call(bytes)
+ else
+ Digest.hexencode(bytes)
+ end
+ ensure
+ unless data_blob[:pbData].null?
+ Chef::ReservedNames::Win32::Memory.local_free(data_blob[:pbData])
+ end
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/workstation_config_loader.rb b/lib/chef/workstation_config_loader.rb
index dd02ad9a66..2454c9cccf 100644
--- a/lib/chef/workstation_config_loader.rb
+++ b/lib/chef/workstation_config_loader.rb
@@ -19,6 +19,7 @@
require 'chef/config_fetcher'
require 'chef/config'
require 'chef/null_logger'
+require 'chef/util/path_helper'
class Chef
@@ -112,9 +113,9 @@ class Chef
candidate_configs << File.join(chef_config_dir, 'knife.rb')
end
# Look for $HOME/.chef/knife.rb
- if env['HOME']
- candidate_configs << File.join(env['HOME'], '.chef', 'config.rb')
- candidate_configs << File.join(env['HOME'], '.chef', 'knife.rb')
+ Chef::Util::PathHelper.home('.chef') do |dot_chef_dir|
+ candidate_configs << File.join(dot_chef_dir, 'config.rb')
+ candidate_configs << File.join(dot_chef_dir, 'knife.rb')
end
candidate_configs.find do | candidate_config |
diff --git a/spec/functional/file_content_management/deploy_strategies_spec.rb b/spec/functional/file_content_management/deploy_strategies_spec.rb
index 03a6c504c1..8a995d0e41 100644
--- a/spec/functional/file_content_management/deploy_strategies_spec.rb
+++ b/spec/functional/file_content_management/deploy_strategies_spec.rb
@@ -43,7 +43,7 @@ shared_examples_for "a content deploy strategy" do
##
# UNIX Context
- let(:default_mode) { normalize_mode(0100666 - File.umask) }
+ let(:default_mode) { normalize_mode(0666 & ~File.umask) }
it "touches the file to create it (UNIX)", :unix_only do
content_deployer.create(target_file_path)
diff --git a/spec/functional/resource/cookbook_file_spec.rb b/spec/functional/resource/cookbook_file_spec.rb
index 7797ed0041..6d4c5b4a8f 100644
--- a/spec/functional/resource/cookbook_file_spec.rb
+++ b/spec/functional/resource/cookbook_file_spec.rb
@@ -32,7 +32,7 @@ describe Chef::Resource::CookbookFile do
content
end
- let(:default_mode) { ((0100666 - File.umask) & 07777).to_s(8) }
+ let(:default_mode) { (0666 & ~File.umask).to_s(8) }
it_behaves_like "a securable resource with reporting"
diff --git a/spec/functional/resource/deploy_revision_spec.rb b/spec/functional/resource/deploy_revision_spec.rb
index e5f5341fcd..bd45e32771 100644
--- a/spec/functional/resource/deploy_revision_spec.rb
+++ b/spec/functional/resource/deploy_revision_spec.rb
@@ -201,6 +201,41 @@ describe Chef::Resource::DeployRevision, :unix_only => true do
end
end
+ describe "setting default parameters to nil" do
+ before do
+ FileUtils.mkdir_p(rel_path("releases"))
+ FileUtils.mkdir_p(rel_path("shared"))
+ end
+
+ it "supports setting symlink_before_migrate to nil" do
+ deploy_to_latest_rev.symlink_before_migrate(nil)
+ expect(deploy_to_latest_rev.symlink_before_migrate).to eql(nil)
+ deploy_to_latest_rev.run_action(:deploy)
+ expect(deploy_to_latest_rev).to be_updated_by_last_action
+ end
+
+ it "supports setting symlinks to nil" do
+ deploy_to_latest_rev.symlinks(nil)
+ expect(deploy_to_latest_rev.symlinks).to eql(nil)
+ deploy_to_latest_rev.run_action(:deploy)
+ expect(deploy_to_latest_rev).to be_updated_by_last_action
+ end
+
+ it "supports setting purge_before_symlink to nil" do
+ deploy_to_latest_rev.purge_before_symlink(nil)
+ expect(deploy_to_latest_rev.purge_before_symlink).to eql(nil)
+ deploy_to_latest_rev.run_action(:deploy)
+ expect(deploy_to_latest_rev).to be_updated_by_last_action
+ end
+
+ it "supports setting create_dirs_before_symlink to nil" do
+ deploy_to_latest_rev.create_dirs_before_symlink(nil)
+ expect(deploy_to_latest_rev.create_dirs_before_symlink).to eql(nil)
+ deploy_to_latest_rev.run_action(:deploy)
+ expect(deploy_to_latest_rev).to be_updated_by_last_action
+ end
+ end
+
describe "back to a previously deployed revision, with the directory structure precreated" do
before do
FileUtils.mkdir_p(rel_path("releases"))
diff --git a/spec/functional/resource/directory_spec.rb b/spec/functional/resource/directory_spec.rb
index 2c4025f83e..88a810964f 100644
--- a/spec/functional/resource/directory_spec.rb
+++ b/spec/functional/resource/directory_spec.rb
@@ -23,7 +23,7 @@ describe Chef::Resource::Directory do
let(:directory_base) { "directory_spec" }
- let(:default_mode) { ((0100777 - File.umask) & 07777).to_s(8) }
+ let(:default_mode) { (0777 & ~File.umask).to_s(8) }
def create_resource
events = Chef::EventDispatch::Dispatcher.new
diff --git a/spec/functional/resource/dsc_resource_spec.rb b/spec/functional/resource/dsc_resource_spec.rb
new file mode 100644
index 0000000000..6f453eeb9f
--- /dev/null
+++ b/spec/functional/resource/dsc_resource_spec.rb
@@ -0,0 +1,93 @@
+#
+# Author:: Jay Mundrawala (<jdm@chef.io>)
+# Copyright:: Copyright (c) 2015 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 'spec_helper'
+
+describe Chef::Resource::DscResource, :windows_powershell_dsc_only do
+ before(:all) do
+ @ohai = Ohai::System.new
+ @ohai.all_plugins(['platform', 'os', 'languages/powershell'])
+ end
+
+ let(:event_dispatch) { Chef::EventDispatch::Dispatcher.new }
+
+ let(:node) {
+ Chef::Node.new.tap do |n|
+ n.consume_external_attrs(@ohai.data, {})
+ end
+ }
+
+ let(:run_context) { Chef::RunContext.new(node, {}, event_dispatch) }
+
+ let(:new_resource) {
+ Chef::Resource::DscResource.new("dsc_resource_test", run_context)
+ }
+
+ context 'when Powershell does not support Invoke-DscResource'
+ context 'when Powershell supports Invoke-DscResource' do
+ before do
+ if !Chef::Platform.supports_dsc_invoke_resource?(node)
+ skip 'Requires Powershell >= 5.0.10018.0'
+ end
+ end
+ context 'with an invalid dsc resource' do
+ it 'raises an exception if the resource is not found' do
+ new_resource.resource 'thisdoesnotexist'
+ expect { new_resource.run_action(:run) }.to raise_error(
+ Chef::Exceptions::ResourceNotFound)
+ end
+ end
+
+ context 'with a valid dsc resource' do
+ let(:tmp_file_name) { Dir::Tmpname.create('tmpfile') {} }
+ let(:test_text) { "'\"!@#$%^&*)(}{][\u2713~n"}
+
+ before do
+ new_resource.resource :File
+ new_resource.property :Contents, test_text
+ new_resource.property :DestinationPath, tmp_file_name
+ end
+
+ after do
+ File.delete(tmp_file_name) if File.exists? tmp_file_name
+ end
+
+ it 'converges the resource if it is not converged' do
+ new_resource.run_action(:run)
+ contents = File.open(tmp_file_name, 'rb:bom|UTF-16LE') do |f|
+ f.read.encode('UTF-8')
+ end
+ expect(contents).to eq(test_text)
+ expect(new_resource).to be_updated
+ end
+
+ it 'does not converge the resource if it is already converged' do
+ new_resource.run_action(:run)
+ expect(new_resource).to be_updated
+ reresource =
+ Chef::Resource::DscResource.new("dsc_resource_retest", run_context)
+ reresource.resource :File
+ reresource.property :Contents, test_text
+ reresource.property :DestinationPath, tmp_file_name
+ reresource.run_action(:run)
+ expect(reresource).not_to be_updated
+ end
+ end
+
+ end
+end
diff --git a/spec/functional/resource/env_spec.rb b/spec/functional/resource/env_spec.rb
index 16caec14bf..b9dcd7b33a 100755
--- a/spec/functional/resource/env_spec.rb
+++ b/spec/functional/resource/env_spec.rb
@@ -29,14 +29,15 @@ describe Chef::Resource::Env, :windows_only do
let(:env_value_expandable) { '%SystemRoot%' }
let(:test_run_context) {
node = Chef::Node.new
+ node.default['os'] = 'windows'
node.default['platform'] = 'windows'
node.default['platform_version'] = '6.1'
empty_events = Chef::EventDispatch::Dispatcher.new
Chef::RunContext.new(node, {}, empty_events)
}
- let(:test_resource) {
- Chef::Resource::Env.new('unknown', test_run_context)
- }
+ let(:test_resource) {
+ Chef::Resource::Env.new('unknown', test_run_context)
+ }
before(:each) do
resource_lower = Chef::Resource::Env.new(chef_env_test_lower_case, test_run_context)
diff --git a/spec/functional/resource/file_spec.rb b/spec/functional/resource/file_spec.rb
index cf70c349fb..f1a290dea4 100644
--- a/spec/functional/resource/file_spec.rb
+++ b/spec/functional/resource/file_spec.rb
@@ -64,7 +64,7 @@ describe Chef::Resource::File do
provider.current_resource
end
- let(:default_mode) { ((0100666 - File.umask) & 07777).to_s(8) }
+ let(:default_mode) { (0666 & ~File.umask).to_s(8) }
it_behaves_like "a file resource"
diff --git a/spec/functional/resource/powershell_spec.rb b/spec/functional/resource/powershell_spec.rb
index 1b3ac844e0..56a905efe7 100644
--- a/spec/functional/resource/powershell_spec.rb
+++ b/spec/functional/resource/powershell_spec.rb
@@ -56,7 +56,8 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
resource.run_action(:run)
end
- it "returns the -27 for a powershell script that exits with -27" do
+ it "returns the -27 for a powershell script that exits with -27", :windows_powershell_dsc_only do
+ # This is broken on Powershell < 4.0
file = Tempfile.new(['foo', '.ps1'])
begin
file.write "exit -27"
diff --git a/spec/functional/resource/remote_directory_spec.rb b/spec/functional/resource/remote_directory_spec.rb
index bcafca7399..37ffbbc971 100644
--- a/spec/functional/resource/remote_directory_spec.rb
+++ b/spec/functional/resource/remote_directory_spec.rb
@@ -22,7 +22,7 @@ describe Chef::Resource::RemoteDirectory do
include_context Chef::Resource::Directory
let(:directory_base) { "directory_spec" }
- let(:default_mode) { ((0100777 - File.umask) & 07777).to_s(8) }
+ let(:default_mode) { (0777 & ~File.umask).to_s(8) }
def create_resource
cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks"))
diff --git a/spec/functional/resource/remote_file_spec.rb b/spec/functional/resource/remote_file_spec.rb
index 29091fd785..4fbcd2d24b 100644
--- a/spec/functional/resource/remote_file_spec.rb
+++ b/spec/functional/resource/remote_file_spec.rb
@@ -52,7 +52,7 @@ describe Chef::Resource::RemoteFile do
create_resource
end
- let(:default_mode) { ((0100666 - File.umask) & 07777).to_s(8) }
+ let(:default_mode) { (0666 & ~File.umask).to_s(8) }
context "when fetching files over HTTP" do
before(:all) do
diff --git a/spec/functional/resource/template_spec.rb b/spec/functional/resource/template_spec.rb
index d7b35e7450..35c5166e31 100644
--- a/spec/functional/resource/template_spec.rb
+++ b/spec/functional/resource/template_spec.rb
@@ -58,7 +58,7 @@ describe Chef::Resource::Template do
create_resource
end
- let(:default_mode) { ((0100666 - File.umask) & 07777).to_s(8) }
+ let(:default_mode) { (0666 & ~File.umask).to_s(8) }
it_behaves_like "a file resource"
diff --git a/spec/functional/resource/user/dscl_spec.rb b/spec/functional/resource/user/dscl_spec.rb
index 45b6754453..5d960daf11 100644
--- a/spec/functional/resource/user/dscl_spec.rb
+++ b/spec/functional/resource/user/dscl_spec.rb
@@ -19,9 +19,8 @@ require 'spec_helper'
require 'chef/mixin/shell_out'
metadata = {
- :unix_only => true,
+ :mac_osx_only => true,
:requires_root => true,
- :provider => {:user => Chef::Provider::User::Dscl},
:not_supported_on_mac_osx_106 => true,
}
diff --git a/spec/functional/resource/user/useradd_spec.rb b/spec/functional/resource/user/useradd_spec.rb
index 9ac88d7b60..3e4e4e7604 100644
--- a/spec/functional/resource/user/useradd_spec.rb
+++ b/spec/functional/resource/user/useradd_spec.rb
@@ -32,6 +32,7 @@ end
metadata = { :unix_only => true,
:requires_root => true,
+ :not_supported_on_mac_osx => true,
:provider => {:user => user_provider_for_platform}
}
@@ -76,9 +77,22 @@ describe Chef::Provider::User::Useradd, metadata do
end
end
+ def try_cleanup
+ ['/home/cheftestfoo', '/home/cheftestbar'].each do |f|
+ FileUtils.rm_rf(f) if File.exists? f
+ end
+
+ ['cf-test'].each do |u|
+ r = Chef::Resource::User.new("DELETE USER", run_context)
+ r.username('cf-test')
+ r.run_action(:remove)
+ end
+ end
+
before do
# Silence shell_out live stream
Chef::Log.level = :warn
+ try_cleanup
end
after do
@@ -386,18 +400,18 @@ describe Chef::Provider::User::Useradd, metadata do
end
context "and home directory is updated" do
- let(:existing_home) { "/home/foo" }
- let(:home) { "/home/bar" }
+ let(:existing_home) { "/home/cheftestfoo" }
+ let(:home) { "/home/cheftestbar" }
it "ensures the home directory is set to the desired value" do
- expect(pw_entry.home).to eq("/home/bar")
+ expect(pw_entry.home).to eq("/home/cheftestbar")
end
context "and manage_home is enabled" do
let(:existing_manage_home) { true }
let(:manage_home) { true }
it "moves the home directory to the new location" do
- expect(File).not_to exist("/home/foo")
- expect(File).to exist("/home/bar")
+ expect(File).not_to exist("/home/cheftestfoo")
+ expect(File).to exist("/home/cheftestbar")
end
end
@@ -409,19 +423,19 @@ describe Chef::Provider::User::Useradd, metadata do
# Inconsistent behavior. See: CHEF-2205
it "created the home dir b/c of CHEF-2205 so it still exists" do
# This behavior seems contrary to expectation and non-convergent.
- expect(File).not_to exist("/home/foo")
- expect(File).to exist("/home/bar")
+ expect(File).not_to exist("/home/cheftestfoo")
+ expect(File).to exist("/home/cheftestbar")
end
elsif ohai[:platform] == "aix"
it "creates the home dir in the desired location" do
- expect(File).not_to exist("/home/foo")
- expect(File).to exist("/home/bar")
+ expect(File).not_to exist("/home/cheftestfoo")
+ expect(File).to exist("/home/cheftestbar")
end
else
it "does not create the home dir in the desired location (XXX)" do
# This behavior seems contrary to expectation and non-convergent.
- expect(File).not_to exist("/home/foo")
- expect(File).not_to exist("/home/bar")
+ expect(File).not_to exist("/home/cheftestfoo")
+ expect(File).not_to exist("/home/cheftestbar")
end
end
end
@@ -432,8 +446,8 @@ describe Chef::Provider::User::Useradd, metadata do
it "leaves the old home directory around (XXX)" do
# Would it be better to remove the old home?
- expect(File).to exist("/home/foo")
- expect(File).not_to exist("/home/bar")
+ expect(File).to exist("/home/cheftestfoo")
+ expect(File).not_to exist("/home/cheftestbar")
end
end
end
diff --git a/spec/functional/win32/crypto_spec.rb b/spec/functional/win32/crypto_spec.rb
new file mode 100644
index 0000000000..1492995886
--- /dev/null
+++ b/spec/functional/win32/crypto_spec.rb
@@ -0,0 +1,57 @@
+#
+# Author:: Jay Mundrawala(<jdm@chef.io>)
+# Copyright:: Copyright (c) 2015 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 'spec_helper'
+if Chef::Platform.windows?
+ require 'chef/win32/crypto'
+end
+
+describe 'Chef::ReservedNames::Win32::Crypto', :windows_only do
+ describe '#encrypt' do
+ before(:all) do
+ ohai_reader = Ohai::System.new
+ ohai_reader.all_plugins("platform")
+
+ new_node = Chef::Node.new
+ new_node.consume_external_attrs(ohai_reader.data,{})
+
+ events = Chef::EventDispatch::Dispatcher.new
+
+ @run_context = Chef::RunContext.new(new_node, {}, events)
+ end
+
+ let (:plaintext) { 'p@assword' }
+
+ it 'can be decrypted by powershell' do
+ encrypted = Chef::ReservedNames::Win32::Crypto.encrypt(plaintext)
+ resource = Chef::Resource::WindowsScript::PowershellScript.new("Powershell resource functional test", @run_context)
+ resource.code <<-EOF
+$encrypted = '#{encrypted}' | ConvertTo-SecureString
+$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($encrypted)
+$plaintext = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
+if ($plaintext -ne '#{plaintext}') {
+ Write-Error 'Got: ' $plaintext
+ exit 1
+}
+exit 0
+ EOF
+ resource.returns(0)
+ resource.run_action(:run)
+ end
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 8888efc424..fb284c721b 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -110,10 +110,13 @@ RSpec.configure do |config|
# Tests that randomly fail, but may have value.
config.filter_run_excluding :volatile => true
config.filter_run_excluding :volatile_on_solaris => true if solaris?
+ config.filter_run_excluding :volatile_from_verify => false
# Add jruby filters here
config.filter_run_excluding :windows_only => true unless windows?
config.filter_run_excluding :not_supported_on_mac_osx_106 => true if mac_osx_106?
+ config.filter_run_excluding :not_supported_on_mac_osx=> true if mac_osx?
+ config.filter_run_excluding :mac_osx_only=> true if !mac_osx?
config.filter_run_excluding :not_supported_on_win2k3 => true if windows_win2k3?
config.filter_run_excluding :not_supported_on_solaris => true if solaris?
config.filter_run_excluding :win2k3_only => true unless windows_win2k3?
diff --git a/spec/support/platform_helpers.rb b/spec/support/platform_helpers.rb
index a412fe38e1..da0313758b 100644
--- a/spec/support/platform_helpers.rb
+++ b/spec/support/platform_helpers.rb
@@ -88,6 +88,20 @@ def mac_osx_106?
false
end
+def mac_osx?
+ if File.exists? "/usr/bin/sw_vers"
+ result = ShellHelpers.shell_out("/usr/bin/sw_vers")
+ result.stdout.each_line do |line|
+ if line =~ /^ProductName:\sMac OS X.*$/
+ return true
+ end
+ end
+ end
+
+ false
+end
+
+
# detects if the hardware is 64-bit (evaluates to true in "WOW64" mode in a 32-bit app on a 64-bit system)
def windows64?
windows? && ( ENV['PROCESSOR_ARCHITECTURE'] == 'AMD64' || ENV['PROCESSOR_ARCHITEW6432'] == 'AMD64' )
diff --git a/spec/support/shared/functional/securable_resource_with_reporting.rb b/spec/support/shared/functional/securable_resource_with_reporting.rb
index 8a2ceed837..37fc538801 100644
--- a/spec/support/shared/functional/securable_resource_with_reporting.rb
+++ b/spec/support/shared/functional/securable_resource_with_reporting.rb
@@ -35,7 +35,7 @@ shared_examples_for "a securable resource with reporting" do
# Default mode varies based on implementation. Providers that use a tempfile
# will default to 0600. Providers that use File.open will default to 0666 -
# umask
- # let(:default_mode) { ((0100666 - File.umask) & 07777).to_s(8) }
+ # let(:default_mode) { (0666 & ~File.umask).to_s(8) }
describe "reading file security metadata for reporting on unix", :unix_only => true do
# According to POSIX standard created files get either the
@@ -185,7 +185,7 @@ shared_examples_for "a securable resource with reporting" do
# TODO: most stable way to specify?
expect(current_resource.owner).to eq(Etc.getpwuid(Process.uid).name)
expect(current_resource.group).to eq(@expected_group_name)
- expect(current_resource.mode).to eq("0#{((0100666 - File.umask) & 07777).to_s(8)}")
+ expect(current_resource.mode).to eq("0#{(0666 & ~File.umask).to_s(8)}")
end
end
@@ -239,8 +239,8 @@ shared_examples_for "a securable resource with reporting" do
end
context "and mode is specified as a String" do
- let(:default_create_mode) { (0100666 - File.umask) }
- let(:expected_mode) { "0#{(default_create_mode & 07777).to_s(8)}" }
+ let(:default_create_mode) { 0666 & ~File.umask }
+ let(:expected_mode) { "0#{default_create_mode.to_s(8)}" }
before do
resource.mode(expected_mode)
@@ -252,7 +252,7 @@ shared_examples_for "a securable resource with reporting" do
end
context "and mode is specified as an Integer" do
- let(:set_mode) { (0100666 - File.umask) & 07777 }
+ let(:set_mode) { 0666 & ~File.umask }
let(:expected_mode) { "0#{set_mode.to_s(8)}" }
before do
diff --git a/spec/unit/application/client_spec.rb b/spec/unit/application/client_spec.rb
index ea2ad473e5..501251b7fe 100644
--- a/spec/unit/application/client_spec.rb
+++ b/spec/unit/application/client_spec.rb
@@ -305,7 +305,7 @@ describe Chef::Application::Client, "run_application", :unix_only do
allow(Chef::Daemon).to receive(:daemonize).and_return(true)
end
- it "should exit hard with exitstatus 3" do
+ it "should exit hard with exitstatus 3", :volatile do
pid = fork do
@app.run_application
end
@@ -320,9 +320,9 @@ describe Chef::Application::Client, "run_application", :unix_only do
end
expect(@pipe[0].gets).to eq("started\n")
Process.kill("TERM", pid)
- Process.wait
- sleep 1 # Make sure we give the converging child process enough time to finish
- expect(IO.select([@pipe[0]], nil, nil, 0)).not_to be_nil
+ Process.wait(pid)
+ # The timeout value needs to be large enough for the child process to finish
+ expect(IO.select([@pipe[0]], nil, nil, 15)).not_to be_nil
expect(@pipe[0].gets).to eq("finished\n")
end
end
diff --git a/spec/unit/audit/audit_reporter_spec.rb b/spec/unit/audit/audit_reporter_spec.rb
index 84d7ea82f0..4bf889510a 100644
--- a/spec/unit/audit/audit_reporter_spec.rb
+++ b/spec/unit/audit/audit_reporter_spec.rb
@@ -203,7 +203,7 @@ describe Chef::Audit::AuditReporter do
it "doesn't send reports" do
expect(reporter).to receive(:auditing_enabled?).and_return(true)
expect(reporter).to receive(:run_status).and_return(nil)
- expect(Chef::Log).to receive(:debug).with("Run failed before audits were initialized, not sending audit report to server")
+ expect(Chef::Log).to receive(:debug).with("Run failed before audit mode was initialized, not sending audit report to server")
reporter.run_completed(node)
end
diff --git a/spec/unit/audit/runner_spec.rb b/spec/unit/audit/runner_spec.rb
index 801147bdb9..0bd4c18388 100644
--- a/spec/unit/audit/runner_spec.rb
+++ b/spec/unit/audit/runner_spec.rb
@@ -36,6 +36,15 @@ describe Chef::Audit::Runner do
RSpec::Core::Sandbox.sandboxed { ex.run }
end
+ context "when we run in audit mode" do
+ let(:paths) { [ "/opt/chef/lib/chef/", 'C:\windows/here/lib/chef/' , "/opt/chef/extra/folders/lib/chef/"] }
+ it "excludes the current path from backtrace" do
+ paths.each do |path|
+ expect(runner.exclusion_pattern).to match(path)
+ end
+ end
+ end
+
describe "#initialize" do
it "correctly sets the run_context during initialization" do
expect(runner.instance_variable_get(:@run_context)).to eq(run_context)
@@ -72,6 +81,7 @@ describe Chef::Audit::Runner do
expect(RSpec.configuration.color).to eq(color)
expect(RSpec.configuration.expose_dsl_globally?).to eq(false)
+ expect(RSpec.configuration.backtrace_exclusion_patterns).to include(runner.exclusion_pattern)
expect(Specinfra.configuration.backend).to eq(:exec)
end
diff --git a/spec/unit/config_spec.rb b/spec/unit/config_spec.rb
index 06178f7733..6ea67246b5 100644
--- a/spec/unit/config_spec.rb
+++ b/spec/unit/config_spec.rb
@@ -360,18 +360,12 @@ describe Chef::Config do
describe "Chef::Config[:user_home]" do
it "should set when HOME is provided" do
expected = to_platform("/home/kitten")
- allow(Chef::Config).to receive(:env).and_return({ 'HOME' => expected })
- expect(Chef::Config[:user_home]).to eq(expected)
- end
-
- it "should be set when only USERPROFILE is provided" do
- expected = to_platform("/users/kitten")
- allow(Chef::Config).to receive(:env).and_return({ 'USERPROFILE' => expected })
+ allow(Chef::Util::PathHelper).to receive(:home).and_return(expected)
expect(Chef::Config[:user_home]).to eq(expected)
end
it "falls back to the current working directory when HOME and USERPROFILE is not set" do
- allow(Chef::Config).to receive(:env).and_return({})
+ allow(Chef::Util::PathHelper).to receive(:home).and_return(nil)
expect(Chef::Config[:user_home]).to eq(Dir.pwd)
end
end
diff --git a/spec/unit/knife/bootstrap_spec.rb b/spec/unit/knife/bootstrap_spec.rb
index 848af11db5..f1ca510ed3 100644
--- a/spec/unit/knife/bootstrap_spec.rb
+++ b/spec/unit/knife/bootstrap_spec.rb
@@ -115,7 +115,7 @@ describe Chef::Knife::Bootstrap do
end
def configure_env_home
- ENV['HOME'] = "/env/home"
+ allow(Chef::Util::PathHelper).to receive(:home).with(".chef", "bootstrap", "example.erb").and_yield(env_home_template_path)
end
def configure_gem_files
@@ -123,15 +123,9 @@ describe Chef::Knife::Bootstrap do
end
before(:each) do
- @original_home = ENV['HOME']
- ENV['HOME'] = nil
expect(File).to receive(:exists?).with(bootstrap_template).and_return(false)
end
- after(:each) do
- ENV['HOME'] = @original_home
- end
-
context "when file is available everywhere" do
before do
configure_chef_config_dir
@@ -161,7 +155,7 @@ describe Chef::Knife::Bootstrap do
end
end
- context "when file is available in ENV['HOME']" do
+ context "when file is available in home directory" do
before do
configure_chef_config_dir
configure_env_home
@@ -180,7 +174,25 @@ describe Chef::Knife::Bootstrap do
context "when file is available in Gem files" do
before do
configure_chef_config_dir
+ configure_env_home
+ configure_gem_files
+
+ expect(File).to receive(:exists?).with(builtin_template_path).and_return(false)
+ expect(File).to receive(:exists?).with(chef_config_dir_template_path).and_return(false)
+ expect(File).to receive(:exists?).with(env_home_template_path).and_return(false)
+ expect(File).to receive(:exists?).with(gem_files_template_path).and_return(true)
+ end
+
+ it "should load the template from Gem files" do
+ expect(knife.find_template).to eq(gem_files_template_path)
+ end
+ end
+
+ context "when file is available in Gem files and home dir doesn't exist" do
+ before do
+ configure_chef_config_dir
configure_gem_files
+ allow(Chef::Util::PathHelper).to receive(:home).with(".chef", "bootstrap", "example.erb").and_return(nil)
expect(File).to receive(:exists?).with(builtin_template_path).and_return(false)
expect(File).to receive(:exists?).with(chef_config_dir_template_path).and_return(false)
diff --git a/spec/unit/knife/core/subcommand_loader_spec.rb b/spec/unit/knife/core/subcommand_loader_spec.rb
index df42cff2ea..7f9308b28a 100644
--- a/spec/unit/knife/core/subcommand_loader_spec.rb
+++ b/spec/unit/knife/core/subcommand_loader_spec.rb
@@ -19,23 +19,29 @@
require 'spec_helper'
describe Chef::Knife::SubcommandLoader do
+ let(:loader) { Chef::Knife::SubcommandLoader.new(File.join(CHEF_SPEC_DATA, 'knife-site-subcommands')) }
+ let(:home) { File.join(CHEF_SPEC_DATA, 'knife-home') }
+ let(:plugin_dir) { File.join(home, '.chef', 'plugins', 'knife') }
+
before do
allow(Chef::Platform).to receive(:windows?) { false }
- @home = File.join(CHEF_SPEC_DATA, 'knife-home')
- @env = {'HOME' => @home}
- @loader = Chef::Knife::SubcommandLoader.new(File.join(CHEF_SPEC_DATA, 'knife-site-subcommands'), @env)
+ Chef::Util::PathHelper.class_variable_set(:@@home_dir, home)
+ end
+
+ after do
+ Chef::Util::PathHelper.class_variable_set(:@@home_dir, nil)
end
it "builds a list of the core subcommand file require paths" do
- expect(@loader.subcommand_files).not_to be_empty
- @loader.subcommand_files.each do |require_path|
+ expect(loader.subcommand_files).not_to be_empty
+ loader.subcommand_files.each do |require_path|
expect(require_path).to match(/chef\/knife\/.*|plugins\/knife\/.*/)
end
end
it "finds files installed via rubygems" do
- expect(@loader.find_subcommands_via_rubygems).to include('chef/knife/node_create')
- @loader.find_subcommands_via_rubygems.each {|rel_path, abs_path| expect(abs_path).to match(%r[chef/knife/.+])}
+ expect(loader.find_subcommands_via_rubygems).to include('chef/knife/node_create')
+ loader.find_subcommands_via_rubygems.each {|rel_path, abs_path| expect(abs_path).to match(%r[chef/knife/.+])}
end
it "finds files from latest version of installed gems" do
@@ -54,23 +60,23 @@ describe Chef::Knife::SubcommandLoader do
expect(gems[0]).to receive(:full_gem_path).and_return('/usr/lib/ruby/gems/knife-ec2-0.5.12')
expect(Dir).to receive(:[]).with('/usr/lib/ruby/gems/knife-ec2-0.5.12/lib/chef/knife/*.rb').and_return(gem_files)
end
- expect(@loader).to receive(:find_subcommands_via_dirglob).and_return({})
- expect(@loader.find_subcommands_via_rubygems.values.select { |file| file =~ /knife-ec2/ }.sort).to eq(gem_files)
+ expect(loader).to receive(:find_subcommands_via_dirglob).and_return({})
+ expect(loader.find_subcommands_via_rubygems.values.select { |file| file =~ /knife-ec2/ }.sort).to eq(gem_files)
end
it "finds files using a dirglob when rubygems is not available" do
- expect(@loader.find_subcommands_via_dirglob).to include('chef/knife/node_create')
- @loader.find_subcommands_via_dirglob.each {|rel_path, abs_path| expect(abs_path).to match(%r[chef/knife/.+])}
+ expect(loader.find_subcommands_via_dirglob).to include('chef/knife/node_create')
+ loader.find_subcommands_via_dirglob.each {|rel_path, abs_path| expect(abs_path).to match(%r[chef/knife/.+])}
end
it "finds user-specific subcommands in the user's ~/.chef directory" do
- expected_command = File.join(@home, '.chef', 'plugins', 'knife', 'example_home_subcommand.rb')
- expect(@loader.site_subcommands).to include(expected_command)
+ expected_command = File.join(home, '.chef', 'plugins', 'knife', 'example_home_subcommand.rb')
+ expect(loader.site_subcommands).to include(expected_command)
end
it "finds repo specific subcommands by searching for a .chef directory" do
expected_command = File.join(CHEF_SPEC_DATA, 'knife-site-subcommands', 'plugins', 'knife', 'example_subcommand.rb')
- expect(@loader.site_subcommands).to include(expected_command)
+ expect(loader.site_subcommands).to include(expected_command)
end
# https://github.com/opscode/chef-dk/issues/227
@@ -137,25 +143,19 @@ describe Chef::Knife::SubcommandLoader do
end
before do
- expect(@loader).to receive(:find_files_latest_gems).with("chef/knife/*.rb").and_return(all_found_commands)
- expect(@loader).to receive(:find_subcommands_via_dirglob).and_return({})
+ expect(loader).to receive(:find_files_latest_gems).with("chef/knife/*.rb").and_return(all_found_commands)
+ expect(loader).to receive(:find_subcommands_via_dirglob).and_return({})
end
it "ignores commands from the non-matching gem install" do
- expect(@loader.find_subcommands_via_rubygems.values).to eq(expected_valid_commands)
+ expect(loader.find_subcommands_via_rubygems.values).to eq(expected_valid_commands)
end
end
describe "finding 3rd party plugins" do
- let(:env_home) { "/home/alice" }
- let(:manifest_path) { env_home + "/.chef/plugin_manifest.json" }
-
- before do
- env_dup = ENV.to_hash
- allow(ENV).to receive(:[]) { |key| env_dup[key] }
- allow(ENV).to receive(:[]).with("HOME").and_return(env_home)
- end
+ let(:home) { "/home/alice" }
+ let(:manifest_path) { home + "/.chef/plugin_manifest.json" }
context "when there is not a ~/.chef/plugin_manifest.json file" do
before do
@@ -168,14 +168,14 @@ describe Chef::Knife::SubcommandLoader do
else
expect(Gem.source_index).to receive(:latest_specs).and_call_original
end
- @loader.subcommand_files.each do |require_path|
+ loader.subcommand_files.each do |require_path|
expect(require_path).to match(/chef\/knife\/.*|plugins\/knife\/.*/)
end
end
context "and HOME environment variable is not set" do
before do
- allow(ENV).to receive(:[]).with("HOME").and_return(nil)
+ allow(Chef::Util::PathHelper).to receive(:home).and_return(nil)
end
it "searches rubygems for plugins" do
@@ -184,7 +184,7 @@ describe Chef::Knife::SubcommandLoader do
else
expect(Gem.source_index).to receive(:latest_specs).and_call_original
end
- @loader.subcommand_files.each do |require_path|
+ loader.subcommand_files.each do |require_path|
expect(require_path).to match(/chef\/knife\/.*|plugins\/knife\/.*/)
end
end
@@ -215,7 +215,7 @@ describe Chef::Knife::SubcommandLoader do
it "uses paths from the manifest instead of searching gems" do
expect(Gem::Specification).not_to receive(:latest_specs).and_call_original
- expect(@loader.subcommand_files).to include(ec2_server_create_plugin)
+ expect(loader.subcommand_files).to include(ec2_server_create_plugin)
end
end
diff --git a/spec/unit/mixin/params_validate_spec.rb b/spec/unit/mixin/params_validate_spec.rb
index 85e1c1abab..1b61f9b238 100644
--- a/spec/unit/mixin/params_validate_spec.rb
+++ b/spec/unit/mixin/params_validate_spec.rb
@@ -339,69 +339,83 @@ describe Chef::Mixin::ParamsValidate do
end.to raise_error(Chef::Exceptions::ValidationFailed)
end
- it "should set and return a value, then return the same value" do
- value = "meow"
- expect(@vo.set_or_return(:test, value, {}).object_id).to eq(value.object_id)
- expect(@vo.set_or_return(:test, nil, {}).object_id).to eq(value.object_id)
+ def self.test_set_or_return_method(method)
+ # caller is responsible for passing in the right arg to get 'return' behavior
+ return_arg = method == :nillable_set_or_return ? TinyClass::NULL_ARG : nil
+
+ it "#{method} should set and return a value, then return the same value" do
+ value = "meow"
+ expect(@vo.send(method,:test, value, {}).object_id).to eq(value.object_id)
+ expect(@vo.send(method,:test, return_arg, {}).object_id).to eq(value.object_id)
+ end
+
+ it "#{method} should set and return a default value when the argument is nil, then return the same value" do
+ value = "meow"
+ expect(@vo.send(method,:test, return_arg, { :default => value }).object_id).to eq(value.object_id)
+ expect(@vo.send(method,:test, return_arg, {}).object_id).to eq(value.object_id)
+ end
+
+ it "#{method} should raise an ArgumentError when argument is nil and required is true" do
+ expect {
+ @vo.send(method,:test, return_arg, { :required => true })
+ }.to raise_error(ArgumentError)
+ end
+
+ it "#{method} should not raise an error when argument is nil and required is false" do
+ expect {
+ @vo.send(method,:test, return_arg, { :required => false })
+ }.not_to raise_error
+ end
+
+ it "#{method} should set and return @name, then return @name for foo when argument is nil" do
+ value = "meow"
+ expect(@vo.send(method,:name, value, { }).object_id).to eq(value.object_id)
+ expect(@vo.send(method,:foo, return_arg, { :name_attribute => true }).object_id).to eq(value.object_id)
+ end
+
+ it "#{method} should allow DelayedEvaluator instance to be set for value regardless of restriction" do
+ value = Chef::DelayedEvaluator.new{ 'test' }
+ @vo.send(method,:test, value, {:kind_of => Numeric})
+ end
+
+ it "#{method} should raise an error when delayed evaluated attribute is not valid" do
+ value = Chef::DelayedEvaluator.new{ 'test' }
+ @vo.send(method,:test, value, {:kind_of => Numeric})
+ expect do
+ @vo.send(method,:test, return_arg, {:kind_of => Numeric})
+ end.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "#{method} should create DelayedEvaluator instance when #lazy is used" do
+ @vo.send(method,:delayed, @vo.lazy{ 'test' }, {})
+ expect(@vo.instance_variable_get(:@delayed)).to be_a(Chef::DelayedEvaluator)
+ end
+
+ it "#{method} should execute block on each call when DelayedEvaluator" do
+ value = 'fubar'
+ @vo.send(method,:test, @vo.lazy{ value }, {})
+ expect(@vo.send(method,:test, return_arg, {})).to eq('fubar')
+ value = 'foobar'
+ expect(@vo.send(method,:test, return_arg, {})).to eq('foobar')
+ value = 'fauxbar'
+ expect(@vo.send(method,:test, return_arg, {})).to eq('fauxbar')
+ end
+
+ it "#{method} should not evaluate non DelayedEvaluator instances" do
+ value = lambda{ 'test' }
+ @vo.send(method,:test, value, {})
+ expect(@vo.send(method,:test, return_arg, {}).object_id).to eq(value.object_id)
+ expect(@vo.send(method,:test, return_arg, {})).to be_a(Proc)
+ end
end
- it "should set and return a default value when the argument is nil, then return the same value" do
- value = "meow"
- expect(@vo.set_or_return(:test, nil, { :default => value }).object_id).to eq(value.object_id)
- expect(@vo.set_or_return(:test, nil, {}).object_id).to eq(value.object_id)
- end
-
- it "should raise an ArgumentError when argument is nil and required is true" do
- expect {
- @vo.set_or_return(:test, nil, { :required => true })
- }.to raise_error(ArgumentError)
- end
-
- it "should not raise an error when argument is nil and required is false" do
- expect {
- @vo.set_or_return(:test, nil, { :required => false })
- }.not_to raise_error
- end
-
- it "should set and return @name, then return @name for foo when argument is nil" do
- value = "meow"
- expect(@vo.set_or_return(:name, value, { }).object_id).to eq(value.object_id)
- expect(@vo.set_or_return(:foo, nil, { :name_attribute => true }).object_id).to eq(value.object_id)
- end
+ test_set_or_return_method(:set_or_return)
+ test_set_or_return_method(:nillable_set_or_return)
- it "should allow DelayedEvaluator instance to be set for value regardless of restriction" do
- value = Chef::DelayedEvaluator.new{ 'test' }
- @vo.set_or_return(:test, value, {:kind_of => Numeric})
+ it "nillable_set_or_return supports nilling values" do
+ expect(@vo.nillable_set_or_return(:test, "meow", {})).to eq("meow")
+ expect(@vo.nillable_set_or_return(:test, TinyClass::NULL_ARG, {})).to eq("meow")
+ expect(@vo.nillable_set_or_return(:test, nil, {})).to be_nil
+ expect(@vo.nillable_set_or_return(:test, TinyClass::NULL_ARG, {})).to be_nil
end
-
- it "should raise an error when delayed evaluated attribute is not valid" do
- value = Chef::DelayedEvaluator.new{ 'test' }
- @vo.set_or_return(:test, value, {:kind_of => Numeric})
- expect do
- @vo.set_or_return(:test, nil, {:kind_of => Numeric})
- end.to raise_error(Chef::Exceptions::ValidationFailed)
- end
-
- it "should create DelayedEvaluator instance when #lazy is used" do
- @vo.set_or_return(:delayed, @vo.lazy{ 'test' }, {})
- expect(@vo.instance_variable_get(:@delayed)).to be_a(Chef::DelayedEvaluator)
- end
-
- it "should execute block on each call when DelayedEvaluator" do
- value = 'fubar'
- @vo.set_or_return(:test, @vo.lazy{ value }, {})
- expect(@vo.set_or_return(:test, nil, {})).to eq('fubar')
- value = 'foobar'
- expect(@vo.set_or_return(:test, nil, {})).to eq('foobar')
- value = 'fauxbar'
- expect(@vo.set_or_return(:test, nil, {})).to eq('fauxbar')
- end
-
- it "should not evaluate non DelayedEvaluator instances" do
- value = lambda{ 'test' }
- @vo.set_or_return(:test, value, {})
- expect(@vo.set_or_return(:test, nil, {}).object_id).to eq(value.object_id)
- expect(@vo.set_or_return(:test, nil, {})).to be_a(Proc)
- end
-
end
diff --git a/spec/unit/mixin/powershell_type_coercions_spec.rb b/spec/unit/mixin/powershell_type_coercions_spec.rb
new file mode 100644
index 0000000000..988c3926c1
--- /dev/null
+++ b/spec/unit/mixin/powershell_type_coercions_spec.rb
@@ -0,0 +1,72 @@
+#
+# Author:: Jay Mundrawala (<jdm@chef.io>)
+# Copyright:: Copyright (c) 2015 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 'spec_helper'
+require 'chef/mixin/powershell_type_coercions'
+require 'base64'
+
+class Chef::PSTypeTester
+ include Chef::Mixin::PowershellTypeCoercions
+end
+
+describe Chef::Mixin::PowershellTypeCoercions do
+ let (:test_class) { Chef::PSTypeTester.new }
+
+ describe '#translate_type' do
+ it 'should single quote a string' do
+ expect(test_class.translate_type('foo')).to eq("'foo'")
+ end
+
+ ["'", '"', '#', '`'].each do |c|
+ it "should base64 encode a string that contains #{c}" do
+ expect(test_class.translate_type("#{c}")).to match(Base64.strict_encode64(c))
+ end
+ end
+
+ it 'should not quote an integer' do
+ expect(test_class.translate_type(123)).to eq('123')
+ end
+
+ it 'should not quote a floating point number' do
+ expect(test_class.translate_type(123.4)).to eq('123.4')
+ end
+
+ it 'should return $false when an instance of FalseClass is provided' do
+ expect(test_class.translate_type(false)).to eq('$false')
+ end
+
+ it 'should return $true when an instance of TrueClass is provided' do
+ expect(test_class.translate_type(true)).to eq('$true')
+ end
+
+ it 'should translate all members of a hash and wrap them in @{} separated by ;' do
+ expect(test_class.translate_type({"a" => 1, "b" => 1.2, "c" => false, "d" => true
+ })).to eq("@{a=1;b=1.2;c=$false;d=$true}")
+ end
+
+ it 'should translat all members of an array and them by a ,' do
+ expect(test_class.translate_type([true, false])).to eq('@($true,$false)')
+ end
+
+ it 'should fall back :to_psobject if we have not defined at explicit rule' do
+ ps_obj = double("PSObject")
+ expect(ps_obj).to receive(:to_psobject).and_return('$true')
+ expect(test_class.translate_type(ps_obj)).to eq('($true)')
+ end
+ end
+end
diff --git a/spec/unit/platform/query_helpers_spec.rb b/spec/unit/platform/query_helpers_spec.rb
index 7aafc287ea..1dbd07a021 100644
--- a/spec/unit/platform/query_helpers_spec.rb
+++ b/spec/unit/platform/query_helpers_spec.rb
@@ -53,3 +53,25 @@ describe 'Chef::Platform#supports_dsc?' do
end
end
end
+
+describe 'Chef::Platform#supports_dsc_invoke_resource?' do
+ it 'returns false if powershell is not present' do
+ node = Chef::Node.new
+ expect(Chef::Platform.supports_dsc_invoke_resource?(node)).to be_falsey
+ end
+
+ ['1.0', '2.0', '3.0', '4.0', '5.0.10017.9'].each do |version|
+ it "returns false for Powershell #{version}" do
+ node = Chef::Node.new
+ node.automatic[:languages][:powershell][:version] = version
+ expect(Chef::Platform.supports_dsc_invoke_resource?(node)).to be_falsey
+ end
+ end
+
+ it "returns true for Powershell 5.0.10018.0" do
+ node = Chef::Node.new
+ node.automatic[:languages][:powershell][:version] = "5.0.10018.0"
+ expect(Chef::Platform.supports_dsc_invoke_resource?(node)).to be_truthy
+ end
+end
+
diff --git a/spec/unit/platform_spec.rb b/spec/unit/platform_spec.rb
index fb65ef0fea..e0115bc42a 100644
--- a/spec/unit/platform_spec.rb
+++ b/spec/unit/platform_spec.rb
@@ -20,8 +20,6 @@ require 'spec_helper'
describe "Chef::Platform supports" do
[
- :mac_os_x,
- :mac_os_x_server,
:freebsd,
:ubuntu,
:debian,
@@ -34,9 +32,6 @@ describe "Chef::Platform supports" do
:gentoo,
:arch,
:solaris,
- :mswin,
- :mingw32,
- :windows,
:gcel,
:ibm_powerkvm
].each do |platform|
diff --git a/spec/unit/provider/dsc_resource_spec.rb b/spec/unit/provider/dsc_resource_spec.rb
new file mode 100644
index 0000000000..0a6c22bdcf
--- /dev/null
+++ b/spec/unit/provider/dsc_resource_spec.rb
@@ -0,0 +1,84 @@
+#
+# Author:: Jay Mundrawala (<jdm@chef.io>)
+#
+# Copyright:: Copyright (c) 2014 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'
+require 'spec_helper'
+
+describe Chef::Provider::DscResource do
+ let (:events) { Chef::EventDispatch::Dispatcher.new }
+ let (:run_context) { Chef::RunContext.new(node, {}, events) }
+ let (:resource) { Chef::Resource::DscResource.new("dscresource", run_context) }
+ let (:provider) do
+ Chef::Provider::DscResource.new(resource, run_context)
+ end
+
+ context 'when Powershell does not support Invoke-DscResource' do
+ let (:node) {
+ node = Chef::Node.new
+ node.automatic[:languages][:powershell][:version] = '4.0'
+ node
+ }
+
+ it 'raises a NoProviderAvailable exception' do
+ expect(provider).not_to receive(:meta_configuration)
+ expect{provider.run_action(:run)}.to raise_error(
+ Chef::Exceptions::NoProviderAvailable, /5\.0\.10018\.0/)
+ end
+ end
+
+ context 'when Powershell supports Invoke-DscResource' do
+ let (:node) {
+ node = Chef::Node.new
+ node.automatic[:languages][:powershell][:version] = '5.0.10018.0'
+ node
+ }
+
+ context 'when RefreshMode is not set to Disabled' do
+ let (:meta_configuration) { {'RefreshMode' => 'AnythingElse'}}
+
+ it 'raises an exception' do
+ expect(provider).to receive(:meta_configuration).and_return(
+ meta_configuration)
+ expect { provider.run_action(:run) }.to raise_error(
+ Chef::Exceptions::NoProviderAvailable, /Disabled/)
+ end
+ end
+
+ context 'when RefreshMode is set to Disabled' do
+ let (:meta_configuration) { {'RefreshMode' => 'Disabled'}}
+
+ it 'does not update the resource if it is up to date' do
+ expect(provider).to receive(:meta_configuration).and_return(
+ meta_configuration)
+ expect(provider).to receive(:test_resource).and_return(true)
+ provider.run_action(:run)
+ expect(resource).not_to be_updated
+ end
+
+ it 'converges the resource if it is not up to date' do
+ expect(provider).to receive(:meta_configuration).and_return(
+ meta_configuration)
+ expect(provider).to receive(:test_resource).and_return(false)
+ expect(provider).to receive(:set_resource)
+ provider.run_action(:run)
+ expect(resource).to be_updated
+ end
+ end
+ end
+end
diff --git a/spec/unit/provider/package/openbsd_spec.rb b/spec/unit/provider/package/openbsd_spec.rb
index ee9c9e89fb..b0cdb9969d 100644
--- a/spec/unit/provider/package/openbsd_spec.rb
+++ b/spec/unit/provider/package/openbsd_spec.rb
@@ -21,28 +21,116 @@ require 'ostruct'
describe Chef::Provider::Package::Openbsd do
+ let(:node) do
+ node = Chef::Node.new
+ node.default['kernel'] = {'name' => 'OpenBSD', 'release' => '5.5', 'machine' => 'amd64'}
+ node
+ end
+
+ let (:provider) do
+ events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, events)
+ Chef::Provider::Package::Openbsd.new(new_resource, run_context)
+ end
+
+ let(:new_resource) { Chef::Resource::Package.new(name)}
+
before(:each) do
- @node = Chef::Node.new
- @node.default['kernel'] = {'name' => 'OpenBSD', 'release' => '5.5', 'machine' => 'amd64'}
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
ENV['PKG_PATH'] = nil
end
describe "install a package" do
- before do
- @name = 'ihavetoes'
- @new_resource = Chef::Resource::Package.new(@name)
- @current_resource = Chef::Resource::Package.new(@name)
- @provider = Chef::Provider::Package::Openbsd.new(@new_resource, @run_context)
- @provider.current_resource = @current_resource
- end
- it "should run the installation command" do
- expect(@provider).to receive(:shell_out!).with(
- "pkg_add -r #{@name}",
- {:env => {"PKG_PATH" => "http://ftp.OpenBSD.org/pub/OpenBSD/5.5/packages/amd64/"}}
- ) {OpenStruct.new :status => true}
- @provider.install_package(@name, nil)
+ let(:name) { 'ihavetoes' }
+ let(:version) {'0.0'}
+
+ context 'when not already installed' do
+ before do
+ allow(provider).to receive(:shell_out!).with("pkg_info -e \"#{name}->0\"", anything()).and_return(instance_double('shellout', :stdout => ''))
+ end
+
+ context 'when there is a single candidate' do
+
+ context 'when installing from source' do
+ it 'should run the installation command' do
+ pending('Installing from source is not supported yet')
+ # This is a consequence of load_current_resource being called before define_resource_requirements
+ # It can be deleted once an implementation is provided
+ allow(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}\"", anything()).and_return(
+ instance_double('shellout', :stdout => "#{name}-#{version}\n"))
+ new_resource.source('/some/path/on/disk.tgz')
+ provider.run_action(:install)
+ end
+ end
+
+ context 'when source is not provided' do
+ it 'should run the installation command' do
+ expect(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}\"", anything()).and_return(
+ instance_double('shellout', :stdout => "#{name}-#{version}\n"))
+ expect(provider).to receive(:shell_out!).with(
+ "pkg_add -r #{name}-#{version}",
+ {:env => {"PKG_PATH" => "http://ftp.OpenBSD.org/pub/OpenBSD/5.5/packages/amd64/"}}
+ ) {OpenStruct.new :status => true}
+ provider.run_action(:install)
+ end
+ end
+ end
+
+ context 'when there are multiple candidates' do
+ let(:flavor_a) { 'flavora' }
+ let(:flavor_b) { 'flavorb' }
+
+ context 'if no version is specified' do
+ it 'should raise an exception' do
+ expect(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}\"", anything()).and_return(
+ instance_double('shellout', :stdout => "#{name}-#{version}-#{flavor_a}\n#{name}-#{version}-#{flavor_b}\n"))
+ expect { provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /multiple matching candidates/)
+ end
+ end
+
+ context 'if a flavor is specified' do
+
+ let(:flavor) { 'flavora' }
+ let(:package_name) {'ihavetoes' }
+ let(:name) { "#{package_name}--#{flavor}" }
+
+ context 'if no version is specified' do
+ it 'should run the installation command' do
+ expect(provider).to receive(:shell_out!).with("pkg_info -e \"#{package_name}->0\"", anything()).and_return(instance_double('shellout', :stdout => ''))
+ expect(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}\"", anything()).and_return(
+ instance_double('shellout', :stdout => "#{name}-#{version}-#{flavor}\n"))
+ expect(provider).to receive(:shell_out!).with(
+ "pkg_add -r #{name}-#{version}-#{flavor}",
+ {:env => {"PKG_PATH" => "http://ftp.OpenBSD.org/pub/OpenBSD/5.5/packages/amd64/"}}
+ ) {OpenStruct.new :status => true}
+ provider.run_action(:install)
+ end
+ end
+
+ context 'if a version is specified' do
+ it 'runs the installation command' do
+ pending('Specifying both a version and flavor is not supported')
+ new_resource.version(version)
+ allow(provider).to receive(:shell_out!).with(/pkg_info -e/, anything()).and_return(instance_double('shellout', :stdout => ''))
+ allow(provider).to receive(:candidate_version).and_return("#{package_name}-#{version}-#{flavor}")
+ provider.run_action(:install)
+ end
+ end
+ end
+
+ context 'if a version is specified' do
+ it 'should use the flavor from the version' do
+ expect(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}-#{version}-#{flavor_b}\"", anything()).and_return(
+ instance_double('shellout', :stdout => "#{name}-#{version}-#{flavor_a}\n"))
+
+ new_resource.version("#{version}-#{flavor_b}")
+ expect(provider).to receive(:shell_out!).with(
+ "pkg_add -r #{name}-#{version}-#{flavor_b}",
+ {:env => {"PKG_PATH" => "http://ftp.OpenBSD.org/pub/OpenBSD/5.5/packages/amd64/"}}
+ ) {OpenStruct.new :status => true}
+ provider.run_action(:install)
+ end
+ end
+ end
end
end
diff --git a/spec/unit/provider/service/macosx_spec.rb b/spec/unit/provider/service/macosx_spec.rb
index fb751592df..9905a6e4ae 100644
--- a/spec/unit/provider/service/macosx_spec.rb
+++ b/spec/unit/provider/service/macosx_spec.rb
@@ -22,17 +22,17 @@ describe Chef::Provider::Service::Macosx do
describe ".gather_plist_dirs" do
context "when HOME directory is set" do
before do
- allow(ENV).to receive(:[]).with('HOME').and_return("/User/someuser")
+ allow(Chef::Util::PathHelper).to receive(:home).with('Library', 'LaunchAgents').and_yield('/Users/someuser/Library/LaunchAgents')
end
it "includes users's LaunchAgents folder" do
- expect(described_class.gather_plist_dirs).to include("#{ENV['HOME']}/Library/LaunchAgents")
+ expect(described_class.gather_plist_dirs).to include("/Users/someuser/Library/LaunchAgents")
end
end
context "when HOME directory is not set" do
before do
- allow(ENV).to receive(:[]).with('HOME').and_return(nil)
+ allow(Chef::Util::PathHelper).to receive(:home).with('Library', 'LaunchAgents').and_return(nil)
end
it "doesn't include user's LaunchAgents folder" do
diff --git a/spec/unit/provider_resolver_spec.rb b/spec/unit/provider_resolver_spec.rb
index a9fa08ebfd..63c381f08e 100644
--- a/spec/unit/provider_resolver_spec.rb
+++ b/spec/unit/provider_resolver_spec.rb
@@ -452,6 +452,137 @@ describe Chef::ProviderResolver do
end
+ describe "for the package provider" do
+ let(:resource_name) { :package }
+
+ before do
+ expect(provider_resolver).not_to receive(:maybe_chef_platform_lookup)
+ end
+
+ %w{mac_os_x mac_os_x_server}.each do |platform|
+ describe "on #{platform}" do
+ let(:os) { "darwin" }
+ let(:platform) { platform }
+ let(:platform_family) { "mac_os_x" }
+ let(:platform_version) { "10.9.2" }
+
+
+ it "returns a Chef::Provider::Package::Homebrew provider" do
+ expect(resolved_provider).to eql(Chef::Provider::Package::Homebrew)
+ end
+ end
+ end
+ end
+
+ provider_mapping = {
+ "mac_os_x" => {
+ :package => Chef::Provider::Package::Homebrew,
+ :user => Chef::Provider::User::Dscl,
+ :group => Chef::Provider::Group::Dscl,
+ },
+ "mac_os_x_server" => {
+ :package => Chef::Provider::Package::Homebrew,
+ :user => Chef::Provider::User::Dscl,
+ :group => Chef::Provider::Group::Dscl,
+ },
+ "mswin" => {
+ :env => Chef::Provider::Env::Windows,
+ :user => Chef::Provider::User::Windows,
+ :group => Chef::Provider::Group::Windows,
+ :mount => Chef::Provider::Mount::Windows,
+ :batch => Chef::Provider::Batch,
+ :powershell_script => Chef::Provider::PowershellScript,
+ },
+ "mingw32" => {
+ :env => Chef::Provider::Env::Windows,
+ :user => Chef::Provider::User::Windows,
+ :group => Chef::Provider::Group::Windows,
+ :mount => Chef::Provider::Mount::Windows,
+ :batch => Chef::Provider::Batch,
+ :powershell_script => Chef::Provider::PowershellScript,
+ },
+ "windows" => {
+ :env => Chef::Provider::Env::Windows,
+ :user => Chef::Provider::User::Windows,
+ :group => Chef::Provider::Group::Windows,
+ :mount => Chef::Provider::Mount::Windows,
+ :batch => Chef::Provider::Batch,
+ :powershell_script => Chef::Provider::PowershellScript,
+ },
+ "aix" => {
+ :cron => Chef::Provider::Cron::Aix,
+ },
+ "netbsd"=> {
+ :group => Chef::Provider::Group::Groupmod,
+ },
+ "openbsd" => {
+ :group => Chef::Provider::Group::Usermod,
+ :package => Chef::Provider::Package::Openbsd,
+ },
+ }
+
+ def self.do_platform(platform_hash)
+ platform_hash.each do |resource, provider|
+ describe "for #{resource}" do
+ let(:resource_name) { resource }
+
+ it "resolves to a #{provider}" do
+ expect(resolved_provider).to eql(provider)
+ end
+ end
+ end
+ end
+
+ describe "individual platform mappings" do
+ let(:resource_name) { :user }
+
+ before do
+ expect(provider_resolver).not_to receive(:maybe_chef_platform_lookup)
+ end
+
+ %w{mac_os_x mac_os_x_server}.each do |platform|
+ describe "on #{platform}" do
+ let(:os) { "darwin" }
+ let(:platform) { platform }
+ let(:platform_family) { "mac_os_x" }
+ let(:platform_version) { "10.9.2" }
+
+ do_platform(provider_mapping[platform])
+ end
+ end
+
+ %w{mswin mingw32 windows}.each do |platform|
+ describe "on #{platform}" do
+ let(:os) { "windows" }
+ let(:platform) { platform }
+ let(:platform_family) { "windows" }
+ let(:platform_version) { "10.9.2" }
+
+ do_platform(provider_mapping[platform])
+ end
+ end
+
+ describe "on AIX" do
+ let(:os) { "aix" }
+ let(:platform) { "aix" }
+ let(:platform_family) { "aix" }
+ let(:platform_version) { "6.2" }
+
+ do_platform(provider_mapping['aix'])
+ end
+
+ %w{netbsd openbsd}.each do |platform|
+ describe "on #{platform}" do
+ let(:os) { platform }
+ let(:platform) { platform }
+ let(:platform_family) { platform }
+ let(:platform_version) { "10.0-RELEASE" }
+
+ do_platform(provider_mapping[platform])
+ end
+ end
+ end
+
describe "resolving static providers" do
def resource_class(resource)
Chef::Resource.const_get(convert_to_class_name(resource.to_s))
@@ -481,6 +612,7 @@ describe Chef::ProviderResolver do
link: Chef::Provider::Link,
log: Chef::Provider::Log::ChefLog,
macports_package: Chef::Provider::Package::Macports,
+ mdadm: Chef::Provider::Mdadm,
pacman_package: Chef::Provider::Package::Pacman,
paludis_package: Chef::Provider::Package::Paludis,
perl: Chef::Provider::Script,
diff --git a/spec/unit/recipe_spec.rb b/spec/unit/recipe_spec.rb
index 8d0b1bcfd2..e1604483f3 100644
--- a/spec/unit/recipe_spec.rb
+++ b/spec/unit/recipe_spec.rb
@@ -593,5 +593,9 @@ describe Chef::Recipe do
expect(recipe.singleton_class.included_modules).to include(Chef::DSL::Audit)
expect(recipe.respond_to?(:control_group)).to be true
end
+
+ it "should respond to :ps_credential from Chef::DSL::Powershell" do
+ expect(recipe.respond_to?(:ps_credential)).to be true
+ end
end
end
diff --git a/spec/unit/resource/deploy_spec.rb b/spec/unit/resource/deploy_spec.rb
index 0403a7ba6b..07f5f973c0 100644
--- a/spec/unit/resource/deploy_spec.rb
+++ b/spec/unit/resource/deploy_spec.rb
@@ -30,12 +30,35 @@ describe Chef::Resource::Deploy do
class << self
+
+ def resource_has_a_hash_attribute(attr_name)
+ it "has a Hash attribute for #{attr_name.to_s}" do
+ @resource.send(attr_name, {foo: "bar"})
+ expect(@resource.send(attr_name)).to eql({foo: "bar"})
+ expect {@resource.send(attr_name, 8675309)}.to raise_error(ArgumentError)
+ end
+
+ it "the Hash attribute for #{attr_name.to_s} is nillable" do
+ @resource.send(attr_name, {foo: "bar"})
+ expect(@resource.send(attr_name)).to eql({foo: "bar"})
+ @resource.send(attr_name, nil)
+ expect(@resource.send(attr_name)).to eql(nil)
+ end
+ end
+
def resource_has_a_string_attribute(attr_name)
it "has a String attribute for #{attr_name.to_s}" do
@resource.send(attr_name, "this is a string")
expect(@resource.send(attr_name)).to eql("this is a string")
expect {@resource.send(attr_name, 8675309)}.to raise_error(ArgumentError)
end
+
+ it "the String attribute for #{attr_name.to_s} is nillable" do
+ @resource.send(attr_name, "this is a string")
+ expect(@resource.send(attr_name)).to eql("this is a string")
+ @resource.send(attr_name, nil)
+ expect(@resource.send(attr_name)).to eql(nil)
+ end
end
def resource_has_a_boolean_attribute(attr_name, opts={:defaults_to=>false})
@@ -189,6 +212,10 @@ describe Chef::Resource::Deploy do
expect(@resource.symlink_before_migrate).to eq({"wtf?" => "wtf is going on"})
end
+ resource_has_a_hash_attribute :symlink_before_migrate
+ resource_has_a_hash_attribute :symlinks
+ resource_has_a_hash_attribute :additional_remotes
+
resource_has_a_callback_attribute :before_migrate
resource_has_a_callback_attribute :before_symlink
resource_has_a_callback_attribute :before_restart
diff --git a/spec/unit/resource/dsc_resource_spec.rb b/spec/unit/resource/dsc_resource_spec.rb
new file mode 100644
index 0000000000..ae15f56eaf
--- /dev/null
+++ b/spec/unit/resource/dsc_resource_spec.rb
@@ -0,0 +1,85 @@
+#
+# Author:: Adam Edwards (<adamed@getchef.com>)
+# Copyright:: Copyright (c) 2014 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 'spec_helper'
+
+describe Chef::Resource::DscResource do
+ let(:dsc_test_resource_name) { 'DSCTest' }
+ let(:dsc_test_property_name) { :DSCTestProperty }
+ let(:dsc_test_property_value) { 'DSCTestValue' }
+
+ context 'when Powershell supports Dsc' do
+ let(:dsc_test_run_context) {
+ node = Chef::Node.new
+ node.automatic[:languages][:powershell][:version] = '5.0.10018.0'
+ empty_events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, empty_events)
+ }
+ let(:dsc_test_resource) {
+ Chef::Resource::DscResource.new(dsc_test_resource_name, dsc_test_run_context)
+ }
+
+ it "has a default action of `:run`" do
+ expect(dsc_test_resource.action).to eq(:run)
+ end
+
+ it "has an allowed_actions attribute with only the `:run` and `:nothing` attributes" do
+ expect(dsc_test_resource.allowed_actions.to_set).to eq([:run,:nothing].to_set)
+ end
+
+ it "allows the resource attribute to be set" do
+ dsc_test_resource.resource(dsc_test_resource_name)
+ expect(dsc_test_resource.resource).to eq(dsc_test_resource_name)
+ end
+
+ it "allows the module_name attribute to be set" do
+ dsc_test_resource.module_name(dsc_test_resource_name)
+ expect(dsc_test_resource.module_name).to eq(dsc_test_resource_name)
+ end
+
+ context "when setting a dsc property" do
+ it "allows setting a dsc property with a property name of type Symbol" do
+ dsc_test_resource.property(dsc_test_property_name, dsc_test_property_value)
+ expect(dsc_test_resource.property(dsc_test_property_name)).to eq(dsc_test_property_value)
+ expect(dsc_test_resource.properties[dsc_test_property_name]).to eq(dsc_test_property_value)
+ end
+
+ it "raises a TypeError if property_name is not a symbol" do
+ expect{
+ dsc_test_resource.property('Foo', dsc_test_property_value)
+ }.to raise_error(TypeError)
+ end
+
+ context "when using DelayedEvaluators" do
+ it "allows setting a dsc property with a property name of type Symbol" do
+ dsc_test_resource.property(dsc_test_property_name, Chef::DelayedEvaluator.new {
+ dsc_test_property_value
+ })
+ expect(dsc_test_resource.property(dsc_test_property_name)).to eq(dsc_test_property_value)
+ expect(dsc_test_resource.properties[dsc_test_property_name]).to eq(dsc_test_property_value)
+ end
+ end
+ end
+
+ context 'Powershell DSL methods' do
+ it "responds to :ps_credential" do
+ expect(dsc_test_resource.respond_to?(:ps_credential)).to be true
+ end
+ end
+ end
+end
diff --git a/spec/unit/shell_spec.rb b/spec/unit/shell_spec.rb
index 0e028f4359..617abcfde2 100644
--- a/spec/unit/shell_spec.rb
+++ b/spec/unit/shell_spec.rb
@@ -56,7 +56,7 @@ describe Shell do
describe "configuring IRB" do
it "configures irb history" do
Shell.configure_irb
- expect(Shell.irb_conf[:HISTORY_FILE]).to eq("~/.chef/chef_shell_history")
+ expect(Shell.irb_conf[:HISTORY_FILE]).to eq("#{ENV['HOME']}/.chef/chef_shell_history")
expect(Shell.irb_conf[:SAVE_HISTORY]).to eq(1000)
end
diff --git a/spec/unit/util/dsc/resource_store.rb b/spec/unit/util/dsc/resource_store.rb
new file mode 100644
index 0000000000..a89e73fcaa
--- /dev/null
+++ b/spec/unit/util/dsc/resource_store.rb
@@ -0,0 +1,76 @@
+#
+# Author:: Jay Mundrawala <jdm@chef.io>
+# Copyright:: Copyright (c) 2015 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'
+require 'chef/util/dsc/resource_store'
+
+describe Chef::Util::DSC::ResourceStore do
+ let(:resource_store) { Chef::Util::DSC::ResourceStore.new }
+ let(:resource_a) { {
+ 'ResourceType' => 'AFoo',
+ 'Name' => 'Foo',
+ 'Module' => {'Name' => 'ModuleA'}
+ }
+ }
+
+ let(:resource_b) { {
+ 'ResourceType' => 'BFoo',
+ 'Name' => 'Foo',
+ 'Module' => {'Name' => 'ModuleB'}
+ }
+ }
+
+ context 'when resources are not cached' do
+ context 'when calling #resources' do
+ it 'returns an empty array' do
+ expect(resource_store.resources).to eql([])
+ end
+ end
+
+ context 'when calling #find' do
+ it 'returns an empty list if it cannot find any matching resources' do
+ expect(resource_store).to receive(:query_resource).and_return([])
+ expect(resource_store.find('foo')).to eql([])
+ end
+
+ it 'returns the resource if it is found (comparisons are case insensitive)' do
+ expect(resource_store).to receive(:query_resource).and_return([resource_a])
+ expect(resource_store.find('foo')).to eql([resource_a])
+ end
+
+ it 'returns multiple resoures if they are found' do
+ expect(resource_store).to receive(:query_resource).and_return([resource_a, resource_b])
+ expect(resource_store.find('foo')).to include(resource_a, resource_b)
+ end
+
+ it 'deduplicates resources by ResourceName' do
+ expect(resource_store).to receive(:query_resource).and_return([resource_a, resource_a])
+ resource_store.find('foo')
+ expect(resource_store.resources).to eq([resource_a])
+ end
+ end
+ end
+
+ context 'when resources are cached' do
+ it 'recalls resources from the cache if present' do
+ expect(resource_store).not_to receive(:query_resource)
+ expect(resource_store).to receive(:resources).and_return([resource_a])
+ resource_store.find('foo')
+ end
+ end
+end
diff --git a/spec/unit/util/powershell/ps_credential_spec.rb b/spec/unit/util/powershell/ps_credential_spec.rb
new file mode 100644
index 0000000000..bac58b02e5
--- /dev/null
+++ b/spec/unit/util/powershell/ps_credential_spec.rb
@@ -0,0 +1,37 @@
+#
+# Author:: Jay Mundrawala <jdm@chef.io>
+# Copyright:: Copyright (c) 2015 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'
+require 'chef/util/powershell/ps_credential'
+
+describe Chef::Util::Powershell::PSCredential do
+ let (:username) { 'foo' }
+ let (:password) { 'password' }
+
+ context 'when username and password are provided' do
+ let(:ps_credential) { Chef::Util::Powershell::PSCredential.new(username, password)}
+ context 'when calling to_psobject' do
+ it 'should create the script to create a PSCredential when calling' do
+ allow(ps_credential).to receive(:encrypt).with(password).and_return('encrypted')
+ expect(ps_credential.to_psobject).to eq(
+ "New-Object System.Management.Automation.PSCredential("\
+ "'#{username}',('encrypted' | ConvertTo-SecureString))")
+ end
+ end
+ end
+end
diff --git a/spec/unit/workstation_config_loader_spec.rb b/spec/unit/workstation_config_loader_spec.rb
index a865103188..72631f3dfa 100644
--- a/spec/unit/workstation_config_loader_spec.rb
+++ b/spec/unit/workstation_config_loader_spec.rb
@@ -65,7 +65,7 @@ describe Chef::WorkstationConfigLoader do
let(:home) { "/Users/example.user" }
before do
- env["HOME"] = home
+ allow(Chef::Util::PathHelper).to receive(:home).with('.chef').and_yield(File.join(home, '.chef'))
allow(config_loader).to receive(:path_exists?).with("#{home}/.chef/knife.rb").and_return(true)
end