diff options
author | Jesse Campbell <hikeit@gmail.com> | 2013-03-01 12:25:01 -0500 |
---|---|---|
committer | Jesse Campbell <hikeit@gmail.com> | 2013-03-01 12:25:01 -0500 |
commit | a2faf73b16726fef02dd644437105d05c7700bf1 (patch) | |
tree | 3eff391b63dd9e9c496d34fff5f7be88ce630b49 | |
parent | 2139a6888756fdcedce559c9e38a8ea76a5f7b54 (diff) | |
parent | 77f911b99e68bf1ae068ce79f70f670f52943b2e (diff) | |
download | chef-a2faf73b16726fef02dd644437105d05c7700bf1.tar.gz |
Merge branch 'master' into rest_client
Conflicts:
lib/chef/provider/remote_file.rb
lib/chef/provider/remote_file/ftp.rb
lib/chef/providers.rb
lib/chef/resource/remote_file.rb
spec/unit/provider/remote_file/ftp_spec.rb
spec/unit/provider/remote_file_spec.rb
spec/unit/resource/remote_file_spec.rb
116 files changed, 2952 insertions, 414 deletions
@@ -3,7 +3,10 @@ source :rubygems gemspec gem "activesupport", :group => :compat_testing, :platform => "ruby" -gem "ronn" + +group(:docgen) do + gem "ronn" +end group(:development, :test) do gem 'rack', "~> 1.5.1" @@ -1,6 +1,6 @@ # Chef -* Documentation: [http://wiki.opscode.com/display/chef/Home/](http://wiki.opscode.com/display/chef/Home/) +* Documentation: [http://docs.opscode.com](http://docs.opscode.com) * Source: [http://github.com/opscode/chef/tree/master](http://github.com/opscode/chef/tree/master) * Tickets/Issues: [http://tickets.opscode.com](http://tickets.opscode.com) * IRC: `#chef` and `#chef-hacking` on Freenode diff --git a/bin/chef-service-manager b/bin/chef-service-manager new file mode 100755 index 0000000000..c7f2fa43a9 --- /dev/null +++ b/bin/chef-service-manager @@ -0,0 +1,31 @@ +#!/usr/bin/env ruby +# +# ./chef-service-manager - Control chef-service on Windows platforms. +# +# Author:: Serdar Sutay (serdar@opscode.com) +# Copyright:: Copyright (c) 2013 Opscode, 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 'rubygems' +$:.unshift(File.join(File.dirname(__FILE__), "..", "lib")) +require 'chef' +require 'chef/application/windows_service_manager' + +if Chef::Platform.windows? + Chef::Application::WindowsServiceManager.new.run +else + puts "chef-service-manager is only available on Windows platforms." +end + diff --git a/chef.gemspec b/chef.gemspec index 9f97141aea..4e2f57af44 100644 --- a/chef.gemspec +++ b/chef.gemspec @@ -20,19 +20,25 @@ Gem::Specification.new do |s| s.add_dependency "ohai", ">= 0.6.0" s.add_dependency "rest-client", ">= 1.0.4", "< 1.7.0" - s.add_dependency "json", ">= 1.4.4", "<= 1.6.1" + + # The JSON gem reliably releases breaking changes as a patch release + s.add_dependency "json", ">= 1.4.4", "<= 1.7.7" s.add_dependency "yajl-ruby", "~> 1.1" - s.add_dependency "net-ssh", "~> 2.2.2" + s.add_dependency "net-ssh", "~> 2.6" s.add_dependency "net-ssh-multi", "~> 1.1.0" # CHEF-3027: The knife-cloud plugins require newer features from highline, core chef should not. s.add_dependency "highline", ">= 1.6.9" s.add_dependency "erubis" - %w(rdoc sdoc ronn rake rack rspec_junit_formatter).each { |gem| s.add_development_dependency gem } + %w(rdoc sdoc rake rack rspec_junit_formatter).each { |gem| s.add_development_dependency gem } %w(rspec-core rspec-expectations rspec-mocks).each { |gem| s.add_development_dependency gem, "~> 2.12.0" } s.bindir = "bin" - s.executables = %w( chef-client chef-solo knife chef-shell shef chef-apply ) + # chef-service-manager is a windows only executable. + # However gemspec doesn't give us a way to have this executable only + # on windows. So we're including this in all platforms. + s.executables = %w( chef-client chef-solo knife chef-shell shef chef-apply chef-service-manager ) + s.require_path = 'lib' s.files = %w(Rakefile LICENSE README.md CONTRIBUTING.md) + Dir.glob("{distro,lib,tasks,spec}/**/*") end diff --git a/ci/jenkins_run_tests.bat b/ci/jenkins_run_tests.bat index b5076949f3..4d1b1d1477 100644 --- a/ci/jenkins_run_tests.bat +++ b/ci/jenkins_run_tests.bat @@ -1,7 +1,7 @@ set PATH=C:\Ruby192\bin;%PATH% ruby -v -call bundle install --binstubs --path vendor/bundle || ( call rm Gemfile.lock && call bundle install --binstubs --path vendor/bundle ) +call bundle install --binstubs --without docgen --path vendor/bundle || ( call rm Gemfile.lock && call bundle install --binstubs --path vendor/bundle ) ruby bin\rspec -r rspec_junit_formatter -f RspecJunitFormatter -o test.xml -f documentation spec/functional spec/unit spec/stress set RSPEC_ERRORLVL=%ERRORLEVEL% diff --git a/ci/jenkins_run_tests.sh b/ci/jenkins_run_tests.sh index 90a1fec0c8..9330d7cd07 100755 --- a/ci/jenkins_run_tests.sh +++ b/ci/jenkins_run_tests.sh @@ -5,7 +5,7 @@ export PATH=/usr/local/bin:$PATH ruby -v; # remove the Gemfile.lock and try again if bundler fails. # This should take care of Gemfile changes that result in "bad" bundles without forcing us to rebundle every time -bundle install --path vendor/bundle || ( rm Gemfile.lock && bundle install --path vendor/bundle ) +bundle install --without docgen --path vendor/bundle || ( rm Gemfile.lock && bundle install --path vendor/bundle ) bundle exec rspec -r rspec_junit_formatter -f RspecJunitFormatter -o test.xml -f documentation spec; RSPEC_RETURNCODE=$? diff --git a/distro/common/html/chef-client.8.html b/distro/common/html/chef-client.8.html index 0f46109bb0..867e01fc2c 100644 --- a/distro/common/html/chef-client.8.html +++ b/distro/common/html/chef-client.8.html @@ -124,9 +124,9 @@ wiki, http://wiki.opscode.com/display/chef/Home.</p> <h2 id="AUTHOR">AUTHOR</h2> -<p>Chef was written by Adam Jacob <a href="mailto:adam@ospcode.com" data-bare-link="true">adam@ospcode.com</a> of Opscode +<p>Chef was written by Adam Jacob <a data-bare-link="true" href="mailto:adam@ospcode.com">adam@ospcode.com</a> of Opscode (http://www.opscode.com), with contributions from the community. This -manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a> with +manual page was written by Joshua Timberman <a data-bare-link="true" href="mailto:joshua@opscode.com">joshua@opscode.com</a> with help2man. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> @@ -135,7 +135,7 @@ found in /usr/share/common-licenses/Apache-2.0.</p> <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>chef-client(8)</li> </ol> diff --git a/distro/common/html/chef-expander.8.html b/distro/common/html/chef-expander.8.html index d8afd7fe7a..1668b3fc73 100644 --- a/distro/common/html/chef-expander.8.html +++ b/distro/common/html/chef-expander.8.html @@ -143,9 +143,9 @@ wiki, http://wiki.opscode.com/display/chef/Home.</p> <h2 id="AUTHOR">AUTHOR</h2> -<p>Chef was written by Adam Jacob <a href="mailto:adam@ospcode.com" data-bare-link="true">adam@ospcode.com</a> of Opscode +<p>Chef was written by Adam Jacob <a data-bare-link="true" href="mailto:adam@ospcode.com">adam@ospcode.com</a> of Opscode (http://www.opscode.com), with contributions from the community. This -manual page was created by Nuo Yan <a href="mailto:nuo@opscode.com" data-bare-link="true">nuo@opscode.com</a>. Permission is +manual page was created by Nuo Yan <a data-bare-link="true" href="mailto:nuo@opscode.com">nuo@opscode.com</a>. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> @@ -154,7 +154,7 @@ found in /usr/share/common-licenses/Apache-2.0.</p> <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>chef-expander(8)</li> </ol> diff --git a/distro/common/html/chef-expanderctl.8.html b/distro/common/html/chef-expanderctl.8.html index c944dabf1a..cfe63ea536 100644 --- a/distro/common/html/chef-expanderctl.8.html +++ b/distro/common/html/chef-expanderctl.8.html @@ -125,9 +125,9 @@ wiki, http://wiki.opscode.com/display/chef/Home.</p> <h2 id="AUTHOR">AUTHOR</h2> -<p>Chef was written by Adam Jacob <a href="mailto:adam@ospcode.com" data-bare-link="true">adam@ospcode.com</a> of Opscode +<p>Chef was written by Adam Jacob <a data-bare-link="true" href="mailto:adam@ospcode.com">adam@ospcode.com</a> of Opscode (http://www.opscode.com), with contributions from the community. This -manual page was created by Nuo Yan <a href="mailto:nuo@opscode.com" data-bare-link="true">nuo@opscode.com</a>. Permission is +manual page was created by Nuo Yan <a data-bare-link="true" href="mailto:nuo@opscode.com">nuo@opscode.com</a>. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> @@ -136,7 +136,7 @@ found in /usr/share/common-licenses/Apache-2.0.</p> <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>chef-expanderctl(8)</li> </ol> diff --git a/distro/common/html/chef-server-webui.8.html b/distro/common/html/chef-server-webui.8.html index fa766a9cdf..8cd11313ac 100644 --- a/distro/common/html/chef-server-webui.8.html +++ b/distro/common/html/chef-server-webui.8.html @@ -163,9 +163,9 @@ is located on the Chef wiki, http://wiki.opscode.com/display/chef/Home.</p> <h2 id="AUTHOR">AUTHOR</h2> -<p>Chef was written by Adam Jacob <a href="mailto:adam@ospcode.com" data-bare-link="true">adam@ospcode.com</a> of Opscode +<p>Chef was written by Adam Jacob <a data-bare-link="true" href="mailto:adam@ospcode.com">adam@ospcode.com</a> of Opscode (http://www.opscode.com), with contributions from the community. This -manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a> with +manual page was written by Joshua Timberman <a data-bare-link="true" href="mailto:joshua@opscode.com">joshua@opscode.com</a> with help2man for the Debian project (but may be used by others). Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> @@ -175,7 +175,7 @@ found in /usr/share/common-licenses/Apache-2.0.</p> <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>chef-server-webui(8)</li> </ol> diff --git a/distro/common/html/chef-server.8.html b/distro/common/html/chef-server.8.html index 67a619a0bf..c4d5577e6d 100644 --- a/distro/common/html/chef-server.8.html +++ b/distro/common/html/chef-server.8.html @@ -161,9 +161,9 @@ wiki, http://wiki.opscode.com/display/chef/Home.</p> <h2 id="AUTHOR">AUTHOR</h2> -<p>Chef was written by Adam Jacob <a href="mailto:adam@ospcode.com" data-bare-link="true">adam@ospcode.com</a> of Opscode +<p>Chef was written by Adam Jacob <a data-bare-link="true" href="mailto:adam@ospcode.com">adam@ospcode.com</a> of Opscode (http://www.opscode.com), with contributions from the community. This -manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a> with +manual page was written by Joshua Timberman <a data-bare-link="true" href="mailto:joshua@opscode.com">joshua@opscode.com</a> with help2man. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> @@ -172,7 +172,7 @@ found in /usr/share/common-licenses/Apache-2.0.</p> <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>chef-server(8)</li> </ol> diff --git a/distro/common/html/chef-shell.1.html b/distro/common/html/chef-shell.1.html index ab042518c0..220413ab87 100644 --- a/distro/common/html/chef-shell.1.html +++ b/distro/common/html/chef-shell.1.html @@ -260,13 +260,13 @@ libraries.</p> <h2 id="AUTHOR">AUTHOR</h2> -<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many +<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community. chef-shell was written by Daniel DeLeo.</p> <h2 id="DOCUMENTATION">DOCUMENTATION</h2> -<p> This manual page was written by Daniel DeLeo <a href="mailto:dan@opscode.com" data-bare-link="true">dan@opscode.com</a>. +<p> This manual page was written by Daniel DeLeo <a href="mailto:dan@opscode.com" data-bare-link="true">dan@opscode.com</a>. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> @@ -276,7 +276,7 @@ libraries.</p> <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>chef-shell(1)</li> </ol> diff --git a/distro/common/html/chef-solo.8.html b/distro/common/html/chef-solo.8.html index 7e9e7aac23..7c842cc67f 100644 --- a/distro/common/html/chef-solo.8.html +++ b/distro/common/html/chef-solo.8.html @@ -170,9 +170,9 @@ http://wiki.opscode.com/display/chef/Home.</p> <h2 id="AUTHOR">AUTHOR</h2> -<p>Chef was written by Adam Jacob <a href="mailto:adam@ospcode.com" data-bare-link="true">adam@ospcode.com</a> of Opscode +<p>Chef was written by Adam Jacob <a href="mailto:adam@ospcode.com" data-bare-link="true">adam@ospcode.com</a> of Opscode (http://www.opscode.com), with contributions from the community. This -manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a> with +manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a> with help2man. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> @@ -181,7 +181,7 @@ found in /usr/share/common-licenses/Apache-2.0.</p> <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>chef-solo(8)</li> </ol> diff --git a/distro/common/html/chef-solr.8.html b/distro/common/html/chef-solr.8.html index ffe7495dc3..45ed7c05dd 100644 --- a/distro/common/html/chef-solr.8.html +++ b/distro/common/html/chef-solr.8.html @@ -144,9 +144,9 @@ wiki, http://wiki.opscode.com/display/chef/Home.</p> <h2 id="AUTHOR">AUTHOR</h2> -<p>Chef was written by Adam Jacob <a href="mailto:adam@ospcode.com" data-bare-link="true">adam@ospcode.com</a> of Opscode +<p>Chef was written by Adam Jacob <a href="mailto:adam@ospcode.com" data-bare-link="true">adam@ospcode.com</a> of Opscode (http://www.opscode.com), with contributions from the community. This -manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a> with +manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a> with help2man. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> @@ -155,7 +155,7 @@ found in /usr/share/common-licenses/Apache-2.0.</p> <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>chef-solr(8)</li> </ol> diff --git a/distro/common/html/knife-bootstrap.1.html b/distro/common/html/knife-bootstrap.1.html index dfd44fa890..578fe57631 100644 --- a/distro/common/html/knife-bootstrap.1.html +++ b/distro/common/html/knife-bootstrap.1.html @@ -218,11 +218,11 @@ to other users via the process list using tools such as <span class="man-ref">ps <h2 id="AUTHOR">AUTHOR</h2> -<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> +<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> <h2 id="DOCUMENTATION">DOCUMENTATION</h2> -<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>. +<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> <h2 id="CHEF">CHEF</h2> @@ -231,7 +231,7 @@ to other users via the process list using tools such as <span class="man-ref">ps <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>knife-bootstrap(1)</li> </ol> diff --git a/distro/common/html/knife-client.1.html b/distro/common/html/knife-client.1.html index fdb72adf68..746646972b 100644 --- a/distro/common/html/knife-client.1.html +++ b/distro/common/html/knife-client.1.html @@ -196,20 +196,20 @@ setting up a host for management with Chef.</p> <h2 id="AUTHOR">AUTHOR</h2> -<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> +<p> Chef was written by Adam Jacob <a data-bare-link="true" href="mailto:adam@opscode.com">adam@opscode.com</a> with many contributions from the community.</p> <h2 id="DOCUMENTATION">DOCUMENTATION</h2> -<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>. +<p> This manual page was written by Joshua Timberman <a data-bare-link="true" href="mailto:joshua@opscode.com">joshua@opscode.com</a>. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> <h2 id="CHEF">CHEF</h2> -<p> Knife is distributed with Chef. <a href="http://wiki.opscode.com/display/chef/Home" data-bare-link="true">http://wiki.opscode.com/display/chef/Home</a></p> +<p> Knife is distributed with Chef. <a data-bare-link="true" href="http://wiki.opscode.com/display/chef/Home">http://wiki.opscode.com/display/chef/Home</a></p> <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>knife-client(1)</li> </ol> diff --git a/distro/common/html/knife-configure.1.html b/distro/common/html/knife-configure.1.html index 0174a9c524..6a6f58f816 100644 --- a/distro/common/html/knife-configure.1.html +++ b/distro/common/html/knife-configure.1.html @@ -147,11 +147,11 @@ may need to modify that setting after copying to a remote host.</p></li> <h2 id="AUTHOR">AUTHOR</h2> -<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> +<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> <h2 id="DOCUMENTATION">DOCUMENTATION</h2> -<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>. +<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> <h2 id="CHEF">CHEF</h2> @@ -160,7 +160,7 @@ may need to modify that setting after copying to a remote host.</p></li> <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>knife-configure(1)</li> </ol> diff --git a/distro/common/html/knife-cookbook-site.1.html b/distro/common/html/knife-cookbook-site.1.html index 65129b3eb9..b0e354687b 100644 --- a/distro/common/html/knife-cookbook-site.1.html +++ b/distro/common/html/knife-cookbook-site.1.html @@ -192,7 +192,7 @@ configuration file.</p> <h2 id="DESCRIPTION">DESCRIPTION</h2> -<p>The cookbook site, <a href="http://community.opscode.com/" data-bare-link="true">http://community.opscode.com/</a>, is a cookbook +<p>The cookbook site, <a data-bare-link="true" href="http://community.opscode.com/">http://community.opscode.com/</a>, is a cookbook distribution service operated by Opscode. This service provides users with a central location to publish cookbooks for sharing with other community members.</p> @@ -214,24 +214,24 @@ configuration file.</p> <h2 id="SEE-ALSO">SEE ALSO</h2> <p> <strong><span class="man-ref">knife-cookbook<span class="s">(1)</span></span></strong> - <a href="http://community.opscode.com/cookbooks" data-bare-link="true">http://community.opscode.com/cookbooks</a></p> + <a data-bare-link="true" href="http://community.opscode.com/cookbooks">http://community.opscode.com/cookbooks</a></p> <h2 id="AUTHOR">AUTHOR</h2> -<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> +<p> Chef was written by Adam Jacob <a data-bare-link="true" href="mailto:adam@opscode.com">adam@opscode.com</a> with many contributions from the community.</p> <h2 id="DOCUMENTATION">DOCUMENTATION</h2> -<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>. +<p> This manual page was written by Joshua Timberman <a data-bare-link="true" href="mailto:joshua@opscode.com">joshua@opscode.com</a>. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> <h2 id="CHEF">CHEF</h2> -<p> Knife is distributed with Chef. <a href="http://wiki.opscode.com/display/chef/Home" data-bare-link="true">http://wiki.opscode.com/display/chef/Home</a></p> +<p> Knife is distributed with Chef. <a data-bare-link="true" href="http://wiki.opscode.com/display/chef/Home">http://wiki.opscode.com/display/chef/Home</a></p> <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>knife-cookbook-site(1)</li> </ol> diff --git a/distro/common/html/knife-cookbook.1.html b/distro/common/html/knife-cookbook.1.html index 193754aa23..0557ccb638 100644 --- a/distro/common/html/knife-cookbook.1.html +++ b/distro/common/html/knife-cookbook.1.html @@ -361,11 +361,11 @@ cookbook.</p> <h2 id="AUTHOR">AUTHOR</h2> -<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> +<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> <h2 id="DOCUMENTATION">DOCUMENTATION</h2> -<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>. +<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> <h2 id="CHEF">CHEF</h2> @@ -374,7 +374,7 @@ cookbook.</p> <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>knife-cookbook(1)</li> </ol> diff --git a/distro/common/html/knife-data-bag.1.html b/distro/common/html/knife-data-bag.1.html index 0a4fff4621..23e05d795c 100644 --- a/distro/common/html/knife-data-bag.1.html +++ b/distro/common/html/knife-data-bag.1.html @@ -215,11 +215,11 @@ encryption keys.</p> <h2 id="AUTHOR">AUTHOR</h2> -<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> +<p> Chef was written by Adam Jacob <a data-bare-link="true" href="mailto:adam@opscode.com">adam@opscode.com</a> with many contributions from the community.</p> <h2 id="DOCUMENTATION">DOCUMENTATION</h2> -<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>. +<p> This manual page was written by Joshua Timberman <a data-bare-link="true" href="mailto:joshua@opscode.com">joshua@opscode.com</a>. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> <h2 id="CHEF">CHEF</h2> @@ -228,7 +228,7 @@ encryption keys.</p> <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>knife-data-bag(1)</li> </ol> diff --git a/distro/common/html/knife-environment.1.html b/distro/common/html/knife-environment.1.html index 1e158de643..e1951f5c6d 100644 --- a/distro/common/html/knife-environment.1.html +++ b/distro/common/html/knife-environment.1.html @@ -239,25 +239,25 @@ override_attributes "aws_s3_bucket" => "production" <h2 id="SEE-ALSO">SEE ALSO</h2> <p> <strong><span class="man-ref">knife-node<span class="s">(1)</span></span></strong> <strong><span class="man-ref">knife-cookbook<span class="s">(1)</span></span></strong> <strong><span class="man-ref">knife-role<span class="s">(1)</span></span></strong> - <a href="http://wiki.opscode.com/display/chef/Environments" data-bare-link="true">http://wiki.opscode.com/display/chef/Environments</a> - <a href="http://wiki.opscode.com/display/chef/Version+Constraints" data-bare-link="true">http://wiki.opscode.com/display/chef/Version+Constraints</a></p> + <a data-bare-link="true" href="http://wiki.opscode.com/display/chef/Environments">http://wiki.opscode.com/display/chef/Environments</a> + <a data-bare-link="true" href="http://wiki.opscode.com/display/chef/Version+Constraints">http://wiki.opscode.com/display/chef/Version+Constraints</a></p> <h2 id="AUTHOR">AUTHOR</h2> -<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> +<p> Chef was written by Adam Jacob <a data-bare-link="true" href="mailto:adam@opscode.com">adam@opscode.com</a> with many contributions from the community.</p> <h2 id="DOCUMENTATION">DOCUMENTATION</h2> -<p> This manual page was written by Daniel DeLeo <a href="mailto:dan@opscode.com" data-bare-link="true">dan@opscode.com</a>. +<p> This manual page was written by Daniel DeLeo <a data-bare-link="true" href="mailto:dan@opscode.com">dan@opscode.com</a>. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> <h2 id="CHEF">CHEF</h2> -<p> Knife is distributed with Chef. <a href="http://wiki.opscode.com/display/chef/Home" data-bare-link="true">http://wiki.opscode.com/display/chef/Home</a></p> +<p> Knife is distributed with Chef. <a data-bare-link="true" href="http://wiki.opscode.com/display/chef/Home">http://wiki.opscode.com/display/chef/Home</a></p> <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>knife-environment(1)</li> </ol> diff --git a/distro/common/html/knife-exec.1.html b/distro/common/html/knife-exec.1.html index affde6c700..b0308c3c55 100644 --- a/distro/common/html/knife-exec.1.html +++ b/distro/common/html/knife-exec.1.html @@ -111,11 +111,11 @@ description of the commands available.</p> <h2 id="AUTHOR">AUTHOR</h2> -<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> +<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> <h2 id="DOCUMENTATION">DOCUMENTATION</h2> -<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>. +<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> <h2 id="CHEF">CHEF</h2> @@ -124,7 +124,7 @@ description of the commands available.</p> <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>knife-exec(1)</li> </ol> diff --git a/distro/common/html/knife-index.1.html b/distro/common/html/knife-index.1.html index 633098cbb1..e85c25d0db 100644 --- a/distro/common/html/knife-index.1.html +++ b/distro/common/html/knife-index.1.html @@ -102,20 +102,20 @@ time for all objects to be indexed and available for search.</p> <h2 id="AUTHOR">AUTHOR</h2> -<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> +<p> Chef was written by Adam Jacob <a data-bare-link="true" href="mailto:adam@opscode.com">adam@opscode.com</a> with many contributions from the community.</p> <h2 id="DOCUMENTATION">DOCUMENTATION</h2> -<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>. +<p> This manual page was written by Joshua Timberman <a data-bare-link="true" href="mailto:joshua@opscode.com">joshua@opscode.com</a>. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> <h2 id="CHEF">CHEF</h2> -<p> Knife is distributed with Chef. <a href="http://wiki.opscode.com/display/chef/Home" data-bare-link="true">http://wiki.opscode.com/display/chef/Home</a></p> +<p> Knife is distributed with Chef. <a data-bare-link="true" href="http://wiki.opscode.com/display/chef/Home">http://wiki.opscode.com/display/chef/Home</a></p> <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>knife-index(1)</li> </ol> diff --git a/distro/common/html/knife-node.1.html b/distro/common/html/knife-node.1.html index ba60474098..29c30c177e 100644 --- a/distro/common/html/knife-node.1.html +++ b/distro/common/html/knife-node.1.html @@ -227,11 +227,11 @@ run list, the correct syntax is "role[ROLE_NAME]"</p> <h2 id="AUTHOR">AUTHOR</h2> -<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> +<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> <h2 id="DOCUMENTATION">DOCUMENTATION</h2> -<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>. +<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> <h2 id="CHEF">CHEF</h2> @@ -240,7 +240,7 @@ run list, the correct syntax is "role[ROLE_NAME]"</p> <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>knife-node(1)</li> </ol> diff --git a/distro/common/html/knife-role.1.html b/distro/common/html/knife-role.1.html index 0c61868c70..d3a01b760c 100644 --- a/distro/common/html/knife-role.1.html +++ b/distro/common/html/knife-role.1.html @@ -177,11 +177,11 @@ run_list.</p> <h2 id="AUTHOR">AUTHOR</h2> -<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> +<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> <h2 id="DOCUMENTATION">DOCUMENTATION</h2> -<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>. +<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> <h2 id="CHEF">CHEF</h2> @@ -190,7 +190,7 @@ run_list.</p> <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>knife-role(1)</li> </ol> diff --git a/distro/common/html/knife-search.1.html b/distro/common/html/knife-search.1.html index 08f0cc2695..a60d441b88 100644 --- a/distro/common/html/knife-search.1.html +++ b/distro/common/html/knife-search.1.html @@ -265,11 +265,11 @@ www.example.com:</p> <h2 id="AUTHOR">AUTHOR</h2> -<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> +<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> <h2 id="DOCUMENTATION">DOCUMENTATION</h2> -<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>. +<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> <h2 id="CHEF">CHEF</h2> @@ -278,7 +278,7 @@ www.example.com:</p> <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>knife-search(1)</li> </ol> diff --git a/distro/common/html/knife-ssh.1.html b/distro/common/html/knife-ssh.1.html index 72d80369b7..f6a5f6d154 100644 --- a/distro/common/html/knife-ssh.1.html +++ b/distro/common/html/knife-ssh.1.html @@ -133,20 +133,20 @@ option.</dd> <h2 id="AUTHOR">AUTHOR</h2> -<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> +<p> Chef was written by Adam Jacob <a data-bare-link="true" href="mailto:adam@opscode.com">adam@opscode.com</a> with many contributions from the community.</p> <h2 id="DOCUMENTATION">DOCUMENTATION</h2> -<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>. +<p> This manual page was written by Joshua Timberman <a data-bare-link="true" href="mailto:joshua@opscode.com">joshua@opscode.com</a>. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> <h2 id="CHEF">CHEF</h2> -<p> Knife is distributed with Chef. <a href="http://wiki.opscode.com/display/chef/Home" data-bare-link="true">http://wiki.opscode.com/display/chef/Home</a></p> +<p> Knife is distributed with Chef. <a data-bare-link="true" href="http://wiki.opscode.com/display/chef/Home">http://wiki.opscode.com/display/chef/Home</a></p> <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>knife-ssh(1)</li> </ol> diff --git a/distro/common/html/knife-status.1.html b/distro/common/html/knife-status.1.html index 332dc8fd18..201c2a1bea 100644 --- a/distro/common/html/knife-status.1.html +++ b/distro/common/html/knife-status.1.html @@ -105,11 +105,11 @@ may not be publicly reachable.</p> <h2 id="AUTHOR">AUTHOR</h2> -<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> +<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> <h2 id="DOCUMENTATION">DOCUMENTATION</h2> -<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>. +<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> <h2 id="CHEF">CHEF</h2> @@ -118,7 +118,7 @@ may not be publicly reachable.</p> <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>knife-status(1)</li> </ol> diff --git a/distro/common/html/knife-tag.1.html b/distro/common/html/knife-tag.1.html index 47c91b7d4e..98fd8734bb 100644 --- a/distro/common/html/knife-tag.1.html +++ b/distro/common/html/knife-tag.1.html @@ -114,11 +114,11 @@ <h2 id="AUTHOR">AUTHOR</h2> -<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> +<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> with many contributions from the community.</p> <h2 id="DOCUMENTATION">DOCUMENTATION</h2> -<p> This manual page was written by Daniel DeLeo <a href="mailto:dan@opscode.com" data-bare-link="true">dan@opscode.com</a>. +<p> This manual page was written by Daniel DeLeo <a href="mailto:dan@opscode.com" data-bare-link="true">dan@opscode.com</a>. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p> <h2 id="CHEF">CHEF</h2> @@ -127,7 +127,7 @@ <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>knife-tag(1)</li> </ol> diff --git a/distro/common/html/knife.1.html b/distro/common/html/knife.1.html index 8b5343bd79..6d0b3076b0 100644 --- a/distro/common/html/knife.1.html +++ b/distro/common/html/knife.1.html @@ -286,12 +286,12 @@ data editing entirely.</dd> <h2 id="AUTHOR">AUTHOR</h2> -<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> of Opscode +<p> Chef was written by Adam Jacob <a href="mailto:adam@opscode.com" data-bare-link="true">adam@opscode.com</a> of Opscode (<a href="http://www.opscode.com" data-bare-link="true">http://www.opscode.com</a>), with contributions from the community.</p> <h2 id="DOCUMENTATION">DOCUMENTATION</h2> -<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>.</p> +<p> This manual page was written by Joshua Timberman <a href="mailto:joshua@opscode.com" data-bare-link="true">joshua@opscode.com</a>.</p> <h2 id="LICENSE">LICENSE</h2> @@ -305,7 +305,7 @@ data editing entirely.</dd> <ol class='man-decor man-foot man foot'> - <li class='tl'>Chef 11.0.0</li> + <li class='tl'>Chef 11.2.0</li> <li class='tc'>February 2013</li> <li class='tr'>knife(1)</li> </ol> diff --git a/distro/common/man/man1/chef-shell.1 b/distro/common/man/man1/chef-shell.1 index 35caa9ec51..adaa3aac0d 100644 --- a/distro/common/man/man1/chef-shell.1 +++ b/distro/common/man/man1/chef-shell.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "CHEF\-SHELL" "1" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "CHEF\-SHELL" "1" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBchef\-shell\fR \- Interactive Chef Console diff --git a/distro/common/man/man1/knife-bootstrap.1 b/distro/common/man/man1/knife-bootstrap.1 index dce7c86746..56eee90d4a 100644 --- a/distro/common/man/man1/knife-bootstrap.1 +++ b/distro/common/man/man1/knife-bootstrap.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "KNIFE\-BOOTSTRAP" "1" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "KNIFE\-BOOTSTRAP" "1" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBknife\-bootstrap\fR \- Install Chef Client on a remote host diff --git a/distro/common/man/man1/knife-client.1 b/distro/common/man/man1/knife-client.1 index 98a34cc006..a0c36be126 100644 --- a/distro/common/man/man1/knife-client.1 +++ b/distro/common/man/man1/knife-client.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "KNIFE\-CLIENT" "1" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "KNIFE\-CLIENT" "1" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBknife\-client\fR \- Manage Chef API Clients diff --git a/distro/common/man/man1/knife-configure.1 b/distro/common/man/man1/knife-configure.1 index 72246f786d..81828674ee 100644 --- a/distro/common/man/man1/knife-configure.1 +++ b/distro/common/man/man1/knife-configure.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "KNIFE\-CONFIGURE" "1" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "KNIFE\-CONFIGURE" "1" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBknife\-configure\fR \- Generate configuration files for knife or Chef Client diff --git a/distro/common/man/man1/knife-cookbook-site.1 b/distro/common/man/man1/knife-cookbook-site.1 index 21ee1810dd..feca0c4314 100644 --- a/distro/common/man/man1/knife-cookbook-site.1 +++ b/distro/common/man/man1/knife-cookbook-site.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "KNIFE\-COOKBOOK\-SITE" "1" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "KNIFE\-COOKBOOK\-SITE" "1" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBknife\-cookbook\-site\fR \- Install and update open source cookbooks diff --git a/distro/common/man/man1/knife-cookbook.1 b/distro/common/man/man1/knife-cookbook.1 index a16ff5e94f..a308025ef4 100644 --- a/distro/common/man/man1/knife-cookbook.1 +++ b/distro/common/man/man1/knife-cookbook.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "KNIFE\-COOKBOOK" "1" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "KNIFE\-COOKBOOK" "1" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBknife\-cookbook\fR \- upload and manage chef cookbooks diff --git a/distro/common/man/man1/knife-data-bag.1 b/distro/common/man/man1/knife-data-bag.1 index d07ca2e208..9322bb4f2b 100644 --- a/distro/common/man/man1/knife-data-bag.1 +++ b/distro/common/man/man1/knife-data-bag.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "KNIFE\-DATA\-BAG" "1" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "KNIFE\-DATA\-BAG" "1" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBknife\-data\-bag\fR \- Store arbitrary data on a Chef Server diff --git a/distro/common/man/man1/knife-environment.1 b/distro/common/man/man1/knife-environment.1 index 6285cfff8c..c0fb06c2c5 100644 --- a/distro/common/man/man1/knife-environment.1 +++ b/distro/common/man/man1/knife-environment.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "KNIFE\-ENVIRONMENT" "1" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "KNIFE\-ENVIRONMENT" "1" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBknife\-environment\fR \- Define cookbook policies for the environments in your infrastructure diff --git a/distro/common/man/man1/knife-exec.1 b/distro/common/man/man1/knife-exec.1 index 7225c6f8e3..a2e64332d0 100644 --- a/distro/common/man/man1/knife-exec.1 +++ b/distro/common/man/man1/knife-exec.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "KNIFE\-EXEC" "1" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "KNIFE\-EXEC" "1" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBknife\-exec\fR \- Run user scripts using the Chef API DSL diff --git a/distro/common/man/man1/knife-index.1 b/distro/common/man/man1/knife-index.1 index dee8933ac6..283d66ca4d 100644 --- a/distro/common/man/man1/knife-index.1 +++ b/distro/common/man/man1/knife-index.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "KNIFE\-INDEX" "1" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "KNIFE\-INDEX" "1" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBknife\-index\fR \- Rebuild the search index on a Chef Server diff --git a/distro/common/man/man1/knife-node.1 b/distro/common/man/man1/knife-node.1 index f1e77da10d..b6c0e0bc1c 100644 --- a/distro/common/man/man1/knife-node.1 +++ b/distro/common/man/man1/knife-node.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "KNIFE\-NODE" "1" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "KNIFE\-NODE" "1" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBknife\-node\fR \- Manage the hosts in your infrastructure diff --git a/distro/common/man/man1/knife-role.1 b/distro/common/man/man1/knife-role.1 index f42b223a94..babfcf14a8 100644 --- a/distro/common/man/man1/knife-role.1 +++ b/distro/common/man/man1/knife-role.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "KNIFE\-ROLE" "1" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "KNIFE\-ROLE" "1" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBknife\-role\fR \- Group common configuration settings diff --git a/distro/common/man/man1/knife-search.1 b/distro/common/man/man1/knife-search.1 index 886862ef6b..d85499d6a3 100644 --- a/distro/common/man/man1/knife-search.1 +++ b/distro/common/man/man1/knife-search.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "KNIFE\-SEARCH" "1" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "KNIFE\-SEARCH" "1" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBknife\-search\fR \- Find objects on a Chef Server by query diff --git a/distro/common/man/man1/knife-ssh.1 b/distro/common/man/man1/knife-ssh.1 index 0af22b8279..1b2e867378 100644 --- a/distro/common/man/man1/knife-ssh.1 +++ b/distro/common/man/man1/knife-ssh.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "KNIFE\-SSH" "1" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "KNIFE\-SSH" "1" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBknife\-ssh\fR \- Run a command or interactive session on multiple remote hosts diff --git a/distro/common/man/man1/knife-status.1 b/distro/common/man/man1/knife-status.1 index ad0aaf361b..ddf882c9f8 100644 --- a/distro/common/man/man1/knife-status.1 +++ b/distro/common/man/man1/knife-status.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "KNIFE\-STATUS" "1" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "KNIFE\-STATUS" "1" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBknife\-status\fR \- Display status information for the nodes in your infrastructure diff --git a/distro/common/man/man1/knife-tag.1 b/distro/common/man/man1/knife-tag.1 index b7332969b5..6226417d9e 100644 --- a/distro/common/man/man1/knife-tag.1 +++ b/distro/common/man/man1/knife-tag.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "KNIFE\-TAG" "1" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "KNIFE\-TAG" "1" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBknife\-tag\fR \- Apply tags to nodes on a Chef Server diff --git a/distro/common/man/man1/knife.1 b/distro/common/man/man1/knife.1 index b5f208df9a..1d2dbb6e3f 100644 --- a/distro/common/man/man1/knife.1 +++ b/distro/common/man/man1/knife.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "KNIFE" "1" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "KNIFE" "1" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBknife\fR \- Chef Server API client utility diff --git a/distro/common/man/man8/chef-client.8 b/distro/common/man/man8/chef-client.8 index 3b20fc5a85..cba180a246 100644 --- a/distro/common/man/man8/chef-client.8 +++ b/distro/common/man/man8/chef-client.8 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "CHEF\-CLIENT" "8" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "CHEF\-CLIENT" "8" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBchef\-client\fR \- Runs a client node connecting to a chef\-server\. diff --git a/distro/common/man/man8/chef-expander.8 b/distro/common/man/man8/chef-expander.8 index 0f0c0fe9ef..f4d5e2f7ce 100644 --- a/distro/common/man/man8/chef-expander.8 +++ b/distro/common/man/man8/chef-expander.8 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "CHEF\-EXPANDER" "8" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "CHEF\-EXPANDER" "8" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBchef\-expander\fR \- fetches messages from RabbitMQ, processes, and loads into chef\-solr diff --git a/distro/common/man/man8/chef-expanderctl.8 b/distro/common/man/man8/chef-expanderctl.8 index 6c8d3ebdc5..5556ccc00e 100644 --- a/distro/common/man/man8/chef-expanderctl.8 +++ b/distro/common/man/man8/chef-expanderctl.8 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "CHEF\-EXPANDERCTL" "8" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "CHEF\-EXPANDERCTL" "8" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBchef\-expanderctl\fR \- management program for chef\-expander diff --git a/distro/common/man/man8/chef-server-webui.8 b/distro/common/man/man8/chef-server-webui.8 index ad5df7a543..0ddc871bf5 100644 --- a/distro/common/man/man8/chef-server-webui.8 +++ b/distro/common/man/man8/chef-server-webui.8 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "CHEF\-SERVER\-WEBUI" "8" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "CHEF\-SERVER\-WEBUI" "8" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBchef\-server\-webui\fR \- Start the Chef Server merb application slice providing Web User Interface (Management Console)\. diff --git a/distro/common/man/man8/chef-server.8 b/distro/common/man/man8/chef-server.8 index f2896cb766..e1e846dbb4 100644 --- a/distro/common/man/man8/chef-server.8 +++ b/distro/common/man/man8/chef-server.8 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "CHEF\-SERVER" "8" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "CHEF\-SERVER" "8" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBchef\-server\fR \- Start the Chef Server merb application slice\. diff --git a/distro/common/man/man8/chef-solo.8 b/distro/common/man/man8/chef-solo.8 index 39f6444892..05c121e3c0 100644 --- a/distro/common/man/man8/chef-solo.8 +++ b/distro/common/man/man8/chef-solo.8 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "CHEF\-SOLO" "8" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "CHEF\-SOLO" "8" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBchef\-solo\fR \- Runs chef in solo mode against a specified cookbook location\. diff --git a/distro/common/man/man8/chef-solr.8 b/distro/common/man/man8/chef-solr.8 index d8c49590fe..d7e42d8be2 100644 --- a/distro/common/man/man8/chef-solr.8 +++ b/distro/common/man/man8/chef-solr.8 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "CHEF\-SOLR" "8" "February 2013" "Chef 11.0.0" "Chef Manual" +.TH "CHEF\-SOLR" "8" "February 2013" "Chef 11.2.0" "Chef Manual" . .SH "NAME" \fBchef\-solr\fR \- Runs as Chef\'s search server diff --git a/lib/chef/application.rb b/lib/chef/application.rb index 6e65e48384..0ed8d3dd9f 100644 --- a/lib/chef/application.rb +++ b/lib/chef/application.rb @@ -29,9 +29,6 @@ require 'rbconfig' class Chef::Application include Mixlib::CLI - class Wakeup < Exception - end - def initialize super @@ -125,7 +122,7 @@ class Chef::Application # that a user has configured a log_location in client.rb, but is running # chef-client by hand to troubleshoot a problem. def configure_logging - Chef::Log.init(Chef::Config[:log_location]) + Chef::Log.init(MonoLogger.new(Chef::Config[:log_location])) if want_additional_logger? configure_stdout_logger end @@ -133,7 +130,7 @@ class Chef::Application end def configure_stdout_logger - stdout_logger = Logger.new(STDOUT) + stdout_logger = MonoLogger.new(STDOUT) STDOUT.sync = true stdout_logger.formatter = Chef::Log.logger.formatter Chef::Log.loggers << stdout_logger diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb index c72f82d7bd..fca2e9a92d 100644 --- a/lib/chef/application/client.rb +++ b/lib/chef/application/client.rb @@ -306,9 +306,6 @@ class Chef::Application::Client < Chef::Application else Chef::Application.exit! "Exiting", 0 end - rescue Chef::Application::Wakeup => e - Chef::Log.debug("Received Wakeup signal. Starting run.") - next rescue SystemExit => e raise rescue Exception => e diff --git a/lib/chef/application/windows_service.rb b/lib/chef/application/windows_service.rb index 0d4a022fdf..5b77a44392 100644 --- a/lib/chef/application/windows_service.rb +++ b/lib/chef/application/windows_service.rb @@ -17,6 +17,7 @@ # require 'chef' +require 'chef/monologger' require 'chef/application' require 'chef/client' require 'chef/config' @@ -56,58 +57,54 @@ class Chef :description => "Set the number of seconds to wait between chef-client runs", :proc => lambda { |s| s.to_i } - option :override_runlist, - :short => "-o RunlistItem,RunlistItem...", - :long => "--override-runlist RunlistItem,RunlistItem...", - :description => "Replace current run list with specified items", - :proc => lambda{|items| - items = items.split(',') - items.compact.map{|item| - Chef::RunList::RunListItem.new(item) - } - } - def service_init + @service_action_mutex = Mutex.new + @service_signal = ConditionVariable.new + reconfigure Chef::Log.info("Chef Client Service initialized") end def service_main(*startup_parameters) + # Chef::Config is initialized during service_init + # Set the initial timeout to splay sleep time + timeout = rand Chef::Config[:splay] - while running? - if state == RUNNING + while running? do + # Grab the service_action_mutex to make a chef-client run + @service_action_mutex.synchronize do begin + Chef::Log.info("Next chef-client run will happen in #{timeout} seconds") + @service_signal.wait(@service_action_mutex, timeout) + + # Continue only if service is RUNNING + next if state != RUNNING + # Reconfigure each time through to pick up any changes in the client file Chef::Log.info("Reconfiguring with startup parameters") reconfigure(startup_parameters) + timeout = Chef::Config[:interval] - splay = rand Chef::Config[:splay] - Chef::Log.debug("Splay sleep #{splay} seconds") - sleep splay + # Honor splay sleep config + timeout += rand Chef::Config[:splay] - # If we've stopped, then bail out now, instead of going on to run Chef + # run chef-client only if service is in RUNNING state next if state != RUNNING + Chef::Log.info("Chef-Client service is starting a chef-client run...") run_chef_client - - Chef::Log.debug("Sleeping for #{Chef::Config[:interval]} seconds") - client_sleep Chef::Config[:interval] - rescue Chef::Application::Wakeup => e - Chef::Log.debug("Received Wakeup signal. Starting run.") - next rescue SystemExit => e - raise + # Do not raise any of the errors here in order to + # prevent service crash + Chef::Log.error("#{e.class}: #{e}") rescue Exception => e Chef::Log.error("#{e.class}: #{e}") Chef::Application.debug_stacktrace(e) - Chef::Log.error("Sleeping for #{Chef::Config[:interval]} seconds before trying again") - client_sleep Chef::Config[:interval] - retry end - else # PAUSED or IDLE - sleep 5 end end + + Chef::Log.debug("Exiting service...") end ################################################################################ @@ -115,19 +112,45 @@ class Chef ################################################################################ def service_stop - Chef::Log.info("SERVICE_CONTROL_STOP received, stopping") + Chef::Log.info("STOP request from operating system.") + if @service_action_mutex.try_lock + @service_signal.signal + @service_action_mutex.unlock + Chef::Log.info("Service is stopping....") + else + Chef::Log.info("Currently a chef-client run is happening.") + Chef::Log.info("Service will stop once it's completed.") + end end def service_pause - Chef::Log.info("SERVICE_CONTROL_PAUSE received, pausing") + Chef::Log.info("PAUSE request from operating system.") + + # We don't need to wake up the service_main if it's waiting + # since this is a PAUSE signal. + + if @service_action_mutex.locked? + Chef::Log.info("Currently a chef-client run is happening.") + Chef::Log.info("Service will pause once it's completed.") + else + Chef::Log.info("Service is pausing....") + end end def service_resume - Chef::Log.info("SERVICE_CONTROL_CONTINUE received, resuming") + # We don't need to wake up the service_main if it's waiting + # since this is a RESUME signal. + + Chef::Log.info("RESUME signal received from the OS.") + Chef::Log.info("Service is resuming....") end def service_shutdown - Chef::Log.info("SERVICE_CONTROL_SHUTDOWN received, shutting down") + Chef::Log.info("SHUTDOWN signal received from the OS.") + + # Treat shutdown similar to stop. + + service_stop end ################################################################################ @@ -136,6 +159,19 @@ class Chef private + # Initializes Chef::Client instance and runs it + def run_chef_client + @chef_client = Chef::Client.new( + @chef_client_json, + :override_runlist => config[:override_runlist] + ) + @chef_client_json = nil + + @chef_client.run + @chef_client = nil + end + + def apply_config(config_file_path) Chef::Config.from_file(config_file_path) Chef::Config.merge!(config) @@ -155,16 +191,52 @@ class Chef Chef::Config[:interval] ||= 1800 end - # Lifted from Chef::Application and Chef::Application::Client - # MUST BE RUN AFTER configuration has been parsed! + # Lifted from application.rb + # See application.rb for related comments. + def configure_logging - # Implementation from Chef::Application - Chef::Log.init(Chef::Config[:log_location]) - Chef::Log.level = Chef::Config[:log_level] + Chef::Log.init(MonoLogger.new(Chef::Config[:log_location])) + if want_additional_logger? + configure_stdout_logger + end + Chef::Log.level = resolve_log_level + end - # Implementation from Chef::Application::Client - Mixlib::Authentication::Log.use_log_devices( Chef::Log ) - Ohai::Log.use_log_devices( Chef::Log ) + def configure_stdout_logger + stdout_logger = MonoLogger.new(STDOUT) + STDOUT.sync = true + stdout_logger.formatter = Chef::Log.logger.formatter + Chef::Log.loggers << stdout_logger + end + + # Based on config and whether or not STDOUT is a tty, should we setup a + # secondary logger for stdout? + def want_additional_logger? + ( Chef::Config[:log_location] != STDOUT ) && STDOUT.tty? && (!Chef::Config[:daemonize]) && (Chef::Config[:force_logger]) + end + + # Use of output formatters is assumed if `force_formatter` is set or if + # `force_logger` is not set and STDOUT is to a console (tty) + def using_output_formatter? + Chef::Config[:force_formatter] || (!Chef::Config[:force_logger] && STDOUT.tty?) + end + + def auto_log_level? + Chef::Config[:log_level] == :auto + end + + # if log_level is `:auto`, convert it to :warn (when using output formatter) + # or :info (no output formatter). See also +using_output_formatter?+ + def resolve_log_level + if auto_log_level? + if using_output_formatter? + :warn + else + :info + end + else + Chef::Config[:log_level] + end end def configure_chef(startup_parameters) @@ -205,20 +277,6 @@ class Chef end end - # Since we need to be able to respond to signals between Chef runs, we need to periodically - # wake up to see if we're still in the running state. The method returns when it has slept - # for +sec+ seconds (but at least +10+ seconds), or when the service - # is no client_sleep in the +RUNNING+ state, whichever comes first. - def client_sleep(sec) - chunk_length = 10 - chunks = sec / chunk_length - chunks = 1 if chunks < 1 - (1..chunks).each do - return unless state == RUNNING - sleep chunk_length - end - end - end end end diff --git a/lib/chef/application/windows_service_manager.rb b/lib/chef/application/windows_service_manager.rb new file mode 100644 index 0000000000..ef93e88517 --- /dev/null +++ b/lib/chef/application/windows_service_manager.rb @@ -0,0 +1,165 @@ +# +# Author:: Seth Chisamore (<schisamo@opscode.com>) +# Copyright:: Copyright (c) 2011 Opscode, 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 'win32/service' +require 'chef/config' +require 'mixlib/cli' + +class Chef + class Application + class WindowsServiceManager + include Mixlib::CLI + + option :action, + :short => "-a ACTION", + :long => "--action ACTION", + :default => "start", + :description => "Action to carry out on chef-service (install, uninstall, status, start, stop, pause, or resume)" + + option :config_file, + :short => "-c CONFIG", + :long => "--config CONFIG", + :default => "#{ENV['SYSTEMDRIVE']}/chef/client.rb", + :description => "The configuration file to use for chef runs" + + option :log_location, + :short => "-L LOGLOCATION", + :long => "--logfile LOGLOCATION", + :description => "Set the log file location for chef-service", + :default => "#{ENV['SYSTEMDRIVE']}/chef/client.log" + + option :splay, + :short => "-s SECONDS", + :long => "--splay SECONDS", + :description => "The splay time for running at intervals, in seconds", + :proc => lambda { |s| s.to_i } + + option :interval, + :short => "-i SECONDS", + :long => "--interval SECONDS", + :description => "Set the number of seconds to wait between chef-client runs", + :proc => lambda { |s| s.to_i } + + option :help, + :short => "-h", + :long => "--help", + :description => "Show this message", + :on => :tail, + :boolean => true, + :show_options => true, + :exit => 0 + + CHEF_SERVICE_NAME = "chef-client" + CHEF_SERVICE_DISPLAY_NAME = "Chef-Client Service" + CHEF_SERVICE_DESCRIPTION = "Runs chef-client periodically" + + def run + parse_options + + case config[:action] + when 'install' + if service_exists? + puts "Service #{CHEF_SERVICE_NAME} already exists on the system." + else + ruby = File.join(RbConfig::CONFIG['bindir'], 'ruby') + path = File.expand_path(File.join(File.dirname(__FILE__), 'windows_service.rb')) + + opts = "" + opts << " -c #{config[:config_file]}" if config[:config_file] + opts << " -L #{config[:log_location]}" if config[:log_location] + opts << " -i #{config[:interval]}" if config[:interval] + opts << " -s #{config[:splay]}" if config[:splay] + + # Quote the full paths to deal with possible spaces in the path name. + # Also ensure all forward slashes are backslashes + cmd = "\"#{ruby}\" \"#{path}\" #{opts}".gsub(File::SEPARATOR, File::ALT_SEPARATOR) + + ::Win32::Service.new( + :service_name => CHEF_SERVICE_NAME, + :display_name => CHEF_SERVICE_DISPLAY_NAME, + :description => CHEF_SERVICE_DESCRIPTION, + :start_type => ::Win32::Service::SERVICE_AUTO_START, + :binary_path_name => cmd) + puts "Service '#{CHEF_SERVICE_NAME}' has successfully been installed." + end + when 'status' + if !service_exists? + puts "Service #{CHEF_SERVICE_NAME} doesn't exist on the system." + else + puts "State of #{CHEF_SERVICE_NAME} service is: #{current_state}" + end + when 'start' + # TODO: allow override of startup parameters here? + take_action('start', RUNNING) + when 'stop' + take_action('stop', STOPPED) + when 'uninstall', 'delete' + take_action('stop', STOPPED) + unless service_exists? + puts "Service #{CHEF_SERVICE_NAME} doesn't exist on the system." + else + ::Win32::Service.delete(CHEF_SERVICE_NAME) + puts "Service #{CHEF_SERVICE_NAME} deleted" + end + when 'pause' + take_action('pause', PAUSED) + when 'resume' + take_action('resume', RUNNING) + end + end + + private + + # Just some state constants + STOPPED = "stopped" + RUNNING = "running" + PAUSED = "paused" + + def service_exists? + return ::Win32::Service.exists?(CHEF_SERVICE_NAME) + end + + def take_action(action=nil, desired_state=nil) + if service_exists? + if current_state != desired_state + ::Win32::Service.send(action, CHEF_SERVICE_NAME) + wait_for_state(desired_state) + puts "Service '#{CHEF_SERVICE_NAME}' is now '#{current_state}'." + else + puts "Service '#{CHEF_SERVICE_NAME}' is already '#{desired_state}'." + end + else + puts "Cannot '#{action}' service '#{CHEF_SERVICE_NAME}', service does not exist." + end + end + + def current_state + ::Win32::Service.status(CHEF_SERVICE_NAME).current_state + end + + # Helper method that waits for a status to change its state since state + # changes aren't usually instantaneous. + def wait_for_state(desired_state) + while current_state != desired_state + puts "One moment... #{current_state}" + sleep 1 + end + end + end + end +end diff --git a/lib/chef/cookbook_version.rb b/lib/chef/cookbook_version.rb index 580a64ffbe..a70c892e1b 100644 --- a/lib/chef/cookbook_version.rb +++ b/lib/chef/cookbook_version.rb @@ -565,7 +565,7 @@ class Chef cookbook_version.manifest = o # We don't need the following step when we decide to stop supporting deprecated operators in the metadata (e.g. <<, >>) - cookbook_version.manifest["metadata"] = JSON.parse(cookbook_version.metadata.to_json) + cookbook_version.manifest["metadata"] = Chef::JSONCompat.from_json(cookbook_version.metadata.to_json) cookbook_version.freeze_version if o["frozen?"] cookbook_version diff --git a/lib/chef/data_bag.rb b/lib/chef/data_bag.rb index 9ce6215b20..be88d07e59 100644 --- a/lib/chef/data_bag.rb +++ b/lib/chef/data_bag.rb @@ -101,7 +101,7 @@ class Chef end Dir.glob(File.join(Chef::Config[:data_bag_path], "#{name}", "*.json")).inject({}) do |bag, f| - item = JSON.parse(IO.read(f)) + item = Chef::JSONCompat.from_json(IO.read(f)) bag[item['id']] = item bag end diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb index c8654d7801..783a640654 100644 --- a/lib/chef/exceptions.rb +++ b/lib/chef/exceptions.rb @@ -97,6 +97,8 @@ class Chef # Attempting to run windows code on a not-windows node class Win32NotWindows < RuntimeError; end class WindowsNotAdmin < RuntimeError; end + # Attempting to access a 64-bit only resource on a 32-bit Windows system + class Win32ArchitectureIncorrect < RuntimeError; end class ObsoleteDependencySyntax < ArgumentError; end class InvalidDataBagPath < ArgumentError; end @@ -131,7 +133,7 @@ class Chef class StaleAttributeRead < StandardError; end #Registry Helper throws the following errors - class Win32RegArchitectureIncorrect < RuntimeError; end + class Win32RegArchitectureIncorrect < Win32ArchitectureIncorrect; end class Win32RegHiveMissing < ArgumentError; end class Win32RegKeyMissing < RuntimeError; end class Win32RegValueMissing < RuntimeError; end diff --git a/lib/chef/json_compat.rb b/lib/chef/json_compat.rb index 9f59a41839..e4795cfd7e 100644 --- a/lib/chef/json_compat.rb +++ b/lib/chef/json_compat.rb @@ -24,7 +24,22 @@ class Chef class JSONCompat JSON_MAX_NESTING = 1000 + JSON_CLASS = "json_class".freeze + + CHEF_APICLIENT = "Chef::ApiClient".freeze + CHEF_CHECKSUM = "Chef::Checksum".freeze + CHEF_COOKBOOKVERSION = "Chef::CookbookVersion".freeze + CHEF_DATABAG = "Chef::DataBag".freeze + CHEF_DATABAGITEM = "Chef::DataBagItem".freeze + CHEF_ENVIRONMENT = "Chef::Environment".freeze + CHEF_NODE = "Chef::Node".freeze + CHEF_ROLE = "Chef::Role".freeze + CHEF_SANDBOX = "Chef::Sandbox".freeze + CHEF_RESOURCE = "Chef::Resource".freeze + CHEF_RESOURCECOLLECTION = "Chef::ResourceCollection".freeze + class <<self + # See CHEF-1292/PL-538. Increase the max nesting for JSON, which defaults # to 19, and isn't enough for some (for example, a Node within a Node) # structures. @@ -38,7 +53,49 @@ class Chef # Just call the JSON gem's parse method with a modified :max_nesting field def from_json(source, opts = {}) - ::JSON.parse(source, opts_add_max_nesting(opts)) + obj = ::Yajl::Parser.parse(source) + + # JSON gem requires top level object to be a Hash or Array (otherwise + # you get the "must contain two octets" error). Yajl doesn't impose the + # same limitation. For compatibility, we re-impose this condition. + unless obj.kind_of?(Hash) or obj.kind_of?(Array) + raise JSON::ParserError, "Top level JSON object must be a Hash or Array. (actual: #{obj.class})" + end + + # The old default in the json gem (which we are mimicing because we + # sadly rely on this misfeature) is to "create additions" i.e., convert + # JSON objects into ruby objects. Explicit :create_additions => false + # is required to turn it off. + if opts[:create_additions].nil? || opts[:create_additions] + map_to_rb_obj(obj) + else + obj + end + end + + # Look at an object that's a basic type (from json parse) and convert it + # to an instance of Chef classes if desired. + def map_to_rb_obj(json_obj) + case json_obj + when Hash + mapped_hash = map_hash_to_rb_obj(json_obj) + if json_obj.has_key?(JSON_CLASS) && (class_to_inflate = class_for_json_class(json_obj[JSON_CLASS])) + class_to_inflate.json_create(mapped_hash) + else + mapped_hash + end + when Array + json_obj.map {|e| map_to_rb_obj(e) } + else + json_obj + end + end + + def map_hash_to_rb_obj(json_hash) + json_hash.each do |key, value| + json_hash[key] = map_to_rb_obj(value) + end + json_hash end def to_json(obj, opts = nil) @@ -48,6 +105,46 @@ class Chef def to_json_pretty(obj, opts = nil) ::JSON.pretty_generate(obj, opts_add_max_nesting(opts)) end + + + # Map +json_class+ to a Class object. We use a +case+ instead of a Hash + # assigned to a constant because otherwise this file could not be loaded + # until all the constants were defined, which means you'd have to load + # the world to get json, which would make knife very slow. + def class_for_json_class(json_class) + case json_class + when CHEF_APICLIENT + Chef::ApiClient + when CHEF_CHECKSUM + Chef::Checksum + when CHEF_COOKBOOKVERSION + Chef::CookbookVersion + when CHEF_DATABAG + Chef::DataBag + when CHEF_DATABAGITEM + Chef::DataBagItem + when CHEF_ENVIRONMENT + Chef::Environment + when CHEF_NODE + Chef::Node + when CHEF_ROLE + Chef::Role + when CHEF_SANDBOX + # a falsey return here will disable object inflation/"create + # additions" in the caller. In Chef 11 this is correct, we just have + # a dummy Chef::Sandbox class for compat with Chef 10 servers. + false + when CHEF_RESOURCE + Chef::Resource + when CHEF_RESOURCECOLLECTION + Chef::ResourceCollection + when /^Chef::Resource/ + Chef::Resource.find_subclass_by_name(json_class) + else + raise JSON::ParserError, "Unsupported `json_class` type '#{json_class}'" + end + end + end end end diff --git a/lib/chef/knife/configure.rb b/lib/chef/knife/configure.rb index e818239a89..eaf42544c0 100644 --- a/lib/chef/knife/configure.rb +++ b/lib/chef/knife/configure.rb @@ -27,6 +27,7 @@ class Chef deps do require 'ohai' Chef::Knife::ClientCreate.load_deps + Chef::Knife::UserCreate.load_deps end banner "knife configure (options)" @@ -40,15 +41,15 @@ class Chef :short => "-i", :long => "--initial", :boolean => true, - :description => "Create an initial API Client" + :description => "Create an initial API User" option :admin_client_name, :long => "--admin-client-name NAME", - :description => "The existing admin clientname (usually chef-webui)" + :description => "The existing admin clientname (usually admin)" option :admin_client_key, :long => "--admin-client-key PATH", - :description => "The path to the admin client's private key (usually a file named webui.pem)" + :description => "The path to the admin client's private key (usually a file named admin.pem)" option :validation_client_name, :long => "--validation-client-name NAME", @@ -93,13 +94,15 @@ EOH Chef::Config[:chef_server_url] = chef_server Chef::Config[:node_name] = admin_client_name Chef::Config[:client_key] = admin_client_key - client_create = Chef::Knife::ClientCreate.new - client_create.name_args = [ new_client_name ] - client_create.config[:admin] = true - client_create.config[:file] = new_client_key - client_create.config[:yes] = true - client_create.config[:disable_editing] = true - client_create.run + user_create = Chef::Knife::UserCreate.new + user_create.name_args = [ new_client_name ] + user_create.config[:user_password] = config[:user_password] || + ui.ask("Please enter a password for the new user: ") {|q| q.echo = false} + user_create.config[:admin] = true + user_create.config[:file] = new_client_key + user_create.config[:yes] = true + user_create.config[:disable_editing] = true + user_create.run else ui.msg("*****") ui.msg("") @@ -132,9 +135,9 @@ EOH server_name = guess_servername @chef_server = config[:chef_server_url] || ask_question("Please enter the chef server URL: ", :default => "http://#{server_name}:4000") if config[:initial] - @new_client_name = config[:node_name] || ask_question("Please enter a clientname for the new client: ", :default => Etc.getlogin) - @admin_client_name = config[:admin_client_name] || ask_question("Please enter the existing admin clientname: ", :default => 'chef-webui') - @admin_client_key = config[:admin_client_key] || ask_question("Please enter the location of the existing admin client's private key: ", :default => '/etc/chef/webui.pem') + @new_client_name = config[:node_name] || ask_question("Please enter a name for the new user: ", :default => Etc.getlogin) + @admin_client_name = config[:admin_client_name] || ask_question("Please enter the existing admin name: ", :default => 'admin') + @admin_client_key = config[:admin_client_key] || ask_question("Please enter the location of the existing admin's private key: ", :default => '/etc/chef/admin.pem') @admin_client_key = File.expand_path(@admin_client_key) else @new_client_name = config[:node_name] || ask_question("Please enter an existing username or clientname for the API: ", :default => Etc.getlogin) diff --git a/lib/chef/knife/user_create.rb b/lib/chef/knife/user_create.rb new file mode 100644 index 0000000000..fa889f29ec --- /dev/null +++ b/lib/chef/knife/user_create.rb @@ -0,0 +1,93 @@ +# +# Author:: Steven Danna (<steve@opscode.com>) +# Copyright:: Copyright (c) 2012 Opscode, 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/knife' + +class Chef + class Knife + class UserCreate < Knife + + deps do + require 'chef/user' + require 'chef/json_compat' + end + + option :file, + :short => "-f FILE", + :long => "--file FILE", + :description => "Write the private key to a file" + + option :admin, + :short => "-a", + :long => "--admin", + :description => "Create the user as an admin", + :boolean => true + + option :user_password, + :short => "-p PASSWORD", + :long => "--password PASSWORD", + :description => "Password for newly created user", + :default => "" + + option :user_key, + :long => "--user-key FILENAME", + :description => "Public key for newly created user. By default a key will be created for you." + + banner "knife user create USER (options)" + + def run + @user_name = @name_args[0] + + if @user_name.nil? + show_usage + ui.fatal("You must specify a user name") + exit 1 + end + + if config[:user_password].length == 0 + show_usage + ui.fatal("You must specify a non-blank password") + exit 1 + end + + user = Chef::User.new + user.name(@user_name) + user.admin(config[:admin]) + user.password config[:user_password] + + if config[:user_key] + user.public_key File.read(File.expand_path(config[:user_key])) + end + + output = edit_data(user) + user = Chef::User.from_hash(output).create + + ui.info("Created #{user}") + if user.private_key + if config[:file] + File.open(config[:file], "w") do |f| + f.print(user.private_key) + end + else + puts user.private_key + end + end + end + end + end +end diff --git a/lib/chef/knife/user_delete.rb b/lib/chef/knife/user_delete.rb new file mode 100644 index 0000000000..b7af11bec8 --- /dev/null +++ b/lib/chef/knife/user_delete.rb @@ -0,0 +1,46 @@ +# +# Author:: Steven Danna (<steve@opscode.com>) +# Copyright:: Copyright (c) 2012 Opscode, 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/knife' + +class Chef + class Knife + class UserDelete < Knife + + deps do + require 'chef/user' + require 'chef/json_compat' + end + + banner "knife user delete USER (options)" + + def run + @user_name = @name_args[0] + + if @user_name.nil? + show_usage + ui.fatal("You must specify a user name") + exit 1 + end + + delete_object(Chef::User, @user_name) + end + + end + end +end diff --git a/lib/chef/knife/user_edit.rb b/lib/chef/knife/user_edit.rb new file mode 100644 index 0000000000..ae319c8872 --- /dev/null +++ b/lib/chef/knife/user_edit.rb @@ -0,0 +1,53 @@ +# +# Author:: Steven Danna (<steve@opscode.com>) +# Copyright:: Copyright (c) 2012 Opscode, 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/knife' + +class Chef + class Knife + class UserEdit < Knife + + deps do + require 'chef/user' + require 'chef/json_compat' + end + + banner "knife user edit USER (options)" + + def run + @user_name = @name_args[0] + + if @user_name.nil? + show_usage + ui.fatal("You must specify a user name") + exit 1 + end + + original_user = Chef::User.load(@user_name).to_hash + edited_user = edit_data(original_user) + if original_user != edited_user + user = Chef::User.from_hash(edited_user) + user.update + ui.msg("Saved #{user}.") + else + ui.msg("User unchaged, not saving.") + end + end + end + end +end diff --git a/lib/chef/knife/user_list.rb b/lib/chef/knife/user_list.rb new file mode 100644 index 0000000000..5d2e735a73 --- /dev/null +++ b/lib/chef/knife/user_list.rb @@ -0,0 +1,42 @@ +# +# Author:: Steven Danna (<steve@opscode.com>) +# Copyright:: Copyright (c) 2012 Opscode, 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/knife' + +class Chef + class Knife + class UserList < Knife + + deps do + require 'chef/user' + require 'chef/json_compat' + end + + banner "knife user list (options)" + + option :with_uri, + :short => "-w", + :long => "--with-uri", + :description => "Show corresponding URIs" + + def run + output(format_list_for_display(Chef::User.list)) + end + end + end +end diff --git a/lib/chef/knife/user_reregister.rb b/lib/chef/knife/user_reregister.rb new file mode 100644 index 0000000000..946150e6e4 --- /dev/null +++ b/lib/chef/knife/user_reregister.rb @@ -0,0 +1,59 @@ +# +# Author:: Steven Danna (<steve@opscode.com>) +# Copyright:: Copyright (c) 2012 Opscode, 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/knife' + +class Chef + class Knife + class UserReregister < Knife + + deps do + require 'chef/user' + require 'chef/json_compat' + end + + banner "knife user reregister USER (options)" + + option :file, + :short => "-f FILE", + :long => "--file FILE", + :description => "Write the private key to a file" + + def run + @user_name = @name_args[0] + + if @user_name.nil? + show_usage + ui.fatal("You must specify a user name") + exit 1 + end + + user = Chef::User.load(@user_name).reregister + Chef::Log.debug("Updated user data: #{user.inspect}") + key = user.private_key + if config[:file] + File.open(config[:file], "w") do |f| + f.print(key) + end + else + ui.msg key + end + end + end + end +end diff --git a/lib/chef/knife/user_show.rb b/lib/chef/knife/user_show.rb new file mode 100644 index 0000000000..5088210b4d --- /dev/null +++ b/lib/chef/knife/user_show.rb @@ -0,0 +1,52 @@ +# +# Author:: Steven Danna (<steve@opscode.com>) +# Copyright:: Copyright (c) 2009 Opscode, 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/knife' + +class Chef + class Knife + class UserShow < Knife + + deps do + require 'chef/user' + require 'chef/json_compat' + end + + banner "knife user show USER (options)" + + option :attribute, + :short => "-a ATTR", + :long => "--attribute ATTR", + :description => "Show only one attribute" + + def run + @user_name = @name_args[0] + + if @user_name.nil? + show_usage + ui.fatal("You must specify a user name") + exit 1 + end + + user = Chef::User.load(@user_name) + output(format_for_display(user)) + end + + end + end +end diff --git a/lib/chef/log.rb b/lib/chef/log.rb index 7355ec7574..131d706a5e 100644 --- a/lib/chef/log.rb +++ b/lib/chef/log.rb @@ -18,6 +18,7 @@ # limitations under the License. require 'logger' +require 'chef/monologger' require 'mixlib/log' class Chef @@ -25,8 +26,7 @@ class Chef extend Mixlib::Log # Force initialization of the primary log device (@logger) - init - + init(MonoLogger.new(STDOUT)) class Formatter def self.show_time=(*args) diff --git a/lib/chef/mixin/windows_architecture_helper.rb b/lib/chef/mixin/windows_architecture_helper.rb new file mode 100644 index 0000000000..38c08e236d --- /dev/null +++ b/lib/chef/mixin/windows_architecture_helper.rb @@ -0,0 +1,91 @@ +# +# Author:: Adam Edwards (<adamed@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, 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/exceptions' +require 'win32/api' if Chef::Platform.windows? + +class Chef + module Mixin + module WindowsArchitectureHelper + + def node_windows_architecture(node) + node[:kernel][:machine].to_sym + end + + def wow64_architecture_override_required?(node, desired_architecture) + is_i386_windows_process? && + node_windows_architecture(node) == :x86_64 && + desired_architecture == :x86_64 + end + + def node_supports_windows_architecture?(node, desired_architecture) + assert_valid_windows_architecture!(desired_architecture) + return (node_windows_architecture(node) == :x86_64 || + desired_architecture == :i386) ? true : false + end + + def valid_windows_architecture?(architecture) + return (architecture == :x86_64) || (architecture == :i386) + end + + def assert_valid_windows_architecture!(architecture) + if ! valid_windows_architecture?(architecture) + raise Chef::Exceptions::Win32ArchitectureIncorrect, + "The specified architecture was not valid. It must be one of :i386 or :x86_64" + end + end + + def is_i386_windows_process? + Chef::Platform.windows? && 'X86'.casecmp(ENV['PROCESSOR_ARCHITECTURE']) == 0 + end + + def disable_wow64_file_redirection( node ) + original_redirection_state = ['0'].pack('P') + + if ( ( node_windows_architecture(node) == :x86_64) && ::Chef::Platform.windows?) + win32_wow_64_disable_wow_64_fs_redirection = + ::Win32::API.new('Wow64DisableWow64FsRedirection', 'P', 'L', 'kernel32') + + succeeded = win32_wow_64_disable_wow_64_fs_redirection.call(original_redirection_state) + + if succeeded == 0 + raise Win32APIError "Failed to disable Wow64 file redirection" + end + + end + + original_redirection_state + end + + def restore_wow64_file_redirection( node, original_redirection_state ) + if ( (node_windows_architecture(node) == :x86_64) && ::Chef::Platform.windows?) + win32_wow_64_revert_wow_64_fs_redirection = + ::Win32::API.new('Wow64RevertWow64FsRedirection', 'P', 'L', 'kernel32') + + succeeded = win32_wow_64_revert_wow_64_fs_redirection.call(original_redirection_state) + + if succeeded == 0 + raise Win32APIError "Failed to revert Wow64 file redirection" + end + end + end + + end + end +end diff --git a/lib/chef/monologger.rb b/lib/chef/monologger.rb new file mode 100644 index 0000000000..fed60514d7 --- /dev/null +++ b/lib/chef/monologger.rb @@ -0,0 +1,93 @@ +require 'logger' + +require 'pp' + +#== MonoLogger +# A subclass of Ruby's stdlib Logger with all the mutex and logrotation stuff +# ripped out. +class MonoLogger < Logger + + # + # === Synopsis + # + # Logger.new(name, shift_age = 7, shift_size = 1048576) + # Logger.new(name, shift_age = 'weekly') + # + # === Args + # + # +logdev+:: + # The log device. This is a filename (String) or IO object (typically + # +STDOUT+, +STDERR+, or an open file). + # +shift_age+:: + # Number of old log files to keep, *or* frequency of rotation (+daily+, + # +weekly+ or +monthly+). + # +shift_size+:: + # Maximum logfile size (only applies when +shift_age+ is a number). + # + # === Description + # + # Create an instance. + # + def initialize(logdev) + @progname = nil + @level = DEBUG + @default_formatter = Formatter.new + @formatter = nil + @logdev = nil + if logdev + @logdev = LocklessLogDevice.new(logdev) + end + end + + + class LocklessLogDevice < LogDevice + + def initialize(log = nil) + @dev = @filename = @shift_age = @shift_size = nil + if log.respond_to?(:write) and log.respond_to?(:close) + @dev = log + else + @dev = open_logfile(log) + @dev.sync = true + @filename = log + end + end + + def write(message) + @dev.write(message) + rescue Exception => ignored + warn("log writing failed. #{ignored}") + end + + def close + @dev.close rescue nil + end + + private + + def open_logfile(filename) + if (FileTest.exist?(filename)) + open(filename, (File::WRONLY | File::APPEND)) + else + create_logfile(filename) + end + end + + def create_logfile(filename) + logdev = open(filename, (File::WRONLY | File::APPEND | File::CREAT)) + logdev.sync = true + add_log_header(logdev) + logdev + end + + def add_log_header(file) + file.write( + "# Logfile created on %s by %s\n" % [Time.now.to_s, Logger::ProgName] + ) + end + + end + + +end + diff --git a/lib/chef/platform.rb b/lib/chef/platform.rb index 6e6de6004e..b176e9c731 100644 --- a/lib/chef/platform.rb +++ b/lib/chef/platform.rb @@ -72,6 +72,14 @@ class Chef :mdadm => Chef::Provider::Mdadm } }, + :gcel => { + :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, diff --git a/lib/chef/provider/batch.rb b/lib/chef/provider/batch.rb new file mode 100644 index 0000000000..e4b35b64f3 --- /dev/null +++ b/lib/chef/provider/batch.rb @@ -0,0 +1,35 @@ +# +# Author:: Adam Edwards (<adamed@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, 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/provider/windows_script' + +class Chef + class Provider + class Batch < Chef::Provider::WindowsScript + + def initialize (new_resource, run_context) + super(new_resource, run_context, '.bat') + end + + def flags + @new_resource.flags.nil? ? '/c' : new_resource.flags + ' /c' + end + + end + end +end diff --git a/lib/chef/provider/package/rubygems.rb b/lib/chef/provider/package/rubygems.rb index e60d73ab62..b451450a8c 100644 --- a/lib/chef/provider/package/rubygems.rb +++ b/lib/chef/provider/package/rubygems.rb @@ -31,7 +31,14 @@ require 'rubygems/version' require 'rubygems/dependency' require 'rubygems/spec_fetcher' require 'rubygems/platform' -require 'rubygems/format' + +# Compatibility note: Rubygems 2.0 removes rubygems/format in favor of +# rubygems/package. +begin + require 'rubygems/format' +rescue LoadError + require 'rubygems/package' +end require 'rubygems/dependency_installer' require 'rubygems/uninstaller' require 'rubygems/specification' @@ -106,6 +113,22 @@ class Chef end ## + # Extracts the gemspec from a (on-disk) gem package. + # === Returns + # Gem::Specification + # + #-- + # Compatibility note: Rubygems 1.x uses Gem::Format, 2.0 moved this + # code into Gem::Package. + def spec_from_file(file) + if defined?(Gem::Format) + Gem::Format.from_file_by_path(file).spec + else + Gem::Package.new(file).spec + end + end + + ## # Determines the candidate version for a gem from a .gem file on disk # and checks if it matches the version contraints in +gem_dependency+ # === Returns @@ -114,7 +137,7 @@ class Chef # nil returns nil if the gem on disk doesn't match the # version constraints for +gem_dependency+ def candidate_version_from_file(gem_dependency, source) - spec = Gem::Format.from_file_by_path(source).spec + spec = spec_from_file(source) if spec.satisfies_requirement?(gem_dependency) logger.debug {"#{@new_resource} found candidate gem version #{spec.version} from local gem package #{source}"} spec.version @@ -142,17 +165,26 @@ class Chef # Find the newest gem version available from Gem.sources that satisfies # the constraints of +gem_dependency+ def find_newest_remote_version(gem_dependency, *sources) - # DependencyInstaller sorts the results such that the last one is - # always the one it considers best. - spec_with_source = dependency_installer.find_gems_with_sources(gem_dependency).last + available_gems = dependency_installer.find_gems_with_sources(gem_dependency) + spec, source = if available_gems.respond_to?(:last) + # DependencyInstaller sorts the results such that the last one is + # always the one it considers best. + spec_with_source = available_gems.last + spec_with_source && spec_with_source + else + # Rubygems 2.0 returns a Gem::Available set, which is a + # collection of AvailableSet::Tuple structs + available_gems.pick_best! + best_gem = available_gems.set.first + best_gem && [best_gem.spec, best_gem.source] + end - spec = spec_with_source && spec_with_source[0] - version = spec && spec_with_source[0].version + version = spec && spec.version if version - logger.debug { "#{@new_resource} found gem #{spec.name} version #{version} for platform #{spec.platform} from #{spec_with_source[1]}" } + logger.debug { "#{@new_resource} found gem #{spec.name} version #{version} for platform #{spec.platform} from #{source}" } version else - source_list = sources.compact.empty? ? "[#{Gem.sources.join(', ')}]" : "[#{sources.join(', ')}]" + source_list = sources.compact.empty? ? "[#{Gem.sources.to_a.join(', ')}]" : "[#{sources.join(', ')}]" logger.warn { "#{@new_resource} failed to find gem #{gem_dependency} from #{source_list}" } nil end diff --git a/lib/chef/provider/powershell.rb b/lib/chef/provider/powershell.rb new file mode 100644 index 0000000000..aaa4a9255e --- /dev/null +++ b/lib/chef/provider/powershell.rb @@ -0,0 +1,35 @@ +# +# Author:: Adam Edwards (<adamed@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, 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/provider/windows_script' + +class Chef + class Provider + class Powershell < Chef::Provider::WindowsScript + + def initialize (new_resource, run_context) + super(new_resource, run_context, '.ps1') + end + + def flags + @new_resource.flags.nil? ? '-ExecutionPolicy RemoteSigned -Command' : @new_resource.flags + '-ExecutionPolicy RemoteSigned -Command' + end + + end + end +end diff --git a/lib/chef/provider/remote_file/ftp.rb b/lib/chef/provider/remote_file/ftp.rb index cb06766bb3..74cc0d4ced 100644 --- a/lib/chef/provider/remote_file/ftp.rb +++ b/lib/chef/provider/remote_file/ftp.rb @@ -49,7 +49,7 @@ class Chef raise ArgumentError, "invalid typecode: #{@typecode.inspect}" end @ftp_active_mode = ftp_active_mode - @hostname = uri.hostname + @hostname = uri.host @port = uri.port @ftp = Net::FTP.new if uri.userinfo diff --git a/lib/chef/provider/script.rb b/lib/chef/provider/script.rb index 9e5a7d7fe1..f1b765d52a 100644 --- a/lib/chef/provider/script.rb +++ b/lib/chef/provider/script.rb @@ -29,7 +29,7 @@ class Chef set_owner_and_group - @new_resource.command("\"#{@new_resource.interpreter}\" #{@new_resource.flags} \"#{script_file.path}\"") + @new_resource.command("\"#{interpreter}\" #{flags} \"#{script_file.path}\"") super converge_by(nil) do # ensure script is unlinked at end of converge! @@ -52,6 +52,13 @@ class Chef @script_file && @script_file.close! end + def interpreter + @new_resource.interpreter + end + + def flags + @new_resource.flags + end end end end diff --git a/lib/chef/provider/windows_script.rb b/lib/chef/provider/windows_script.rb new file mode 100644 index 0000000000..398e1aee6e --- /dev/null +++ b/lib/chef/provider/windows_script.rb @@ -0,0 +1,73 @@ +# +# Author:: Adam Edwards (<adamed@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, 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/provider/script' +require 'chef/mixin/windows_architecture_helper' + +class Chef + class Provider + class WindowsScript < Chef::Provider::Script + + protected + + include Chef::Mixin::WindowsArchitectureHelper + + def initialize( new_resource, run_context, script_extension='') + super( new_resource, run_context ) + @script_extension = script_extension + + target_architecture = new_resource.architecture.nil? ? + node_windows_architecture(run_context.node) : new_resource.architecture + + @is_wow64 = wow64_architecture_override_required?(run_context.node, target_architecture) + + if ( target_architecture == :i386 ) && ! is_i386_windows_process? + raise Chef::Exceptions::Win32ArchitectureIncorrect, + "Support for the i386 architecture from a 64-bit Ruby runtime is not yet implemented" + end + end + + public + + def action_run + wow64_redirection_state = nil + + if @is_wow64 + wow64_redirection_state = disable_wow64_file_redirection(@run_context.node) + end + + begin + super + rescue + raise + ensure + if ! wow64_redirection_state.nil? + restore_wow64_file_redirection(@run_context.node, wow64_redirection_state) + end + end + end + + def script_file + base_script_name = "chef-script" + temp_file_arguments = [ base_script_name, @script_extension ] + + @script_file ||= Tempfile.open(temp_file_arguments) + end + end + end +end diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb index 0c7aed61b8..f4b3175546 100644 --- a/lib/chef/providers.rb +++ b/lib/chef/providers.rb @@ -16,6 +16,7 @@ # limitations under the License. # +require 'chef/provider/batch' require 'chef/provider/breakpoint' require 'chef/provider/cookbook_file' require 'chef/provider/cron' @@ -36,6 +37,7 @@ require 'chef/provider/ohai' require 'chef/provider/mdadm' require 'chef/provider/mount' require 'chef/provider/package' +require 'chef/provider/powershell' require 'chef/provider/remote_directory' require 'chef/provider/remote_file' require 'chef/provider/route' diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb index d18e8b0136..729632a69c 100644 --- a/lib/chef/resource.rb +++ b/lib/chef/resource.rb @@ -129,6 +129,23 @@ F extend Chef::Mixin::ConvertToClassName + # Track all subclasses of Resource. This is used so names can be looked up + # when attempting to deserialize from JSON. (See: json_compat) + def self.resource_classes + @resource_classes ||= [] + end + + # Callback when subclass is defined. Adds subclass to list of subclasses. + def self.inherited(subclass) + resource_classes << subclass + end + + # Look up a subclass by +class_name+ which should be a string that matches + # `Subclass.name` + def self.find_subclass_by_name(class_name) + resource_classes.first {|c| c.name == class_name } + end + # Set or return the list of "state attributes" implemented by the Resource # subclass. State attributes are attributes that describe the desired state # of the system, such as file permissions or ownership. In general, state diff --git a/lib/chef/resource/batch.rb b/lib/chef/resource/batch.rb new file mode 100644 index 0000000000..705260bbce --- /dev/null +++ b/lib/chef/resource/batch.rb @@ -0,0 +1,31 @@ +# +# Author:: Adam Edwards (<adamed@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, 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/resource/windows_script' + +class Chef + class Resource + class Batch < Chef::Resource::WindowsScript + + def initialize(name, run_context=nil) + super(name, run_context, :batch, "cmd.exe") + end + + end + end +end diff --git a/lib/chef/resource/powershell.rb b/lib/chef/resource/powershell.rb new file mode 100644 index 0000000000..e726e6f35a --- /dev/null +++ b/lib/chef/resource/powershell.rb @@ -0,0 +1,31 @@ +# +# Author:: Adam Edwards (<adamed@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, 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/resource/windows_script' + +class Chef + class Resource + class Powershell < Chef::Resource::WindowsScript + + def initialize(name, run_context=nil) + super(name, run_context, :powershell, "powershell.exe") + end + + end + end +end diff --git a/lib/chef/resource/windows_script.rb b/lib/chef/resource/windows_script.rb new file mode 100644 index 0000000000..5f2311a5bb --- /dev/null +++ b/lib/chef/resource/windows_script.rb @@ -0,0 +1,62 @@ +# +# Author:: Adam Edwards (<adamed@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, 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/resource/script' +require 'chef/mixin/windows_architecture_helper' + +class Chef + class Resource + class WindowsScript < Chef::Resource::Script + + protected + + def initialize(name, run_context, resource_name, interpreter_command) + super(name, run_context) + @interpreter = interpreter_command + @resource_name = resource_name + end + + include Chef::Mixin::WindowsArchitectureHelper + + public + + def architecture(arg=nil) + assert_architecture_compatible!(arg) if ! arg.nil? + result = set_or_return( + :architecture, + arg, + :kind_of => Symbol + ) + end + + protected + + def assert_architecture_compatible!(desired_architecture) + if ! node_supports_windows_architecture?(node, desired_architecture) + raise Chef::Exceptions::Win32ArchitectureIncorrect, + "cannot execute script with requested architecture '#{desired_architecture.to_s}' on a system with architecture '#{node_windows_architecture(node)}'" + end + end + + def node + run_context && run_context.node + end + + end + end +end diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb index 6dea46bfc9..1b295fc4e1 100644 --- a/lib/chef/resources.rb +++ b/lib/chef/resources.rb @@ -18,6 +18,7 @@ require 'chef/resource/apt_package' require 'chef/resource/bash' +require 'chef/resource/batch' require 'chef/resource/breakpoint' require 'chef/resource/cookbook_file' require 'chef/resource/chef_gem' @@ -49,6 +50,7 @@ require 'chef/resource/package' require 'chef/resource/pacman_package' require 'chef/resource/perl' require 'chef/resource/portage_package' +require 'chef/resource/powershell' require 'chef/resource/python' require 'chef/resource/registry_key' require 'chef/resource/remote_directory' diff --git a/lib/chef/user.rb b/lib/chef/user.rb new file mode 100644 index 0000000000..3f592e4b65 --- /dev/null +++ b/lib/chef/user.rb @@ -0,0 +1,182 @@ +# +# Author:: Steven Danna (steve@opscode.com) +# Copyright:: Copyright 2012 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +require 'chef/config' +require 'chef/mixin/params_validate' +require 'chef/mixin/from_file' +require 'chef/mash' +require 'chef/json_compat' +require 'chef/search/query' + +class Chef + class User + + include Chef::Mixin::FromFile + include Chef::Mixin::ParamsValidate + + def initialize + @name = '' + @public_key = nil + @private_key = nil + @password = nil + @admin = false + end + + def name(arg=nil) + set_or_return(:name, arg, + :regex => /^[a-z0-9\-_]+$/) + end + + def admin(arg=nil) + set_or_return(:admin, + arg, :kind_of => [TrueClass, FalseClass]) + end + + def public_key(arg=nil) + set_or_return(:public_key, + arg, :kind_of => String) + end + + def private_key(arg=nil) + set_or_return(:private_key, + arg, :kind_of => String) + end + + def password(arg=nil) + set_or_return(:password, + arg, :kind_of => String) + end + + def to_hash + result = { + "name" => @name, + "public_key" => @public_key, + "admin" => @admin + } + result["private_key"] = @private_key if @private_key + result["password"] = @password if @password + result + end + + def to_json(*a) + to_hash.to_json(*a) + end + + def destroy + Chef::REST.new(Chef::Config[:chef_server_url]).delete_rest("users/#{@name}") + end + + def create + payload = {:name => self.name, :admin => self.admin, :password => self.password } + payload[:public_key] = public_key if public_key + new_user =Chef::REST.new(Chef::Config[:chef_server_url]).post_rest("users", payload) + Chef::User.from_hash(self.to_hash.merge(new_user)) + end + + def update(new_key=false) + payload = {:name => name, :admin => admin} + payload[:private_key] = new_key if new_key + payload[:password] = password if password + updated_user = Chef::REST.new(Chef::Config[:chef_server_url]).put_rest("users/#{name}", payload) + Chef::User.from_hash(self.to_hash.merge(updated_user)) + end + + def save(new_key=false) + begin + create + rescue Net::HTTPServerException => e + if e.response.code == "409" + update(new_key) + else + raise e + end + end + end + + def reregister + r = Chef::REST.new(Chef::Config[:chef_server_url]) + reregistered_self = r.put_rest("users/#{name}", { :name => name, :admin => admin, :private_key => true }) + private_key(reregistered_self["private_key"]) + self + end + + def to_s + "user[#{@name}]" + end + + def inspect + "Chef::User name:'#{name}' admin:'#{admin.inspect}'" + + "public_key:'#{public_key}' private_key:#{private_key}" + end + + # Class Methods + + def self.from_hash(user_hash) + user = Chef::User.new + user.name user_hash['name'] + user.private_key user_hash['private_key'] if user_hash.key?('private_key') + user.password user_hash['password'] if user_hash.key?('password') + user.public_key user_hash['public_key'] + user.admin user_hash['admin'] + user + end + + def self.from_json(json) + Chef::User.from_hash(Chef::JSONCompat.from_json(json)) + end + + class << self + alias_method :json_create, :from_json + end + + def self.list(inflate=false) + response = if inflate + users = Chef::REST.new(Chef::Config[:chef_server_url]).get_rest('users') + users.map do |name| + Chef::User.load(name) + end + else + Chef::REST.new(Chef::Config[:chef_server_url]).get_rest('users') + end + if response.is_a? Array + transform_ohc_list_response(response) + else + response + end + end + + def self.load(name) + response = Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("users/#{name}") + Chef::User.from_hash(response) + end + + private + + # Gross. Transforms an API response in the form of: + # [ { "user" => { "username" => USERNAME }}, ...] + # into the form + # { "USERNAME" => "URI" } + def self.transform_ohc_list_response(response) + new_response = Hash.new + response.each do |u| + name = u['user']['username'] + new_response[name] = Chef::Config[:chef_server_url] + "/users/#{name}" + end + new_response + end + end +end diff --git a/lib/chef/version.rb b/lib/chef/version.rb index 1c928d2d6b..faad63658c 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 = '11.0.0' + VERSION = '11.4.0' end # NOTE: the Chef::Version class is defined in version_class.rb diff --git a/lib/chef/win32/api/process.rb b/lib/chef/win32/api/process.rb index d18ad411b4..0aca992ed5 100644 --- a/lib/chef/win32/api/process.rb +++ b/lib/chef/win32/api/process.rb @@ -33,6 +33,7 @@ class Chef safe_attach_function :GetCurrentProcess, [], :HANDLE safe_attach_function :GetProcessHandleCount, [ :HANDLE, :LPDWORD ], :BOOL safe_attach_function :GetProcessId, [ :HANDLE ], :DWORD + safe_attach_function :CloseHandle, [ :HANDLE ], :BOOL end end diff --git a/lib/chef/win32/handle.rb b/lib/chef/win32/handle.rb index 60e35916ad..3e92703db9 100644 --- a/lib/chef/win32/handle.rb +++ b/lib/chef/win32/handle.rb @@ -26,6 +26,10 @@ class Chef class Handle extend Chef::ReservedNames::Win32::API::Process + # See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683179(v=vs.85).aspx + # The handle value returned by the GetCurrentProcess function is the pseudo handle (HANDLE)-1 (which is 0xFFFFFFFF) + CURRENT_PROCESS_HANDLE = 4294967295 + def initialize(handle) @handle = handle ObjectSpace.define_finalizer(self, Handle.close_handle_finalizer(handle)) @@ -34,7 +38,10 @@ class Chef attr_reader :handle def self.close_handle_finalizer(handle) - proc { close_handle(handle) } + # According to http://msdn.microsoft.com/en-us/library/windows/desktop/ms683179(v=vs.85).aspx, it is not necessary + # to close the pseudo handle returned by the GetCurrentProcess function. The docs also say that it doesn't hurt to call + # CloseHandle on it. However, doing so from inside of Ruby always seems to produce an invalid handle error. + proc { close_handle(handle) unless handle == CURRENT_PROCESS_HANDLE } end def self.close_handle(handle) diff --git a/spec/data/big_json.json b/spec/data/big_json.json index 8e095bc148..96c2818894 100644 --- a/spec/data/big_json.json +++ b/spec/data/big_json.json @@ -1 +1,2 @@ -{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":"test"}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} +{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":"test" +}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
\ No newline at end of file diff --git a/spec/data/big_json_plus_one.json b/spec/data/big_json_plus_one.json index 973f7fae50..8ea4b74644 100644 --- a/spec/data/big_json_plus_one.json +++ b/spec/data/big_json_plus_one.json @@ -1 +1,2 @@ -{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":"test"}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} +{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":"test" +}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} diff --git a/spec/functional/resource/batch_spec.rb b/spec/functional/resource/batch_spec.rb new file mode 100644 index 0000000000..4374dd0730 --- /dev/null +++ b/spec/functional/resource/batch_spec.rb @@ -0,0 +1,70 @@ +# +# Author:: Adam Edwards (<adamed@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, 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' + +shared_context Chef::Resource::WindowsScript do + let(:ohai) do + ohai_reader = Ohai::System.new + ohai_reader.require_plugin("os") + ohai_reader.require_plugin("windows::platform") + ohai_reader + end + + let(:node) do + new_node = Chef::Node.new + new_node.consume_external_attrs(ohai.data,{}) + new_node + end + + let(:run_context) do + events = Chef::EventDispatch::Dispatcher.new + + run_context = Chef::RunContext.new(node, {}, events) + end + + let(:script_output_path) do + File.join(Dir.tmpdir, make_tmpname("windows_script_test")) + end + + before(:each) do +k File.delete(script_output_path) if File.exists?(script_output_path) + end + + after(:each) do + File.delete(script_output_path) if File.exists?(script_output_path) + end +end + + +describe Chef::Resource::WindowsScript::Batch, :windows_only do + let(:script_content) { "whoami" } + + let!(:resource) do + Chef::Resource::WindowsScript::Batch.new("Batch resource functional test", run_context) + end + + context "when the run action is invoked on Windows" do + include_context Chef::Resource::WindowsScript + it "executes the script code" do + resource.code(script_content + " > #{script_output_path}") + resource.returns(0) + resource.run_action(:run) + end + end +end diff --git a/spec/functional/resource/powershell_spec.rb b/spec/functional/resource/powershell_spec.rb new file mode 100644 index 0000000000..0673e06126 --- /dev/null +++ b/spec/functional/resource/powershell_spec.rb @@ -0,0 +1,40 @@ +# +# Author:: Adam Edwards (<adamed@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, 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 'functional/resource/batch_spec.rb' + +describe Chef::Resource::WindowsScript::Powershell, :windows_only do + let(:script_content) { "whoami" } + + let!(:resource) do + r = Chef::Resource::WindowsScript::Powershell.new("Powershell resource functional test", run_context) + r.code(script_content) + r + end + + include_context Chef::Resource::WindowsScript + + context "when the run action is invoked on Windows" do + it "executes the script code" do + resource.code(script_content + " > #{script_output_path}") + resource.returns(0) + resource.run_action(:run) + end + end +end diff --git a/spec/functional/run_lock_spec.rb b/spec/functional/run_lock_spec.rb index 6b8039794d..5e64e42a0b 100644 --- a/spec/functional/run_lock_spec.rb +++ b/spec/functional/run_lock_spec.rb @@ -22,6 +22,10 @@ describe Chef::RunLock do # This behavior is believed to work on windows, but the tests use UNIX APIs. describe "when locking the chef-client run", :unix_only => true do + + ## + # Lockfile location and helpers + let(:random_temp_root) do Kernel.srand(Time.now.to_i + Process.pid) "/tmp/#{Kernel.rand(Time.now.to_i + Process.pid)}" @@ -30,75 +34,203 @@ describe Chef::RunLock do let(:file_cache_path){ "/var/chef/cache" } let(:lockfile){ "#{random_temp_root}/this/long/path/does/not/exist/chef-client-running.pid" } + # make sure to start with a clean slate. + before(:each){ FileUtils.rm_r(random_temp_root) if File.exist?(random_temp_root) } after(:each){ FileUtils.rm_r(random_temp_root) } + def wait_on_lock + tries = 0 + until File.exist?(lockfile) + raise "Lockfile never created, abandoning test" if tries > 10 + tries += 1 + sleep 0.1 + end + end + + ## + # Side channel via a pipe allows child processes to send errors to the parent + + # Don't lazy create the pipe or else we might not share it with subprocesses + let!(:error_pipe) do + r,w = IO.pipe + w.sync = true + [r,w] + end + + let(:error_read) { error_pipe[0] } + let(:error_write) { error_pipe[1] } + + after do + error_read.close unless error_read.closed? + error_write.close unless error_write.closed? + end + + # Send a RuntimeError from the child process to the parent process. Also + # prints error to $stdout, just in case something goes wrong with the error + # marshaling stuff. + def send_side_channel_error(message) + $stderr.puts(message) + $stderr.puts(caller) + e = RuntimeError.new(message) + error_write.print(Marshal.dump(e)) + end + + # Read the error (if any) from the error channel. If a marhaled error is + # present, it is unmarshaled and raised (which will fail the test) + def raise_side_channel_error! + error_write.close + err = error_read.read + error_read.close + begin + # ArgumentError from Marshal.load indicates no data, which we assume + # means no error in child process. + raise Marshal.load(err) + rescue ArgumentError + nil + end + end + + ## + # Interprocess synchronization via a pipe. This allows us to control the + # state of the processes competing over the lock without relying on sleep. + + let!(:sync_pipe) do + r,w = IO.pipe + w.sync = true + [r,w] + end + let(:sync_read) { sync_pipe[0] } + let(:sync_write) { sync_pipe[1] } + + after do + sync_read.close unless sync_read.closed? + sync_write.close unless sync_write.closed? + end + + # Wait on synchronization signal. If not received within the timeout, an + # error is sent via the error channel, and the process exits. + def sync_wait + if IO.select([sync_read], nil, nil, 20).nil? + # timeout reading from the sync pipe. + send_side_channel_error("Error syncing processes in run lock test (timeout)") + exit!(1) + else + sync_read.getc + end + end + + # Sends a character in the sync pipe, which wakes ("unlocks") another + # process that is waiting on the sync signal + def sync_send + sync_write.putc("!") + sync_write.flush + end + + ## + # IPC to record test results in a pipe. Tests can read pipe contents to + # check that operations occur in the expected order. + + let!(:results_pipe) do + r,w = IO.pipe + w.sync = true + [r,w] + end + let(:results_read) { results_pipe[0] } + let(:results_write) { results_pipe[1] } + + after do + results_read.close unless results_read.closed? + results_write.close unless results_write.closed? + end + + # writes the message to the results pipe for later checking. + # note that nothing accounts for the pipe filling and waiting forever on a + # read or write call, so don't put too much data in. + def record(message) + results_write.puts(message) + results_write.flush + end + + def results + results_write.flush + results_write.close + message = results_read.read + results_read.close + message + end + + ## + # Run lock is the system under test + let!(:run_lock) { Chef::RunLock.new(:file_cache_path => file_cache_path, :lockfile => lockfile) } + it "creates the full path to the lockfile" do - run_lock = Chef::RunLock.new(:file_cache_path => file_cache_path, :lockfile => lockfile) lambda { run_lock.acquire }.should_not raise_error(Errno::ENOENT) File.should exist(lockfile) end it "allows only one chef client run per lockfile" do - read, write = IO.pipe - run_lock = Chef::RunLock.new(:file_cache_path => file_cache_path, :lockfile => lockfile) + # First process, gets the lock and keeps it. p1 = fork do run_lock.acquire - write.puts 1 - #puts "[#{Time.new.to_i % 100}] p1 (#{Process.pid}) running with lock" + record "p1 has lock" + # Wait until the other process is trying to get the lock: + sync_wait + # sleep a little bit to make process p2 wait on the lock sleep 2 - write.puts 2 - #puts "[#{Time.new.to_i % 100}] p1 (#{Process.pid}) releasing lock" + record "p1 releasing lock" run_lock.release + exit!(0) end - sleep 0.5 + # Wait until p1 creates the lockfile + wait_on_lock p2 = fork do + # inform process p1 that we're trying to get the lock + sync_send run_lock.acquire - write.puts 3 - #puts "[#{Time.new.to_i % 100}] p2 (#{Process.pid}) running with lock" + record "p2 has lock" run_lock.release + exit!(0) end Process.waitpid2(p1) Process.waitpid2(p2) - write.close - order = read.read - read.close + raise_side_channel_error! - order.should == "1\n2\n3\n" + expected=<<-E +p1 has lock +p1 releasing lock +p2 has lock +E + results.should == expected end it "clears the lock if the process dies unexpectedly" do - read, write = IO.pipe - run_lock = Chef::RunLock.new(:file_cache_path => file_cache_path, :lockfile => lockfile) p1 = fork do run_lock.acquire - write.puts 1 - #puts "[#{Time.new.to_i % 100}] p1 (#{Process.pid}) running with lock" - sleep 1 - write.puts 2 - #puts "[#{Time.new.to_i % 100}] p1 (#{Process.pid}) releasing lock" - run_lock.release + record "p1 has lock" + sleep 60 + record "p1 still has lock" + exit! 1 end + wait_on_lock + Process.kill(:KILL, p1) + Process.waitpid2(p1) + + p2 = fork do run_lock.acquire - write.puts 3 - #puts "[#{Time.new.to_i % 100}] p2 (#{Process.pid}) running with lock" + record "p2 has lock" run_lock.release + exit! 0 end - Process.kill(:KILL, p1) - Process.waitpid2(p1) Process.waitpid2(p2) - write.close - order = read.read - read.close - - order.should =~ /3\Z/ + results.should =~ /p2 has lock\Z/ end end diff --git a/spec/support/shared/unit/execute_resource.rb b/spec/support/shared/unit/execute_resource.rb new file mode 100644 index 0000000000..609e77ad63 --- /dev/null +++ b/spec/support/shared/unit/execute_resource.rb @@ -0,0 +1,125 @@ +# +# Author:: Adam Jacob (<adam@opscode.com>) +# Author:: Tyler Cloke (<tyler@opscode.com>) +# Copyright:: Copyright (c) 2008 Opscode, 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' + +shared_examples_for "an execute resource" do + + before(:each) do + @resource = execute_resource + end + + it "should create a new Chef::Resource::Execute" do + @resource.should be_a_kind_of(Chef::Resource) + @resource.should be_a_kind_of(Chef::Resource::Execute) + end + + it "should set the command to the first argument to new" do + @resource.command.should eql(resource_instance_name) + end + + it "should accept an array on instantiation, too" do + resource = Chef::Resource::Execute.new(%w{something else}) + resource.should be_a_kind_of(Chef::Resource) + resource.should be_a_kind_of(Chef::Resource::Execute) + resource.command.should eql(%w{something else}) + end + + it "should accept a string for the command to run" do + @resource.command "something" + @resource.command.should eql("something") + end + + it "should accept an array for the command to run" do + @resource.command %w{something else} + @resource.command.should eql(%w{something else}) + end + + it "should accept a string for the cwd" do + @resource.cwd "something" + @resource.cwd.should eql("something") + end + + it "should accept a hash for the environment" do + test_hash = { :one => :two } + @resource.environment(test_hash) + @resource.environment.should eql(test_hash) + end + + it "allows the environment to be specified with #env" do + @resource.should respond_to(:env) + end + + it "should accept a string for the group" do + @resource.group "something" + @resource.group.should eql("something") + end + + it "should accept an integer for the group" do + @resource.group 1 + @resource.group.should eql(1) + end + + it "should accept an array for the execution path" do + @resource.path ["woot"] + @resource.path.should eql(["woot"]) + end + + it "should accept an integer for the return code" do + @resource.returns 1 + @resource.returns.should eql(1) + end + + it "should accept an integer for the timeout" do + @resource.timeout 1 + @resource.timeout.should eql(1) + end + + it "should accept a string for the user" do + @resource.user "something" + @resource.user.should eql("something") + end + + it "should accept an integer for the user" do + @resource.user 1 + @resource.user.should eql(1) + end + + it "should accept a string for creates" do + @resource.creates "something" + @resource.creates.should eql("something") + end + + describe "when it has cwd, environment, group, path, return value, and a user" do + before do + @resource.command("grep") + @resource.cwd("/tmp/") + @resource.environment({ :one => :two }) + @resource.group("legos") + @resource.path(["/var/local/"]) + @resource.returns(1) + @resource.user("root") + end + + it "returns the command as its identity" do + @resource.identity.should == "grep" + end + end +end + diff --git a/spec/support/shared/unit/script_resource.rb b/spec/support/shared/unit/script_resource.rb new file mode 100644 index 0000000000..0451f4694a --- /dev/null +++ b/spec/support/shared/unit/script_resource.rb @@ -0,0 +1,52 @@ +# +# Author:: Adam Jacob (<adam@opscode.com>) +# Author:: Tyler Cloke (<tyler@opscode.com>) +# Copyright:: Copyright (c) 2008 Opscode, 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' + +shared_examples_for "a script resource" do + + before(:each) do + @resource = script_resource + end + + it "should create a new Chef::Resource::Script" do + @resource.should be_a_kind_of(Chef::Resource) + @resource.should be_a_kind_of(Chef::Resource::Script) + end + + it "should have a resource name of :script" do + @resource.resource_name.should eql(resource_name) + end + + it "should set command to the argument provided to new" do + @resource.command.should eql(resource_instance_name) + end + + it "should accept a string for the code" do + @resource.code "hey jude" + @resource.code.should eql("hey jude") + end + + it "should accept a string for the flags" do + @resource.flags "-f" + @resource.flags.should eql("-f") + end + +end + diff --git a/spec/support/shared/unit/windows_script_resource.rb b/spec/support/shared/unit/windows_script_resource.rb new file mode 100644 index 0000000000..0a0079a287 --- /dev/null +++ b/spec/support/shared/unit/windows_script_resource.rb @@ -0,0 +1,48 @@ +# +# Author:: Adam Edwards (<adamed@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, 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 'support/shared/unit/execute_resource' +require 'support/shared/unit/script_resource' + +shared_examples_for "a Windows script resource" do + before(:each) do + node = Chef::Node.new + + node.default["kernel"] = Hash.new + node.default["kernel"][:machine] = :x86_64.to_s + + run_context = Chef::RunContext.new(node, nil, nil) + + @resource = resource_instance + + end + + it "should be a kind of Chef::Resource::WindowsScript" do + @resource.should be_a_kind_of(Chef::Resource) + @resource.should be_a_kind_of(Chef::Resource::WindowsScript) + end + + context "script" do + let(:script_resource) { resource_instance } + it_should_behave_like "a script resource" + end + +end + diff --git a/spec/unit/application_spec.rb b/spec/unit/application_spec.rb index e0b0ab7568..4ec9112c4f 100644 --- a/spec/unit/application_spec.rb +++ b/spec/unit/application_spec.rb @@ -183,7 +183,9 @@ describe Chef::Application do it "should initialise the chef logger" do Chef::Log.stub!(:level=) - Chef::Log.should_receive(:init).with(Chef::Config[:log_location]).and_return(true) + @monologger = mock("Monologger") + MonoLogger.should_receive(:new).with(Chef::Config[:log_location]).and_return(@monologger) + Chef::Log.should_receive(:init).with(@monologger) @app.configure_logging end diff --git a/spec/unit/json_compat_spec.rb b/spec/unit/json_compat_spec.rb index f21493e060..cce31b0c7c 100644 --- a/spec/unit/json_compat_spec.rb +++ b/spec/unit/json_compat_spec.rb @@ -36,7 +36,7 @@ describe Chef::JSONCompat do end end - describe "with a file with 1000 or less nested entries" do + describe "with a file with 300 or less nested entries" do before(:all) do @json = IO.read(File.join(CHEF_SPEC_DATA, 'big_json.json')) @hash = Chef::JSONCompat.from_json(@json) @@ -46,23 +46,23 @@ describe Chef::JSONCompat do it "should create a Hash from the file" do @hash.should be_kind_of(Hash) end - it "should has 'test' as a 1000th nested value" do - @hash['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key'].should == 'test' + it "should has 'test' as a 300th nested value" do + @hash['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key'].should == 'test' end end end - describe "with a file with more than 1000 nested entries" do + describe "with a file with more than 300 nested entries" do before(:all) do @json = IO.read(File.join(CHEF_SPEC_DATA, 'big_json_plus_one.json')) - @hash = Chef::JSONCompat.from_json(@json, {:max_nesting => 1001}) + @hash = Chef::JSONCompat.from_json(@json, {:max_nesting => 301}) end describe "when a big json file is loaded" do it "should create a Hash from the file" do @hash.should be_kind_of(Hash) end - it "should has 'test' as a 1001th nested value" do - @hash['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key'].should == 'test' + it "should has 'test' as a 301st nested value" do + @hash['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key'].should == 'test' end end end diff --git a/spec/unit/knife/configure_spec.rb b/spec/unit/knife/configure_spec.rb index 692fc5ea15..8d5b51551d 100644 --- a/spec/unit/knife/configure_spec.rb +++ b/spec/unit/knife/configure_spec.rb @@ -41,7 +41,7 @@ describe Chef::Knife::Configure do @knife.config[:initial] = true Etc.stub!(:getlogin).and_return("a-new-user") @knife.ask_user_for_config - @out.string.should match(Regexp.escape("Please enter a clientname for the new client: [a-new-user]")) + @out.string.should match(Regexp.escape("Please enter a name for the new user: [a-new-user]")) @knife.new_client_name.should == Etc.getlogin end @@ -50,7 +50,7 @@ describe Chef::Knife::Configure do @knife.config[:node_name] = 'testnode' Etc.stub!(:getlogin).and_return("a-new-user") @knife.ask_user_for_config - @out.string.should_not match(Regexp.escape("Please enter a clientname for the new client")) + @out.string.should_not match(Regexp.escape("Please enter a name for the new user")) @knife.new_client_name.should == 'testnode' end @@ -64,32 +64,32 @@ describe Chef::Knife::Configure do it "asks the user for the existing admin client's name if -i is specified" do @knife.config[:initial] = true @knife.ask_user_for_config - @out.string.should match(Regexp.escape("Please enter the existing admin clientname: [chef-webui]")) - @knife.admin_client_name.should == 'chef-webui' + @out.string.should match(Regexp.escape("Please enter the existing admin name: [admin]")) + @knife.admin_client_name.should == 'admin' end it "should not ask the user for the existing admin client's name if -i and --admin-client_name are specified" do @knife.config[:initial] = true @knife.config[:admin_client_name] = 'my-webui' @knife.ask_user_for_config - @out.string.should_not match(Regexp.escape("Please enter the existing admin clientname:")) + @out.string.should_not match(Regexp.escape("Please enter the existing admin:")) @knife.admin_client_name.should == 'my-webui' end it "should not ask the user for the existing admin client's name if -i is not specified" do @knife.ask_user_for_config - @out.string.should_not match(Regexp.escape("Please enter the existing admin clientname: [chef-webui]")) - @knife.admin_client_name.should_not == 'chef-webui' + @out.string.should_not match(Regexp.escape("Please enter the existing admin: [admin]")) + @knife.admin_client_name.should_not == 'admin' end it "asks the user for the location of the existing admin key if -i is specified" do @knife.config[:initial] = true @knife.ask_user_for_config - @out.string.should match(Regexp.escape("Please enter the location of the existing admin client's private key: [/etc/chef/webui.pem]")) + @out.string.should match(Regexp.escape("Please enter the location of the existing admin's private key: [/etc/chef/admin.pem]")) if windows? - @knife.admin_client_key.should == 'C:/etc/chef/webui.pem' + @knife.admin_client_key.should == 'C:/etc/chef/admin.pem' else - @knife.admin_client_key.should == '/etc/chef/webui.pem' + @knife.admin_client_key.should == '/etc/chef/admin.pem' end end @@ -111,7 +111,7 @@ describe Chef::Knife::Configure do if windows? @knife.admin_client_key.should_not == 'C:/etc//chef/webui.pem' else - @knife.admin_client_key.should_not == '/etc/chef/webui.pem' + @knife.admin_client_key.should_not == '/etc/chef/webui.pem' end end @@ -140,7 +140,7 @@ describe Chef::Knife::Configure do if windows? @knife.validation_key.should == 'C:/etc/chef/validation.pem' else - @knife.validation_key.should == '/etc/chef/validation.pem' + @knife.validation_key.should == '/etc/chef/validation.pem' end end @@ -208,28 +208,25 @@ describe Chef::Knife::Configure do File.should_receive(:expand_path).with("/home/you/.chef/knife.rb").and_return("/home/you/.chef/knife.rb") File.should_receive(:expand_path).with("/home/you/.chef/a-new-user.pem").and_return("/home/you/.chef/a-new-user.pem") File.should_receive(:expand_path).with("/etc/chef/validation.pem").and_return("/etc/chef/validation.pem") - File.should_receive(:expand_path).with("/etc/chef/webui.pem").and_return("/etc/chef/webui.pem") + File.should_receive(:expand_path).with("/etc/chef/admin.pem").and_return("/etc/chef/admin.pem") Chef::Config[:node_name] = "webmonkey.example.com" - - client_command_config = {} - - client_command = mock("knife client create command", :config => client_command_config) - client_command.should_receive(:name_args=).with(["a-new-user"]) - client_command.stub!(:name_args).and_return(["a-new-user"]) - client_command.should_receive(:run) + user_command = Chef::Knife::UserCreate.new + user_command.should_receive(:run) Etc.stub!(:getlogin).and_return("a-new-user") - Chef::Knife::ClientCreate.stub!(:new).and_return(client_command) + Chef::Knife::UserCreate.stub!(:new).and_return(user_command) FileUtils.should_receive(:mkdir_p).with("/home/you/.chef") ::File.should_receive(:open).with("/home/you/.chef/knife.rb", "w") @knife.config[:initial] = true + @knife.config[:user_password] = "blah" @knife.run - client_command.name_args.should == Array("a-new-user") - client_command.config[:admin].should be_true - client_command.config[:file].should == "/home/you/.chef/a-new-user.pem" - client_command.config[:yes].should be_true - client_command.config[:disable_editing].should be_true + user_command.name_args.should == Array("a-new-user") + user_command.config[:user_password].should == "blah" + user_command.config[:admin].should be_true + user_command.config[:file].should == "/home/you/.chef/a-new-user.pem" + user_command.config[:yes].should be_true + user_command.config[:disable_editing].should be_true end end diff --git a/spec/unit/knife/user_create_spec.rb b/spec/unit/knife/user_create_spec.rb new file mode 100644 index 0000000000..0e36e148ac --- /dev/null +++ b/spec/unit/knife/user_create_spec.rb @@ -0,0 +1,86 @@ +# +# Author:: Steven Danna (<steve@opscode.com>) +# Copyright:: Copyright (c) 2012 Opscode, 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' + +Chef::Knife::UserCreate.load_deps + +describe Chef::Knife::UserCreate do + before(:each) do + @knife = Chef::Knife::UserCreate.new + @knife.name_args = [ 'a_user' ] + @knife.config[:user_password] = "foobar" + @user = Chef::User.new + @user.name "a_user" + @user_with_private_key = Chef::User.new + @user_with_private_key.name "a_user" + @user_with_private_key.private_key 'private_key' + @user.stub!(:create).and_return(@user_with_private_key) + Chef::User.stub!(:new).and_return(@user) + Chef::User.stub!(:from_hash).and_return(@user) + @knife.stub!(:edit_data).and_return(@user.to_hash) + @stdout = StringIO.new + @stderr = StringIO.new + @knife.ui.stub!(:stdout).and_return(@stdout) + @knife.ui.stub!(:stderr).and_return(@stderr) + end + + it "creates a new user" do + Chef::User.should_receive(:new).and_return(@user) + @user.should_receive(:create) + @knife.run + @stdout.string.should match /created user.+a_user/i + end + + it "sets the password" do + @knife.config[:user_password] = "a_password" + @user.should_receive(:password).with("a_password") + @knife.run + end + + it "exits with an error if password is blank" do + @knife.config[:user_password] = '' + lambda { @knife.run }.should raise_error SystemExit + @stderr.string.should match /You must specify a non-blank password/ + end + + it "sets the user name" do + @user.should_receive(:name).with("a_user") + @knife.run + end + + it "sets the public key if given" do + @knife.config[:user_key] = "/a/filename" + File.stub(:read).with(File.expand_path("/a/filename")).and_return("a_key") + @user.should_receive(:public_key).with("a_key") + @knife.run + end + + it "allows you to edit the data" do + @knife.should_receive(:edit_data).with(@user) + @knife.run + end + + it "writes the private key to a file when --file is specified" do + @knife.config[:file] = "/tmp/a_file" + filehandle = mock("filehandle") + filehandle.should_receive(:print).with('private_key') + File.should_receive(:open).with("/tmp/a_file", "w").and_yield(filehandle) + @knife.run + end +end diff --git a/spec/unit/knife/user_delete_spec.rb b/spec/unit/knife/user_delete_spec.rb new file mode 100644 index 0000000000..be027e5128 --- /dev/null +++ b/spec/unit/knife/user_delete_spec.rb @@ -0,0 +1,39 @@ +# +# Author:: Steven Danna (<steve@opscode.com>) +# Copyright:: Copyright (c) 2012 Opscode, 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::Knife::UserDelete do + before(:each) do + Chef::Knife::UserDelete.load_deps + @knife = Chef::Knife::UserDelete.new + @knife.name_args = [ 'my_user' ] + end + + it 'deletes the user' do + @knife.should_receive(:delete_object).with(Chef::User, 'my_user') + @knife.run + end + + it 'prints usage and exits when a user name is not provided' do + @knife.name_args = [] + @knife.should_receive(:show_usage) + @knife.ui.should_receive(:fatal) + lambda { @knife.run }.should raise_error(SystemExit) + end +end diff --git a/spec/unit/knife/user_edit_spec.rb b/spec/unit/knife/user_edit_spec.rb new file mode 100644 index 0000000000..d5b380a12f --- /dev/null +++ b/spec/unit/knife/user_edit_spec.rb @@ -0,0 +1,42 @@ +# +# Author:: Steven Danna (<steve@opscode.com>) +# Copyright:: Copyright (c) 2012 Opscode, 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::Knife::UserEdit do + before(:each) do + Chef::Knife::UserEdit.load_deps + @knife = Chef::Knife::UserEdit.new + @knife.name_args = [ 'my_user' ] + @knife.config[:disable_editing] = true + end + + it 'loads and edits the user' do + data = { :name => "my_user" } + Chef::User.stub(:load).with("my_user").and_return(data) + @knife.should_receive(:edit_data).with(data).and_return(data) + @knife.run + end + + it 'prints usage and exits when a user name is not provided' do + @knife.name_args = [] + @knife.should_receive(:show_usage) + @knife.ui.should_receive(:fatal) + lambda { @knife.run }.should raise_error(SystemExit) + end +end diff --git a/spec/unit/knife/user_list_spec.rb b/spec/unit/knife/user_list_spec.rb new file mode 100644 index 0000000000..7a47f9ddba --- /dev/null +++ b/spec/unit/knife/user_list_spec.rb @@ -0,0 +1,32 @@ +# +# Author:: Steven Danna +# Copyright:: Copyright (c) 2012 Opscode, 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::Knife::UserList do + before(:each) do + Chef::Knife::UserList.load_deps + @knife = Chef::Knife::UserList.new + end + + it 'lists the users' do + Chef::User.should_receive(:list) + @knife.should_receive(:format_list_for_display) + @knife.run + end +end diff --git a/spec/unit/knife/user_reregister_spec.rb b/spec/unit/knife/user_reregister_spec.rb new file mode 100644 index 0000000000..fddab57467 --- /dev/null +++ b/spec/unit/knife/user_reregister_spec.rb @@ -0,0 +1,53 @@ +# +# Author:: Steven Danna (<steve@opscode.com>) +# Copyright:: Copyright (c) 2012 Opscode, 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::Knife::UserReregister do + before(:each) do + Chef::Knife::UserReregister.load_deps + @knife = Chef::Knife::UserReregister.new + @knife.name_args = [ 'a_user' ] + @user_mock = mock('user_mock', :private_key => "private_key") + Chef::User.stub!(:load).and_return(@user_mock) + @stdout = StringIO.new + @knife.ui.stub!(:stdout).and_return(@stdout) + end + + it 'prints usage and exits when a user name is not provided' do + @knife.name_args = [] + @knife.should_receive(:show_usage) + @knife.ui.should_receive(:fatal) + lambda { @knife.run }.should raise_error(SystemExit) + end + + it 'reregisters the user and prints the key' do + @user_mock.should_receive(:reregister).and_return(@user_mock) + @knife.run + @stdout.string.should match( /private_key/ ) + end + + it 'writes the private key to a file when --file is specified' do + @user_mock.should_receive(:reregister).and_return(@user_mock) + @knife.config[:file] = '/tmp/a_file' + filehandle = StringIO.new + File.should_receive(:open).with('/tmp/a_file', 'w').and_yield(filehandle) + @knife.run + filehandle.string.should == "private_key" + end +end diff --git a/spec/unit/knife/user_show_spec.rb b/spec/unit/knife/user_show_spec.rb new file mode 100644 index 0000000000..f2bd959d15 --- /dev/null +++ b/spec/unit/knife/user_show_spec.rb @@ -0,0 +1,41 @@ +# +# Author:: Steven Danna (<steve@opscode.com>) +# Copyright:: Copyright (c) 2012 Opscode, 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::Knife::UserShow do + before(:each) do + Chef::Knife::UserShow.load_deps + @knife = Chef::Knife::UserShow.new + @knife.name_args = [ 'my_user' ] + @user_mock = mock('user_mock') + end + + it 'loads and displays the user' do + Chef::User.should_receive(:load).with('my_user').and_return(@user_mock) + @knife.should_receive(:format_for_display).with(@user_mock) + @knife.run + end + + it 'prints usage and exits when a user name is not provided' do + @knife.name_args = [] + @knife.should_receive(:show_usage) + @knife.ui.should_receive(:fatal) + lambda { @knife.run }.should raise_error(SystemExit) + end +end diff --git a/spec/unit/mixin/windows_architecture_helper_spec.rb b/spec/unit/mixin/windows_architecture_helper_spec.rb new file mode 100644 index 0000000000..c7813a5efc --- /dev/null +++ b/spec/unit/mixin/windows_architecture_helper_spec.rb @@ -0,0 +1,83 @@ +# +# Author:: Adam Edwards (<adamed@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, 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/windows_architecture_helper' + + + +describe Chef::Mixin::WindowsArchitectureHelper do + include Chef::Mixin::WindowsArchitectureHelper + + before do + @valid_architectures = [ :i386, :x86_64 ] + @invalid_architectures = [ "i386", "x86_64", :x64, :x86, :arm ] + + @node_i386 = Chef::Node.new + @node_x86_64 = Chef::Node.new + end + + it "returns true when valid architectures are passed to valid_windows_architecture?" do + @valid_architectures.each do | architecture | + valid_windows_architecture?(architecture).should == true + end + end + + it "returns false when invalid architectures are passed to valid_windows_architecture?" do + @invalid_architectures.each do | architecture | + valid_windows_architecture?(architecture).should == false + end + end + + it "does not raise an exception when a valid architecture is passed to assert_valid_windows_architecture!" do + @valid_architectures.each do | architecture | + assert_valid_windows_architecture!(architecture) + end + end + + it "raises an error if an invalid architecture is passed to assert_valid_windows_architecture!" do + @invalid_architectures.each do | architecture | + begin + assert_valid_windows_architecture!(architecture).should raise_error Chef::Exceptions::Win32ArchitectureIncorrect + rescue Chef::Exceptions::Win32ArchitectureIncorrect + end + end + end + + it "returns true for each supported desired architecture for all nodes with each valid architecture passed to node_supports_windows_architecture" do + enumerate_architecture_node_combinations(true) + end + + it "returns false for each unsupported desired architecture for all nodes with each valid architecture passed to node_supports_windows_architecture?" do + enumerate_architecture_node_combinations(true) + end + + def enumerate_architecture_node_combinations(only_valid_combinations) + @valid_architectures.each do | node_architecture | + new_node = Chef::Node.new + new_node.default["kernel"] = Hash.new + new_node.default["kernel"][:machine] = node_architecture.to_s + + @valid_architectures.each do | supported_architecture | + node_supports_windows_architecture?(new_node, supported_architecture).should == true if only_valid_combinations && (supported_architecture != :x86_64 && node_architecture != :i386 ) + node_supports_windows_architecture?(new_node, supported_architecture).should == false if ! only_valid_combinations && (supported_architecture == :x86_64 && node_architecture == :i386 ) + end + end + end +end diff --git a/spec/unit/platform_spec.rb b/spec/unit/platform_spec.rb index b09a68b528..6897837a42 100644 --- a/spec/unit/platform_spec.rb +++ b/spec/unit/platform_spec.rb @@ -35,7 +35,8 @@ describe "Chef::Platform supports" do :solaris, :mswin, :mingw32, - :windows + :windows, + :gcel ].each do |platform| it "#{platform}" do Chef::Platform.platforms.should have_key(platform) diff --git a/spec/unit/provider/package/rubygems_spec.rb b/spec/unit/provider/package/rubygems_spec.rb index b3b7a030db..d7941af76d 100644 --- a/spec/unit/provider/package/rubygems_spec.rb +++ b/spec/unit/provider/package/rubygems_spec.rb @@ -95,6 +95,18 @@ describe Chef::Provider::Package::Rubygems::CurrentGemEnvironment do @gem_env.candidate_version_from_remote(Gem::Dependency.new('rspec', '>= 0')).should == Gem::Version.new('1.3.0') end + it "finds a matching gem candidate version on rubygems 2.0.0+" do + dep = Gem::Dependency.new('rspec', '>= 0') + dep_installer = Gem::DependencyInstaller.new + @gem_env.stub!(:dependency_installer).and_return(dep_installer) + best_gem = mock("best gem match", :spec => gemspec("rspec", Gem::Version.new("1.3.0")), :source => "https://rubygems.org") + available_set = mock("Gem::AvailableSet test double") + available_set.should_receive(:pick_best!) + available_set.should_receive(:set).and_return([best_gem]) + dep_installer.should_receive(:find_gems_with_sources).with(dep).and_return(available_set) + @gem_env.candidate_version_from_remote(Gem::Dependency.new('rspec', '>= 0')).should == Gem::Version.new('1.3.0') + end + it "gives the candidate version as nil if none is found" do dep = Gem::Dependency.new('rspec', '>= 0') latest = [] diff --git a/spec/unit/provider/powershell_spec.rb b/spec/unit/provider/powershell_spec.rb new file mode 100644 index 0000000000..038de47742 --- /dev/null +++ b/spec/unit/provider/powershell_spec.rb @@ -0,0 +1,38 @@ +# +# Author:: Adam Edwards (<adamed@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, 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::Provider::Powershell, "action_run" do + + before(:each) do + @node = Chef::Node.new + + @node.default["kernel"] = Hash.new + @node.default["kernel"][:machine] = :x86_64.to_s + + @run_context = Chef::RunContext.new(@node, {}, @events) + @new_resource = Chef::Resource::Powershell.new('run some powershell code', @run_context) + + @provider = Chef::Provider::Powershell.new(@new_resource, @run_context) + end + + it "should set the -command flag as the last flag" do + @provider.flags.split(' ').pop.should == "-Command" + end + +end diff --git a/spec/unit/provider/remote_file/ftp_spec.rb b/spec/unit/provider/remote_file/ftp_spec.rb index 246301c0e1..bf27e1aee0 100644 --- a/spec/unit/provider/remote_file/ftp_spec.rb +++ b/spec/unit/provider/remote_file/ftp_spec.rb @@ -110,7 +110,7 @@ describe Chef::Provider::RemoteFile::FTP do describe "when it finishes downloading" do it "should return a tempfile" do - ftpfile, mtime, target_matched = Chef::Provider::RemoteFile::FTP.fetch(@uri, nil, false, nil) + ftpfile, mtime = Chef::Provider::RemoteFile::FTP.fetch(@uri, nil, false, nil) ftpfile.should equal @tempfile ftpfile.close! end diff --git a/spec/unit/resource/batch_spec.rb b/spec/unit/resource/batch_spec.rb new file mode 100644 index 0000000000..91b840908e --- /dev/null +++ b/spec/unit/resource/batch_spec.rb @@ -0,0 +1,48 @@ +# +# Author:: Adam Edwards (<adamed@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, 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::Batch do + + before(:each) do + node = Chef::Node.new + + node.default["kernel"] = Hash.new + node.default["kernel"][:machine] = :x86_64.to_s + + run_context = Chef::RunContext.new(node, nil, nil) + + @resource = Chef::Resource::Batch.new("batch_unit_test", run_context) + + end + + it "should create a new Chef::Resource::Batch" do + @resource.should be_a_kind_of(Chef::Resource::Batch) + end + + context "windows script" do + let(:resource_instance) { @resource } + let(:resource_instance_name ) { @resource.command } + let(:resource_name) { :batch } + let(:interpreter_file_name) { 'cmd.exe' } + + it_should_behave_like "a Windows script resource" + end + +end diff --git a/spec/unit/resource/execute_spec.rb b/spec/unit/resource/execute_spec.rb index 0dcdab7409..8c8dcfb6ca 100644 --- a/spec/unit/resource/execute_spec.rb +++ b/spec/unit/resource/execute_spec.rb @@ -20,105 +20,7 @@ require 'spec_helper' describe Chef::Resource::Execute do - - before(:each) do - @resource = Chef::Resource::Execute.new("some command") - end - - it "should create a new Chef::Resource::Execute" do - @resource.should be_a_kind_of(Chef::Resource) - @resource.should be_a_kind_of(Chef::Resource::Execute) - end - - it "should set the command to the first argument to new" do - @resource.command.should eql("some command") - end - - it "should accept an array on instantiation, too" do - resource = Chef::Resource::Execute.new(%w{something else}) - resource.should be_a_kind_of(Chef::Resource) - resource.should be_a_kind_of(Chef::Resource::Execute) - resource.command.should eql(%w{something else}) - end - - it "should accept a string for the command to run" do - @resource.command "something" - @resource.command.should eql("something") - end - - it "should accept an array for the command to run" do - @resource.command %w{something else} - @resource.command.should eql(%w{something else}) - end - - it "should accept a string for the cwd" do - @resource.cwd "something" - @resource.cwd.should eql("something") - end - - it "should accept a hash for the environment" do - test_hash = { :one => :two } - @resource.environment(test_hash) - @resource.environment.should eql(test_hash) - end - - it "allows the environment to be specified with #env" do - @resource.should respond_to(:env) - end - - it "should accept a string for the group" do - @resource.group "something" - @resource.group.should eql("something") - end - - it "should accept an integer for the group" do - @resource.group 1 - @resource.group.should eql(1) - end - - it "should accept an array for the execution path" do - @resource.path ["woot"] - @resource.path.should eql(["woot"]) - end - - it "should accept an integer for the return code" do - @resource.returns 1 - @resource.returns.should eql(1) - end - - it "should accept an integer for the timeout" do - @resource.timeout 1 - @resource.timeout.should eql(1) - end - - it "should accept a string for the user" do - @resource.user "something" - @resource.user.should eql("something") - end - - it "should accept an integer for the user" do - @resource.user 1 - @resource.user.should eql(1) - end - - it "should accept a string for creates" do - @resource.creates "something" - @resource.creates.should eql("something") - end - - describe "when it has cwd, environment, group, path, return value, and a user" do - before do - @resource.command("grep") - @resource.cwd("/tmp/") - @resource.environment({ :one => :two }) - @resource.group("legos") - @resource.path(["/var/local/"]) - @resource.returns(1) - @resource.user("root") - end - - it "returns the command as its identity" do - @resource.identity.should == "grep" - end - end + let(:resource_instance_name) { "some command" } + let(:execute_resource) { Chef::Resource::Execute.new(resource_instance_name) } + it_behaves_like "an execute resource" end diff --git a/spec/unit/resource/powershell_spec.rb b/spec/unit/resource/powershell_spec.rb new file mode 100644 index 0000000000..22d5b5371b --- /dev/null +++ b/spec/unit/resource/powershell_spec.rb @@ -0,0 +1,48 @@ +# +# Author:: Adam Edwards (<adamed@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, 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::Powershell do + + before(:each) do + node = Chef::Node.new + + node.default["kernel"] = Hash.new + node.default["kernel"][:machine] = :x86_64.to_s + + run_context = Chef::RunContext.new(node, nil, nil) + + @resource = Chef::Resource::Powershell.new("powershell_unit_test", run_context) + + end + + it "should create a new Chef::Resource::Powershell" do + @resource.should be_a_kind_of(Chef::Resource::Powershell) + end + + context "windowsscript" do + let(:resource_instance) { @resource } + let(:resource_instance_name ) { @resource.command } + let(:resource_name) { :powershell } + let(:interpreter_file_name) { 'powershell.exe' } + + it_should_behave_like "a Windows script resource" + end + +end diff --git a/spec/unit/resource/script_spec.rb b/spec/unit/resource/script_spec.rb index 569602008b..53735daf01 100644 --- a/spec/unit/resource/script_spec.rb +++ b/spec/unit/resource/script_spec.rb @@ -20,50 +20,27 @@ require 'spec_helper' describe Chef::Resource::Script do + let(:resource_instance_name) { "fakey_fakerton" } + let(:script_resource) { Chef::Resource::Script.new(resource_instance_name) } + let(:resource_name) { :script } - before(:each) do - @resource = Chef::Resource::Script.new("fakey_fakerton") - end - - it "should create a new Chef::Resource::Script" do - @resource.should be_a_kind_of(Chef::Resource) - @resource.should be_a_kind_of(Chef::Resource::Script) - end - - it "should have a resource name of :script" do - @resource.resource_name.should eql(:script) - end - - it "should set command to the argument provided to new" do - @resource.command.should eql("fakey_fakerton") - end - - it "should accept a string for the code" do - @resource.code "hey jude" - @resource.code.should eql("hey jude") - end - it "should accept a string for the interpreter" do - @resource.interpreter "naaaaNaNaNaaNaaNaaNaa" - @resource.interpreter.should eql("naaaaNaNaNaaNaaNaaNaa") - end - - it "should accept a string for the flags" do - @resource.flags "-f" - @resource.flags.should eql("-f") + script_resource.interpreter "naaaaNaNaNaaNaaNaaNaa" + script_resource.interpreter.should eql("naaaaNaNaNaaNaaNaaNaa") end describe "when it has interpreter and flags" do before do - @resource.command("grep") - @resource.interpreter("gcc") - @resource.flags("-al") + script_resource.command("grep") + script_resource.interpreter("gcc") + script_resource.flags("-al") end - it "returns the command as its identity" do - @resource.identity.should == "grep" + it "returns the command as its identity" do + script_resource.identity.should == "grep" end end - - + + it_behaves_like "a script resource" end + diff --git a/spec/unit/user_spec.rb b/spec/unit/user_spec.rb new file mode 100644 index 0000000000..9121babeed --- /dev/null +++ b/spec/unit/user_spec.rb @@ -0,0 +1,255 @@ +# +# Author:: Steven Danna (steve@opscode.com) +# Copyright:: Copyright (c) 2012 Opscode, 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/user' +require 'tempfile' + +describe Chef::User do + before(:each) do + @user = Chef::User.new + end + + describe "initialize" do + it "should be a Chef::User" do + @user.should be_a_kind_of(Chef::User) + end + end + + describe "name" do + it "should let you set the name to a string" do + @user.name("ops_master").should == "ops_master" + end + + it "should return the current name" do + @user.name "ops_master" + @user.name.should == "ops_master" + end + + # It is not feasible to check all invalid characters. Here are a few + # that we probably care about. + it "should not accept invalid characters" do + # capital letters + lambda { @user.name "Bar" }.should raise_error(ArgumentError) + # slashes + lambda { @user.name "foo/bar" }.should raise_error(ArgumentError) + # ? + lambda { @user.name "foo?" }.should raise_error(ArgumentError) + # & + lambda { @user.name "foo&" }.should raise_error(ArgumentError) + end + + + it "should not accept spaces" do + lambda { @user.name "ops master" }.should raise_error(ArgumentError) + end + + it "should throw an ArgumentError if you feed it anything but a string" do + lambda { @user.name Hash.new }.should raise_error(ArgumentError) + end + end + + describe "admin" do + it "should let you set the admin bit" do + @user.admin(true).should == true + end + + it "should return the current admin value" do + @user.admin true + @user.admin.should == true + end + + it "should default to false" do + @user.admin.should == false + end + + it "should throw an ArgumentError if you feed it anything but true or false" do + lambda { @user.name Hash.new }.should raise_error(ArgumentError) + end + end + + describe "public_key" do + it "should let you set the public key" do + @user.public_key("super public").should == "super public" + end + + it "should return the current public key" do + @user.public_key("super public") + @user.public_key.should == "super public" + end + + it "should throw an ArgumentError if you feed it something lame" do + lambda { @user.public_key Hash.new }.should raise_error(ArgumentError) + end + end + + describe "private_key" do + it "should let you set the private key" do + @user.private_key("super private").should == "super private" + end + + it "should return the private key" do + @user.private_key("super private") + @user.private_key.should == "super private" + end + + it "should throw an ArgumentError if you feed it something lame" do + lambda { @user.private_key Hash.new }.should raise_error(ArgumentError) + end + end + + describe "when serializing to JSON" do + before(:each) do + @user.name("black") + @user.public_key("crowes") + @json = @user.to_json + end + + it "serializes as a JSON object" do + @json.should match(/^\{.+\}$/) + end + + it "includes the name value" do + @json.should include(%q{"name":"black"}) + end + + it "includes the public key value" do + @json.should include(%{"public_key":"crowes"}) + end + + it "includes the 'admin' flag" do + @json.should include(%q{"admin":false}) + end + + it "includes the private key when present" do + @user.private_key("monkeypants") + @user.to_json.should include(%q{"private_key":"monkeypants"}) + end + + it "does not include the private key if not present" do + @json.should_not include("private_key") + end + + it "includes the password if present" do + @user.password "password" + @user.to_json.should include(%q{"password":"password"}) + end + + it "does not include the password if not present" do + @json.should_not include("password") + end + end + + describe "when deserializing from JSON" do + before(:each) do + user = { "name" => "mr_spinks", + "public_key" => "turtles", + "private_key" => "pandas", + "password" => "password", + "admin" => true } + @user = Chef::User.from_json(user.to_json) + end + + it "should deserialize to a Chef::User object" do + @user.should be_a_kind_of(Chef::User) + end + + it "preserves the name" do + @user.name.should == "mr_spinks" + end + + it "preserves the public key" do + @user.public_key.should == "turtles" + end + + it "preserves the admin status" do + @user.admin.should be_true + end + + it "includes the private key if present" do + @user.private_key.should == "pandas" + end + + it "includes the password if present" do + @user.password.should == "password" + end + + end + + describe "API Interactions" do + before (:each) do + @user = Chef::User.new + @user.name "foobar" + @http_client = mock("Chef::REST mock") + Chef::REST.stub!(:new).and_return(@http_client) + end + + describe "list" do + before(:each) do + Chef::Config[:chef_server_url] = "http://www.example.com" + @osc_response = { "admin" => "http://www.example.com/users/admin"} + @ohc_response = [ { "user" => { "username" => "admin" }} ] + end + + it "lists all clients on an OSC server" do + @http_client.stub!(:get_rest).with("users").and_return(@osc_response) + Chef::User.list.should == @osc_response + end + + it "lists all clients on an OHC/OPC server" do + @http_client.stub!(:get_rest).with("users").and_return(@ohc_response) + # We expect that Chef::User.list will give a consistent response + # so OHC API responses should be transformed to OSC-style output. + Chef::User.list.should == @osc_response + end + end + + describe "create" do + it "creates a new user via the API" do + @user.password "password" + @http_client.should_receive(:post_rest).with("users", {:name => "foobar", :admin => false, :password => "password"}).and_return({}) + @user.create + end + end + + describe "read" do + it "loads a named user from the API" do + @http_client.should_receive(:get_rest).with("users/foobar").and_return({"name" => "foobar", "admin" => true, "public_key" => "pubkey"}) + user = Chef::User.load("foobar") + user.name.should == "foobar" + user.admin.should == true + user.public_key.should == "pubkey" + end + end + + describe "update" do + it "updates an existing user on via the API" do + @http_client.should_receive(:put_rest).with("users/foobar", {:name => "foobar", :admin => false}).and_return({}) + @user.update + end + end + + describe "destroy" do + it "deletes the specified user via the API" do + @http_client.should_receive(:delete_rest).with("users/foobar") + @user.destroy + end + end + end +end |